c/c++语言开发共享UART学习之路(三)基于STM32F103的USART实验

关于STM32串口的资料可以在RM0008 Reference Manual中找到,有中文版的资料。STM32F103支持5个串口,选取USART1用来实验,其对应的IO口为PA9和PA10。这次的实验基于ALIENTEK的开发板,开发版通过CH340G实现将串口转成USB。因此需要做好一些准备工作 …

  关于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,用来存放接收到的数据。

程序的运行结果如下:

UART学习之路(三)基于STM32F103的USART实验

  分别发送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的作用是当数据出现错误时完成报错,是可选功能,程序中给了一种思路,未做调试。结果如下:

UART学习之路(三)基于STM32F103的USART实验

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/606770.html

(0)
上一篇 2021年5月14日
下一篇 2021年5月14日

精彩推荐