[其他ST产品] STM32串口发送和接收多个数据教程基于气体传感器实战

[复制链接]
3672|26
 楼主| 4c1l 发表于 2023-10-11 15:53 | 显示全部楼层 |阅读模式
串口的数据接收和发送
在这里我想把串口这一部分内容好好说一下,从事stm32开发已经好几年了,自以为对stm32已经掌握的很好了,后来才发现自己只是浮于表面,没有好好的深入学习stm32的底层

1.简介
串口是MCU至关重要的外部接口,同时也是软件开发过程中重要的调试工具,现在基本上所有的MCU都会带有串口。
串口的设置步骤:

串口时钟使能,GPIO时钟使能。

设置引脚复用器映射:调用GPIO_PinAFConfig函数。

GPIO 初始化设置:要设置模式为复用功能。

串口参数初始化:设置波特率,字长,奇偶校验等参数。

开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。

使能串口。

编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。
关于7中的中断处理函数,有一个需要注意的,当使用的串口是USART时,中断处理函数名: USARTxIRQHandler(x 对应串口号),当使用的串口时UART时,中断处理函数名: UARTxIRQHandler(x 对应串口号),切记这点,否则会导致串口中断函数无法进入,而且DEBUG时该中断函数无法设置断点。

评论

———————————————— 版权声明:本文为CSDN博主「不会武功不懂江湖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_43903002/article/details/125743272  发表于 2023-10-11 15:54
 楼主| 4c1l 发表于 2023-10-11 15:55 | 显示全部楼层
串口配置程序
我使用的是串口5,UART5,TX为PC12,RX为PD2

  1. void Usart5_Init(u32 bound)
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure; //1.GPIO口初始化       
  4.         USART_InitTypeDef  USART_InitStructure; //串口初始化
  5.         NVIC_InitTypeDef   NVIC_InitStructure; //中断初始化
  6.        
  7.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD,ENABLE); //使能GPIOC时钟
  8.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);//使能USART5时钟
  9.        
  10.         //开启串口5对应IO引脚复用映射
  11.         GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
  12.         GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
  13.        
  14.         //IO口初始化配置
  15.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  16.         GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF ;
  17.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  18.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  19.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //速度50MHz
  20.         GPIO_Init(GPIOC, &GPIO_InitStructure);
  21.        
  22.                 //IO口初始化配置
  23.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  24.         GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF ;
  25.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  26.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  27.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //速度50MHz
  28.         GPIO_Init(GPIOD, &GPIO_InitStructure);
  29.        
  30.                 //GPIO  // RS485EN5,此处代码与RS485有关
  31.         GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3;        //
  32.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//
  33.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  34.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//
  35.         GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化GPIOD
  36.        
  37.         //串口初始化配置
  38.         USART_InitStructure.USART_BaudRate = bound;//波特率
  39.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据位
  40.         USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位
  41.         USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶校验位
  42.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式
  43.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  44.         USART_Init(UART5,&USART_InitStructure);
  45.        
  46.         USART_Cmd(UART5, ENABLE);  //使能串口5
  47.        
  48.         USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//开启相关中断
  49.        
  50.         // USART5 NVIC初始化
  51.         NVIC_InitStructure.NVIC_IRQChannel= UART5_IRQn;
  52.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 3;
  53.         NVIC_InitStructure.NVIC_IRQChannelSubPriority= 3;
  54.         NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
  55.        
  56.         NVIC_Init(&NVIC_InitStructure);
  57.        
  58.        
  59. }
 楼主| 4c1l 发表于 2023-10-11 15:55 | 显示全部楼层
串口发送程序
此处有多个发送程序,但是每个串口发送程序都是通过往UART5->DR寄存器里写数据来实现发送数据,接收同理,都是读UART5->DR寄存器的数据。 当需要发送数据时,在while中调用下面这些函数就可以。
 楼主| 4c1l 发表于 2023-10-11 15:55 | 显示全部楼层
  1. /发送一个字节数据
  2. //input:byte,待发送的数据
  3. void UART5_send_byte(uint8_t byte)
  4. {
  5.         while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==RESET);//等待发送完成
  6.         //UART5->DR=byte;        //byte
  7.         USART_SendData(UART5,byte);
  8. }

  9. //发送多字节数据
  10. void UART5_Send_bytes(uint8_t *Buffer, uint8_t Length)
  11. {
  12.         uint8_t i=0;
  13.         while(i<Length)
  14.         {
  15.                 UART5_send_byte(Buffer[i++]);
  16.         }
  17.         GPIO_ResetBits(GPIOD, GPIO_Pin_3);        //USART5 SEND DATA 开关 1:send  0;receive
  18. }

  19. //发送多字节数据+校验和
  20. void UART5_Send(uint8_t *Buffer, uint8_t Length)
  21. {
  22.         uint8_t i=0;
  23.         while(i<Length)
  24.         {
  25.                 if(i<(Length-1))
  26.                 Buffer[Length-1]+=Buffer[i];//累加Length-1前的数据
  27.                 if(i==(Length-1))
  28.                 {
  29.                         Buffer[Length-1]=Buffer[Length-1]&0xFF;
  30.                 }
  31.                 UART5_send_byte(Buffer[i++]);
  32.         }
  33. }
 楼主| 4c1l 发表于 2023-10-11 15:55 | 显示全部楼层
