DMA的非阻塞特性
在使用DMA进行发送时,应该额外注意时间的问题。DMA是一个外设,其发送时CPU是非阻塞式的,可以说DMA的发送根本不占用CPU的时间。那么问题就可能存在:
- 当DMA发送正在执行时,数据已经被销毁;
- 当DMA接收正在执行时,数据还未接收完整就已经被使用;
以上两个问题都是由于DMA的非阻塞特性造成的。DMA在运作时,与CPU并行,这一点是特别需要注意的。
void printf_tjc(const char *fmt, ...)
{
va_list args;
size_t length;
static char buf[TJC_SINGLE_MESSAGE_SIZE];
/** 先等待DMA为空闲,否则数据可能被迅速覆盖 2024-12-17 Tuesday 23:07:14*/
while(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC)==RESET);
va_start(args, fmt);
/* the return value of vsnprintf is the number of bytes that would be
* written to buffer had if the size of the buffer been sufficiently
* large excluding the terminating null byte. If the output string
* would be larger than the rt_log_buf, we have to adjust the output
* length. */
length = vsnprintf(buf, sizeof(buf) - 1, fmt, args);
if (length > TJC_SINGLE_MESSAGE_SIZE - 1)
length = TJC_SINGLE_MESSAGE_SIZE - 1;
va_end(args);
/** 打印到特定页面 2024-12-14 Saturday 00:25:58 */
/** 将显示信息修改为追加模式:+= 2024-12-14 Saturday 10:44:40 */
/** 使用DMA发送的数据内存段应该定义为静态 2024-12-17 Tuesday 21:03:46*/
static char buff_send[TJC_SINGLE_MESSAGE_SIZE];
/** 因为strcat的存在,这里需要先清除缓存 2024-12-17 Tuesday 22:40:10 */
memset(buff_send,0,sizeof(buff_send));
/** strcat将源字符串拼接到目标字符串上
* 其与静态变量相互作用又会出问题,因此使用memset清除一下变量
* 2024-12-17 Tuesday 22:42:01 */
strcat(buff_send,TJC_TEXT_PAGE1);
strcat(buff_send,buf);
strcat(buff_send,"\"\xff\xff\xff");
/* 使用USART2 DMA模式进行输出 2024/05/15 Wednesday 23:24:28 */
HAL_UART_Transmit_DMA(&huart2,(uint8_t *)buff_send,strlen(buff_send));
}
以上代码可以正常运作。如果把buff_send的static修饰去掉,就不正常了。理解了DMA的非阻塞特性就能够明白这一点。那么为了解决这个问题,除了将该内存块定义为静态意外,可以在启动了DMA之后加一定延时,防止在DMA发送完成之前就清除局部变量,这个方式也测试可行。