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