RTThread使用DMA串口接收数据不连续的问题

发布时间 2023-05-03 23:46:13作者: 当最后一片树叶落下

RTThread使用DMA接收串口数据的问题

问题/现象

使用RTThread的DMA接收串口数据,数据不连续,即IDLE中断没有起到作为一个frame的判定.

经过对serial和drv_uarts源码的分析,得出原因:

graph LR RX_INT[USART1_IRQHandler] -->process1(...) process1 -->rx_isr1[dma_recv_isr] rx_isr1 -->flag1{isr_flag} flag1 -->|UART_RX_DMA_IT_IDLE_FLAG| serial_isr1[rt_hw_serial_isr] serial_isr1 -->event{event} DMA_INT[UART1_DMA_RX_IRQHandler] -->process2(...) process2 -->rx_isr2[dma_recv_isr] rx_isr2 -->flag2{isr_flag} flag2 -->|UART_RX_DMA_IT_HT_FLAG| serial_isr2[rt_hw_serial_isr] flag2 -->|UART_RX_DMA_IT_TC_FLAG| serial_isr2[rt_hw_serial_isr] serial_isr2 -->event{event} event -->|RT_SERIAL_EVENT_RX_DMADONE|do_something(...)

从上图可知,发生IDLE中断时,USART1_IRQHandler调用的是和UART1_DMA_RX_IRQHandler相同的接口 —— rt_hw_serial_isr.

这就造成无法区分是IDLE中断还是DMA中断.


不改变源码的情况下,仍使用DMA+IDLE中断,目前这两种方式是比较好的

解决方式①

  • 接收数据
/* 接收数据回调函数 */
static rt_err_t uart2_input(rt_device_t dev, rt_size_t size)
{
    rt_err_t result;
    result = rt_sem_release(&serial2_sem);/*通知serial_thread_entry线程,有数据了*/
    if ( result == RT_EOK)
    {
        rt_kprintf("sem release error!\n");
    }
    return result;
}

static void serial_thread_entry(void *parameter)
{
    rt_err_t result;
    rt_uint8_t c = 0;
    rt_uint8_t i = 0, rx_state = SERIAL2_STATE_WAIT_FRAME;
    rt_device_t serial2 = rt_device_find("uart2");
    struct frame_msg msg;
    while (1)
    {
		switch(rx_state)
		{
			case SERIAL2_STATE_WAIT_FRAME:
			{
				result = rt_sem_take(&serial2_sem, RT_WAITING_FOREVER); /* 等待新的一帧数据 */
				if (result == RT_EOK)
				{
					rt_kprintf("%s: frame start\n", __func__);
					rx_state = SERIAL2_STATE_RECV_DATA;
					i = 0;
				}
				break;
			}
			case SERIAL2_STATE_RECV_DATA:
			{
				/* 读取一段数据 —— 数个字节(不足一帧) */
				while(rt_device_read(serial2, 0, &c, 1) != 0)
				{
					framebuf[i] = c;
					i++;
				}

				result = rt_sem_take(&serial2_sem, 10); /* 将信号量设置为带有超时的信号量,等待下一段数据 */
				if(result == -RT_ETIMEOUT)  /* 超过10个OSTicks没有读到数据,判定该帧结束 */
				{
					framebuf[i] = '\0';
					msg.data = framebuf;
					msg.size = i;
					result = rt_mq_send(&rtc_rx_mq, (void*)&msg, sizeof(struct frame_msg)); /* 给其它线程通信 */
					if(result != RT_EOK)
					{
						rt_kprintf("%s: msgqueue send error-[%d]\n", __func__, result);
					}
					rx_state = SERIAL2_STATE_WAIT_FRAME;    /* 一帧结束 */
					rt_kprintf("%s: frame end\n", __func__);
				}
				else
				{
					/*接收下一段数据*/
					rt_kprintf("%s: frame recv ing...\n", __func__);
				}
				break;
			}
			default:
				break;
		}
    }
}

解决方式②

把信号量换成消息队列.


其它问题

代码中的几个局部变量在线程中具有了和线程一样长的生成周期

life_circle(serial_thread_entry) = life_circle(variable(i))
                                = life_circle(variable(rx_state))
                                = life_circle(variable(serial2))
                                = ...

这样是不是意味着thread的stack一直被占用?

嗯,是的.