本文章介绍一种在FreeRTOS项目中比较常用的LOG调试方法,不同任务通过DEBUG_LOG()接口通过队列发送调试日志,再通过log发送任务集中获取数据向串口发送,避免数据因为各个任务优先级不同造成打印数据丢失。
程序实现整体思路如下:
一、串口DMA初始化处理
1.串口GPIO配置
void Usart0GpioConfiguration(void)
{
/* enable COM GPIO clock */
rcu_periph_clock_enable(RCU_GPIOB);
/* connect port to USARTx_Tx */
gpio_af_set(COM0_GPIO_PORT, COM0_AF, COM0_TX_PIN);
/* connect port to USARTx_Rx */
gpio_af_set(COM0_GPIO_PORT, COM0_AF, COM0_RX_PIN);
/* configure USART Tx as alternate function push-pull */
gpio_mode_set(COM0_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, COM0_TX_PIN);
gpio_output_options_set(COM0_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, COM0_TX_PIN);
/* configure USART Rx as alternate function push-pull */
gpio_mode_set(COM0_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, COM0_RX_PIN);
gpio_output_options_set(COM0_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, COM0_RX_PIN);
}
2.DMA配置
void Usart0DmaConfiguration(void)
{
dma_parameter_struct dma_init_struct;
/* initialize DMA channel1 */
rcu_periph_clock_enable(RCU_DMA);
dma_deinit(DMA_CH1);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)usart0_tx_buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = USART0_TX_SIZE;
dma_init_struct.periph_addr = (uint32_t) &USART_TDATA(USART0);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_LOW;
dma_init(DMA_CH1, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA_CH1);
dma_memory_to_memory_disable(DMA_CH1);
/* enable DMA channel1 */
dma_channel_enable(DMA_CH1);
/* USART DMA enable for transmission */
usart_dma_transmit_config(USART0, USART_DENT_ENABLE);
/* enable DMA channel1 transfer complete interrupt */
// dma_interrupt_enable(DMA_CH1, DMA_INT_FTF);
/* initialize DMA channel2 */
dma_deinit(DMA_CH2);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)usart0_rx_buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = USART0_RX_SIZE;
dma_init_struct.periph_addr = (uint32_t) &USART_RDATA(USART0);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.memory_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_LOW;
dma_init(DMA_CH2, &dma_init_struct);
/* enable DMA channel2 */
dma_channel_enable(DMA_CH2);
/* configure DMA mode */
dma_circulation_enable(DMA_CH2);
dma_memory_to_memory_disable(DMA_CH2);
/* USART DMA enable for reception */
usart_dma_receive_config(USART0, USART_DENR_ENABLE);
/* enable DMA channel2 transfer complete interrupt */
// dma_interrupt_enable(DMA_CH2, DMA_INT_FTF);
}
3.串口配置
void Usart0Configuration(void)
{
/* enable USART clock */
rcu_periph_clock_enable(RCU_USART0);
/* USART configure */
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U);
usart_parity_config(USART0, USART_PM_NONE);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_overrun_disable(USART0);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_enable(USART0);
/* enable USART RBNE interrupt */
usart_interrupt_enable(USART0,USART_INT_IDLE);
usart_flag_clear(USART0,USART_FLAG_IDLE);
}
4.中断等级配置
void NvicConfiguration(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
nvic_irq_enable(DMA_Channel0_IRQn, 5, 0);
nvic_irq_enable(USART0_IRQn, 5, 0);
}
5.中断服务函数
void USART0_IRQHandler(void)
{
if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE))
{
usart_data_receive(USART0);
dma_channel_disable(DMA_CH2);
//将数据从DMA数据转移到RX缓存数组
memcpy(debug_receive_fifo,usart0_rx_buffer,sizeof(usart0_rx_buffer));
//清空DMA数组
memset(usart0_rx_buffer ,0,sizeof(usart0_rx_buffer));
/* Reset DMA transfers */
dma_memory_address_config(DMA_CH2,(uint32_t)usart0_rx_buffer);
dma_transfer_number_config(DMA_CH2,sizeof(usart0_rx_buffer));
dma_channel_enable(DMA_CH2);
//释放信号量
usart_interrupt_flag_clear(USART0,USART_INT_FLAG_IDLE);
}
}
二、DEBUG LOG驱动处理
1.创建任务、队列、信号量
xQueueHandle uart_debug_queue;
SemaphoreHandle_t uart0_receive_semaphore;
static void InitializeTask(void)
{
uart0_receive_semaphore = xSemaphoreCreateBinary();
uart_debug_queue = xQueueCreate(SEND_QUEUELENGTH, sizeof(DebugQueueTypeDef));
xQueueReset(uart_debug_queue);
taskENTER_CRITICAL();
xTaskCreate(DebugSendTask,"DebugSendTask",128,NULL,1,NULL);
xTaskCreate(DebugReceiveTask,"DebugReceiveTask",128,NULL,1,NULL);
taskEXIT_CRITICAL();
}
2.创建缓存数组
#define DMA_USART_DEBUG_SIZE 10
#define DMA_USART_DEBUG_LENGTH 128
#define USART0_TX_SIZE 128
#define DEBUG_LENGTH_MAX 128
#define USART0_RX_SIZE 64
char g_fifo_idx = 0;
char debug_string[DEBUG_LENGTH_MAX] = {0};
uint8_t usart0_tx_buffer[USART0_TX_SIZE];//DMA发送数组
uint8_t usart0_rx_buffer[USART0_RX_SIZE];//DMA接收数组
char debug_send_fifo[DMA_USART_DEBUG_SIZE][DMA_USART_DEBUG_LENGTH] = { 0 };//实际缓存数组
char debug_receive_fifo[USART0_RX_SIZE] = { 0 };//实际结束数组
bool debug_task_ready_flag = false; //发送任务运行标记
3.DEBUG_LOG()接口
//获取.c文件的名称
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define DEBUG_LOG(format, ...) \
snprintf(debug_string, DEBUG_LENGTH_MAX, "%s(%d):"format, __FILENAME__,__LINE__, ##__VA_ARGS__);\
DebugPrintString((uint8_t *)debug_string);
使用记录缓存数组的方式进行数据的传递,使队列更高效传递数据,在发送任务获取队列中的下标再放进debug_send_fifo数据既可以获取到数据
static void PDebugPrintString(uint8_t *p_debug_data)
{
uint8_t fifo_length = 0;
while(*p_debug_data)
{
debug_send_fifo[g_fifo_idx][fifo_length] = *p_debug_data;
fifo_length++;
p_debug_data++;
if(fifo_length > DMA_USART_DEBUG_LENGTH-1)//防止横向溢出
{
break;
}
}
if(!GetDebugTaskFlag()) //DEBUG LOG发送线程未创建,使用普通串口发送
{
DebugDmaString((uint8_t *)debug_send_fifo[g_fifo_idx],fifo_length+1);
}
else
{
DebugQueueTypeDef queue_send_temp;
xQueueHandle uart_debug_queue;
//记录缓存下标
queue_send_temp.idx = g_fifo_idx;
queue_send_temp.length = fifo_length;
BaseType_t higherPriorityTaskWoken = false;
if(xQueueSendToBackFromISR(uart_debug_queue, &queue_send_temp, &higherPriorityTaskWoken) != pdTRUE)
{
return;
}
g_fifo_idx++; //为下次数据做准备
if(g_fifo_idx >= (DMA_USART_DEBUG_SIZE)) //防止纵向溢出
{
g_fifo_idx = 0;
}
//队列忙碌
if(uxQueueMessagesWaiting(uart_debug_queue) >= (DMA_USART_DEBUG_SIZE - 1))
{
vTaskDelay(50 / portTICK_RATE_MS);
}
}
}
static void WaitingSending(uint32_t usart_periph)
{
if(usart_periph==USART0)
{
//未收到等待串口发送空闲标记,进入阻塞,等待发送空闲
while(RESET == usart_flag_get(USART0,USART_FLAG_TC))
{
Delay(1);
}
}
}
void DebugDmaString(uint8_t *data, uint16_t length)
{
WaitingSending(USART0);
usart_flag_clear(USART0,USART_FLAG_TC);
Usartx_TX_DMA_Send(USART0,data,length);
}
static void Usartx_TX_DMA_Send(uint32_t usart_periph,uint8_t* data_buffer,uint8_t length)
{
if(usart_periph==USART0)
{
/* Channel disable */
dma_channel_disable(DMA_CH1);
dma_memory_address_config(DMA_CH1,(uint32_t)data_buffer);
dma_transfer_number_config(DMA_CH1,length);
/* enable DMA channel to start send */
dma_channel_enable(DMA_CH1);
WaitingSending(USART0);
}
}
void SetDebugTaskFlag(bool flag)
{
debug_task_ready_flag = flag;
}
bool GetDebugTaskFlag(void)
{
return debug_task_ready_flag;
}
三、DEBUG LOG任务处理
1.LOG发送任务
void DebugSendTask(void * pvParameters)
{
DebugQueueTypeDef queue_usart_data;
SetDebugTaskFlag(true); //发送任务已准备好
while(1)
{
portBASE_TYPE xTaskWokenByReceive = pdFALSE;
int debug_msg_len = uxQueueMessagesWaiting(uart_debug_queue); .//获取队列个数
if(debug_msg_len)
{
for(uint8_t i = 0; i < debug_msg_len; i++)
{
if(xQueueReceiveFromISR(uart_debug_queue, (void *)&queue_usart_data, &xTaskWokenByReceive) == pdPASS)
{
//将下标放到缓存数组,得到数据并且向串口发送
DebugDmaString(debug_send_fifo[queue_usart_data.idx],queue_usart_data.length);
}
}
}
}
}
2.LOG接收任务
void DebugReceiveTask(void * pvParameters)
{
while(1)
{
//获取信号量,做数据处理
DEBUG_LOG(debug_receive_fifo);
}
}
3.运行验证
四、总结
我在项目中只用到了LOG发送功能,所以在接收部分并没有去实现,上面接收只提供一个大概的思路,可以用信号量的方式,也可以使用全局变量标志位的方式去实现。