串口接收数据
串口接收数据的方式有两种,1是采用轮询方式,2是采用中断方式。采用中断来接收串口数据不需要手动调用接收函数,因为串口的接收数据实在中断中所实现的,是需要编写中断函数和接收数据处理函数即可。
 楼主| 4c1l 发表于 2023-10-11 15:55 | 显示全部楼层
  1. void usart5_data_analyse(u8 *buf,u8 num,u8 WhichGas) //串口接受数据处理程序
  2. {
  3. //接收数据处理函数,自己根据需求来填写
  4. }
  5. void UART5_IRQHandler(void) //中断处理函数,当发送中断时MCU自动调用该函数。
  6. {
  7.         uint8_t res; static u8 i1=0;
  8.         if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)//判断接收标志
  9.         {
  10.                 res = UART5->DR;
  11.                 UART5_RX_BUF[i1] = res;
  12.                 i1++;
  13.                 if(i1 > 17 )//
  14.                 {               
  15.                         usart5_data_analyse(UART5_RX_BUF,18,WhichGas);//接收数据处理函数
  16.                         i1=0;//缓存清0
  17.                         UART5_RX_STA=1;
  18.                 }
  19.         }
  20. }
 楼主| 4c1l 发表于 2023-10-11 15:56 | 显示全部楼层
串口的中断接收数据方式有两种,一种是空闲中断IDLE,一种是RXNE(读数据寄存器非空),当RXNE位被置 1 的时候,就是提示已经有数据被接收到了并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。
 楼主| 4c1l 发表于 2023-10-11 15:57 | 显示全部楼层
USART框图
这个框图就很清楚的描述清楚了串口的数据收发过程,发送过程是1-->2-->3, 接收过程是:4-->5-->6
1557652655585bffa.png
 楼主| 4c1l 发表于 2023-10-11 15:57 | 显示全部楼层
USART_SR串口状态寄存器

195906526556694b5c.png

有几个寄存器需要了解一下。
 楼主| 4c1l 发表于 2023-10-11 15:58 | 显示全部楼层
TXE:发送数据寄存器为空 (Transmit data register empty)
当 TDR 寄存器的内容已传输到移位寄存器时,该位由硬件置 1。如果 USART_CR1 寄存器
中 TXEIE 位 = 1,则会生成中断。通过对 USART_DR 寄存器执行写入操作将该位清零。
0:数据未传输到移位寄存器
1:数据传输到移位寄存器
 楼主| 4c1l 发表于 2023-10-11 15:59 | 显示全部楼层
TC:发送完成 (Transmission complete)
如果已完成对包含数据的帧的发送并且 TXE 置 1,则该位由硬件置 1。如果 USART_CR1 寄存
器中 TCIE = 1,则会生成中断。该位由软件序列清零(读取 USART_SR 寄存器,然后写入
USART_DR 寄存器)。 TC 位也可以通过向该位写入‘0’来清零。建议仅在多缓冲区通信
时使用此清零序列。
0:传送未完成
1:传送已完成
 楼主| 4c1l 发表于 2023-10-11 16:00 | 显示全部楼层
RXNE:读取数据寄存器不为空 (Read data register not empty)
当 RDR 移 位 寄 存 器 的 内 容 已 传 输 到 USART_DR 寄 存 器 时,该 位 由 硬 件 置 1。如 果
USART_CR1 寄存器中 RXNEIE = 1,则会生成中断。通过对 USART_DR 寄存器执行读入
操作将该位清零。 RXNE 标志也可以通过向该位写入零来清零。建议仅在多缓冲区通信时使
用此清零序列。
0:未接收到数据
1:已准备好读取接收到的数据
 楼主| 4c1l 发表于 2023-10-11 16:00 | 显示全部楼层
IDLE:检测到空闲线路 (IDLE line detected)
检测到空闲线路时,该位由硬件置 1。如果 USART_CR1 寄存器中 IDLEIE = 1,则会生成中
断。该位由软件序列清零(读入 USART_SR 寄存器,然后读入 USART_DR 寄存器)。
0:未检测到空闲线路
1:检测到空闲线路
 楼主| 4c1l 发表于 2023-10-11 16:00 | 显示全部楼层
ORE:上溢错误 (Overrun error)
在 RXNE = 1 的情况下,当移位寄存器中当前正在接收的字准备好传输到 RDR 寄存器时,该
位由硬件置 1。如果 USART_CR1 寄存器中 RXNEIE = 1,则会生成中断。该位由软件序列清
零(读入 USART_SR 寄存器,然后读入 USART_DR 寄存器)。
0:无上溢错误
1:检测到上溢错误

 楼主| 4c1l 发表于 2023-10-11 16:01 | 显示全部楼层
