【GD32】FreeRTOS-USART-DMA

本文章介绍一种在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发送功能,所以在接收部分并没有去实现,上面接收只提供一个大概的思路,可以用信号量的方式,也可以使用全局变量标志位的方式去实现。

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值