UART學習之路(三)基於STM32F103的USART實驗


  關於STM32串口的資料可以在RM0008 Reference Manual中找到,有中文版的資料。STM32F103支持5個串口,選取USART1用來實驗,其對應的IO口為PA9和PA10。這次的實驗基於ALIENTEK的開發板,開發版通過CH340G實現將串口轉成USB。因此需要做好一些准備工作。

1.PC端安裝Keil v5 MDK開發工具;

2.PC端安裝CH340G的驅動;

3.PC端安裝ATK XCOM串口收發程序

 

  STM32的串口編程思路:

1.串口時鍾設置和復位;

2.選取發射口和接收口的引腳,並設置GPIO端口參數;

3.串口參數的初始化(完成波特率、字長、奇偶校驗、收發模式等參數的設置);

4.初始化NVIC(Nested Vectored Interrupt Controller,內嵌向量中斷控制器);

5.開啟中斷和使能串口

 

代碼如下:

 1 //main.c:  2 #include "uart.h"
 3 
 4 
 5 int main()  6 {  7  uart1_init();  8     while(1)  9  { 10  } 11 }
 1 //USART.c  2 #include "uart.h"
 3 
 4 
 5 #define USART1_REC_LEN 256
 6 
 7 u8 Uart1_RevBuf_Tail = 0;//接收緩沖區尾部  8 u8 Uart1_RevBuf[USART1_REC_LEN];//接收緩沖區數組  9 
10 void uart1_init() 11 { 12   //GPIO端口設置
13  GPIO_InitTypeDef GPIO_InitStructure; 14  USART_InitTypeDef USART_InitStructure; 15  NVIC_InitTypeDef NVIC_InitStructure; 16 
17   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); 18  USART_DeInit(USART1); 19  
20 
21   //USART1端口配置 22   //UASART_TX PA9
23   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
24   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 25   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //復用推挽輸出
26   GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9 27   //USART1_RX PA10
28   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 29   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
30   GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10 31 
32   //USART1 初始化設置
33   USART_InitStructure.USART_BaudRate = 9600;//波特率設置
34   USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式
35   USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
36   USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
37   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制
38   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收發模式 
39   USART_Init(USART1, &USART_InitStructure); //初始化串口1 40 
41     //Usart1 NVIC 配置
42   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中斷通道
43   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//搶占優先級3
44   NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;        //子優先級3
45   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
46   NVIC_Init(&NVIC_InitStructure);    //根據指定的參數初始化VIC寄存器
47     
48   USART_Init(USART1, &USART_InitStructure); 49   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟相關中斷
50   USART_Cmd(USART1, ENABLE);  //使能串口1
51 
52 } 53 
54 //串口1中斷服務程序
55 void USART1_IRQHandler(void) 56 { 57     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷
58  { 59         
60         Uart1_RevBuf[Uart1_RevBuf_Tail] = USART_ReceiveData(USART1);//讀取接收到的數據,將尾標后移
61         USART_SendData(USART1,Uart1_RevBuf[Uart1_RevBuf_Tail]);//發送接收到的數據
62         while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) 63  {} 64         Uart1_RevBuf_Tail++; 65         if(Uart1_RevBuf_Tail>USART1_REC_LEN-1) 66  { 67             Uart1_RevBuf_Tail = 0; 68  } 69  } 70 }

  主函數非常簡單,就是調用uart_init()然后等待串口1的接收中斷觸發。串口1的中斷服務函數功能是:當PC端發送據后,將接收到的數據重新發回給PC機。uart_init()的功能是完成串口的配置。在接收數據的時候設置了一個容量位256的數據緩沖區Uart1_RevBuf,用來存放接收到的數據。

程序的運行結果如下。分別發送AA,BB,CC后PC端接收到了AA 0D 0A BB 0D 0A CC 0D 0A,0D和0A分別表示回車和換行。說明結果正確。

 

