增加一些注释吧,这个代码能帮上忙就行。 ________________________________________________________ 实在受不了中断了。 这里使用DMA循环方式接收串口数据,不用中断,空闲时间去查询DMA状态,然后拷贝数据即可。 F407的部分代码如下,F103的类似: // 宏定义 #define USART3_RXBUF_SIZ 256 /* UART接收缓冲,必须为2^n,增加这个数值可以放宽处理UART接收数据的时间间隔 */ #define USART3_RX_DMA_CHANNEL DMA_Channel_4 /* UART接收通道 */ #define USART3_RX_DMA_STREAM DMA1_Stream1 /* UART接收DMA */ // 全局缓冲区 static uint8_t gl_Usart3Rxbuf[USART3_RXBUF_SIZ] = { 0, }; // 初始化串口和DMA.. static void MX_Uart_Init(void) { USART_InitTypeDef USART_InitStruct; DMA_InitTypeDef DMA_InitStruct; // 初始化USART3的通讯 ---------------------------------------------------------- USART_StructInit(&USART_InitStruct); USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART3, &USART_InitStruct); /* 配置UART为DMA循环接收方式,即DMA按照RingBuffer方式写入UART接收缓冲,CPU不参与UART接收 */ DMA_StructInit(&DMA_InitStruct); DMA_InitStruct.DMA_BufferSize = sizeof(gl_Usart3Rxbuf) ; DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable ; DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ; DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single ; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_PeripheralBaseAddr =(uint32_t) (&(USART3->DR)) ; DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; DMA_InitStruct.DMA_Channel = USART3_RX_DMA_CHANNEL; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)gl_Usart3Rxbuf; DMA_Init(USART3_RX_DMA_STREAM, &DMA_InitStruct); USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); DMA_Cmd(USART3_RX_DMA_STREAM, ENABLE); USART_Cmd(USART3, ENABLE); } // 处理接收的串口数据 void Idle_Proc(void) { static uint16_t lastDataCounter = 0; /* 保存已处理的UARt接收数据index */ static char line[256]; static int ncin = 0; uint16_t currDataCounter; bool newLine = false; uint8_t ch; // 处理USART3接收的数据 ---------------------------------------------------- /* 获取DMA中数据计数,用于计算当前所接收字符的index */ do { currDataCounter = DMA_GetCurrDataCounter(USART3_RX_DMA_STREAM); } while (currDataCounter != DMA_GetCurrDataCounter(USART3_RX_DMA_STREAM)); currDataCounter = sizeof(gl_Usart3Rxbuf) - currDataCounter; /* 计算当前UART接收的index */ while (((currDataCounter - lastDataCounter) & (USART3_RXBUF_SIZ - 1)) != 0) { /* 一直处理到已接收的index */ ch = gl_Usart3Rxbuf[lastDataCounter++ & (USART3_RXBUF_SIZ - 1)]; /* 拷贝已接收的UART数据,所保存的index增长 */ ...... } } /* 增加主函数 */ int main(void) { for (;;) { process1(); process2(); process3(); ... process99(); Idle_Proc(); } } 其他的诸如时钟初始化、GPIO初始化等这个就不贴了。 PS:中断是个好东西,但是,...,能不用就不用吧。。。,还有别把程序写成阻塞的。 |
考虑的不应该是数据多了或少了的问题,而是最“严酷”条件的问题,即如果UART带宽100%使用时,查询DMA状态的时间间隔问题。
因为UART是很慢的外设,给个一定长度的buffer,就有相当宽裕的容许间隔。只要保证在时间间隔内程序必定能够再次查询到DMA状态即可。这个对于一般的ARM都不成问题(如果这样都有问题,那真得考虑CPU选型了)。
当然,还是那句话,别阻塞。
怎么会“一直”呢,查询一下,有就收,没有就干别的事,函数就是Idle_Proc()。
用DMA+一段缓冲,可以放宽查询的时间间隔要求,而不是来一个字符中断一下地搞。
中断还是留给重要的事情。
如果软件任务简单用几个中断当然无所谓,但任务多了,如USB芯片、FPGA等等好多地方都要中断,而公司又明确不允许使用RTOS,那么...
每个程序任务的背景都不尽相同,我的代码也只是一种思路罢了。
如果设置了串口空闲中断,那么2帧发送数据之间的间隔就会产出一次串口空闲中断。
至于buff的大小就要看你发送的数据一帧有多大了,合理设置即可。
既然用DMA了,为何不用串口空闲中断+DMA呢,这就不用频繁的查询了,一次能接收不定长的一帧数据。
你的理由是什么呢?
这应该是串口接收数据比较好的方法之一的,中断的方法和这个没法比。
没有查询串口,是查询DMA的状态,DMA的设置自行完成了RingBuffer的功能。
串口接收的缓冲长度决定了读取RingBuffer的时间裕度。
串口空闲中断,什么时候串口有空闲呢。应该保证必须保证串口空闲中断发出之前buffer没满。
当然,整个程序必须没有阻塞,或者阻塞的时间保证串口通讯肯定不会“溢出”Buffer。
中断也没什么不好的,但一般情况下会有更需要中断的地方。
串口接收这种事情留给空闲时间即可。