0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32串口发送数据和接收数据有哪些方式

Q4MP_gh_c472c21 来源:嵌入式ARM 作者:可以吃的鱼 2020-12-30 10:25 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

串口发送数据

1、串口发送数据最直接的方式就是标准调用库函数 。

voidUSART_SendData(USART_TypeDef*USARTx,uint16_tData);

第一个参数是发送的串口号,第二个参数是要发送的数据,但是用过的朋友应该觉得不好用,一次只能发送单个字符,所以我们有必要根据这个函数加以扩展:

voidSend_data(u8*s)
{
while(*s!='')
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,*s);
s++;
}
}

以上程序的形参就是我们调用该函数时要发送的字符串,这里通过循环调用USART_SendData来一 一发送我们的字符串。

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);

这句话有必要加,它是用于检查串口是否发送完成的标志,如果不加这句话会发生数据丢失的情况。这个函数只能用于串口1发送。有些时候根据需要,要用到多个串口发送,那么就还需要改进这个程序。如下:

voidSend_data(USART_TypeDef*USARTx,u8*s)
{
while(*s!='')
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
USART_SendData(USARTx,*s);
s++;
}
}

这样就可实现任意的串口发送。但有一点,我在使用实时操作系统的时候(如UCOS,Freertos等),需考虑函数重入的问题。

当然也可以简单的实现把该函数复制一下,然后修改串口号也可以避免该问题。然而这个函数不能像printf那样传递多个参数,所以还可以再改进,最终程序如下:

voidUSART_printf(USART_TypeDef*USARTx,char*Data,...)
{
constchar*s;
intd;
charbuf[16];

va_listap;
va_start(ap,Data);

while(*Data!=0)//判断是否到达字符串结束符
{
if(*Data==0x5c)//''
{
switch(*++Data)
{
case'r'://回车符
USART_SendData(USARTx,0x0d);
Data++;
break;

case'n'://换行符
USART_SendData(USARTx,0x0a);
Data++;
break;

default:
Data++;
break;
}
}

elseif(*Data=='%')
{//
switch(*++Data)
{
case's'://字符串
s=va_arg(ap,constchar*);

for(;*s;s++)
{
USART_SendData(USARTx,*s);
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}

Data++;

break;

case'd':
//十进制
d=va_arg(ap,int);

itoa(d,buf,10);

for(s=buf;*s;s++)
{
USART_SendData(USARTx,*s);
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}

Data++;

break;

default:
Data++;

break;

}
}

elseUSART_SendData(USARTx,*Data++);

while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);

}
}

该函数就可以像printf使用可变参数,方便很多。通过观察函数但这个函数只支持了%d,%s的参数,想要支持更多,可以仿照printf的函数写法加以补充。

2、 直接使用printf函数。

很多朋友都知道STM32直接使用printf不行的。需要加上以下的重映射函数:

fa51f286-49b8-11eb-8b86-12bb97331649.png

如果不想添加以上代码,也可以勾选以下的Use MicroLI选项来支持printf函数使用:

fa75d4a8-49b8-11eb-8b86-12bb97331649.png

串口接收数据

串口接收最后应有一定的协议,如发送一帧数据应该有头标志或尾标志,也可两个标志都有。

这样在处理数据时既能能保证数据的正确接收,也有利于接收完后我们处理数据。串口的配置在这里就不再赘述,这里以串口2接收中断服务程序函数且接收的数据包含头尾标识为例。

#defineMax_BUFF_Len18
unsignedcharUart2_Buffer[Max_BUFF_Len];
unsignedintUart2_Rx=0;
voidUSART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)//中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE);//清除中断标志

Uart2_Buffer[Uart2_Rx]=USART_ReceiveData(USART2);//接收串口1数据到buff缓冲区
Uart2_Rx++;

if(Uart2_Buffer[Uart2_Rx-1]==0x0a||Uart2_Rx==Max_BUFF_Len)//如果接收到尾标识是换行符(或者等于最大接受数就清空重新接收)
{
if(Uart2_Buffer[0]=='+')//检测到头标识是我们需要的
{
printf("%s
",Uart2_Buffer);//这里我做打印数据处理
Uart2_Rx=0;
}
else
{
Uart2_Rx=0;//不是我们需要的数据或者达到最大接收数则开始重新接收
}
}
}
}