在實際應用中,上位機可以通過多個串口和多個從設備進行通信,因此在串口通信的時候要自行規定一個通信協議。比如由1.頭,2.設備號,3.數據長度,4.數據,5.結束位,6.間隔位組成一個數據包。根據協議編寫解包函數。解包函數的大致思路就是將接收到的數據一步一步的進行判斷,最終完成解出數據的功能。

1.數據包定義:

頭:0xAB,設備號:0x01(一號設備),數據長度:0x08(8位數據),數據位:DATA,結束位:0xFF,間隔位:0xFF 0xFF

2.解包函數:

PC機發送一個數據包:AB 01 08 00 01 02 03 04 05 06 07 FF FF FF,解包函數能夠將數據00 01 02 03 04 05 06 07取出來並再次發送給PC機。PC機將數據發送給STM32F103,觸發接收中斷,將數據存入接收緩沖區中,解包函數從緩沖區的頭部開始檢索,完成數據分析,取出數據。代碼如下:

#include "stm32f10x.h" #include <stdio.h>

#define Usart1RecLength 256 u8 Uart1_RevBuf_Tail = 0; u8 Uart1_RevBuf_Head = 0; u8 Uart1_RevBuf[Usart1RecLength]; u8 RecState = 0; u8 TemplateData; u8 DataLength = 8; u8 Data[8]={0}; typedef struct { u8 StartDataError; u8 DeviceDataError; u8 LengthDataError; u8 StopDataError; u8 DataReady; }DataFrameFlag; DataFrameFlag USART1_FrameFlags; void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷
 { Uart1_RevBuf[Uart1_RevBuf_Tail] = USART_ReceiveData(USART1);    //讀取接收到的數據
        Uart1_RevBuf_Tail++; if(Uart1_RevBuf_Tail > Usart1RecLength-1) { Uart1_RevBuf_Tail = 0; } } } void RecDataAnalysis() { u8 i = 0; if(Uart1_RevBuf_Head != Uart1_RevBuf_Tail)//判斷是否有數據
 { TemplateData = Uart1_RevBuf[Uart1_RevBuf_Head];//從數據緩沖區取數據
      Uart1_RevBuf_Head ++; if(Uart1_RevBuf_Head > Usart1RecLength-1) { Uart1_RevBuf_Head = 0; } USART1_FrameFlags.DeviceDataError = 0; USART1_FrameFlags.StopDataError = 0; USART1_FrameFlags.LengthDataError = 0; USART1_FrameFlags.StartDataError = 0; switch(RecState) { case 0: if(TemplateData == 0xAB)//
 { RecState = 1; } else { RecState = 0; USART1_FrameFlags.StartDataError = 1; } break; case 1: if(TemplateData == 0x01)//設備號
 { RecState = 2; } else { RecState = 0; USART1_FrameFlags.DeviceDataError = 1; } break; case 2: if(TemplateData == 0x08)//數據位
 { RecState = 3; } else { RecState = 0; USART1_FrameFlags.LengthDataError = 1; } break; case 3://轉存數據
            if(DataLength == 0) { RecState = 4; USART1_FrameFlags.DataReady = 1; } else if(DataLength != 0) { Data[8-DataLength] = TemplateData; DataLength = DataLength -1; } break; case 4: if(TemplateData == 0xFF)//尾部
 { RecState = 0; DataLength = 8; } else { for(i=0;i < 8;i++) { Data[i] = 0; } RecState = 0; DataLength = 8; USART1_FrameFlags.StopDataError = 1; USART1_FrameFlags.DataReady = 0; } break; default: for(i=0;i < 8;i++) { Data[i] = 0; } RecState = 0; DataLength = 8; break; } } } void Resend()//測試用重發數據函數
{ u8 i = 0; if(USART1_FrameFlags.DataReady == 1) { for(i=0;i<8;i++) { USART_SendData(USART1,Data[i]); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART1_FrameFlags.DataReady = 0; } } }

 

函數RecDataAnalysis()完成數據解包,函數Resend()在解包函數准備好數據將數據回發給PC機。結構體DataFrameFlag的作用是當數據出現錯誤時完成報錯,是可選功能,程序中給了一種思路,未做調試。結果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM