分享个我的, 任意个串口, 你按你的需求改改就行了. 我已经在N个项目里用过了.
字符中断方式接收, 空闲中断判断帧尾, 阻塞方式发送. 哪位有兴趣了可以试试加上中断方式发送, 这样CPU负担轻一些. 不用DMA是因为串口多了没有那么多DMA通道用, 就算够用, SPI/ADC之类也没得用了.
// 请添加你自己的头文件 #define MAX_LEN 128 // 帧长度, 按你的需要 #define USART_TOTAL_NUM 4 // 串口个数 typedef struct { const USART_TypeDef* usartx; unsigned char msg[MAX_LEN]; unsigned char rxbuf[MAX_LEN]; // 用两个缓冲区, 进入后处理函数后接收缓冲区就可以用于新一轮接收, 只是比较浪费内存 unsigned char* rxptr; int size, updated; } usartx_t; static struct { usartx_t usart[USART_TOTAL_NUM]; void (*parser_f)(const void* msg, int size, int source); // 后处理函数指针, 用source区分是哪个串口 } g; void USART_BindParser(void (*parser_f)(const void* msg, int size, int source)) // 初始化串口后用它绑定你自己的回调函数做进一步处理 { g.parser_f = parser_f; } void USART_ReConfig(USART_TypeDef* USARTx, unsigned long baudrate) { USART_InitTypeDef uis; // USART_DeInit(USARTx); USART_Cmd(USARTx, DISABLE); USART_StructInit(&uis); uis.USART_WordLength = USART_WordLength_8b; uis.USART_StopBits = USART_StopBits_1; uis.USART_Parity = USART_Parity_No; uis.USART_HardwareFlowControl = USART_HardwareFlowControl_None; uis.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; uis.USART_BaudRate = baudrate; USART_Init(USARTx, &uis); // if(USARTx == USART1) // USART_SWAPPinCmd(USARTx, ENABLE); // 根据你的需求, 交换串口的tx/rx脚, 只在f0和f3系列有效 USART_Cmd(USARTx, ENABLE); USART_ITConfig(USARTx, USART_IT_IDLE, ENABLE); USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); USART_ITConfig(USARTx, USART_IT_ORE, ENABLE); USART_ITConfig(USARTx, USART_IT_FE, ENABLE); } void USART_Config(void) { NVIC_InitTypeDef nis; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE); // RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE); nis.NVIC_IRQChannel = USART1_IRQn; nis.NVIC_IRQChannelPreemptionPriority = 1; nis.NVIC_IRQChannelSubPriority = 1; nis.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nis); nis.NVIC_IRQChannel = UART5_IRQn; NVIC_Init(&nis); for(int i = 0; i < USART_TOTAL_NUM; i++) { g.usart[i].rxptr = g.usart[i].rxbuf; g.usart[i].size = 0; g.usart[i].updated = 0; } g.usart[0].usartx = USART1; g.usart[1].usartx = USART2; g.usart[2].usartx = USART3; g.usart[3].usartx = UART4; // g.usart[4].usartx = UART5; USART_ReConfig(USART1, 500000UL); // 依次初始化你需要的串口 // USART_ReConfig(UART5, 500000UL); } void USART_WriteData(USART_TypeDef* USARTx, const void* data, int num) // 阻塞方式, 中断方式自己写吧. DMA方式虽然好但是没那么多通道 { while(num--) { while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); USART_SendData(USARTx, *((unsigned char*)data)); data++; } while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); } void USART_WriteByte(USART_TypeDef* USARTx, unsigned char byte) // 一般用不到, 有些库可能需要单字节发送函数 { USART_WriteData(USARTx, &byte, 1); } void USART_Poll(void) // 在主循环里调用, 不要放在中断里 { for(int i = 0; i < USART_TOTAL_NUM; i++) { usartx_t* p = &(g.usart[i]); if(p->updated) { g.parser_f(p->msg, p->size, i); // 在这里调用你自己的回调函数做进一步处理 p->updated = 0; } } } void USART_RXNE_IRQHandler(USART_TypeDef* USARTx) // 在RXNE中断里调用 { for(int i = 0; i < USART_TOTAL_NUM; i++) { usartx_t* p = &(g.usart[i]); if(USARTx == p->usartx) { if(p->rxptr - p->rxbuf >= MAX_LEN) break; *(p->rxptr) = p->usartx->RDR; // 在F0和F3是RDR, 在F1是DR p->rxptr++; break; } } } void USART_IDLE_IRQHandler(USART_TypeDef* USARTx) // 在IDLE中断里调用 { for(int i = 0; i < USART_TOTAL_NUM; i++) { usartx_t* p = &(g.usart[i]); if(USARTx == p->usartx) { p->size = p->rxptr - p->rxbuf; memcpy(p->msg, p->rxbuf, p->size); p->rxptr = p->rxbuf; p->updated = 1; break; } } }