RT_Thread 使用UART+DMA接收導致數據分包輸出問題

直接使用官方給出的例子,具體參考 https://www.rt-thread.org/document/site/programming-manual/device/uart/uart/

/*
 * 程序清單:這是一個串口設備 DMA 接收使用例程
 * 例程導出了 uart_dma_sample 命令到控制終端
 * 命令調用格式:uart_dma_sample uart3
 * 命令解釋:命令第二個參數是要使用的串口設備名稱,爲空則使用默認的串口設備
 * 程序功能:通過串口輸出字符串"hello RT-Thread!",並通過串口輸出接收到的數據,然後打印接收到的數據。
*/

#include <rtthread.h>

#define SAMPLE_UART_NAME       "uart3"      /* 串口設備名稱 */

/* 串口接收消息結構*/
struct rx_msg
{
    rt_device_t dev;
    rt_size_t size;
};
/* 串口設備句柄 */
static rt_device_t serial;
/* 消息隊列控制塊 */
static struct rt_messagequeue rx_mq;

/* 接收數據回調函數 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    struct rx_msg msg;
    rt_err_t result;
    msg.dev = dev;
    msg.size = size;

    result = rt_mq_send(&rx_mq, &msg, sizeof(msg));
    if ( result == -RT_EFULL)
    {
        /* 消息隊列滿 */
        rt_kprintf("message queue full!\n");
    }
    return result;
}

static void serial_thread_entry(void *parameter)
{
    struct rx_msg msg;
    rt_err_t result;
    rt_uint32_t rx_length;
    static char rx_buffer[RT_SERIAL_RB_BUFSZ + 1];

    while (1)
    {
        rt_memset(&msg, 0, sizeof(msg));
        /* 從消息隊列中讀取消息*/
        result = rt_mq_recv(&rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER);
        if (result == RT_EOK)
        {
            /* 從串口讀取數據*/
            rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);
            rx_buffer[rx_length] = '\0';
            /* 通過串口設備 serial 輸出讀取到的消息 */
            rt_device_write(serial, 0, rx_buffer, rx_length);
            /* 打印數據 */
            rt_kprintf("%s\n",rx_buffer);
        }
    }
}

static int uart_dma_sample(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    char uart_name[RT_NAME_MAX];
    static char msg_pool[256];
    char str[] = "hello RT-Thread!\r\n";

    if (argc == 2)
    {
        rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
    }
    else
    {
        rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
    }

    /* 查找串口設備 */
    serial = rt_device_find(uart_name);
    if (!serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }

    /* 初始化消息隊列 */
    rt_mq_init(&rx_mq, "rx_mq",
               msg_pool,                 /* 存放消息的緩衝區 */
               sizeof(struct rx_msg),    /* 一條消息的最大長度 */
               sizeof(msg_pool),         /* 存放消息的緩衝區大小 */
               RT_IPC_FLAG_FIFO);        /* 如果有多個線程等待,按照先來先得到的方法分配消息 */

    /* 以 DMA 接收及輪詢發送方式打開串口設備 */
    rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX);
    /* 設置接收回調函數 */
    rt_device_set_rx_indicate(serial, uart_input);
    /* 發送字符串 */
    rt_device_write(serial, 0, str, (sizeof(str) - 1));

    /* 創建 serial 線程 */
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    /* 創建成功則啓動線程 */
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        ret = RT_ERROR;
    }

    return ret;
}
/* 導出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_dma_sample, uart device dma sample);

串口調試助手查看串口數據輸出,發現了分包問題。

 

 目前的解決方法是:

 第一步: 將這個HAL庫函數裏面的內容屏蔽

/**
  * @brief  Rx Transfer completed callback
  * @param  huart: UART handle
  * @note   This example shows a simple way to report end of DMA Rx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//    struct rt_serial_device *serial;
//    struct stm32_uart *uart;
//    rt_size_t recv_len;
//    rt_base_t level;

//    RT_ASSERT(huart != NULL);
//    uart = (struct stm32_uart *)huart;
//    serial = &uart->serial;

//    level = rt_hw_interrupt_disable();

//    recv_len = serial->config.bufsz - uart->dma.last_index;
//    uart->dma.last_index = 0;

//    rt_hw_interrupt_enable(level);
//    if (recv_len)
//    {
//        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));
//    }
}

第二步.根據你接收字符串的最大長度,設置合適的RT_SERIAL_RB_BUFSZ參數

#define RT_SERIAL_RB_BUFSZ 256

 

2020.1.29更新部分

更新了HAL庫後,需要進行屏蔽如下代碼中屏蔽的內容:

/**
  * @brief  Rx Transfer completed callback
  * @param  huart: UART handle
  * @note   This example shows a simple way to report end of DMA Rx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    struct stm32_uart *uart;
    RT_ASSERT(huart != NULL);
    uart = (struct stm32_uart *)huart;
//    dma_isr(&uart->serial);
}

/**
  * @brief  Rx Half transfer completed callback
  * @param  huart: UART handle
  * @note   This example shows a simple way to report end of DMA Rx Half transfer, 
  *         and you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{
    struct stm32_uart *uart;
    RT_ASSERT(huart != NULL);
    uart = (struct stm32_uart *)huart;
//    dma_isr(&uart->serial);
}

 

更多詳細的討論請參考

https://www.rt-thread.org/qa/thread-421316-1-1.html

https://www.rt-thread.org/qa/forum.php?mod=viewthread&tid=11097&extra=page%3D1%26filter%3Dtypeid%26typeid%3D24

https://www.rt-thread.org/qa/thread-11854-1-1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章