背景
在单片机的开发中,经常需要使用printf函数来监测程序运行中的一些变量,但是在程序中添加printf函数会影响程序的执行速度,尤其在一些对时间要求比较高的函数中,会造成比较大的延时,以9600bps的速度来说,发送一个字节(8位,无奇偶校验)需要 1 * 10 / 9600 = 1.04ms。那发送10个字节就有10ms左右的延时,对于一些函数来说可能无法容忍这么大的延迟。因此使用DMA的方式就显得比较重要。
实现思路
实现printf函数主要就是要改写fputc函数
int fputc(int ch, FILE *f)
但是fputc函数每次只能发送一个字节,如果我们把fputc函数直接改成:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit_DMA(&PrintUartHandle, (uint8_t *)&ch, 1);
return (ch);
}
那么至少存在两个问题:
1、DMA每次都发送一个字节,效率比较低。
2、频繁调用fputc,可能DMA上一次的数据还没有发送完,导致这次发送失败。
例如:
printf("HelloWorldrn");
printf先发送H,调用fputc函数,此时DMA开始工作。由之前的分析可知,对于9600bps来说,需要1ms才能把字符H发送完成。在这1ms之内elloWolrdrn都会调用fputc函数,但由于DMA还没有发送完成,会导致其他的字符发送失败。最终成功发出去的只有第一个字符H。
如果想用调用DMA,就要想其他的办法。因为printf函数最终是调用fputc函数的,所以我们要在fputc函数中想办法。我们需要做以下处理:
1、如果fputc函数中DMA是空闲状态的话,那么向DMA中放入数据,并启动DMA发送。
2、如果fputc函数中的DMA现在忙,那么需要把数据暂时存储起来。
3、如果数据发送完成,那么需要判断暂存区有没有数据,如果有数据则需要把暂存区的所有数据都通过DMA发送出去。
那么现在这个问题的关键是在这个暂存区怎么实现,可以想象的出来的,这个暂存区应该是一个环形队列。
背景
在单片机的开发中,经常需要使用printf函数来监测程序运行中的一些变量,但是在程序中添加printf函数会影响程序的执行速度,尤其在一些对时间要求比较高的函数中,会造成比较大的延时,以9600bps的速度来说,发送一个字节(8位,无奇偶校验)需要 1 * 10 / 9600 = 1.04ms。那发送10个字节就有10ms左右的延时,对于一些函数来说可能无法容忍这么大的延迟。因此使用DMA的方式就显得比较重要。
实现思路
实现printf函数主要就是要改写fputc函数
int fputc(int ch, FILE *f)
但是fputc函数每次只能发送一个字节,如果我们把fputc函数直接改成:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit_DMA(&PrintUartHandle, (uint8_t *)&ch, 1);
return (ch);
}
那么至少存在两个问题:
1、DMA每次都发送一个字节,效率比较低。
2、频繁调用fputc,可能DMA上一次的数据还没有发送完,导致这次发送失败。
例如:
printf("HelloWorldrn");
printf先发送H,调用fputc函数,此时DMA开始工作。由之前的分析可知,对于9600bps来说,需要1ms才能把字符H发送完成。在这1ms之内elloWolrdrn都会调用fputc函数,但由于DMA还没有发送完成,会导致其他的字符发送失败。最终成功发出去的只有第一个字符H。
如果想用调用DMA,就要想其他的办法。因为printf函数最终是调用fputc函数的,所以我们要在fputc函数中想办法。我们需要做以下处理:
1、如果fputc函数中DMA是空闲状态的话,那么向DMA中放入数据,并启动DMA发送。
2、如果fputc函数中的DMA现在忙,那么需要把数据暂时存储起来。
3、如果数据发送完成,那么需要判断暂存区有没有数据,如果有数据则需要把暂存区的所有数据都通过DMA发送出去。
那么现在这个问题的关键是在这个暂存区怎么实现,可以想象的出来的,这个暂存区应该是一个环形队列。
举报