delphi 串口通信发送_STM32第五章串口通讯详解
串口通信是串行通信里面的異步方式。串行通信是相對(duì)于并行通信來(lái)說(shuō)的。串口是一個(gè)事實(shí)存在的東西,比如DB9接口。串口通訊里面的波特率,實(shí)際上是比特率。如果這兩點(diǎn)你還不是很清楚的話,好好往下看。
通信涉及的幾個(gè)基礎(chǔ)概念
同步通信和異步通信
(1)、同步和異步的區(qū)別:簡(jiǎn)單來(lái)說(shuō)就是發(fā)送方和接收方按照同一個(gè)時(shí)鐘節(jié)拍工作就叫同步,發(fā)送方和接收方?jīng)]有統(tǒng)一的時(shí)鐘節(jié)拍、而各自按照自己的節(jié)拍工作就叫異步。
(2)、同步通信中,通信雙方按照統(tǒng)一節(jié)拍工作,所以配合很好;一般需要發(fā)送方給接收方發(fā)送信息同時(shí)發(fā)送時(shí)鐘信號(hào),接收方根據(jù)發(fā)送方給它的時(shí)鐘信號(hào)來(lái)安排自己的節(jié)奏。同步通信用在通信雙方信息交換頻率固定,或者經(jīng)常通信時(shí)。帶時(shí)鐘同步信號(hào)傳輸。如-SPI,IIC通信。
(3)、異步通信又叫異步通知。在雙方通信的頻率不固定時(shí)(有時(shí) 3ms 收發(fā)一次,有時(shí) 3 天才收發(fā)一次)不適合使用同步通信,而適合異步通信。異步通信時(shí)接收方不必一直在意發(fā)送方,發(fā)送方需要發(fā)送信息時(shí)會(huì)首先給接收方一個(gè)信息開(kāi)始的起始信號(hào),接收方接收到起始信號(hào)后就認(rèn)為后面緊跟著的就是有效信息,才會(huì)開(kāi)始注意接收信息,直到收到發(fā)送方發(fā)過(guò)來(lái)的結(jié)束標(biāo)志。異步通信:不帶時(shí)鐘同步信號(hào)。如·UART(通用異步收發(fā)器),單總線。
電平信號(hào)和差分信號(hào)
(1)、電平信號(hào)和差分信號(hào)是用來(lái)描述通信線路傳輸方式的。也就是說(shuō)如何在通信線路上表達(dá) 1 和 0.
(2)、電平信號(hào)的傳輸線中有一個(gè)參考電平線(一般是 GND),然后信號(hào)線上的信號(hào)值是由信號(hào)線電平和參考電平線的電壓差決定。
(3)、差分信號(hào)的傳輸線中沒(méi)有參考電平,所有都是信號(hào)線。然后 1 和 0 的表達(dá)靠信號(hào)線之間的電壓差。
總結(jié):電平信號(hào)的 2 根通信線之間的電平差異容易受到干擾,傳輸容易失敗;差分信號(hào)不容易受到干擾因此傳輸質(zhì)量比較穩(wěn)定,現(xiàn)代通信一般都使用差分信號(hào),電平信號(hào)幾乎沒(méi)有了。看起來(lái)似乎相同根數(shù)的通信線下,電平信號(hào)要比差分信號(hào)要快;但是實(shí)際還是差分信號(hào)快,因?yàn)椴罘中盘?hào)抗干擾能力強(qiáng),因此 1 個(gè)發(fā)送周期更短。
并行接口和串行接口
(1)、串行、并行主要是考慮通信線的根數(shù),就是發(fā)送方和接收方同時(shí)可以傳遞的信息量的多少
(2)、譬如在電平信號(hào)下,1 根參考電平線+1 根信號(hào)線可以傳遞 1 位二進(jìn)制;如果我們有 3根線(2 根信號(hào)線+1 根參考線)就可以同時(shí)發(fā)送 2 位二進(jìn)制;如果想同時(shí)發(fā)送 8 位二進(jìn)制就需要 9 根線。
(3)、在差分信號(hào)下,2 根線(彼此差分)可以同時(shí)發(fā)送 1 位二進(jìn)制;如果需要同時(shí)發(fā)送 8 位二進(jìn)制,需要 16 根線。
總結(jié):聽(tīng)起來(lái)似乎并行接口比串行接口要快(串行接口一次只能發(fā)送 1 位二進(jìn)制,而并行接口一次可以發(fā)送多位二進(jìn)制)要更優(yōu)秀;但是實(shí)際上串行接口才是王道,用的比較廣。因?yàn)楦⌒盘?hào)線,而且對(duì)傳輸線的要求更低、成本更低;而且串行時(shí)可以通過(guò)提高通信速度來(lái)提高總體通信性能,不一定非得要并行。異步、串行、差分,譬如 USB 和網(wǎng)絡(luò)通信更勝一籌。
串口通信涉及的基礎(chǔ)概念
異步、電平信號(hào)、串行
(1)、異步:串口通信的發(fā)送方和接收方之間是沒(méi)有統(tǒng)一的時(shí)鐘信號(hào)的。
(2)、電平信號(hào):串口通信出現(xiàn)的時(shí)間較早,速率較低,傳輸?shù)木嚯x較近,所以干擾還不太明顯,因此當(dāng)時(shí)使用了電平信號(hào)傳輸。后期出現(xiàn)的傳輸協(xié)議都改成差分信號(hào)傳輸了。
(3)、串行通信:串口通信每次同時(shí)只能傳輸 1 個(gè)二進(jìn)制位
RS232 電平和 TTL 電平
(1)電平信號(hào)是用信號(hào)線電平減去參考線電平得到電壓差,這個(gè)電壓差決定了傳輸值是 1 還是 0.
(2)在電平信號(hào)時(shí)多少 V 代表 1,多少 V 代表 0 不是固定的,取決于電平標(biāo)準(zhǔn)。譬如 RS232電平中-3V~-15V 表示 1;+3~+15V 表示 0;TTL 電平則是+5V 表示 1,0V 表示 0.
(3)不管哪種電平都是為了在傳輸線上表示 1 和 0.區(qū)別在于適用的環(huán)境和條件不同。RS232的電平定義比較大,適合干擾大、距離遠(yuǎn)的情況;TTL 電平電壓范圍小,適合距離近且干擾小的情況。
(4)我們臺(tái)式電腦后面的串口插座就是 RS232 接口的,在工業(yè)上用串口時(shí)都用這個(gè),傳輸距離小于 15 米;TTL 電平一般用在電路板內(nèi)部?jī)蓚€(gè)芯片之間。
(5)對(duì)編程來(lái)說(shuō),RS232 電平傳輸還是 TTL 電平是沒(méi)有差異的。所以電平標(biāo)準(zhǔn)對(duì)硬件工程師更有意義,而軟件工程師只要略懂即可。(把 TTL 電平和 RS232 電平混接是不可以的)
波特率
(1)衡量通訊性能的一個(gè)非常重要的參數(shù)就是通訊速率,通常以比特率(Bitrate)來(lái)表示,即每秒鐘傳輸?shù)亩M(jìn)制位數(shù),單位為比特每秒(bit/s)。容易與比特率混淆的概念是“波特率”。(Baudrate),它表示每秒鐘傳輸了多少個(gè)碼元。而碼元是通訊信號(hào)調(diào)制的概念,通訊中常用時(shí)間間隔相同的符號(hào)來(lái)表示一個(gè)二進(jìn)制數(shù)字,這樣的信號(hào)稱為碼元。如常見(jiàn)的通訊傳輸中,用 0V表示數(shù)字 0,5V 表示數(shù)字 1,那么一個(gè)碼元可以表示兩種狀態(tài) 0 和 1,所以一個(gè)碼元等于一個(gè)二進(jìn)制比特位,此時(shí)波特率的大小與比特率一致;如果在通訊傳輸中,有 0V、2V、4V以及 6V分別表示二進(jìn)制數(shù) 00、01、10、11,那么每個(gè)碼元可以表示四種狀態(tài),即兩個(gè)二進(jìn)制比特位,所以碼元數(shù)是二進(jìn)制比特位數(shù)的一半,這個(gè)時(shí)候的波特率為比特率的一半。因?yàn)楹芏喑R?jiàn)的通訊中一個(gè)碼元都是表示兩種狀態(tài),人們常常直接以波特率來(lái)表示比特率。譬如每秒種可以傳輸 9600 個(gè)二進(jìn)制位(傳輸一個(gè)二進(jìn)制位需要的時(shí)間是 1/9600秒,也就是 104us),比特率就是 9600.但因?yàn)橐粋€(gè)碼元都是表示兩種狀態(tài),所以比特率=波特率。也通常說(shuō)波特率就是 9600
(2)串口通信的波特率不能隨意設(shè)定,而應(yīng)該在一些值中去選擇。一般最常見(jiàn)的波特率是 9600或者 115200.為什么波特率不可以隨便指定?主要是因?yàn)?#xff1a;第一,通信雙方必須事先設(shè)定相同的波特率這樣才能成功通信,如果發(fā)送方和接收方按照不同的波特率通信則根本收不到,因此波特率最好是大家熟知的而不是隨意指定的。第二,常用的波特率經(jīng)過(guò)長(zhǎng)久發(fā)展,就形成了共識(shí),大家常用就是 9600 或者 115200.
起始位、數(shù)據(jù)位、奇偶校驗(yàn)位、停止位
(1)串口通信時(shí),收發(fā)是一個(gè)周期一個(gè)周期進(jìn)行的,每周期傳輸 n 個(gè)二進(jìn)制位。這一個(gè)周期就叫做一個(gè)通信單元,一個(gè)通信單元是由:起始位+數(shù)據(jù)位+奇偶校驗(yàn)位+停止位組成的。
(2)起始位表示發(fā)送方要開(kāi)始發(fā)送一個(gè)通信單元;數(shù)據(jù)位是一個(gè)通信單元中發(fā)送的有效信息位;奇偶校驗(yàn)位是用來(lái)校驗(yàn)數(shù)據(jù)位,以防止數(shù)據(jù)位出錯(cuò)的;停止位是發(fā)送方用來(lái)表示本通信單元結(jié)束標(biāo)志的。
(3)起始位的定義是串口通信標(biāo)準(zhǔn)事先指定的,是由通信線上的電平變化來(lái)反映的。
(4)數(shù)據(jù)位是本次通信真正要發(fā)送的有效數(shù)據(jù),串口通信一次發(fā)送多少位有效數(shù)據(jù)是可以設(shè)定的(一般可選的有 6、7、8、9,99%情況下我們都是選擇 8 位數(shù)據(jù)位。因?yàn)槲覀円话阃ㄟ^(guò)串口發(fā)送的文字信息都是 ASCII 碼編碼的,而 ASCII 碼中一個(gè)字符剛好編碼為 8 位。)
(5)奇偶校驗(yàn)位是用來(lái)給數(shù)據(jù)位進(jìn)行奇偶校驗(yàn)(把待校驗(yàn)的有效數(shù)據(jù)逐個(gè)位的加起來(lái),總和為奇數(shù)奇偶校驗(yàn)位就為 1,總和為偶數(shù)奇偶校驗(yàn)位就為 0)的,可以在一定程度上防止位反轉(zhuǎn)。
奇校驗(yàn)要求有效數(shù)據(jù)和校驗(yàn)位中“1”的個(gè)數(shù)為奇數(shù),比如一個(gè) 8 位長(zhǎng)的有效數(shù)據(jù)為:01101001,此時(shí)總共有 4 個(gè)“1”,為達(dá)到奇校驗(yàn)效果,校驗(yàn)位為“1”,最后傳輸?shù)臄?shù)據(jù)將是 8 位的有效數(shù)據(jù)加上 1 位的校驗(yàn)位總共 9位。
偶校驗(yàn)與奇校驗(yàn)要求剛好相反,要求幀數(shù)據(jù)和校驗(yàn)位中“1”的個(gè)數(shù)為偶數(shù),比如數(shù)據(jù)幀:11001010,此時(shí)數(shù)據(jù)幀“1”的個(gè)數(shù)為 4 個(gè),所以偶校驗(yàn)位為“0”。
0 校驗(yàn)是不管有效數(shù)據(jù)中的內(nèi)容是什么,校驗(yàn)位總為“0”,1 校驗(yàn)是校驗(yàn)位總為“1”。
在無(wú)校驗(yàn)的情況下,數(shù)據(jù)包中不包含校驗(yàn)位。
(6)停止位的定義是串口通信標(biāo)準(zhǔn)事先指定的,是由通信線上的電平變化來(lái)反映的。常見(jiàn)的有 1 位停止位,1.5 位停止位,2 位停止位等。99%情況下都是用 1 位停止位。總結(jié):串口通信時(shí)因?yàn)槭钱惒酵ㄐ?#xff0c;所以通信雙方必須事先約定好通信參數(shù),這些通信參數(shù)包括:波特率、數(shù)據(jù)位、奇偶校驗(yàn)位、停止位(串口通信中起始位定義是唯一的,所以一般不用選擇)
串口通信的基本原理
全雙工、半雙工及單工通訊
(1)單工就是單方向,雙工就是雙方同時(shí)收發(fā),同時(shí)只能單方向但是方向可以改變叫半雙工
(2)如果只能 A 發(fā) B 收則單工,A 發(fā) B 收或者 B 發(fā) A 收(兩個(gè)方向不能同時(shí))叫半雙工,A發(fā) B 收同時(shí) B 發(fā) A 收叫全雙工。
三根通信線:Rx Tx GND
(1)任何通信都要有信息傳輸載體,或者是有線的或者是無(wú)線的。
(2)串口通信是有線通信,是通過(guò)串口線來(lái)通信的。
(3)串口通信線最少需要 2 根(GND 和信號(hào)線),可以實(shí)現(xiàn)單工通信,也可以使用 3 根通信線(Tx、Rx、GND)來(lái)實(shí)現(xiàn)全雙工。
收發(fā)雙方事先規(guī)定好通信參數(shù)
(1)串口通信屬于基層基本性的通信規(guī)約,它自己本身不會(huì)去協(xié)商通信參數(shù),需要通信前通信雙方事先約定好通信參數(shù)(波特率、數(shù)據(jù)位、奇偶校驗(yàn)位、停止位)
(2)串口通信的任何一個(gè)關(guān)鍵參數(shù)設(shè)置錯(cuò)誤,都會(huì)導(dǎo)致通信失敗。譬如波特率調(diào)錯(cuò)了,發(fā)送方發(fā)送沒(méi)問(wèn)題,接收方也能接收,但是接收到全是亂碼···
信息以二進(jìn)制流的方式在信道上傳輸
(1)、串口通信的發(fā)送方每隔一定時(shí)間(時(shí)間固定為 1/波特率,單位是秒)將有效信息(1或者 0)放到通信線上去,逐個(gè)二進(jìn)制位的進(jìn)行發(fā)送。
(2)接收方通過(guò)定時(shí)(起始時(shí)間由讀到起始位標(biāo)志開(kāi)始,間隔時(shí)間由波特率決定)讀取通信線上的電平高低來(lái)區(qū)分發(fā)送給我的是 1 還是 0。依次讀取數(shù)據(jù)位、奇偶校驗(yàn)位、停止位,停止位就表示這一個(gè)通信單元(幀)結(jié)束,然后中間是不定長(zhǎng)短的非通信時(shí)間(發(fā)送方有可能緊接著就發(fā)送第二幀,也可能半天都不發(fā)第二幀,這就叫異步通信),下來(lái)就是第二幀·····總結(jié):第一,波特率非常重要,波特率錯(cuò)了整個(gè)通信就亂套了;數(shù)據(jù)位、奇偶校驗(yàn)位、停止位也很重要,否則可能認(rèn)不清數(shù)據(jù)。
第三,通過(guò)串口不管發(fā)數(shù)字、還是文本還是命令還是什么,都要先對(duì)發(fā)送內(nèi)容進(jìn)行編碼,編碼成二進(jìn)制再進(jìn)行逐個(gè)位的發(fā)送。
(3)串口發(fā)送的一般都是字符,一般都是 ASCII 碼編碼后的字符,所以一般設(shè)置數(shù)據(jù)位都是 8,方便剛好一幀發(fā)送 1 個(gè)字節(jié)。
STM32串口通訊詳解
串口通訊的物理層有很多標(biāo)準(zhǔn)及變種,主要講解 RS-232 標(biāo)準(zhǔn) ,RS-232標(biāo)準(zhǔn)主要規(guī)定了信號(hào)的用途、通訊接口以及信號(hào)的電平標(biāo)準(zhǔn)。因?yàn)槲覀兂R?jiàn)的市面上的開(kāi)發(fā)板在串口通訊那一講都是關(guān)于RS-232標(biāo)準(zhǔn)的協(xié)議。
我們這里就不講啥結(jié)構(gòu)體了,這些直接去看數(shù)據(jù)手冊(cè)就好了,講一下配置過(guò)程步驟,做到胸有成竹、心中有數(shù)。
1.使能串口引腳GPIOA的時(shí)鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
2.使能串口的時(shí)鐘,串口掛載在AHB2
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,?ENABLE);
3.將串口的引腳復(fù)用到串口中斷線上
/*連接?PA10?復(fù)用到?USART1_Rx*/
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);/*連接?PA9?復(fù)用到?USART1__Tx*/
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);??
STM32有很多的內(nèi)置外設(shè),這些外設(shè)的外部引腳都是與GPIO復(fù)用的。也就是說(shuō),一個(gè)GPIO如果可以復(fù)用為內(nèi)置外設(shè)的功能引腳,那么當(dāng)這個(gè)GPIO作為內(nèi)置外設(shè)使用的時(shí)候,就叫做復(fù)用。例如串口1的發(fā)送接收引腳是PA9,PA10,當(dāng)我們把PA9,PA10不用作GPIO,而用做復(fù)用功能串口1的發(fā)送接收引腳的時(shí)候,叫端口復(fù)用。
4.常規(guī)操作初始化串口引腳的GPIO
/*?GPIO初始化?*/
GPIO_InitStructure.GPIO_OType?=?GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd??=?GPIO_PuPd_UP;??
GPIO_InitStructure.GPIO_Speed?=?GPIO_Speed_50MHz;/*?配置Tx引腳為復(fù)用功能??*/
GPIO_InitStructure.GPIO_Mode?=?GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin?=??GPIO_Pin_9??;??
GPIO_Init(GPIOA,?&GPIO_InitStructure);/*?配置Rx引腳為復(fù)用功能?*/
GPIO_InitStructure.GPIO_Mode?=?GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin?=??GPIO_Pin_10;
GPIO_Init(GPIOA,?&GPIO_InitStructure);
5.串口參數(shù)初始化
串口初始化是通過(guò) USART_Init()函數(shù)實(shí)現(xiàn)的。
/*?波特率設(shè)置:115200?*/
USART_InitStructure.USART_BaudRate?=?115200;/*?字長(zhǎng)(數(shù)據(jù)位+校驗(yàn)位):8 */
USART_InitStructure.USART_WordLength?=?USART_WordLength_8b;????/*?停止位:1個(gè)停止位?*/
USART_InitStructure.USART_StopBits?=?USART_StopBits_1;/*?校驗(yàn)位選擇:偶校驗(yàn)?*/??
USART_InitStructure.USART_Parity?=?USART_Parity_No;/*?硬件流控制:不使用硬件流?*/
USART_InitStructure.USART_HardwareFlowControl?=?USART_HardwareFlowControl_None;/* USART模式控制:同時(shí)使能接收和發(fā)送?*/
USART_InitStructure.USART_Mode?=?USART_Mode_Rx?|?USART_Mode_Tx;/*?完成USART初始化配置?*/
USART_Init(USART1,?&USART_InitStructure);?????/*?使能串口?*/
USART_Cmd(USART1,?ENABLE);????/*開(kāi)啟中斷?接收到數(shù)據(jù)產(chǎn)生中斷?進(jìn)入中斷服務(wù)函數(shù)?*/
USART_ITConfig(USART1,?USART_IT_RXNE,?ENABLE);
初始化需要設(shè)置的參數(shù)為:波特率,字長(zhǎng),停止位,奇偶校驗(yàn)位,硬件數(shù)據(jù)流控制,模式(收,發(fā))。這里面的USART_Cmd();函數(shù)很好理解,就是使能串口。USART_ITConfig();就是開(kāi)啟中斷響應(yīng)了,這里面的第二個(gè)入口參數(shù)我們一般寫(xiě)的是USART_IT_RXNE.也就是打開(kāi)接收中斷,即程序在發(fā)送數(shù)據(jù)結(jié)束的時(shí)候要產(chǎn)生中斷,調(diào)到中斷服務(wù)函數(shù)中。
6.設(shè)置串口中斷優(yōu)先級(jí)分組
我們前一章說(shuō)了,只要你的程序里面用了中斷,就必須配置串口優(yōu)先級(jí)分組。但是我們我們有時(shí)候會(huì)發(fā)現(xiàn)我們?cè)谝恍S家的串口例程中沒(méi)有配置串口中斷優(yōu)先級(jí)分組,這是因?yàn)樗绦蛑挥幸粋€(gè)串口中斷就沒(méi)有所謂的優(yōu)先級(jí),配不配置,程序都只有個(gè)中斷。
NVIC_InitStructure.NVIC_IRQChannel?=?USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority?=?3;//搶占優(yōu)先級(jí)3
NVIC_InitStructure.NVIC_IRQChannelSubPriority?=?0;???????//子優(yōu)先級(jí)0
NVIC_InitStructure.NVIC_IRQChannelCmd?=?ENABLE;
NVIC_Init(&NVIC_InitStructure);
7.編寫(xiě)串口中斷服務(wù)函數(shù)
這個(gè)函數(shù)很重要,既然有中斷就要執(zhí)行串口中斷服務(wù)函數(shù)中去,執(zhí)行相應(yīng)的指令。
void?USART1_IRQHandler(void){if(USART_GetITStatus(?USART1,?USART_IT_RXNE?)?!=?RESET)//獲取接收中斷標(biāo)志位(接收到的數(shù)據(jù)必須是0x0d?0x0a結(jié)尾)
????{???????????
??????Res?=?USART_ReceiveData(?USART1?);if((USART_RX_STA&0x8000)==0)//接收未完成
????????????{if(USART_RX_STA&0x4000)//接收到了0x0d
????????????????{if(Res!=0x0a)USART_RX_STA=0;//接收錯(cuò)誤,重新開(kāi)始else?USART_RX_STA|=0x8000;??//接收完成了?
????????????????}else?//還沒(méi)收到0X0D
????????????????{???if(Res==0x0d)USART_RX_STA|=0x4000;else
????????????????????{
????????????????????USART_RX_BUF[USART_RX_STA&0X3FFF]=Res?;
????????????????????USART_RX_STA++;if(USART_RX_STA>(?USART_Rec_Len-1))USART_RX_STA=0;//接收數(shù)據(jù)錯(cuò)誤,重新開(kāi)始接收?????
????????????????????}????????
????????????????}
????????????}?
????}????
}
7.2 ?USART_GetITStatus()?;見(jiàn)名知意,得到串口中斷的標(biāo)志位,判斷是否發(fā)生串口1接收中斷,如果是串口接收中斷,則讀取串口接受到的數(shù)據(jù)。7.3 Res =USART_ReceiveData(USART1);將從串口1讀取接收到的數(shù)據(jù)賦值給變量Res。
7.4 簡(jiǎn)單的 接 收 協(xié) 議。通 if語(yǔ)句 , 配 合 一 個(gè) 數(shù) 組USART_RX_BUF[ ],一個(gè)接收狀態(tài)寄存器 USART_RX_STA(此寄存器其實(shí)就是一個(gè)全局變量,由讀者自行添加。由于它起到類似寄存器的功能,這里暫且稱之為寄存器)實(shí)現(xiàn)對(duì)串口數(shù)據(jù)的接收管理。USART_RX_BUF 的大小由 ?USART_Rec_Len 定義,也就是一次接收的數(shù)據(jù)最大不能超過(guò) USART_Rec_Len 個(gè)字節(jié)。USART_RX_STA 是一個(gè)接收狀態(tài)寄存器。接收到從電腦串口調(diào)試助手發(fā)過(guò)來(lái)的數(shù)據(jù),把接收到的數(shù)據(jù)保存在 USART_RX_BUF 中,同時(shí)在接收狀態(tài)寄存器(USART_RX_STA)中計(jì)數(shù)接收到的有效數(shù)據(jù)個(gè)數(shù),當(dāng)收到回車(回車的表示由 2 個(gè)字節(jié)組成:回車符的ASCII碼是0X0D 和換行符的ASCII碼是 0X0A)的第一個(gè)字節(jié) 0X0D 時(shí),計(jì)數(shù)器將不再增加,等待0X0A 的到來(lái),而如果 0X0A 沒(méi)有來(lái)到,則認(rèn)為這次接收失敗,重新開(kāi)始下一次接收。如果順利接收到 0X0A,則標(biāo)記 USART_RX_STA 的第 15 位,這樣完成一次接收,并等待該位被其他程序清除,從而開(kāi)始下一次的接收,而如果遲遲沒(méi)有收到 0X0D,那么在接收數(shù)據(jù)超過(guò) USART_Rec_Len 的時(shí)候,則會(huì)丟棄前面的數(shù)據(jù),重新接收。
8.編寫(xiě)自定義發(fā)送函數(shù)
這個(gè)自定義的發(fā)送函數(shù)是我們直接在程序中發(fā)送數(shù)據(jù)到串口調(diào)試助手,這個(gè)自定義的函數(shù)和中斷服務(wù)函數(shù)無(wú)關(guān),因?yàn)橹袛喾?wù)函數(shù)是用來(lái)接收數(shù)據(jù)的,就是我們通過(guò)串口調(diào)試助向單片機(jī)發(fā)送數(shù)據(jù),需要用到中斷服務(wù)函數(shù)。換句話說(shuō),如果你只想通過(guò)單片機(jī)將數(shù)據(jù)發(fā)送到串口調(diào)試助手的話,就不需要寫(xiě)中斷服務(wù)函數(shù)了。但是這樣做法沒(méi)有意義,我們用串口是主要是接收數(shù)據(jù)的,單單發(fā)送一個(gè)數(shù)據(jù)意義不大。
/*發(fā)送一個(gè)字符?*/static?void?Uart_SendByte(uint8_t?ch){/*?發(fā)送一個(gè)字節(jié)數(shù)據(jù)到USART1?*/
????USART_SendData(USART1,ch);??????/*?等待發(fā)送完畢?*/while?(USART_GetFlagStatus(USART1,?USART_FLAG_TXE)?==?RESET);???//獲取發(fā)送狀態(tài)
}/*發(fā)送字符串?*/void?Uart_SendString(uint8_t?*str){uint8_t?k=0;do?
????{
????????Uart_SendByte(*(str?+?k)?);
????????k++;
????}?while(*(str?+?k)!='\0');
}/*指定長(zhǎng)度的發(fā)送字符串?*/void?Uart_SendStr_length(uint8_t?*str,uint32_t?strlen?){uint8_t?k=0;do?
????{
????????Uart_SendByte(?*(str?+?k)?);
????????k++;
????}?while(k?strlen);
}
我們這里編寫(xiě)了三個(gè)函數(shù)主要是功能是:向單片機(jī)發(fā)送一個(gè)字符、向單片機(jī)發(fā)送一個(gè)字符串、向單片機(jī)發(fā)送指定長(zhǎng)度的字符串。
代碼很短,不多解釋。在發(fā)送字節(jié)的時(shí)候用到了獲取狀態(tài)函數(shù),獲取發(fā)送數(shù)據(jù)的寄存器第7位的狀態(tài),當(dāng)為1時(shí),數(shù)據(jù)傳送到移位寄存器。傳送完了就可以發(fā)送了。
9.編寫(xiě)主函數(shù)
int?main(void){????char?ch;
????NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
????USART1_Init();
????Uart_SendString(?(uint8_t?*)"這條數(shù)據(jù)是來(lái)自單片機(jī)發(fā)送的數(shù)據(jù)\n"?);
????Uart_SendString(?(uint8_t?*)"輸入數(shù)據(jù)并以回車鍵結(jié)束\n"?);while(1)
????{???}
}
首先我們需要調(diào)用
NVIC_PriorityGroupConfig();完成中斷優(yōu)先級(jí)的分組設(shè)置。再調(diào)用USART1_Init函數(shù)完成 USART 初始化配置,包括 GPIO配置,USART配置,接收中斷使用等等信息。接下來(lái)就可以調(diào)用字符發(fā)送函數(shù)把數(shù)據(jù)發(fā)送給串口調(diào)試助手了。最后主函數(shù)什么都不做,只是靜靜地等待 USART接收中斷的產(chǎn)生,并在中斷服務(wù)函數(shù)把數(shù)據(jù)回傳。
10.下載驗(yàn)證
保證開(kāi)發(fā)板相關(guān)硬件連接正確,用 USB 線連接開(kāi)發(fā)板的串口調(diào)試usb接口跟電腦,在電腦端打開(kāi)串口調(diào)試助手,配置到波特率、校驗(yàn)位、數(shù)據(jù)位、停止位。把編譯好的程序下載到開(kāi)發(fā)板,此時(shí)串口調(diào)試助手即可收到開(kāi)發(fā)板發(fā)過(guò)來(lái)的數(shù)據(jù)。我們?cè)诖谡{(diào)試助手發(fā)送區(qū)域輸入任意字符,點(diǎn)擊發(fā)送按鈕,馬上在串口調(diào)試助手接收區(qū)即可看到相同的字符。因?yàn)槲以诩依锸稚蠜](méi)有串口線,所以不能演示截圖了。
11.串口調(diào)試助手發(fā)送數(shù)據(jù)指令給單片機(jī)
我們不僅僅可以將數(shù)據(jù)發(fā)送到串口調(diào)試助手,我們還可以在串口調(diào)試助手發(fā)送數(shù)據(jù)給控制器,控制器程序根據(jù)接收到的數(shù)據(jù)進(jìn)行下一步工作。首先,我們來(lái)編寫(xiě)一個(gè)程序?qū)崿F(xiàn)開(kāi)發(fā)板與電腦通信,在開(kāi)發(fā)板上電時(shí)通過(guò) USART發(fā)送一串字符串給電腦,然后開(kāi)發(fā)板進(jìn)入中斷接收等待狀態(tài),如果電腦有發(fā)送數(shù)據(jù)過(guò)來(lái),開(kāi)發(fā)板就會(huì)產(chǎn)生中斷,我們?cè)谥袛喾?wù)函數(shù)接收數(shù)據(jù),并馬上把數(shù)據(jù)返回發(fā)送給電腦。
//重定向c庫(kù)函數(shù)printf到串口,重定向后可使用printf函數(shù)int?fputc(int?ch,?FILE?*f){/*?發(fā)送一個(gè)字節(jié)數(shù)據(jù)到串口?*/
???USART_SendData(USART1,?(uint8_t)?ch);???????/*?等待發(fā)送完畢?*/while?(USART_GetFlagStatus(USART1,?USART_FLAG_TXE)?==?RESET);???????????return?(ch);
}//重定向c庫(kù)函數(shù)scanf到串口,重寫(xiě)向后可使用scanf、getchar等函數(shù)int?fgetc(FILE?*f){/*?等待串口輸入數(shù)據(jù)?*/while?(USART_GetFlagStatus(USART1,?USART_FLAG_RXNE)?==?RESET);return?(int)USART_ReceiveData(USART1);
}
在 C 語(yǔ)言標(biāo)準(zhǔn)庫(kù)中,fputc函數(shù)是 printf 函數(shù)內(nèi)部的一個(gè)函數(shù),功能是將字符 ch寫(xiě)入到文件指針 f所指向文件的當(dāng)前寫(xiě)指針位置,簡(jiǎn)單理解就是把字符寫(xiě)入到特定文件中。我們使用 USART 函數(shù)重新修改 fputc函數(shù)內(nèi)容,達(dá)到類似“寫(xiě)入”的功能。fgetc 函數(shù)與 fputc 函數(shù)非常相似,實(shí)現(xiàn)字符讀取功能。在使用 scanf函數(shù)時(shí)需要注意字符輸入格式。
還有一點(diǎn)需要注意的,使用 fput和 fgetc函數(shù)達(dá)到重定向 C語(yǔ)言標(biāo)準(zhǔn)庫(kù)輸入輸出函數(shù)必須在 MDK的工程選項(xiàng)把“Use MicroLIB”勾選上,MicoroLIB 是缺省 C庫(kù)的備選庫(kù),它對(duì)標(biāo)準(zhǔn) C 庫(kù)進(jìn)行了高度優(yōu)化使代碼更少,占用更少資源。為使用 printf、scanf 函數(shù)需要在文件中包含 stdio.h頭文件。
11.修改主函數(shù)
int?main(void){????char?ch;
??NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
??LED_Init();
??USART1_Init();
??Uart_SendString(?(uint8_t?*)"這條數(shù)據(jù)是來(lái)自串口發(fā)送的\n"?);
??Uart_SendString(?(uint8_t?*)"輸入數(shù)據(jù)并以回車鍵結(jié)束\n"?);while(1)
??{???
?????ch=getchar();printf("接收到字符:%c\n",ch);switch(ch)
??????{case?'1':printf("LED燈亮");
????????????GPIO_ResetBits(GPIOH,GPIO_Pin_12);//PH12接了一個(gè)LED燈break;default:break;??????
??????}???
???}???
}
至此串口通訊的編程詳解就結(jié)束了,現(xiàn)在你會(huì)了嗎?
END往期精彩回顧一、STM32第一章-寄存器你懂嗎二、STM32第二章-啟動(dòng)過(guò)程詳解三、STM32第三章-系統(tǒng)時(shí)鐘配置四、STM32第四章-外部中斷管理如果覺(jué)得文章對(duì)你有幫助,歡迎轉(zhuǎn)發(fā)、分享給你的朋友,感謝您的支持!如需轉(zhuǎn)載請(qǐng)聯(lián)系我!
總結(jié)
以上是生活随笔為你收集整理的delphi 串口通信发送_STM32第五章串口通讯详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: dedecms怎么改php版本_Linu
- 下一篇: freertos 定时器 不启动_Fre