驅(qū)動(dòng)器軟件總體架構(gòu)
先來(lái)看整個(gè)機(jī)器人系統(tǒng)的整體架構(gòu),不難看出,其實(shí)驅(qū)動(dòng)器軟件的上下游是硬件外設(shè)和中間件軟件,那想要開發(fā)驅(qū)動(dòng)器軟件就需要對(duì)上下游非常熟悉嗎?不一定,但是作為一個(gè)嵌入式工程師需要懂得如何和對(duì)應(yīng)開發(fā)者進(jìn)行交流。
首先對(duì)于硬件開發(fā)者,他們會(huì)給我們提供一份原理圖,以及一般性的我們需要知道芯片的使用手冊(cè),這樣才能輔助我們?nèi)フ_的使用硬件的各個(gè)IO,避免如最低級(jí)的正負(fù)級(jí)接反等問(wèn)題;對(duì)于中間件軟件,我們則需要和他們一起制定一個(gè)協(xié)議,這個(gè)協(xié)議約定了雙方如何進(jìn)行正確的數(shù)據(jù)交互。 ? 說(shuō)到這里,大家就很明確對(duì)于此處的嵌入式開發(fā)者而言需要做什么事情了。一方面要正確的使用板卡資源和外設(shè)資源,并對(duì)其數(shù)據(jù)進(jìn)行加工處理來(lái)做一些開發(fā)者企圖實(shí)現(xiàn)的功能。另一方面要將處理的數(shù)據(jù)與上游進(jìn)行交互從而達(dá)到更高效的數(shù)據(jù)利用,當(dāng)然了必要時(shí)也需要接收來(lái)自上游數(shù)據(jù)的指令。 ? 首先對(duì)于硬件開發(fā)者,他們會(huì)給我們提供一份原理圖,以及一般性的我們需要知道芯片的使用手冊(cè),這樣才能輔助我們?nèi)フ_的使用硬件的各個(gè)IO,避免如最低級(jí)的正負(fù)級(jí)接反等問(wèn)題;對(duì)于中間件軟件,我們則需要和他們一起制定一個(gè)協(xié)議,這個(gè)協(xié)議約定了雙方如何進(jìn)行正確的數(shù)據(jù)交互。 ? 說(shuō)到這里,大家就很明確對(duì)于此處的嵌入式開發(fā)者而言需要做什么事情了。一方面要正確的使用板卡資源和外設(shè)資源,并對(duì)其數(shù)據(jù)進(jìn)行加工處理來(lái)做一些開發(fā)者企圖實(shí)現(xiàn)的功能。另一方面要將處理的數(shù)據(jù)與上游進(jìn)行交互從而達(dá)到更高效的數(shù)據(jù)利用,當(dāng)然了必要時(shí)也需要接收來(lái)自上游數(shù)據(jù)的指令。 ? 此處舉一個(gè)例子來(lái)看
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
FreeRTOS 快速上手
搭建軟件環(huán)境 以Keil為例
? ? ? ? ? ? ?
https://www.keil.com/download/product
Stm32配置keil5編譯版本(https://j7h4nezmu0.feishu.cn/docx/UsbUdQdShoxhpLxFPTvceJsmnQb)
FreeRTOS使用
RTOS全稱為 Real Time Operation System,即實(shí)時(shí)操作系統(tǒng)。RTOS強(qiáng)調(diào)的是實(shí)時(shí)性,又分為硬實(shí)時(shí)和軟實(shí)時(shí)。硬實(shí)時(shí)要求在規(guī)定的時(shí)間內(nèi)必須完成操作,不允許超時(shí);而軟實(shí)時(shí)里對(duì)處理過(guò)程超時(shí)的要求則沒(méi)有很嚴(yán)格。RTOS的核心就是任務(wù)調(diào)度。 ? FreeRTOS是RTOS的一種,尺寸非常小,可運(yùn)行于微控制器上。微控制器是尺寸小,資源受限的處理器,它在單個(gè)芯片上包含了處理器本身、用于保存要執(zhí)行的程序的只讀存儲(chǔ)器(ROM或Flash)、所執(zhí)行程序需要的隨機(jī)存取存儲(chǔ)器(RAM),一般情況下程序直接從只讀存儲(chǔ)器執(zhí)行。 ? https://j7h4nezmu0.feishu.cn/docx/PD6iduyKfo49Kux41vAcTURsnrJ#KZlsdtiUFoE3Khx0VfScpejknXA
任務(wù)創(chuàng)建 關(guān)于任務(wù)創(chuàng)建,F(xiàn)reeRTOS 提供了 API 給我們使用。格式如下
代碼實(shí)例
?
?
//Task priority //任務(wù)優(yōu)先級(jí) #define START_TASK_PRIO 1 //Task stack size //任務(wù)堆棧大小 #define START_STK_SIZE 256 //Task handle //任務(wù)句柄 TaskHandle_t StartTask_Handler; //Task function //任務(wù)函數(shù) void start_task(void *pvParameters); //Main function //主函數(shù) int main(void) { systemInit(); //Hardware initialization //硬件初始化 //Create the start task //創(chuàng)建開始任務(wù) xTaskCreate((TaskFunction_t )start_task, //Task function //任務(wù)函數(shù) (const char* )"start_task", //Task name //任務(wù)名稱 (uint16_t )START_STK_SIZE, //Task stack size //任務(wù)堆棧大小 (void* )NULL, //Arguments passed to the task function //傳遞給任務(wù)函數(shù)的參數(shù) (UBaseType_t )START_TASK_PRIO, //Task priority //任務(wù)優(yōu)先級(jí) (TaskHandle_t* )&StartTask_Handler); //Task handle //任務(wù)句柄 vTaskStartScheduler(); //Enables task scheduling //開啟任務(wù)調(diào)度 } //Start task task function //開始任務(wù)任務(wù)函數(shù) void start_task(void *pvParameters) { taskENTER_CRITICAL(); //Enter the critical area //進(jìn)入臨界區(qū) //Create the task //創(chuàng)建任務(wù) xTaskCreate(Balance_task, "Balance_task", BALANCE_STK_SIZE, NULL, BALANCE_TASK_PRIO, NULL); //Vehicle motion control task //小車運(yùn)動(dòng)控制任務(wù) xTaskCreate(MPU6050_task, "MPU6050_task", MPU6050_STK_SIZE, NULL, MPU6050_TASK_PRIO, NULL); //IMU data read task //IMU數(shù)據(jù)讀取任務(wù) xTaskCreate(show_task, "show_task", SHOW_STK_SIZE, NULL, SHOW_TASK_PRIO, NULL); //The OLED display displays tasks //OLED顯示屏顯示任務(wù) xTaskCreate(led_task, "led_task", LED_STK_SIZE, NULL, LED_TASK_PRIO, NULL); //LED light flashing task //LED燈閃爍任務(wù) xTaskCreate(pstwo_task, "PSTWO_task", PS2_STK_SIZE, NULL, PS2_TASK_PRIO, NULL); //Read the PS2 controller task //讀取PS2手柄任務(wù) xTaskCreate(data_task, "DATA_task", DATA_STK_SIZE, NULL, DATA_TASK_PRIO, NULL); //Usartx3, Usartx1 and CAN send data task //串口3、串口1、CAN發(fā)送數(shù)據(jù)任務(wù) vTaskDelete(StartTask_Handler); //Delete the start task //刪除開始任務(wù) taskEXIT_CRITICAL(); //Exit the critical section//退出臨界區(qū) }
?
?
運(yùn)行狀態(tài)
? ? ? ? ? ?
運(yùn)動(dòng)控制與PID使用
PID 原理介紹
為了更好的控制機(jī)器人行走,電機(jī)控制算法通常使用PID算法,PID(proportion integration differentiation)其實(shí)就是指比例,積分,微分控制。當(dāng)我們得到系統(tǒng)的輸出后,將輸出經(jīng)過(guò)比例,積分,微分3種運(yùn)算方式,重新疊加到輸入中,從而控制系統(tǒng)的行為,讓它能精確的到達(dá)我們指定的狀態(tài)。基本形態(tài)如下圖所示:
? 比例環(huán)節(jié)是對(duì)偏差瞬間作出反應(yīng),偏差只要產(chǎn)生,控制器立即產(chǎn)生控制作用, 使控制量向減少偏差的方向變化。
控制作用的強(qiáng)弱數(shù)值表示為誤差值與比例系數(shù)Kp的乘積,取決于比例系數(shù)Kp, 比例系數(shù)Kp越大,控制作用越強(qiáng), 則過(guò)渡過(guò)程越快, 控制過(guò)程的靜態(tài)偏差也就越小;但Kp越大,也越容易產(chǎn)生振蕩, 就會(huì)破壞系統(tǒng)的穩(wěn)定性。 ? 所以, 比例系數(shù)Kp選擇須恰當(dāng), 以期達(dá)到過(guò)渡時(shí)間少、靜態(tài)偏差小而又穩(wěn)定的效果。 ? 積分部分的表達(dá)式為誤差積分值與比例系數(shù)Ki的乘積, 從式中可看出,只要存在偏差, 則它的控制作用就不斷的增加。只有在偏差e(t)=0時(shí), 它的積分才是一個(gè)常數(shù),控制作用才是一個(gè)不會(huì)增加的常數(shù)。可見,積分部分可以消除系統(tǒng)的偏差。
積分環(huán)節(jié)的調(diào)節(jié)作用雖然會(huì)消除靜態(tài)誤差,但也會(huì)降低系統(tǒng)的響應(yīng)速度,增加系統(tǒng)的超調(diào)量。積分常數(shù)Ti越大,積分的積累作用越弱,這時(shí)系統(tǒng)在過(guò)渡時(shí)不會(huì)產(chǎn)生振蕩;但是增大積分常數(shù)Ti會(huì)減慢靜態(tài)誤差的消除過(guò)程,消除偏差所需的時(shí)間也較長(zhǎng), 但可以減少超調(diào)量,提高系統(tǒng)的穩(wěn)定性。 ? 實(shí)際的控制系統(tǒng)中除了消除靜態(tài)誤差外,還要求加快調(diào)節(jié)過(guò)程。在偏差出現(xiàn)的瞬間,或在偏差變化的瞬間, 不但要對(duì)偏差量做出立即響應(yīng)(比例環(huán)節(jié)的作用), 而且要根據(jù)偏差的變化趨勢(shì)預(yù)先適當(dāng)?shù)募m正。為了實(shí)現(xiàn)這一功能作用,須在 PI 控制器的基礎(chǔ)上加入微分環(huán)節(jié),形成 PID 控制器。 ?
? 微分環(huán)節(jié)的作用是阻止偏差的變化。它是根據(jù)偏差的變化趨勢(shì)(變化速度)進(jìn)行控制。偏差變化的越快,微分控制器的輸出就越大,并能在偏差值變大之前進(jìn)行修正。微分作用的引入, 將有助于減小超調(diào)量, 克服振蕩, 使系統(tǒng)趨于穩(wěn)定, 它加快了系統(tǒng)的跟蹤速度。 ?
大家可以通過(guò)以下案例更加直觀性的通過(guò)ROS的視角來(lái)看PID的原理和作用。 https://gitee.com/xiaobairisk/ros-pid-controller 參考鏈接:https://zhuanlan.zhihu.com/p/406496635
工程實(shí)例
在電機(jī)控制中,我們一般性的會(huì)將輪速轉(zhuǎn)變?yōu)镻WM去計(jì)算,那么如何獲取PWM以及發(fā)布目標(biāo)PWM變成了一個(gè)更加直接的問(wèn)題。 origincar_controller 中使用了PI運(yùn)算。通過(guò)獲取編碼器值(當(dāng)前PWM值),以及設(shè)定的目標(biāo)PWM值進(jìn)行PI調(diào)速。 首先需要解決當(dāng)前PWM值獲取。
?
// 獲取當(dāng)前編碼器值 //編碼器原始數(shù)據(jù)轉(zhuǎn)換為車輪速度,單位m/s MOTOR_A.Encoder= Encoder_A_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_B.Encoder= Encoder_B_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_C.Encoder= Encoder_C_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_D.Encoder= Encoder_D_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision;
?
?
對(duì)于阿克曼結(jié)構(gòu)車輛而言,該如何獲取目標(biāo)位置呢?其中需要對(duì)目標(biāo)速度進(jìn)行輪速解算,也就是計(jì)算出最終需要達(dá)到的PWM值。
?
// 結(jié)算目標(biāo)PWM值 { float R, Ratio=636.56, AngleR, Angle_Servo; //對(duì)于阿克曼小車Vz代表右前輪轉(zhuǎn)向角度 AngleR=Vz; R=Axle_spacing/tan(AngleR)-0.5f*Wheel_spacing; //前輪轉(zhuǎn)向角度限幅(舵機(jī)控制前輪轉(zhuǎn)向角度),單位:rad AngleR=target_limit_float(AngleR,-0.49f,0.32f); //運(yùn)動(dòng)學(xué)逆解 if(AngleR!=0) { MOTOR_A.Target = Vx*(R-0.5f*Wheel_spacing)/R; MOTOR_B.Target = Vx*(R+0.5f*Wheel_spacing)/R; } else { MOTOR_A.Target = Vx; MOTOR_B.Target = Vx; } //舵機(jī)PWM值,舵機(jī)控制前輪轉(zhuǎn)向角度 Angle_Servo = -0.628f*pow(AngleR, 3) + 1.269f*pow(AngleR, 2) - 1.772f*AngleR + 1.573f; Servo=SERVO_INIT + (Angle_Servo - 1.572f)*Ratio; //車輪(電機(jī))目標(biāo)速度限幅 MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=0; //沒(méi)有使用到 MOTOR_D.Target=0; //沒(méi)有使用到 Servo=target_limit_int(Servo,800,2200); //舵機(jī)PWM值限幅 }
?
?
PI計(jì)算
?
?
int Incremental_PI (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //計(jì)算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16700)Pwm=16700; if(Pwm<-16700)Pwm=-16700; Last_bias=Bias; //保存上一次偏差 return Pwm; }
?
?
人機(jī)顯示屏交互
人機(jī)交互在驅(qū)動(dòng)板中的體現(xiàn)很多,比如LED等、蜂鳴器、用戶按鍵、繼電器等,但是其中最為直觀的外設(shè)當(dāng)為OLED顯示屏。 首先介紹為什么要引入OLED顯示屏,從設(shè)計(jì)之初的考慮來(lái)看,最重要的一個(gè)原因是方便大家調(diào)試MCU代碼,如大家可以看到當(dāng)前狀態(tài)下關(guān)鍵傳感器信息,如陀螺儀等;此外也可以顯示一些控制器傳下來(lái)的信息,如WIFI0的IP地址。 接下來(lái)介紹一下OLED的主要優(yōu)點(diǎn):
OLED 顯示屏具備主動(dòng)發(fā)光的特點(diǎn),幾乎沒(méi)有視角限制,視角一般可達(dá)到 170 度,具有較寬的視角,從側(cè)面也不會(huì)失真;
OLED 顯示屏低溫特性好,在零下 40 攝氏度都能正常顯示;
OLED 顯示屏的響應(yīng)時(shí)間很快,大約是幾微秒到幾十微秒;
SSD1306驅(qū)動(dòng)芯片實(shí)例介紹 以O(shè)riginCar使用的0.96寸SSD1306 OLED顯示屏為例,以下為其原理圖
如何開發(fā)顯示屏
此處給出參考鏈接:SSD1306開發(fā)介紹(http://t.csdnimg.cn/h9lJl),更關(guān)鍵的一點(diǎn)是在目前的調(diào)試開發(fā)中,大家該如何做二次開發(fā)。 在origincar_controller工程中,已經(jīng)給大家封裝好了使用函數(shù)
?
//oled.h void OLED_WR_Byte(u8 dat,u8 cmd); void OLED_Display_On(void); void OLED_Display_Off(void); void OLED_Refresh_Gram(void); void OLED_Init(void); void OLED_Clear(void); void OLED_DrawPoint(u8 x,u8 y,u8 t); void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode); void OLED_ShowNumber(u8 x,u8 y,u32 num,u8 len,u8 size); void OLED_ShowString(u8 x,u8 y,const u8 *p);
?
?
接下來(lái)給出顯示實(shí)例
?
?
// show.c void oled_show(void) { ... //顯示屏第1行顯示內(nèi)容// OLED_ShowString(0,0,"Akm "); //阿克曼、差速、四驅(qū)、履帶車顯示陀螺儀零點(diǎn) OLED_ShowString(55,0,"BIAS"); if( Deviation_gyro[2]<0) OLED_ShowString(90,0,"-"),OLED_ShowNumber(100,0,-Deviation_gyro[2],3,12); //Zero-drift data of gyroscope Z axis else OLED_ShowString(90,0,"+"),OLED_ShowNumber(100,0, Deviation_gyro[2],3,12); //陀螺儀z軸零點(diǎn)漂移數(shù)據(jù) ... OLED_Refresh_Gram(); }
?
?
實(shí)際開發(fā)中大家需要在類似本例中oled_show中使用OLED_ShowString/OLED_ShowNumber即可,注意在最后需要刷新一次OLED內(nèi)容OLED_Refresh_Gram;
串口通信
串口通訊(Serial Communication)是一種設(shè)備間非常常用的串行通訊方式,因?yàn)樗?jiǎn)單便捷,因此大部分電子設(shè)備都支持該通訊方式,其通訊協(xié)議可分層為協(xié)議層和物理層。物理層規(guī)定通信協(xié)議中具有機(jī)械、電子功能的特性,從而確保原始數(shù)據(jù)在物理媒體的傳播;協(xié)議層主要規(guī)定通訊邏輯,統(tǒng)一雙方的數(shù)據(jù)打包、解包標(biāo)準(zhǔn)。 ? 物理層 RS232是一種串行數(shù)據(jù)傳輸形式,稱其為串行連接,最經(jīng)典的標(biāo)志就是 9 針孔的 DB9 電纜RS232電壓表示邏輯 1 ,0的范圍大極大的增強(qiáng)了容錯(cuò)率,主要用于工業(yè)設(shè)備直接通信。 ? 兩個(gè)通訊設(shè)備的“DB9 接口”之間通過(guò)串口信號(hào)線建立起連接,串口信號(hào)線中使用“RS-232 標(biāo)準(zhǔn)”傳輸數(shù)據(jù)信號(hào)。由于 RS-232 電平標(biāo)準(zhǔn)的信號(hào)不能直接被控制器直接識(shí)別,所以這些信號(hào)會(huì)經(jīng)過(guò)一個(gè)“電平轉(zhuǎn)換芯片”轉(zhuǎn)換成控制器能識(shí)別的“TTL 標(biāo)準(zhǔn)”的電平信號(hào),才能實(shí)現(xiàn)通訊。
USB轉(zhuǎn)串口:主要用于設(shè)備(如STM32)與其他設(shè)備通信。 ? 電平轉(zhuǎn)換芯片一般有CH340、PL2303、CP2102、FT232,使用的時(shí)候電腦要按照電平轉(zhuǎn)換芯片的驅(qū)動(dòng)(虛擬出一個(gè)串口)
? ? ? ? 協(xié)議層 串口通訊的協(xié)議層中,規(guī)定了數(shù)據(jù)包的內(nèi)容,它由啟始位、主體數(shù)據(jù)、校驗(yàn)位以及停止位組成,通訊雙方的數(shù)據(jù)包格式要約定一致(一樣的起始位 數(shù)據(jù) 校驗(yàn)位 停止位)才能正常收發(fā)數(shù)據(jù)
通訊的起始和停止信號(hào)
串口通訊的一個(gè)數(shù)據(jù)包從起始信號(hào)開始,直到停止信號(hào)結(jié)束。數(shù)據(jù)包的起始信號(hào)由一個(gè)邏輯 0 的數(shù)據(jù)位表示,而數(shù)據(jù)包的停止信號(hào)可由 1 或 2 個(gè)邏輯 1 的數(shù)據(jù)位表示 1個(gè)停止位:停止位位數(shù)的默認(rèn)值。 2個(gè)停止位:可用于常規(guī)USART模式、單線模式以及調(diào)制解調(diào)器模式。
有效數(shù)據(jù)
在數(shù)據(jù)包的起始位之后緊接著的就是要傳輸?shù)闹黧w數(shù)據(jù)內(nèi)容,也稱為有效數(shù)據(jù),有效數(shù)據(jù)的長(zhǎng)度常被約定為 5、6、7 或 8 位長(zhǎng)
數(shù)據(jù)校驗(yàn)
偶校驗(yàn):校驗(yàn)位使得一幀中的7或8個(gè)LSB數(shù)據(jù)以及校驗(yàn)位中1的個(gè)數(shù)為偶數(shù)。 例如:數(shù)據(jù)=00110101,有4個(gè)1,如果選擇偶校驗(yàn)(在USART_CR1中的PS=0),校驗(yàn)位將是0,最后數(shù)據(jù)檢驗(yàn)如果數(shù)據(jù)有偶數(shù)個(gè)1則數(shù)據(jù)傳輸沒(méi)有出錯(cuò)(但不是絕對(duì)的,如果同時(shí)兩個(gè)數(shù)據(jù)為發(fā)送錯(cuò)誤(0變成1)則還是偶數(shù)個(gè)1) 奇校驗(yàn):此校驗(yàn)位使得一幀中的7或8個(gè)LSB數(shù)據(jù)以及校驗(yàn)位中1的個(gè)數(shù)為奇數(shù)。例如:數(shù)據(jù)=00110101,有4個(gè)1,如果選擇奇校驗(yàn)(在USART_CR1中的PS=1),校驗(yàn)位將是1,最后數(shù)據(jù)檢驗(yàn)如果數(shù)據(jù)有奇數(shù)個(gè)1則數(shù)據(jù)傳輸沒(méi)有出錯(cuò),但同樣不是絕對(duì)的(同時(shí)兩個(gè)1變成0) 實(shí)例說(shuō)明 以origincar_controller為例,使用USB轉(zhuǎn)串口的方式,將OriginCar主板與電腦PC連接,并在電腦端打開串口助手,即可接收到來(lái)自O(shè)riginCar主板的如下數(shù)據(jù),關(guān)于數(shù)據(jù)解析待下節(jié)分享。
此處大家應(yīng)該思考一個(gè)問(wèn)題,數(shù)據(jù)是如何發(fā)出來(lái)的?
?
?
void data_task(void *pvParameters) { u32 lastWakeTime = getSysTickCnt(); while(1) { vTaskDelayUntil(&lastWakeTime, F2T(RATE_20_HZ)); data_transition(); USART1_SEND(); //串口1發(fā)送數(shù)據(jù) USART3_SEND(); //串口3(ROS)發(fā)送數(shù)據(jù) USART5_SEND(); //串口5發(fā)送數(shù)據(jù) CAN_SEND(); //CAN發(fā)送數(shù)據(jù) } }
?
?
首先如上所示,OriginCar主板基于 FreeRTOS進(jìn)行周期性的數(shù)據(jù)發(fā)送,即data_task 函數(shù),接下來(lái)以USART3_SEND為例看數(shù)據(jù)如何發(fā)送。
?
void USART3_SEND(void) { unsigned char i = 0; for(i=0; i<24; i++) { usart3_send(Send_Data.buffer[i]); } }
?
?
如上,此處只是將Send_Data的數(shù)據(jù)給了usart3_send函數(shù),那么Send_Data數(shù)據(jù)是什么呢?其實(shí)就是協(xié)議層封裝數(shù)據(jù)啦。再看usart3_send
?
void usart3_send(u8 data) { USART3->DR = data; while((USART3->SR&0x40)==0); }
?
?
到此處,大家直接思考的問(wèn)題就變成了USART3是什么?USART3->DR、USART3->SR是什么? 此處其實(shí)使用到了STM32固件庫(kù)編程方式,大家可以通過(guò)查看固件庫(kù)手冊(cè)來(lái)對(duì)寄存器進(jìn)行操作。
狀態(tài)寄存器:USARTX->SR 其用于描述USART的工作狀態(tài),為編程者提供一個(gè)串口的實(shí)時(shí)狀態(tài),一般而言,發(fā)送時(shí)需要判斷上一幀有沒(méi)有發(fā)送完畢;接收時(shí)需要判斷一幀數(shù)據(jù)有沒(méi)有接收完畢,二者需要一個(gè)標(biāo)志位進(jìn)行狀態(tài)表示,這其中的標(biāo)志就在此寄存器中。 數(shù)據(jù)寄存器:USARTX->DR 發(fā)送和接收雖然是兩個(gè)動(dòng)作,但是在單片機(jī)內(nèi)部是一個(gè)數(shù)據(jù)寄存器,這兩個(gè)操作的唯一區(qū)別方法就是,執(zhí)行寫操作就是發(fā)送數(shù)據(jù)寄存器(TDR),執(zhí)行讀操作的時(shí)候就是接受數(shù)據(jù)寄存器(RDR)。這也就解釋了為什么上面的代碼中,讀和寫都是使用的DR寄存器。 ?
審核編輯:黃飛
?
評(píng)論
查看更多