串口是單片機(jī)最常用的通訊方式之一,關(guān)于串口的介紹在RT-thread官網(wǎng)上已經(jīng)有非常詳細(xì)的介紹了,我這里就不多講了,今天主要講一講官網(wǎng)上沒有的東西,讓你更加深入的了解RT-thread是如何配置和使用串口進(jìn)行數(shù)據(jù)收發(fā)的。
RT-thread官網(wǎng)上關(guān)于UART的資料:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/uart
一、配置底層引腳
1、確定串口引腳
先從原理圖查看自己需要使用的串口號及對應(yīng)的引腳。比如我要使用串口3,引腳是PB10和PB11(USART3除了這一組引腳還有PC10、PC11)
2、配置底層引腳
1)配置串口
打開keil工程,使用STM32CubeMX配置底層接口(路徑一般在工程目錄下boardCubeMX_Config文件夾里面)
不懂的同學(xué)可以看下我之前的博客:RT-Thread零基礎(chǔ)快速入門第1講——新建工程
先打開對應(yīng)的串口(Mode選擇Asynchronous即可),然后檢查GPIO是否和原理圖一致。
如果不一致的話可以在右邊的芯片圖上面找到正確的引腳,然點(diǎn)擊引腳,修改該引腳為串口功能,最后再回到上一步,檢查一下串口是否已經(jīng)修改成正確的引腳了。
提示:使用STM32CubeMX配置串口只需要打開串口和配置引腳,其他的像波特率、NVIC這些都需要管,因?yàn)檫@些都是在RTT應(yīng)用層初始化的時(shí)候配置的,即使在這里改了也是不起作用的。
2)生成新的工程
點(diǎn)擊右上角的GENERATE CODE,生成新工程即可。
注:如果生成工程時(shí)提示你是否需要下載新版本的固件庫,可以下載也可以繼續(xù)使用舊的,一般都是沒問題的。
二、編寫應(yīng)用層代碼
1、打開串口使能
用env打開工程,進(jìn)入menuconfig配置頁面,打開串口使能。
提示:這里的使能和上面STM32CubeMX打開串口是不一樣的,env配置使能之后實(shí)際上是打開一個(gè)宏定義,打開之后才能調(diào)用RTT串口相關(guān)的庫函數(shù),而STM32CubeMX使能則是把HAL庫串口相關(guān)的函數(shù)加進(jìn)來。
如果你不知道env怎么使用,可以在下面這個(gè)鏈接查看。
env使用方法:https://www.rt-thread.org/document/site/programming-manual/env/env/#bsp-menuconfig
配置好之后,退出保存即可。
注意:如果menuconfig里面沒有你要使用的串口號怎么辦,這個(gè)問題請?zhí)胶竺娌榭?“常見問題解答”。
2、重新生成工程
在env輸入下面的命令,重新生成新的工程。
提示:會(huì)使用env的話應(yīng)該都知道這個(gè)操作。
scons --target=mdk5
3、編寫串口收發(fā)代碼
串口收發(fā)的示例代碼可以在RT-thread官網(wǎng)查看,有中斷接收的示例,也有DMA接收的示例,都是寫的很詳細(xì)的了,拷貝過來改一下就可以用了。
你也可以在menuconfig里面打開串口接收的示例。
打開示例之后要重新生成工程,也就是上面第2步的操作。
建議:如果只是測試串口功能的話可以直接添加示例文件,但是實(shí)際應(yīng)用中還是建議新建一個(gè)文件,然后編寫串口的應(yīng)用代碼,再把這個(gè)文件加入到keil工程里面。
參考:RT-Thread零基礎(chǔ)快速入門第2講——添加新文件到工程
不管用哪種方法,示例代碼添加進(jìn)來之后還是要按照自己的需求修改配置,比如串口號,波特率,串口接收的數(shù)據(jù)處理等。
我這里簡單的舉個(gè)例子,具體怎么改還是要看你的功能需求。
以中斷接收的demo為例:
1)修改串口號
直接修改SAMPLE_UART_NAME這個(gè)宏即可,比如我這里用的是串口3,就改成"uart3"
#define SAMPLE_UART_NAME "uart3" /* 串口設(shè)備名稱 */
2)修改波特率
在查找串口(rt_device_find)之后,打開串口(rt_device_open)之前添加下面這段代碼即可。
/* 修改串口配置參數(shù) */
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; // 初始化配置參數(shù)
config.baud_rate = BAUD_RATE_9600; //修改波特率為 9600
config.data_bits = DATA_BITS_8; //數(shù)據(jù)位 8
config.stop_bits = STOP_BITS_1; //停止位 1
config.bufsz = 128; //修改緩沖區(qū) buff size 為 128
config.parity = PARITY_NONE; //無奇偶校驗(yàn)位
/* 控制串口設(shè)備。通過控制接口傳入命令控制字,與控制參數(shù) */
rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
如下圖所示:
3)串口數(shù)據(jù)接收處理
如果數(shù)據(jù)不多處理不復(fù)雜,可以直接在接收數(shù)據(jù)的線程進(jìn)行數(shù)據(jù)處理,如下圖所示:
如果數(shù)據(jù)比較多或者處理比較復(fù)雜需要消耗的時(shí)間比較長的情況下,就不太建議在接收的線程進(jìn)行數(shù)據(jù)處理了,因?yàn)樵谶@里處理數(shù)據(jù)會(huì)影響后續(xù)的數(shù)據(jù)接收。
推薦幾種常用的解決方法:
1:使用消息隊(duì)列把接收到的數(shù)據(jù)傳輸到其他線程,在其他的線程做統(tǒng)一的數(shù)據(jù)處理。
2:如果接收是以中斷的方式,可以考慮在接收的時(shí)候就做先做簡單的處理,比如用一個(gè)switch語句,每接收一個(gè)數(shù)據(jù)就處理一次,這樣就可以把每個(gè)字節(jié)接收的間隙時(shí)間利用起來。這個(gè)方法也可以用來判斷數(shù)據(jù)幀是否完整接收完成,是否有錯(cuò)誤的數(shù)據(jù)幀。
數(shù)據(jù)處理的方法有很多,各有優(yōu)劣,具體用哪種方式則要看實(shí)際的情況,適合的方法才是最好的。
利用switch語句處理接收數(shù)據(jù)的示例代碼:
switch(count)
{
case 0:{
if(ch == 0xFC){ // 幀頭
uart_rx_buffer[i++] = ch;
count ++;
}
else{
i = 0;
count = 0;
}
break;
}
case 1:{ // 地址
if(ch == 1 || ch == 2){
uart_rx_buffer[i++] = ch;
count ++;
}
else{
i = 0;
count = 0;
}
break;
}
case 2:{ // 命令
if(ch == 0xD4){
uart_rx_buffer[i++] = ch;
count ++;
}
else{
i = 0;
count = 0;
}
break;
}
case 3:{
uart_rx_buffer[i++] = ch; // 數(shù)據(jù)高位
count ++;
break;
}
case 4:{
uart_rx_buffer[i++] = ch; // 數(shù)據(jù)高位
count ++;
break;
}
case 5:{
uart_rx_buffer[i++] = ch; // 校驗(yàn)
i = 0;
count = 0;
if(verify_check(uart_rx_buffer, 5) == uart_rx_buffer[5]) // 檢查校驗(yàn)
{
rt_sem_release(rs485_timeout_sem);
uart_485_data_handle(uart_rx_buffer);
}
break;
}
default:
i = 0;
count = 0;
break;
}
三、進(jìn)階學(xué)習(xí)
1、串口的硬件小知識
類型 | 說明 |
---|---|
TTL | 電壓范圍0V至+5V,高電平>2.4V,低電平<0.4V,一般MCU直接輸出的串口就是TTL電平的 |
RS232 | 電壓范圍-15V至+15V,高電平為-3V至-15V,低電平為+3V至+15V |
RS485 | 電壓范圍-7V至+12V,不同于TTL和RS232,RS485采用的差分信號負(fù)邏輯,高電平為兩線間的電壓差-2V至-6V,低電平為兩線間的電壓差+2V至+6V |
USB | 有單獨(dú)的協(xié)議,與TTL、RS232和RS485都不同 |
三種電平之間不能直接通信,需要通過轉(zhuǎn)換芯片轉(zhuǎn)換成相同的電平。除了這三種電平以外,我們常常還用到USB,USB有自己的協(xié)議(2.0、3.0、3.1等),USB和TTL、RS232和RS485這些電平之間也是不能直接通信的,也需要轉(zhuǎn)換。一般單片機(jī)或者其他MCU的串口電平都是TTL的,而PC端一般使用USB。
舉個(gè)例子:
如果MCU的串口需要連接到PC端,那么就需要實(shí)現(xiàn)TTL和USB直接的轉(zhuǎn)換,常用的方法如下:
1、通過一個(gè)USB轉(zhuǎn)TTL的芯片轉(zhuǎn)換,可以放在主板上,也可以用那種轉(zhuǎn)換小板,這個(gè)大家應(yīng)該比較熟悉了,某寶上面隨處可見。
2、通過一個(gè)TTL轉(zhuǎn)232的芯片轉(zhuǎn)換,然后再接一根USB轉(zhuǎn)232的連接線,這個(gè)線常用DB9接口。
3、通過一個(gè)TTL轉(zhuǎn)485的芯片轉(zhuǎn)換,然后再接一根USB轉(zhuǎn)485的連接線。
TTL一般是板內(nèi)模塊間通訊用的比較多,因?yàn)榇诰€過長會(huì)有線損,影響通訊,因此,外接的傳感器和MCU的通訊大多使用RS232和RS485這兩種,像RS485,串口線即使長達(dá)一百米,也不影響正常通訊。
2、RS485的使用
上面簡單介紹了三種電平硬件上的區(qū)別,那么在編程上又有什么區(qū)別呢?
對于TTL和RS232來說,編程都是一樣的,只要配置好UART的收發(fā)即可,電平的轉(zhuǎn)換都是通過轉(zhuǎn)換芯片來完成的,與代碼無關(guān)。RS485也是類似的,電平的轉(zhuǎn)換也是通過轉(zhuǎn)換芯片來完成,但不同的是RS232是可以同時(shí)收發(fā)的,而RS485在同一時(shí)間只能發(fā)送或者接收,因此,RS485的轉(zhuǎn)換芯片多了一個(gè)使能引腳,這個(gè)使能腳是用來切換發(fā)送模式和接收模式的,一般是由MCU來控制。
我們在寫代碼的時(shí)候只要把RS485這個(gè)使能腳的控制加進(jìn)去就可以了。
1)先定義485的使能引腳,以及加入GPIO配置要用到的頭文件。
#include <rtdevice.h>
#include <board.h>
#define RS485_RE GET_PIN(G, 8)
提示:引腳號用宏定義,這樣的話如果以后要修改引腳,就不需要把每個(gè)函數(shù)都改一遍。
2)在串口初始化的時(shí)候把485使能引腳的初始化也加進(jìn)去,默認(rèn)設(shè)置為接收模式。
rt_pin_mode(RS485_RE, PIN_MODE_OUTPUT); // 配置RS485使能引腳為輸出
rt_pin_write(RS485_RE, PIN_LOW); // 配置RS485為接收模式(一般低電平是接收模式,高電平是發(fā)送模式,當(dāng)然,也有相反的,主要還是看485用什么芯片)
3)在串口發(fā)送數(shù)據(jù)前切換485為發(fā)送模式,發(fā)送完成后再切回接收模式。
rt_pin_write(RS485_RE, PIN_HIGH); // 發(fā)送模式
rt_device_write(serial, 0, &ch, 1); // 發(fā)送數(shù)據(jù)
rt_pin_write(RS485_RE, PIN_LOW); // 接收模式
四、常見問題解答
1、ENV配置里面沒有我要用的串口號
問題分析:這是因?yàn)槟氵@個(gè)工程menuconfig頁面的配置文件并沒有把所有串口和配置都添加進(jìn)來。
解決辦法:在工程目錄下board文件夾里面有一個(gè)Kconfig文件,這個(gè)就是menuconfig頁面的配置文件,在里面找到串口的配置,可以把原有的配置修改成你想要的串口號,也可以照貓畫虎的抄一遍原有的配置,然后再修改成你想要的串口號。修改完之后重新打開menuconfig即可,如果報(bào)錯(cuò)打不開了,說明的改的有問題,有語法錯(cuò)誤。
還有一個(gè)辦法就是直接在工程文件里面的rtconfig.h添加串口使能的宏定義,實(shí)際上使用env配置修改的也是這個(gè)文件的宏,但是這樣做有一個(gè)問題就是每次你使用env配置好參數(shù)之后,你都要再手動(dòng)添加一遍串口使能的宏,因?yàn)槊看问褂胑nv配置參數(shù)的時(shí)候都會(huì)按照Kconfig文件覆蓋一遍,而Kconfig又沒有你手動(dòng)添加的這個(gè)宏,所以,最好還是用上面的方法,在根本上解決問題。
2、ENV配置的串口配置沒有DMA模式
問題分析:這個(gè)問題和上面那個(gè)問題其實(shí)是一樣的,修改Kconfig即可
3、ENV配置沒錯(cuò),硬件電路也是對的,但是串口就是收不到數(shù)據(jù)
問題分析:假如你ENV的配置是沒錯(cuò)的,硬件電路也確認(rèn)過是正常的,使用list_device也能查找到uart設(shè)備,但是就是收不到數(shù)據(jù),有可能是底層串口沒有配置好,比如底層的引腳號配置的不對或者串口根本就沒有打開,這種情況在修改了電路圖或者工程的時(shí)候很容易出現(xiàn),也很容易忽視,因?yàn)檫@個(gè)問題編譯的時(shí)候并不會(huì)報(bào)錯(cuò),你不仔細(xì)核對原理圖是發(fā)現(xiàn)不了的。
解決辦法:使用STM32CubeMX配置好串口的參數(shù),具體的操作上面已經(jīng)介紹過,就不多說了。
4、STM32CubeMX、ENV及串口初始化等軟件部分都確認(rèn)是對的,但就是沒有數(shù)據(jù)
問題分析:這種情況就要結(jié)合硬件一起排查了,最快的排查方法就是直接上示波器或者邏輯分析儀,可以單獨(dú)寫一個(gè)測試線程,讓單片機(jī)定時(shí)發(fā)數(shù)據(jù)(發(fā)什么數(shù)據(jù)不重要),然后用示波器查看MCU串口的TX引腳的波形,正常的波形是很規(guī)律的,每一幀的數(shù)據(jù)間隔也會(huì)跟你定時(shí)的時(shí)間一致,并且數(shù)據(jù)幀的波形都是比較漂亮的方波。如果沒有波形,排除MCU損壞的情況的話,肯定還是軟件沒有配置好。接收的測試也是一樣的,可以通過PC端的串口助手定時(shí)發(fā)送數(shù)據(jù),然后測試MCU串口的RX引腳,看看波形是否正常,如果波形正常,但是單片機(jī)就是沒有收到數(shù)據(jù),那肯定還是軟件沒有配置好。
5、串口每次都只能通過輸入命令啟動(dòng)嗎
命令行輸入只是為了方便我們測試,實(shí)際使用的話可以使用INIT_APP_EXPORT自動(dòng)加載函數(shù),或者在其他函數(shù)、線程里面調(diào)用。
例如:
INIT_APP_EXPORT(uart_sample);
五、結(jié)束語
串口的使用其實(shí)非常簡單,但是實(shí)際使用的時(shí)候往往會(huì)因?yàn)橐恍┬栴}折騰了半天,這個(gè)時(shí)候一定要冷靜的分析問題,先檢查一遍硬件電路,再按流程檢查一遍配置和代碼,最后實(shí)在沒辦法了就上示波器吧。
好了,關(guān)于RT-thread串口的介紹就到這里,如果還有什么問題,歡迎在評論區(qū)留言。如果這篇文章能夠幫到你,就給我點(diǎn)個(gè)贊吧,如果想了解更多RT-thread和單片機(jī)的內(nèi)容,可以關(guān)注一下博主,后續(xù)我還會(huì)繼續(xù)分享更多的經(jīng)驗(yàn)給大家。