数据的头标识为“ ”既换行符,尾标识为“+”。该函数将串口接收的数据存放在USART_Buffer数组中,然后先判断当前字符是不是尾标识,如果是,说明接收完毕,然后再来判断头标识是不是“+”号,如果还是,那么就是我们想要的数据,接下来就可以进行相应数据的处理了。但如果不是,那么就让Usart2_Rx=0重新接收数据。

这样做有以下好处:

  • 可以接收不定长度的数据,最大接收长度可以通过Max_BUFF_Len来更改

  • 可以接收指定的数据

  • 防止接收的数据使数组越界

这里得把接收正确数据直接打印出来,也可以通过设置标识位,然后在主函数里面轮询再操作。

以上的接收形式是中断一次就接收一个字符,这在UCOS等实时内核系统中频繁的中断,非常消耗CPU资源,在有些时候我们需要接收大量数据时且波特率很高的情况下,长时间中断会带来一些额外的问题。

所以以DMA形式配合串口的IDLE(空闲中断)来接收数据将会大大的提高CPU的利用率,减少系统资源的消耗。首先还是先看代码。

#defineDMA_USART1_RECEIVE_LEN18
voidUSART1_IRQHandler(void)
{
u32temp=0;
uint16_ti=0;

if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
{
USART1->SR;
USART1->DR;//这里我们通过先读SR(状态寄存器)和DR(数据寄存器)来清USART_IT_IDLE标志
DMA_Cmd(DMA1_Channel5,DISABLE);
temp=DMA_USART1_RECEIVE_LEN-DMA_GetCurrDataCounter(DMA1_Channel5);//接收的字符串长度=设置的接收长度-剩余DMA缓存大小
for(i=0;i< temp;i++)  
        {  
            Uart2_Buffer[i] = USART1_RECEIVE_DMABuffer[i];  
                
        }  
        //设置传输数据长度
DMA_SetCurrDataCounter(DMA1_Channel5,DMA_USART1_RECEIVE_LEN);
//打开DMA
DMA_Cmd(DMA1_Channel5,ENABLE);
}
}

之前的串口中断是一个一个字符的接收,现在改为串口空闲中断,就是一帧数据过来才中断进入一次。而且接收的数据时候是DMA来搬运到我们指定的缓冲区(也就是程序中的USART1_RECEIVE_DMABuffer数组),是不占用CPU时间资源的。

最后在讲下DMA的发送:

#defineDMA_USART1_SEND_LEN64
voidDMA_SEND_EN(void)
{
DMA_Cmd(DMA1_Channel4,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4,DMA_USART1_SEND_LEN);
DMA_Cmd(DMA1_Channel4,ENABLE);
}

这里需要注意下DMA_Cmd(DMA1_Channel4,DISABLE)函数需要在设置传输大小之前调用一下,否则不会重新启动DMA发送。

有了以上的接收方式,对一般的串口数据处理是没有问题的了。下面再讲一下,在ucosiii中我使用信号量+消息队列+储存管理的形式来处理我们的串口数据。先来说一下这种方式对比其他方式的一些优缺点。

一般对串口的处理形式是"生产者"和"消费者"的模式,即本次接收的数据要马上处理,否则当数据大量涌进的时候,就来不及"消费"掉生产者(串口接收中断)的数据,那么就会丢失本次的数据处理。所以使用队列就能够很方便的解决这个问题。

在下面的程序中,对数据的处理是先接收,在处理,如果在处理的过程中,有串口中断接收数据,那么就把它依次放在队列中,队列的特征是先进先出,在串口中就是先处理先接收的数据,所以根据生产和消费的速度,定义不同大小的消息队列缓冲区就可以了。缺点就是太占用系统资源,一般51单片机是没可能了。下面是从我做的项目中截取过来的程序:

