由于printf映射到串口后,打印耗时较久,特别是波特率低又有较频繁的打印时,严重影响主程序执行效率。
STM32串口具有DMA功能,可以直接从RAM搬运数据到USART,解放了CPU。因此只要实现printf打印到RAM,再从RAM用DMA搬运到UASRT即可。
为实现这种方法,需要构造一个缓存区。具体代码如下(已在SMT32F103C8T6实测验证):
//*****************自定义printf函数,通过dma实现串口1打印输出******************//
#define PRINTF_BUFFSIZE 50
typedef struct{
volatile uint8_t ms_counter;
uint8_t falg[6];//1:对应数组有数据需要发送;0:对应数组不需要发送
uint8_t dma_data[PRINTF_BUFFSIZE];//DMA发送地址
uint8_t data[6][PRINTF_BUFFSIZE];//定义6*50缓存区,6个50字节数组,根据打印内容长短,缓存大小可调整
}Buff;
Buff txbuff;
//先打印到缓存里
void my_printf_dma(const char *fmt, ...)
{
static uint8_t i = 0;
va_list args;
va_start(args, fmt);
memset(&txbuff.data[i][0], 0, PRINTF_BUFFSIZE);
vsnprintf((char*)&txbuff.data[i][0], PRINTF_BUFFSIZE, fmt, args);
txbuff.falg[i] = 1;
va_end(args);
i++;
if(i>=6)
{
i = 0;
}
}
//按序查找有效的需要DMA发送打印的数组
int8_t my_find_data_id(void)
{
static uint8_t i = 0;
uint8_t tmp = i;
int16_t res;
// printf("tmp: %d\r\n",tmp);
for(;i<6;i++)
{
if(txbuff.falg[i]==1)
{
// printf("txbuff.falg[%d]: %d\r\n",i,txbuff.falg[i]);
break;
}
}
if(i>=6)//刚才找到末尾也未找到需要发送的数组,从头开始找
{
i = 0;
for(;i<tmp;i++)
{
if(txbuff.falg[i]==1)
break;
}
if(i>=tmp)//还是未找到
{
res = -1;
}
else//找到了
{
res = i;
}
}
else
{
res = i;
}
i++;
if(i>=6)
{
i=0;
}
return res;
}
void my_buff_2_usart(void)
{
if(txbuff.ms_counter <2)//每隔2ms检查一下有没有需要打印的内容
{
return;
}
txbuff.ms_counter = 0;
if(HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)
{
return;//串口还未打印完
}
int8_t i = my_find_data_id();
if(i>=0)
{
memset( &txbuff.dma_data[0], 0, PRINTF_BUFFSIZE);
memcpy( &txbuff.dma_data[0], &txbuff.data[i][0],strlen((const char*)(&txbuff.data[i][0])));
HAL_UART_Transmit_DMA(&huart1, &txbuff.dma_data[0], strlen((const char*)&txbuff.dma_data[0]));
txbuff.falg[i]=0;
}
}
//放到ms定时器中断服务函数里
void my_printf_time_service(void)
{
txbuff.ms_counter++;
}