STM32串⼝发送数据和接收数据⽅式总结
⽂章⽬录
串⼝发送数据
1、串⼝发送数据最直接的⽅式就是标准调⽤库函数 。
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
第⼀个参数是发送的串⼝号,第⼆个参数是要发送的数据了。但是⽤过的朋友应该觉得不好⽤,⼀次只能发送单个字符,所以我们有必要根据这个函数加以扩展:
void Send_data(u8 *s)
{
while(*s!='\0')
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET);
USART_SendData(USART1,*s);
s++;
}
}
以上程序的形参就是我们调⽤该函数时要发送的字符串,这⾥通过循环调⽤USART_SendData来⼀ ⼀发送我们的字符串。
while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET);
这句话有必要加,他是⽤于检查串⼝是否发送完成的标志,如果不加这句话会发⽣数据丢失的情况。这个函数只能⽤于串⼝1发送。有些时候根据需要,要⽤到多个串⼝发送那么就还需要改进这个程序。如下:
void Send_data(USART_TypeDef * USARTx,u8 *s)
{
while(*s!='\0')
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC )==RESET);
USART_SendData(USARTx,*s);
s++;
}
}
这样就可实现任意的串⼝发送。但有⼀点,我在使⽤实时操作系统的时候(如UCOS,Freertos等),需考虑函数重⼊的问题。
当然也可以简单的实现把该函数复制⼀下,然后修改串⼝号也可以避免该问题。然⽽这个函数不能像printf那样传递多个参数,所以还可以在改进,最终程序如下:
void USART_printf ( USART_TypeDef * USARTx,char* Data,...)
{
const char*s;
int d;
char buf[16];
va_list ap;
va_start(ap, Data);
while(* Data !=0)// 判断是否到达字符串结束符
{
if(* Data ==0x5c)//'\'
{
switch(*++Data )
{
case'r'://回车符
USART_SendData(USARTx,0x0d);
Data ++;
break;python怎么读取串口数据
case'n'://换⾏符
USART_SendData(USARTx,0x0a);
Data ++;
break;
default:
Data ++;
break;
}
}
else if(* Data =='%')
{//
switch(*++Data )
{
case's'://字符串
s =va_arg(ap,const char*);
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;
}
}
else USART_SendData(USARTx,*Data++);
while( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE )== RESET );
}
}
该函数就可以像printf使⽤可变参数,⽅便很多。通过观察函数但这个函数只⽀持了%d,%s的参数,想要⽀持更多,可以仿照printf的函数写法加以补充。
2、 直接使⽤printf函数。
很多朋友都知道想要STM32要直接使⽤printf不⾏的。需要加上以下的重映射函数;
如果不想添加以上代码,也可以勾选以下的Use MicroLI选项来⽀持printf函数使⽤:
串⼝接受数据
串⼝接收最后应有⼀定的协议,如发送⼀帧数据应该有头标志或尾标志,也可两个标志都有。
这样在处理数据时既能能保证数据的正确接收,也有利于接收完后我们处理数据。串⼝的配置在这⾥就不在赘述,这⾥我以串⼝2接收中断服务程序函数且接收的数据包含头尾标识为例。
#define Max_BUFF_Len 18
unsigned char Uart2_Buffer[Max_BUFF_Len];
unsigned int Uart2_Rx=0;
void USART2_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\r\n",Uart2_Buffer);//这⾥我做打印数据处理
Uart2_Rx=0;
}
else
{
Uart2_Rx=0;//不是我们需要的数据或者达到最⼤接收数则开始重新接收
}
}
}
}
数据的头标识为“\n”既换⾏符,尾标识为“+”。该函数将串⼝接收的数据存放在USART_Buffer数组中,然后先判断当前字符是不是尾标识,如果是说明接收完毕,然后再来判断头标识是不是“+”号,如果还是那么就是我们想要的数据,接下来就可以进⾏相应数据的处理了。但如果不是那么就让Usart2_Rx=0重新接收数据。
这样做的有以下好处:
1.可以接受不定长度的数据,最⼤接收长度可以通过Max_BUFF_Len来更改
2.可以接受指定的数据
3.防⽌接收的数据使数组越界
这⾥我的把接受正确数据直接打印出来,也可以通过设置标识位,然后在主函数⾥⾯轮询再操作。
以上的接收形式,是中断⼀次就接收⼀个字符,这在UCOS等实时内核系统中频繁的中断,⾮常消耗CPU资源,在有些时候我们需要接收⼤量数据时且波特率很⾼的情况下,长时间中断会带来⼀些额外的问题。
所以以DMA形式配合串⼝的IDLE(空闲中断)来接受数据将会⼤⼤的提⾼CPU的利⽤率,减少系统资源的消耗。⾸先还是先看代码。
#define DMA_USART1_RECEIVE_LEN 18
void USART1_IRQHandler(void)
{
u32 temp =0;
uint16_t i =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的发送:
#define DMA_USART1_SEND_LEN 64
void DMA_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发送。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。