OS_MSG_SIZEUsart1_Rx_cnt;//字节大小计数值
unsignedcharUsart1_data;//每次中断接收的数据
unsignedchar*Usart1_Rx_Ptr;//储存管理分配内存的首地址的指针
unsignedchar*Usart1_Rx_Ptr1;//储存首地址的指针

voidUSART1_IRQHandler()
{
OS_ERRerr;
OSIntEnter();

if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)!=RESET)//中断产生
{
USART_ClearFlag(USART1,USART_FLAG_RXNE);//清除中断标志

Usart1_data=USART_ReceiveData(USART1);//接收串口1数据到buff缓冲区

if(Usart1_data=='+')//接收到数据头标识
{
//OSSemPend((OS_SEM*)&SEM_IAR_UART,//这里请求信号量是为了保证分配的存储区,但一般来说不允许
//(OS_TICK)0,//在终端服务函数中调用信号量请求但因为
//(OS_OPT)OS_OPT_PEND_NON_BLOCKING,//我OPT参数设置为非阻塞,所以可以这么写
//(CPU_TS*)0,
//(OS_ERR*)&err);
//if(err==OS_ERR_PEND_WOULD_BLOCK)//检测到当前信号量不可用
//{
//printf("error");
//}
Usart1_Rx_Ptr=(unsignedchar*)OSMemGet((OS_MEM*)&UART1_MemPool,&err);//分配存储区
Usart1_Rx_Ptr1=Usart1_Rx_Ptr;//储存存储区的首地址
}
if(Usart1_data==0x0a)//接收到尾标志
{
*Usart1_Rx_Ptr++=Usart1_data;
Usart1_Rx_cnt++;//字节大小增加
OSTaskQPost((OS_TCB*)&Task1_TaskTCB,
(void*)Usart1_Rx_Ptr1,//发送存储区首地址到消息队列
(OS_MSG_SIZE)Usart1_Rx_cnt,
(OS_OPT)OS_OPT_POST_FIFO,//先进先出,也可设置为后进先出,再有地方很有用
(OS_ERR*)&err);

Usart1_Rx_Ptr=NULL;//将指针指向为空,防止修改
Usart1_Rx_cnt=0;//字节大小计数清零
}
else
{
*Usart1_Rx_Ptr=Usart1_data;//储存接收到的数据
Usart1_Rx_Ptr++;
Usart1_Rx_cnt++;
}
}
OSIntExit();
}

上面被注释掉的代码为了防止当分区中没有空闲的存储块时加入信号量,打印出报警信息。当然我们也可以将存储块直接设置大一点,但是还是无法避免当没有可用存储块时会程序会崩溃现象的发生。希望懂的朋友能告知下~。

下面是串口数据处理任务,这里删去了其他代码,只把他打印出来了而已。

voidtask1_task(void*p_arg)
{
OS_ERRerr;
OS_MSG_SIZEUsart1_Data_size;
u8*p;

while(1)
{
p=(u8*)OSTaskQPend((OS_TICK)0,//请求消息队列,获得储存区首地址
(OS_OPT)OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE*)&Usart1_Data_size,
(CPU_TS*)0,
(OS_ERR*)&err);

printf("%s
",p);//打印数据

delay_ms(100);
OSMemPut((OS_MEM*)&UART1_MemPool,//释放储存区
(void*)p,
(OS_ERR*)&err);

OSSemPost((OS_SEM*)&SEM_IAR_UART,//释放信号量
(OS_OPT)OS_OPT_POST_NO_SCHED,
(OS_ERR*)&err);

OSTimeDlyHMSM(0,0,1,500,OS_OPT_TIME_PERIODIC,&err);
}
}

责任编辑:xj

原文标题:STM32串口发送数据和接收数据方式总结!

文章出处:【微信公众号:嵌入式ARM】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    7349

    浏览量

    95023
  • STM32
    +关注

    关注

    2313

    文章

    11195

    浏览量

    374653
  • 串口
    +关注

    关注

    15

    文章

    1627

    浏览量

    83348