二、USART转RS485
1.串口转RS485电路设计
采用MAX3485芯片实现串口转485.
738516526564dd92e9.png
 楼主| 4c1l 发表于 2023-10-11 16:01 | 显示全部楼层
需要注意的一点是,RE和DE引脚是用来控制数据的接收和发送,当RE和DE为高电平时,芯片处于发送状态,RE和DE为低电平时,芯片处于接收状态
 楼主| 4c1l 发表于 2023-10-11 16:02 | 显示全部楼层
实现USART发送和接收多个数据
串口发送和接受多个数据也是在单个发送和接收的基础上实现的,由于项目需要读取四个气体的浓度,所以需要串口分别发送四个数据请求并接受四个串口数据,注意数据的格式都是16进制
 楼主| 4c1l 发表于 2023-10-11 16:02 | 显示全部楼层
oid Get_Gas_Density(void) //该函数先延时100s,如何轮流发送四个数据并接受四个数据,接受数据是通过中断来实现
{
                if(tx_times==1)
                {               
                        for(WaitTime=0;WaitTime<100;WaitTime++) //wait 30s
                        {
                                delay_ms(1000);
                        }
                        tx_times=2;
                }
                if(tx_times==2)
                {
                                UART5_RX_STA = 0;
                                GPIO_SetBits(GPIOD, GPIO_Pin_3);        //USART5 SEND DATA 开关 1:send  0;receive
                                delay_ms(50);
                                LED1=0;
                                WhichGas = 0;
                                UART5_Send_bytes( UART5_TX_BUF_1, 12);
                                LED1=1;                       
                                tx_times = 3;
                                delay_ms(1000);
                }
                else        if((tx_times==3)&&(UART5_RX_STA))
                {
                                UART5_RX_STA = 0;
                                GPIO_SetBits(GPIOD, GPIO_Pin_3);        //USART5 SEND DATA 开关 1:send  0;receive
                                delay_ms(50);
                                LED1=0;
                                WhichGas = 1;
                                UART5_Send_bytes( UART5_TX_BUF_2, 12);
                                LED1=1;
                                tx_times = 4;
                                delay_ms(1000);       
                }
                else        if((tx_times==4)&&(UART5_RX_STA))
                {
                                UART5_RX_STA = 0;
                                GPIO_SetBits(GPIOD, GPIO_Pin_3);        //USART5 SEND DATA 开关 1:send  0;receive
                                delay_ms(50);
                                LED1=0;
                                WhichGas = 2;
                                UART5_Send_bytes( UART5_TX_BUF_3, 12);       
                                LED1=1;
                                tx_times = 5;
                                delay_ms(1000);
                }
                                else        if((tx_times==5)&&(UART5_RX_STA))
                {
                                UART5_RX_STA = 0;
                                GPIO_SetBits(GPIOD, GPIO_Pin_3);        //USART5 SEND DATA 开关 1:send  0;receive
                                delay_ms(50);
                                LED1=0;
                                WhichGas = 3;
                                UART5_Send_bytes( UART5_TX_BUF_4, 12);               
                                LED1=1;
                                tx_times=2;
                                delay_ms(1000);       
                }
                else
                {}
       
}
 楼主| 4c1l 发表于 2023-10-11 16:02 | 显示全部楼层
void usart5_data_analyse(u8 *buf,u8 num,u8 WhichGas) //串口接受数据处理程序
{

        CalcDedmsoty[1] = (buf[11]&0x0f) * 100;
        CalcDedmsoty[0] = ((buf[11]>>4)&0x0f) * 1000;
        CalcDedmsoty[2] = (buf[12]&0x0f) ;
        CalcDedmsoty[3] = ((buf[12]>>4)&0x0f) * 10;
        GasDensity[WhichGas] = CalcDedmsoty[0]+CalcDedmsoty[1]+CalcDedmsoty[2]+CalcDedmsoty[3];
        switch(buf[13])
        {
                case 0x00:
                        GasDensity[WhichGas]        =        GasDensity[WhichGas];
                        break;
                case 0x01:
                        GasDensity[WhichGas] = GasDensity[WhichGas]/10;
                        break;
                case 0x02:
                        GasDensity[WhichGas] = GasDensity[WhichGas]/100;
                        break;
                case 0x03:
                        GasDensity[WhichGas] = GasDensity[WhichGas]/1000;
                        break;
        }
}
void UART5_IRQHandler(void)
{
        uint8_t res; static u8 i1=0;
        if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)//判断接收标志
        {
                res = UART5->DR;
                UART5_RX_BUF[i1] = res;
                i1++;
                if(i1 > 17 )//
                {               
                        usart5_data_analyse(UART5_RX_BUF,18,WhichGas);
                        LED2=0;
                        i1=0;//缓存清0
                        UART5_RX_STA=1;
                        LED2=1;
                }
        }
}
Stahan 发表于 2023-10-11 19:17 | 显示全部楼层
不定长数据怎么发送啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

703

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部