N32H473RE+N32 CUBE的 UART5+DMA 串口空闲中断收发测试;环形缓冲区
1.基本收发测试1.N32CUBE配置n32h47x_48x_it.c/* NTFx CODE START(UART5_IRQHandler)*//** * brief This function handles UART5_IRQHandler. */voidUART5_IRQHandler(void){/* NTFx CODE END *//* NTFx CODE START */if(USART_GetFlagStatus(UART5,USART_FLAG_IDLEF)){/* NTFx CODE END */// 1. 清除空闲中断标志读STS再读DATvolatileuint32_ttmpUART5-STS;tmpUART5-DAT;(void)tmp;DMA_EnableChannel(DMA1_CH2,DISABLE);// 2. 停止 DMA 接收uint32_tremainDMA_GetCurrDataCounter(DMA1_CH2);rx_lenUART5_BUF_SIZE-remain;// 3. 获取已接收字节数rx_complete1;// 4. 设置接收完成标志主循环会处理打印}/* NTFx CODE START */if(USART_GetFlagStatus(UART5,USART_FLAG_OREF)||USART_GetFlagStatus(UART5,USART_FLAG_NEF)||USART_GetFlagStatus(UART5,USART_FLAG_FEF)||USART_GetFlagStatus(UART5,USART_FLAG_PEF)){/*clear IT flag*//*Read the sts register first,and the read the DAT register to clear the all error flag*/(void)UART5-STS;(void)UART5-DAT;/* NTFx CODE END */}/* NTFx CODE START */}/* NTFx CODE END(UART5)_IRQHandler*//* NTFx CODE START(DMA1_Channel1_IRQHandler)*//** * brief This function handles DMA1_Channel1_IRQHandler. */voidDMA1_Channel1_IRQHandler(void){/* NTFx CODE END *//* NTFx CODE START */if(DMA_GetIntStatus(DMA_INT_TXC1,DMA1)){/*clear IT flag*/DMA_ClrIntPendingBit(DMA_INT_TXC1,DMA1);/* NTFx CODE END */tx_complete1;// 通知发送完成}/* NTFx CODE START */}/* NTFx CODE END(DMA1_Channel1)_IRQHandler*//* NTFx CODE START(DMA1_Channel2_IRQHandler)*//** * brief This function handles DMA1_Channel2_IRQHandler. */voidDMA1_Channel2_IRQHandler(void){/* NTFx CODE END *//* NTFx CODE START */if(DMA_GetIntStatus(DMA_INT_TXC2,DMA1)){/*clear IT flag*/DMA_ClrIntPendingBit(DMA_INT_TXC2,DMA1);/* NTFx CODE END */}/* NTFx CODE START */}/* NTFx CODE END(DMA1_Channel2)_IRQHandler*/uart.h#ifndef__UART_H#define__UART_H#includemain.h#includeapp1.h#defineUART5_BUF_SIZE64externuint8_ttx_complete;externuint8_trx_complete;externuint16_trx_len;//实际接收长度externuint8_trx_buffer[UART5_BUF_SIZE];voidUART5_DMA_Send(uint8_t*buf,uint16_tlen);voidUART5_Init(void);//初始化voidUART5_StartRecv(void);//启动接收voidUART5_PrintRxData(void);#endifuart.c#includeuart.h#includeapp1.h#includelog.h#includestring.h#includestdio.huint16_trx_len0;//实际接收长度uint8_ttx_buffer[UART5_BUF_SIZE]Hello UART5 DMA!\r\n;uint8_trx_buffer[UART5_BUF_SIZE]{0};uint8_ttx_complete0;uint8_trx_complete0;// 接收完成打印函数voidUART5_PrintRxData(void){if(rx_complete1){rx_complete0;// 清除标志log_info(UART5 RECV (%d bytes): ,rx_len);for(inti0;irx_len;i){log_info(%02X ,rx_buffer[i]);}log_info(\r\n);UART5_StartRecv();//重新启动接收}}// 启动DMA接收voidUART5_StartRecv(void){rx_complete0;DMA_EnableChannel(DMA1_CH2,DISABLE);DMA_SetPerMemAddr(DMA1_CH2,(uint32_t)UART5-DAT,(uint32_t)rx_buffer,UART5_BUF_SIZE);DMA_EnableChannel(DMA1_CH2,ENABLE);}// DMA发送voidUART5_DMA_Send(uint8_t*buf,uint16_tlen){if(!tx_complete)return;tx_complete0;DMA_EnableChannel(DMA1_CH1,DISABLE);DMA_SetPerMemAddr(DMA1_CH1,(uint32_t)UART5-DAT,(uint32_t)buf,len);DMA_EnableChannel(DMA1_CH1,ENABLE);}// 初始化voidUART5_Init(void){tx_complete1;UART5_StartRecv();}在main.c调用UART5_Init();while(1){// 发送测试UART5_DMA_Send(123,3);delay_ms(1000);// 接收测试UART5_PrintRxData();// 打印接收数据}串口调试助手发12 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30运行结果UART5 RECV (22 bytes): 12 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30UART5 RECV (22 bytes): 12 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30UART5 RECV (22 bytes): 12 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30UART5 RECV (22 bytes): 12 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30UART5 RECV (22 bytes): 12 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 30 FF 00 302.发送、接收使用环形缓冲区1.uart.h#ifndef__UART_H#define__UART_H#includemain.h/* 配置参数 ---------------------------------------------------------------*/#defineU5_BUF_SIZE32// DMA单次接收缓冲区大小≥最长帧#defineU5_RING_SIZE256// 环形缓冲区大小建议 2~4 倍最长帧#defineU5_TX_QSIZE2// 发送队列深度/* 外部接口函数 ------------------------------------------------------------*/voidUART5_Init(void);// 初始化UART5、DMA、中断voidUART5_Send(uint8_t*buf,uint16_tlen);// 非阻塞发送voidUART5_Process(void);// 主循环调用处理接收数据/* 以下供中断调用用户不直接调用 -------------------------------------------*/voidUART5_RxIdle_IRQHandler(void);// 空闲中断处理voidUART5_TxCplt_IRQHandler(void);// DMA发送完成中断处理#endif2.uart.c#includeuart.h#includelog.h#includeprotocol.h#includestring.h/* 发送环形队列 */staticuint8_tu5_txq[U5_TX_QSIZE][U5_BUF_SIZE];// 发送队列缓冲区staticuint8_tu5_txl[U5_TX_QSIZE];// 每帧数据长度staticvolatileuint8_tu5_txin0;// 队列写入指针staticvolatileuint8_tu5_txout0;// 队列发送指针staticvolatileuint8_tu5_txcnt0;// 队列中待发送帧数uint8_ttx_idle1;// 发送空闲标志/* 接收环形缓冲区 */staticuint8_tu5_ring[U5_RING_SIZE];// 流式数据总缓冲staticvolatileuint16_tu5_rin0;// 中断写入指针staticvolatileuint16_tu5_rout0;// 主循环读取指针/* DMA双缓冲 */staticuint8_tu5_rx0[U5_BUF_SIZE];// DMA接收缓冲0staticuint8_tu5_rx1[U5_BUF_SIZE];// DMA接收缓冲1staticvolatileuint8_tu5_cur_rx0;// 当前DMA使用哪个缓冲/* 全局变量供外部 */uint8_t*rx_frame_ptrNULL;// 输出当前数据指针uint16_trx_frame_len0;// 输出当前数据长度/* ------------------------- 内部辅助函数 ----------------------------------- */// 启动DMA接收指定缓冲区staticvoidu5_rx_start(uint8_t*buf){DMA_EnableChannel(DMA1_CH2,DISABLE);// 先关闭DMA通道DMA_SetPerMemAddr(DMA1_CH2,(uint32_t)UART5-DAT,(uint32_t)buf,U5_BUF_SIZE);// 设置外设、内存、长度DMA_EnableChannel(DMA1_CH2,ENABLE);// 重新启动DMA}// 中断中写入环形缓冲单字节staticvoidu5_ring_write(uint8_tdata){uint16_tnext(u5_rin1)%U5_RING_SIZE;// 计算下一个写入位置if(next!u5_rout){// 缓冲区未满u5_ring[u5_rin]data;// 写入数据u5_rinnext;// 更新写指针}else{LogErr(U5 ring full, lost\r\n);// 满了就丢数据防死锁}}// 获取当前有效数据长度staticuint16_tu5_ring_avail(void){if(u5_rinu5_rout)returnu5_rin-u5_rout;// 未绕回elsereturnU5_RING_SIZE-(u5_rout-u5_rin);// 绕回情况}// 丢弃已处理数据移动读指针staticvoidu5_ring_discard(uint16_tlen){u5_rout(u5_routlen)%U5_RING_SIZE;// 移动读指针删除数据}/* 空闲中断处理 */voidUART5_RxIdle_IRQHandler(void){uint32_tremainDMA_GetCurrDataCounter(DMA1_CH2);// 获取DMA剩余计数uint8_trecvU5_BUF_SIZE-remain;// 实际收到字节数if(recv){// 收到数据0DMA_EnableChannel(DMA1_CH2,DISABLE);// 暂停DMAuint8_t*datau5_cur_rx?u5_rx1:u5_rx0;// 取当前DMA缓冲for(uint8_ti0;irecv;i)u5_ring_write(data[i]);// 写入大环形缓冲u5_cur_rx^1;// 切换0/1双缓冲u5_rx_start(u5_cur_rx?u5_rx1:u5_rx0);// 启动新DMA}else{DMA_EnableChannel(DMA1_CH2,DISABLE);// 空中断重启DMAu5_rx_start(u5_cur_rx?u5_rx1:u5_rx0);}}/* 主循环处理 */voidUART5_Process(void){uint16_tavailu5_ring_avail();// 待处理数据长度if(avail0)return;// 无数据直接返回// 防死锁超过80%清空缓冲if(avail(U5_RING_SIZE*8/10)){LogErr(U5 ring near full, force clear %d bytes\r\n,avail);u5_ring_discard(avail);// 丢弃全部return;}uint8_t*datau5_ring[u5_rout];// 直接取环形缓冲数据rx_frame_ptrdata;// 对外输出数据指针rx_frame_lenavail;// 对外输出数据长度// 打印接收数据调试用log_info(RX %d: ,avail);for(uint16_ti0;iavail;i)log_info(%02X ,data[i]);log_info(\r\n);// 协议解析0成功非0失败if(protocolUi(data,avail)0){u5_ring_discard(avail);// 成功 → 删除已处理数据log_info(Protocol SUCCESS\r\n);}else{LogErr(Protocol FAILED\r\n);// 失败}}/* 发送非阻塞队列 */voidUART5_Send(uint8_t*buf,uint16_tlen){if(lenU5_BUF_SIZE)lenU5_BUF_SIZE;// 超长截断memcpy(u5_txq[u5_txin],buf,len);// 复制到发送队列u5_txl[u5_txin](uint8_t)len;// 记录长度u5_txin(u5_txin1)%U5_TX_QSIZE;// 写指针后移__disable_irq();// 原子操作保护计数u5_txcnt;__enable_irq();if(tx_idleu5_txcnt){// 当前空闲且有数据tx_idle0;// 标记忙DMA_EnableChannel(DMA1_CH1,DISABLE);DMA_SetPerMemAddr(DMA1_CH1,(uint32_t)UART5-DAT,(uint32_t)u5_txq[u5_txout],u5_txl[u5_txout]);DMA_EnableChannel(DMA1_CH1,ENABLE);}}/* DMA发送完成中断 */voidUART5_TxCplt_IRQHandler(void){u5_txout(u5_txout1)%U5_TX_QSIZE;// 发送完成指针后移u5_txcnt--;// 帧数-1if(u5_txcnt){// 还有数据DMA_EnableChannel(DMA1_CH1,DISABLE);DMA_SetPerMemAddr(DMA1_CH1,(uint32_t)UART5-DAT,(uint32_t)u5_txq[u5_txout],u5_txl[u5_txout]);DMA_EnableChannel(DMA1_CH1,ENABLE);}else{tx_idle1;// 无数据标记空闲}}/* 初始化 */voidUART5_Init(void){// 发送队列初始化u5_txin0;u5_txout0;u5_txcnt0;tx_idle1;// 接收环形缓冲初始化u5_rin0;u5_rout0;// DMA双缓冲初始化u5_cur_rx0;u5_rx_start(u5_rx0);// 启动接收}n32h47x_48x_it.c/* NTFx CODE START(UART5_IRQHandler)*//** * brief This function handles UART5_IRQHandler. */voidUART5_IRQHandler(void){/* NTFx CODE END *//* NTFx CODE START */if(USART_GetFlagStatus(UART5,USART_FLAG_IDLEF)){/* NTFx CODE END */volatileuint32_ttmpUART5-STS;tmpUART5-DAT;(void)tmp;UART5_RxIdle_IRQHandler();// 调用空闲中断处理}/* NTFx CODE START */if(USART_GetFlagStatus(UART5,USART_FLAG_OREF)||USART_GetFlagStatus(UART5,USART_FLAG_NEF)||USART_GetFlagStatus(UART5,USART_FLAG_FEF)||USART_GetFlagStatus(UART5,USART_FLAG_PEF)){/*clear IT flag*//*Read the sts register first,and the read the DAT register to clear the all error flag*/(void)UART5-STS;(void)UART5-DAT;/* NTFx CODE END */}/* NTFx CODE START */}/* NTFx CODE END(UART5)_IRQHandler*//* NTFx CODE START(DMA1_Channel1_IRQHandler)*//** * brief This function handles DMA1_Channel1_IRQHandler. */voidDMA1_Channel1_IRQHandler(void){/* NTFx CODE END *//* NTFx CODE START */if(DMA_GetIntStatus(DMA_INT_TXC1,DMA1)){/*clear IT flag*/DMA_ClrIntPendingBit(DMA_INT_TXC1,DMA1);/* NTFx CODE END */UART5_TxCplt_IRQHandler();// 调用发送完成处理}/* NTFx CODE START */}/* NTFx CODE END(DMA1_Channel1)_IRQHandler*/在main.c调用UART5_Init();while(1){if(tim16%20000)UART5_Send(123456789\n,10);// 发送测试UART5_Process();// 处理接收数据}