原文标题:STM32串口发送数据和接收数据方式总结!

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    STM32驱动串口屏,STM32F103C8T6串口发送指令控制HMI串口

    仅是一部分常用的基础指令,更多更仔细的指令或者函数可以访问陶晶驰资料官网。 硬件和接线 硬件需要一块STM32F103C8T6的开发板,杜邦线诺干,2.4寸串口屏一块 接线 串口STM32
    的头像 发表于 02-10 17:07 559次阅读
    <b class='flag-5'>STM32</b>驱动<b class='flag-5'>串口</b>屏,<b class='flag-5'>STM32</b>F103C8T6<b class='flag-5'>串口</b><b class='flag-5'>发送</b>指令控制HMI<b class='flag-5'>串口</b>屏

    串口调试步骤(适合免驱动的232串口设备)

    密码切换到root超级管理下 apt update 更新插件 apt-get install cutecom 安装系统自带的串口调试工具 cutecom 输入串口测试的指令会弹出一个图形界面如下图 32串口协议测试需要将第2和第
    的头像 发表于 12-17 17:23 1634次阅读
    <b class='flag-5'>串口</b>调试步骤(适合免驱动的232<b class='flag-5'>串口</b>设备)

    CW32 UART查询方式接收数据编程的示例

    UARTx_ISR.PE 和 UARTx_ISR.FE,确认数据是否有效,如果数据无效,则进行出错处理,如果数据有效,则读取 UARTx_RDR 寄存器并保存数据; 步骤 12:设置
    发表于 12-16 08:19

    普通IO模拟串口的功能

    就开始接收数据,然后读取8个数据位的电平,在等待结束位结束。这样一个字节的数据接收完成了。 有两种
    发表于 12-09 06:10

    NucleiStudio_IDE_201909串口打印浮点型数据时无数据输出,软件该如何配置串口才能打印浮点型数据

    NucleiStudio_IDE_201909串口打印浮点型数据时无数据输出,软件该如何配置串口才能打印浮点型数据
    发表于 11-07 07:57

    UWB650串口测距通信定位模块规格书

    UWB650串口测距通信定位模块规格书
    发表于 11-03 17:40 1次下载

    UART接口数据线接收发送数据

    具体方法和步骤 UART模块依照UART协议标准的异步方式发送接收数据,每个UART模块有TX和RX两根数据线,TX为输出,RX为输入。使
    发表于 10-29 07:37

    使用MCU200T开发板的UART进行数据传输

    我们想要实现从电脑串口调试助手像MCU软核发送一个8bit字节数据例如0xff,并且收到数据软核回传的数据。首先要对MCU的UART
    发表于 10-28 08:16

    串口通信有哪些方式

    串口通信(Serial Communication)是一种通过串行方式逐位传输数据的通信方式,广泛应用于嵌入式系统、工业控制、仪器仪表等领域。其通信
    的头像 发表于 09-28 18:02 1299次阅读

    rtt vision board openmv串口收发数据接收到的是乱码,为什么?

    利用ttl转Usb ,利用电脑上的串口调试助手发送或者接收数据接收到的是乱码,将板子上的rx,tx短接,可以正常收发
    发表于 09-15 06:09

    【RA4M2-SENSOR】—— 12.串口接收不定长度数据

    交互中都有广泛应用。 但在使用串口通讯的时候,我们并不知道对方会发送多少个数据,也不知道数据什么时候发送完,简单来讲就是:如何确保收到一帧完
    发表于 09-01 11:22

    关于stm32f4zgt6和mspm0g3507串口通信问题

    本人在使用stm32f4zgt6和mspm0g3507串口通信时出现问题,情况如下: m0芯片使用软件超时解析进行数据接收,使用标准库的transmit函数进行
    发表于 09-01 11:11

    CYT2B7串口接收会漏接数据怎么解决?

    串口中断方式接收会漏接数据串口工具发送 :1234567芯片
    发表于 08-14 07:29

    STM32串口发送数据,USART_FLAG_TC无法置位怎么解决?

    STM32串口发送数据,在经过一段时间的数据发送,大概200ms
    发表于 07-29 11:44

    STM32407使用串口闲时中断+DMA方式接收最大接收字节是多少?

    使用串口闲时中断+DMA方式接收数据,波特率为460800,DMA接收长度为1024个字节,并开启串口
    发表于 07-22 08:16