当前位置:网站首页>Stm32 dossiers d'apprentissage: saisie des applications
Stm32 dossiers d'apprentissage: saisie des applications
2022-07-06 15:23:00 【Bitter tea Seeds】
Table des matières
Un.、Application de la capture d'entrée
1.1、Mesure de la largeur ou de la fréquence des impulsions
1.2、Méthode par étapes pour mesurer la fréquence
1.3、 Méthode de mesure de la largeur des impulsions
2.、 Saisissez le processus de travail de capture
2.1、CH1Par exemple,Saisissez le processus de travail de capture
2.3、 Filtre d'entrée et détection de bord
Trois、Comparaison des résultats
3.1、 Le rôle de la comparaison des résultats
4.1、Capture/Registre des modes de comparaison :TIMx_CCMR1
4.2、Capture/Comparer les registres habilitants:TIMx_CCER
4.3、DMA/Registre d'activation d'interruption:TIMx_DIER
Préface
Introduction de l'utilisation d'un minuteur universel comme saisie d'entrée .Avec TIM5 L'accès à 1(PA0)Pour la capture d'entrée,Capture PA0 Largeur de l'impulsion au niveau supérieur(Avec WK_UP Appuyez sur la touche pour entrer le haut niveau),Imprimer le temps de largeur d'impulsion de haut niveau via le port série.
Un.、Application de la capture d'entrée
La saisie des entrées s'applique généralement de deux façons ,Un aspect est Mesure du temps le long du saut d'impulsion ,D'autre part, PWM Mesure d'entrée.
Le mode de capture d'entrée peut être utilisé pour mesurer la largeur de l'impulsion ou la fréquence.STM32 Le minuteur de,Sauf que TIM6 Et TIM7, D'autres minuteurs ont une fonction de capture d'entrée.
STM32 Saisie des entrées pour, C'est en testant TIMx_CHx Signal de bord sur,Saut du signal de bord(Comme le bord ascendant/Bord de descente)Quand,Valeur du minuteur actuel(TIMx_CNT) Capture stockée dans le canal correspondant/Comparer les registres(TIMx_CCRx)À l'intérieur,Terminer une capture.Vous pouvez également configurer si l'interruption est déclenchée lors de la capture/DMA .
Oui. TIM5_CH1 Pour capter la largeur d'impulsion de haut niveau,C'est - à - dire qu'il faut d'abord définir la capture d'entrée comme détection de bord ascendant,Enregistrer quand le bord ascendant se produit TIM5_CNT Valeur de.Le signal de capture est ensuite configuré pour la capture du bord de descente,Quand le bord de descente arrive,Capture survenue,Et enregistrez ceci TIM5_CNT Valeur.Voilà.,Avant et arrière deux fois TIM5_CNT Différence,Est la largeur de l'impulsion de haut niveau, En même temps TIM5 Nous connaissons la fréquence de comptage,On peut ainsi calculer le temps exact de la largeur de l'impulsion de haut niveau.
1.1、Mesure de la largeur ou de la fréquence des impulsions
1.2、Méthode par étapes pour mesurer la fréquence
Lorsque le canal est capturé TIx Lorsque le bord ascendant apparaît sur,Première capture,Compteur CNT La valeur de est verrouillée dans le registre de capture CCR Moyenne,Et il y a une interruption de capture,Enregistrer une capture dans le programme de service d'interruption(Peut être enregistré avec une variable de drapeau),Et lire la valeur dans le registre de capture à value1 Moyenne.Lorsque le deuxième bord ascendant apparaît,Deuxième capture,Compteur CNT La valeur de est verrouillée à nouveau dans le registre de capture CCR Moyenne,Et entrer à nouveau dans l'interruption de capture,Dans l'interruption de capture,Lire la valeur du registre de capture à value3 Moyenne,Et effacer les drapeaux d'enregistrement de capture.Utilisation value3 Et value1 Nous pouvons calculer la période du signal(Fréquence).
1.3、 Méthode de mesure de la largeur des impulsions
Lorsque le canal est capturé TIx Lorsque le bord ascendant apparaît sur,Première capture,Compteur CNT La valeur de est verrouillée dans le registre de capture CCR Moyenne,Et il y a une interruption de capture,Enregistrer une capture dans le programme de service d'interruption(Peut être enregistré avec une variable de drapeau),Et lire la valeur dans le registre de capture à value1 Moyenne.Puis changez le bord de capture en bord de descente,Le but est de capturer le bord de descente arrière.Quand le bord de descente arrive,Deuxième capture,Compteur CNT La valeur de est verrouillée à nouveau dans le registre de capture CCR Moyenne,Et entrer à nouveau dans l'interruption de capture,Dans l'interruption de capture,Lire la valeur du registre de capture à value3 Moyenne,Et effacer les drapeaux d'enregistrement de capture.Puis réglez le bord de capture à la capture de bord ascendant. La polarité du bord de capture doit être commutée d'avant en arrière lors de la mesure de la largeur de l'impulsion,Si la largeur d'impulsion mesurée est plus longue,Le minuteur déborde,Une interruption de mise à jour se produit en cas de débordement,Nous pouvons enregistrer le débordement à l'intérieur de l'interruption.
2.、 Saisissez le processus de travail de capture
2.1、CH1Par exemple,Saisissez le processus de travail de capture
2.2、Canal d'entrée
Lorsque le signal à mesurer est utilisé à partir de la broche externe du minuteur TIMx_CH1/2/3/4 Entrée,Ça s'appelle TI1/2/3/4, Dans les captures ultérieures, les signaux à mesurer sont tous TIx Pour l'appellation standard .
2.3、 Filtre d'entrée et détection de bord
2.4、Canal de capture
2.5、Pré - diviseur
1、ICx Le signal de sortie passe par un pré - diviseur,Utilisé pour déterminer le nombre d'événements à capturer en même temps.
2、 Spécifique par registre CCMRx Position ICxPSC Configuration, Si vous voulez capturer chaque bord du signal ,Pas de division de fréquence.
Trois、Comparaison des résultats
3.1、 Le rôle de la comparaison des résultats
Quatre、Registre de capture
Les registres nécessaires sont::TIMx_ARR、 TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1 .
4.1、Capture/Registre des modes de comparaison :TIMx_CCMR1
Lorsqu'il est utilisé en mode capture d'entrée , Correspond à la description de la deuxième ligne ,Comme le montre la figure,TIMx_CCMR1 C'est clairement pour 2 Configuration des canaux ,8 bits inférieurs[7:0]Pour la capture/Comparer les canaux 1 Contrôle,Et Gao huit Bits[15:8]Pour capturer/Comparer les canaux 2 Contrôle.
Parmi eux CC1S[1:0],Ces deux bits sont utilisés pour CCR1 Configuration du canal pour,Ici, nous mettons en place IC1S[1:0]=01, Ça correspond. Position IC1 Mapping in TI1 Allez.;
Saisie des entrées 1 Pré - diviseur IC1PSC[1:0];Parce que1 Le bord secondaire est déclenché 1 Capture secondaire,Alors choisissez 00 .
Saisie des entrées 1 Filtre IC1F[3:0],Ceci est utilisé pour définir la fréquence d'échantillonnage d'entrée et la longueur du filtre numérique.Parmi eux Est la fréquence d'entrée du minuteur(TIMxCLK),En général 72Mhz.Et Est basé sur TIMx_CR1 De CKD[1:0] Pour déterminer,Si CKD[1:0]Set to 00,Alors .N La valeur est la longueur du filtre,Un exemple simple:Hypothèses IC1F[3:0]=0011,Et la mise en place IC1 Mapping to Channel 1 Allez.,Et déclenché pour le bord ascendant,Donc quand vous capturez le bord ascendant,,Encore une foisFréquence de,Échantillonnage continu à 8 Canal secondaire 1 Niveau,Si tout est haut,Est un déclencheur valide,Déclenche une interruption de capture d'entrée(Si elle est allumée).Cela élimine les impulsions de haut niveau dont la largeur est inférieure à 8 Signaux d'impulsions pour les cycles d'échantillonnage,Pour obtenir l'effet de filtrage.
4.2、Capture/Comparer les registres habilitants:TIMx_CCER
Le plus bas pour utiliser ce registre 2 Bits,CC1E Et CC1P Bits.
Pour activer la capture d'entrée,Vous devez définir CC1E=0,Et CC1P Configurer en fonction de vos besoins.
4.3、DMA/Registre d'activation d'interruption:TIMx_DIER
Une interruption est nécessaire pour traiter les données saisies ,Donc le canal doit être ouvert 1 Dans la comparaison de capture pour Cassé,C'est - à - dire: CC1IE Set to 1.
Cinq、Programmation
5.1、Étapes de configuration
① Le minuteur d'initialisation correspond au canal IOL'horloge de.
② InitialisationIOBouche,Mode comme entrée:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 Entrée
③Initialisation du minuteurARR,PSC
TIM_TimeBaseInit();
④ Initialisation du canal de capture d'entrée
TIM_ICInit();
⑤ Si vous voulez activer l'interruption de capture ,
TIM_ITConfig();
NVIC_Init();
⑥Activer le minuteur:TIM_Cmd();
⑦Écrire une fonction de service d'interruption:TIMx_IRQHandler();
5.2、Programmation
time.cLes codes sont les suivants:
#include "timer.h"
#include "led.h"
#include "usart.h"
/*Initialisation universelle des interruptions de minuterie
Ici, l'horloge est sélectionnée commeAPB1De2X,EtAPB1Pour36M
arr:Valeur de réassemblage automatique.
psc:Fréquence de pré - Division de l'horloge
Le minuteur est utilisé ici3!*/
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //Horloge activée
TIM_TimeBaseStructure.TIM_Period = arr; //Définit la valeur du cycle du registre de recharge automatique pour l'activité de recharge de l'événement de mise à jour suivant Compte à5000Pour500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //Le réglage est utilisé commeTIMxValeur de fréquence pré - fractionnée du diviseur de fréquence d'horloge 10KhzFréquence de comptage de
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //Définir la Division de l'horloge:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIMMode de comptage ascendant
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //SelonTIM_TimeBaseInitStructInitialisation des paramètres spécifiés dansTIMxL'Unit é de base temporelle de
TIM_ITConfig( //Activer ou désactiver la désignationTIMInterruption
TIM3, //TIM2
TIM_IT_Update | //TIM Source d'interruption
TIM_IT_Trigger, //TIM Source d'interruption de déclenchement
ENABLE //Activer
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3Interruption
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //Priorité0Niveau
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //De la priorité3Niveau
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQCanal activé
NVIC_Init(&NVIC_InitStructure); //SelonNVIC_InitStructParamètres spécifiés dans le périphérique d'initialisationNVICRegistres
TIM_Cmd(TIM3, ENABLE); //ActiverTIMxPériphérique
}
void TIM3_IRQHandler(void) //TIM3Interruption
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //Vérifier la désignationTIMInterruption survenue ou non:TIM Source d'interruption
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //EffacerTIMxBit d'interruption en attente pour:TIM Source d'interruption
LED1=!LED1;
}
}
/*PWMInitialisation de la sortie
arr:Valeur de réassemblage automatique
psc:Fréquence de pré - Division de l'horloge*/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //ActiverGPIOPériphériques etAFIOL'horloge du module de fonction multiplexage permet
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3Remap partiel TIM3_CH2->PB5 //PourTIM3DeCH2SortiePWMParLEDAfficher
//Définissez Cette broche comme une fonction de sortie multiplex,ProduitsTIM3 CH2DePWMForme d'onde pulsée
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //Multiplexage Push - Pull Output
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//GPIO_WriteBit(GPIOA, GPIO_Pin_7,Bit_SET); // PA7Tirez
TIM_TimeBaseStructure.TIM_Period = arr; //Définit la valeur du cycle du registre de recharge automatique pour l'activité de recharge de l'événement de mise à jour suivant 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //Le réglage est utilisé commeTIMxValeur de fréquence pré - fractionnée du diviseur de fréquence d'horloge Pas de division de fréquence
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //Définir la Division de l'horloge:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIMMode de comptage ascendant
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //SelonTIM_TimeBaseInitStructInitialisation des paramètres spécifiés dansTIMxL'Unit é de base temporelle de
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //Sélectionnez le mode minuteur:TIMMode de modulation de la largeur des impulsions2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //Comparer la sortie permet
TIM_OCInitStructure.TIM_Pulse = 0; //Définir la valeur d'impulsion du registre de comparaison de capture à charger
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //Polarité de sortie:TIMLa sortie est relativement polaire
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //SelonTIM_OCInitStructParamètres spécifiés dans le périphérique d'initialisationTIMx
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //ActiverTIMxInCCR2Registre préchargé sur
TIM_ARRPreloadConfig(TIM3, ENABLE); //ActiverTIMxInARRRegistre préchargé sur
TIM_Cmd(TIM3, ENABLE); //ActiverTIMxPériphérique
}
//Minuterie5Accès1Configuration de capture d'entrée
TIM_ICInitTypeDef TIM5_ICInitStructure;
void TIM5_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //ActiverTIM5Horloge
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //ActiverGPIOAHorloge
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 Paramètres avant purge
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 Entrée
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 Descendez.
//Initialisation du minuteur5 TIM5
TIM_TimeBaseStructure.TIM_Period = arr; //Régler la valeur de réassemblage automatique du compteur
TIM_TimeBaseStructure.TIM_Prescaler =psc; //Pré - diviseur
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //Définir la Division de l'horloge:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIMMode de comptage ascendant
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //SelonTIM_TimeBaseInitStructInitialisation des paramètres spécifiés dansTIMxL'Unit é de base temporelle de
//InitialisationTIM5Saisissez les paramètres de capture
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 Sélectionner l'entrée IC1Mapping toTI1Allez.
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //Capture de bord ascendant
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //Mapping toTI1Allez.
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //Configuration de la Division des entrées,Pas de division de fréquence
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 Configurer le filtre d'entrée Non filtré
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//Initialisation du Groupe d'interruption
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM3Interruption
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //Priorité2Niveau
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //De la priorité0Niveau
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQCanal activé
NVIC_Init(&NVIC_InitStructure); //SelonNVIC_InitStructParamètres spécifiés dans le périphérique d'initialisationNVICRegistres
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//Autoriser les interruptions de mise à jour ,AllowCC1IECapture interrompue
TIM_Cmd(TIM5,ENABLE ); //Activer le minuteur5
}
u8 TIM5CH1_CAPTURE_STA=0; //Saisissez l'état de capture
u16 TIM5CH1_CAPTURE_VAL; //Saisissez la valeur de capture
//Minuterie5Interruption du programme de service
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//Pas encore capturé avec succès
{
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X40)//On a un niveau élevé.
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//Le haut niveau est trop long.
{
TIM5CH1_CAPTURE_STA|=0X80;//L'étiquette a été capturée avec succès une fois
TIM5CH1_CAPTURE_VAL=0XFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//Capture1Un événement de capture s'est produit
{
if(TIM5CH1_CAPTURE_STA&0X40) //Capture d'un bord de descente
{
TIM5CH1_CAPTURE_STA|=0X80; //Le marqueur a réussi à capturer un bord ascendant
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 Définir comme capture de bord ascendant
}else //Pas encore commencé,Première prise de bord ascendant
{
TIM5CH1_CAPTURE_STA=0; //Vide.
TIM5CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM5,0);
TIM5CH1_CAPTURE_STA|=0X40; //Le marqueur a attrapé le bord ascendant
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 Set to drop Edge capture
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //Effacer le drapeau d'interruption
}
timer.hLa procédure est la suivante::
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
void TIM5_Cap_Init(u16 arr,u16 psc);
#endif
main.cLes codes sont les suivants::
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
extern u8 TIM5CH1_CAPTURE_STA; //Saisissez l'état de capture
extern u16 TIM5CH1_CAPTURE_VAL; //Saisissez la valeur de capture
int main(void)
{
u32 temp=0;
delay_init(); //Initialisation de la fonction de retard
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //ParamètresNVICGroupe d'interruption2:2Priorité de préemption des bits,2Priorité de réponse bit
uart_init(115200); //Le port série est initialisé comme115200
LED_Init(); //LEDInitialisation du port
TIM3_PWM_Init(899,0); //Pas de division de fréquence.PWMFréquence=72000/(899+1)=80Khz
TIM5_Cap_Init(0XFFFF,72-1); //Par1MhzNombre de fréquences pour
while(1)
{
delay_ms(10);
TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);
if(TIM_GetCapture2(TIM3)==300)TIM_SetCompare2(TIM3,0);
if(TIM5CH1_CAPTURE_STA&0X80)//Un bord ascendant a été capturé avec succès
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=65536;//Temps de débordement total
temp+=TIM5CH1_CAPTURE_VAL;//Obtenir le temps total de haut niveau
printf("HIGH:%d us\r\n",temp);//Imprimer le temps total de nivellement des points élevés
TIM5CH1_CAPTURE_STA=0;//Activer la prochaine capture
}
}
}
边栏推荐
- Pedestrian re identification (Reid) - Overview
- Example 071 simulates a vending machine, designs a program of the vending machine, runs the program, prompts the user, enters the options to be selected, and prompts the selected content after the use
- LeetCode#412. Fizz Buzz
- 学习记录:TIM—电容按键检测
- What to do when programmers don't modify bugs? I teach you
- ucore lab7
- LeetCode#19. Delete the penultimate node of the linked list
- Video scrolling subtitle addition, easy to make with this technique
- Introduction to variable parameters
- 软件测试面试回答技巧
猜你喜欢
想跳槽?面试软件测试需要掌握的7个技能你知道吗
几款开源自动化测试框架优缺点对比你知道吗?
Dlib detects blink times based on video stream
MySQL development - advanced query - take a good look at how it suits you
Unpleasant error typeerror: cannot perform 'ROR_‘ with a dtyped [float64] array and scalar of type [bool]
Want to change jobs? Do you know the seven skills you need to master in the interview software test
Eigen User Guide (Introduction)
Intensive learning notes: Sutton book Chapter III exercise explanation (ex17~ex29)
转行软件测试必需要知道的知识
FSM和i2c实验报告
随机推荐
Crawling cat's eye movie review, data visualization analysis source code operation instructions
Preface to the foundations of Hilbert geometry
Should wildcard import be avoided- Should wildcard import be avoided?
MySQL development - advanced query - take a good look at how it suits you
[200 opencv routines] 98 Statistical sorting filter
CSAPP Shell Lab 实验报告
How to solve the poor sound quality of Vos?
Want to change jobs? Do you know the seven skills you need to master in the interview software test
LeetCode#36. Effective Sudoku
ucore lab7
Programmers, how to avoid invalid meetings?
How to build a nail robot that can automatically reply
Mysql database (I)
ucore Lab 1 系统软件启动过程
What is "test paper test" in software testing requirements analysis
ucore lab7 同步互斥 实验报告
ucorelab4
MySQL数据库(三)高级数据查询语句
In Oracle, start with connect by prior recursive query is used to query multi-level subordinate employees.
How to do agile testing in automated testing?