STM32串口发送数据和接收数据方式总结
时间:2021-11-19 17:28:00
串口发送数据
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; 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的函数写法加以增补。
串口接受数据
串口接受最初应有必定和谈,如发送一帧数据应该有标记标记,也可两个标记都有。
#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) Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2); 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从新接受数据。
这里接收精确数据间接打印进去能够经由过程配置标识而后在主函数内里轮询操纵。
以上接受方式中缀一次接受一个字符,这在UCOS及时内核体系频仍中缀异常损耗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); for (i = 0;i < temp;i++) { Uart2_Buffer[i] = USART1_RECEIVE_DMABuffer[i]; } DMA_Cmd(DMA1_Channel5,ENABLE); } }
#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发送。
有了以上接受体式格局普通的串口数据处置是没有题目上面再讲一下,在ucosiii应用信号量新闻行列贮存治理方式来处咱们的串口数据来讲一下这类体式格局比照其余体式格局的一些优缺点。
OS_MSG_SIZE Usart1_Rx_cnt; //字节巨细计数值 unsigned char Usart1_data; //每次中缀接受的数据 unsigned char* Usart1_Rx_Ptr; unsigned char* Usart1_Rx_Ptr1; void USART1_IRQHandler() { OS_ERR err; OSIntEnter(); if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET) Usart1_data = USART_ReceiveData(USART1); 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=(unsigned char*) OSMemGet((OS_MEM*)&UART1_MemPool,&err); } if(Usart1_data == 0x0a ) //字节巨细增添 OSTaskQPost((OS_TCB * )&Task1_TaskTCB, (void * )Usart1_Rx_Ptr1, //发送存储地点新闻行列 (OS_MSG_SIZE )Usart1_Rx_cnt, (OS_OPT )OS_OPT_POST_FIFO, NULL; //将指针指向避免修正 Usart1_Rx_cnt=0; //字节巨细计数清零 } else { *Usart1_Rx_Ptr=Usart1_data;
void task1_task(void *p_arg) { OS_ERR err; OS_MSG_SIZE Usart1_Data_size; u8 *p; while(1) { p=(u8*)OSTaskQPend((OS_TICK )0, 0, (OS_ERR* )&err); printf("%s\r\n",p); //打印数据 delay_ms(100); OSMemPut((OS_MEM* )&UART1_MemPool, void* )p, (OS_ERR* )&err); OSSemPost((OS_SEM* )&SEM_IAR_UART, 0,0,1,500,OS_OPT_TIME_PERIODIC,&err); }}
作者能够吃的鱼
原文:https://blog.csdn.net/qq_35281599