硬汉嵌入式论坛

 找回密码
 立即注册
查看: 615|回复: 15
收起左侧

[Zephyr] zephyr异步串口发送和接收API接口有顺序吗

[复制链接]

2

主题

4

回帖

10

积分

新手上路

积分
10
发表于 2025-8-16 22:50:30 | 显示全部楼层 |阅读模式
通过测试zephyr4.2版本的异步串口,我主要使用uart_rx_enable和uart_tx两个函数,下面所述的基础是基于串口已经测试收发数据已经通过,下面遇到两个问题:
问题1、uart_rx_enable函数在uart_tx函数前面调用,上电还未对串口发送数据时,串口已经接收到乱码数据,如下:
[C] 纯文本查看 复制代码
   
 // 启动接收
    ret = uart_rx_enable(uart_dma, rx_buf, sizeof(rx_buf), 10);
    if (ret < 0) {
        printk("RX enable failed: %d\n", ret);
    }
    // 启动发送
    ret = uart_tx(uart_dma, tx_data, strlen(tx_data), SYS_FOREVER_US);
    if (ret < 0) {
        printk("TX failed: %d\n", ret);
    }

//回调函数
static void dma_uart_callback(const struct device *dev, 
                            struct uart_event *evt, 
                            void *user_data)
{
    switch (evt->type) {
    case UART_TX_DONE:
        printk("TX complete! Sent %zu bytes\n", evt->data.tx.len);
        break;
        
    case UART_RX_RDY:
        printk("RX data: %.*s\n", 
               evt->data.rx.len, 
               evt->data.rx.buf + evt->data.rx.offset);
        break;
        
    case UART_RX_BUF_REQUEST:  
        uart_rx_buf_rsp(dev, rx_buf, sizeof(rx_buf));
        printk("Provided RX buffer\n");
        break;
    default:
        printk("Unhandled event: %d\n", evt->type);
        break;
    }
}

测试结果:上电的时候会打印“RX data: }{swefgo?”,对于这个乱码“}{swefgo?”着实有点让人摸不着头脑
修复方法:针对该问题我将发送和接收的位置进行互换后就不存在该问题了;

问题2:在问题1实验的基础场,我想测试一下串口数据回环效应,我在UART_RX_RDY中调用uart_tx函数,导致一直打印乱码,程序如下:
[C] 纯文本查看 复制代码
    case UART_RX_RDY:
        printk("RX data: %.*s\n", 
               evt->data.rx.len, 
               evt->data.rx.buf + evt->data.rx.offset);
        uart_tx(uart_dma, rx_buf + evt->data.rx.offset, evt->data.rx.len, SYS_FOREVER_US);
        break;

测试结果:点一次发送,uart_tx会打印一堆乱码,每次长度都不一样。
这个暂时还没有修复办法。

这两个问题出现的莫名其妙,有没有大佬帮忙解释一下
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
118335
QQ
发表于 2025-8-18 09:42:20 | 显示全部楼层
帮顶
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-18 16:45:58 | 显示全部楼层
[C] 纯文本查看 复制代码
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/printk.h>
#include <string.h>

#define RING_BUF_SIZE       1000
#define RX_BUF_SIZE         100
#define RECEIVE_TIMEOUT     10

#define STACK_SIZE 2048

#define UART_DEVICE_NODE    DT_NODELABEL(usart2)
static const struct device *const uart_dev = DEVICE_DT_GET(DT_NODELABEL(usart2));

/* uart configuration structure */
const struct uart_config uart_cfg = {.baudrate = 115200,
                                     .parity = UART_CFG_PARITY_NONE,
                                     .stop_bits = UART_CFG_STOP_BITS_1,
                                     .data_bits = UART_CFG_DATA_BITS_8,
                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};

/* define a ring buffer to get raw bytes*/
RING_BUF_DECLARE(ring_buf, RING_BUF_SIZE);

/* define uart rx buffer */
static __aligned(4) uint8_t rx_buffer[RX_BUF_SIZE];

/* define thread  stack size */
static K_THREAD_STACK_DEFINE(uart_rx_stack, STACK_SIZE);

/* struct uart_event async_evt */
static struct k_thread uart_rx_thread_data = {0};

static K_SEM_DEFINE(tx_done, 0, 1);

void print_uart(char *buf, int len)
{
    for (int i = 0; i < len; i++)
    {

        if ((buf[i] == '\n' || buf[i] == '\r'))
        {
            uart_poll_out(uart_dev, '\n');
        }
        else
        {
            uart_poll_out(uart_dev, buf[i]);
        }
    }
}

/* Data processing thread */
static void uart_rx_thread(void *p1, void *p2, void *p3)
{
    uint8_t rx_data[RX_BUF_SIZE];
    size_t len;

    while (1)
    {
        /* Check if there's data in the ring buffer */
        len = ring_buf_get(&ring_buf, rx_data, sizeof(rx_data));

        if (len > 0)
        {
            // printk("send size %d\r\n", len);

            /* Process `len` bytes of data */
            // 同步方式(阻塞发送)
            // print_uart(rx_data, len);
            // 异步方式(中断或者DMA), 配合信号量
            uart_tx(uart_dev, rx_data, len, SYS_FOREVER_US);
            k_sem_take(&tx_done, K_MSEC(1000));
        }
    }
}

void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
    switch (evt->type)
    {
    case UART_RX_RDY:
        // printk("recv size %d\r\n", evt->data.rx.len);
        /* Data received; place into ring buffer */
        ring_buf_put(&ring_buf, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len);
        break;

    case UART_RX_DISABLED:
        /* Re-enable RX */
        uart_rx_enable(uart_dev, rx_buffer, sizeof(rx_buffer), RECEIVE_TIMEOUT);
        break;

    case UART_TX_DONE:
        k_sem_give(&tx_done);
        break;

    default:
        break;
    }
}

int main(void)
{
    if (!uart_dev)
    {
        printk("Failed to get UART device");
        return 1;
    }

    /* uart configuration parameters */
    int err = uart_configure(uart_dev, &uart_cfg);

    if (err == -ENOSYS)
    {
        printk("Configuration is not supported by device or driver,"
               " check the UART settings configuration\n");
        return -ENOSYS;
    }

    /* Configure uart callback */
    uart_callback_set(uart_dev, uart_cb, NULL);

    /* enable uart reception */
    uart_rx_enable(uart_dev, rx_buffer, sizeof(rx_buffer), RECEIVE_TIMEOUT);

    printk("Enter message to fill RX buffer size :\r\n");

    /* start uart data processing thread */
    k_tid_t tid = k_thread_create(&uart_rx_thread_data, uart_rx_stack,
                                  K_THREAD_STACK_SIZEOF(uart_rx_stack), uart_rx_thread, NULL,
                                  NULL, NULL, 5, 0, K_NO_WAIT);
    k_thread_name_set(tid, "RX_thread");
}

回复

使用道具 举报

2

主题

4

回帖

10

积分

新手上路

积分
10
 楼主| 发表于 2025-8-18 22:57:45 | 显示全部楼层
狠狠滴摸鱼冲浪 发表于 2025-8-18 16:45
[mw_shl_code=c,true]#include
#include
#include

感谢大佬回复,通过线程的方法确实可行,但是我现在一直想弄明白着这块为啥不行,按逻辑应该是可以的呀,搞得我现在头都大了。
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-19 08:41:33 | 显示全部楼层
aiqiqi 发表于 2025-8-18 22:57
感谢大佬回复,通过线程的方法确实可行,但是我现在一直想弄明白着这块为啥不行,按逻辑应该是可以的呀, ...

这个是 zephyr/samples/boards/st/uart/circular_dma 示例改的,就把轮询发送改成了异步发送
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-19 08:42:44 | 显示全部楼层
你可以在 sdk 中搜索 UART_RX_BUF_REQUEST 相关的内容,基本上都是使用的双缓冲区的形式
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-19 09:27:18 | 显示全部楼层
尝试发现:不配合 ring_buff 这种缓冲机制,直接在接收回调中调用异步发送会存在一个问题
比如 uart_rx_enable() 配置的 buf 大小为 40,
    当使用串口调试助手单次发送 10 字节时,因为 40/10 = 4 ... 0,刚好能把缓冲区用完,数据回环不会丢数据
    当使用串口调试助手单次发送 11 字节时,40/11 = 3 ... 7,会导致第四包数据出现数据部分丢失
当设备树配置接收 STM32_DMA_MODE_CYCLIC 时,可以发现第二包就会出现数据丢失的情况,因为目前 STM32_DMA_MODE_CYCLIC 会配置DMA半传输完成中断,40字节的初始配置,会在20字节的时候触发中断

[C] 纯文本查看 复制代码
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/printk.h>
#include <string.h>

#define RX_BUF_SIZE         40
#define RECEIVE_TIMEOUT     10

static const struct device *const uart_dev = DEVICE_DT_GET(DT_NODELABEL(usart2));

/* uart configuration structure */
const struct uart_config uart_cfg = {.baudrate = 115200,
                                     .parity = UART_CFG_PARITY_NONE,
                                     .stop_bits = UART_CFG_STOP_BITS_1,
                                     .data_bits = UART_CFG_DATA_BITS_8,
                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};

/* define uart rx buffer */
static __aligned(4) uint8_t rx_buffer[RX_BUF_SIZE];


void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
    switch (evt->type)
    {
    case UART_RX_RDY:
        uart_tx(uart_dev, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len, 0);
        break;

    case UART_RX_DISABLED:
        uart_rx_enable(uart_dev, rx_buffer, sizeof(rx_buffer), RECEIVE_TIMEOUT);
        break;

    default:
        break;
    }
}

int main(void)
{
    if (!uart_dev)
    {
        printk("Failed to get UART device");
        return 1;
    }

    /* uart configuration parameters */
    int err = uart_configure(uart_dev, &uart_cfg);

    if (err == -ENOSYS)
    {
        printk("Configuration is not supported by device or driver,"
               " check the UART settings configuration\n");
        return -ENOSYS;
    }

    /* Configure uart callback */
    uart_callback_set(uart_dev, uart_cb, NULL);

    /* enable uart reception */
    uart_rx_enable(uart_dev, rx_buffer, sizeof(rx_buffer), RECEIVE_TIMEOUT);
}
回复

使用道具 举报

2

主题

20

回帖

26

积分

新手上路

积分
26
发表于 2025-8-19 10:21:57 | 显示全部楼层
想问下,我使用的是smt32h743,可以使用printk,现在想测试下异步串口,也是使用的ring_buffer那个示例。
这是我的overlay代码
[C] 纯文本查看 复制代码
&usart1 {
    dmas = <&dmamux1 0 42 (STM32_DMA_PERIPH_TX | STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS | STM32_DMA_MODE_NORMAL)
        &dmamux1 1 41 (STM32_DMA_PERIPH_RX | STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS | STM32_DMA_MODE_NORMAL)>;
    dma-names = "tx", "rx";
};

&dmamux1 {
    status = "okay";
};

&dma1 {
    status = "okay";
};

&dma2 {
    status = "okay";
};

这是我的conf配置
[C] 纯文本查看 复制代码
CONFIG_GPIO=y
CONFIG_LOG=y
CONFIG_LOG_BACKEND_UART=y
CONFIG_PRINTK=y

CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_RING_BUFFER=y
CONFIG_DMA=y
CONFIG_NOCACHE_MEMORY=y

这样配置后正常的printk也无法打印输出了,请问是哪里出现了问题呢,应该怎么调试呢
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-19 10:25:34 | 显示全部楼层
狠狠滴摸鱼冲浪 发表于 2025-8-18 16:45
[mw_shl_code=c,true]#include
#include
#include

这个程序的设备树中串口接收 DMA 必需配置 STM32_DMA_MODE_CYCLIC 才不会丢数据
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-19 11:02:13 | 显示全部楼层
Ljiangwei 发表于 2025-8-19 10:21
想问下,我使用的是smt32h743,可以使用printk,现在想测试下异步串口,也是使用的ring_buffer那个示例。
...


注意接收 DMA 配置 STM32_DMA_MODE_CYCLIC


我用的 stm32f103,stm32f103_mini  的串口1也就是 zephyr,shell-uart 是没有配置 dma 的,我图方便直接改的板子的设备树

west build -p always -b stm32f103_mini  samples/boards/st/uart/circular_dma

[C] 纯文本查看 复制代码
&usart1 {
	pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
	pinctrl-names = "default";
	current-speed = <115200>;
	status = "okay";

	dmas = <&dma1 4 (STM32_DMA_PERIPH_TX | STM32_DMA_PRIORITY_HIGH)>,
		   <&dma1 5 (STM32_DMA_MODE_CYCLIC | STM32_DMA_PERIPH_RX | STM32_DMA_PRIORITY_HIGH)>;
	dma-names = "tx", "rx";
};
&dma1 {
	status = "okay";
};





回复

使用道具 举报

2

主题

20

回帖

26

积分

新手上路

积分
26
发表于 2025-8-19 21:28:12 | 显示全部楼层
狠狠滴摸鱼冲浪 发表于 2025-8-19 11:02
注意接收 DMA 配置 STM32_DMA_MODE_CYCLIC

测试了下,发现是h7系列的cache问题,必须将用作dma缓存buffer放在nocache才行。但是我printk使用的uart如果开启dma,printk就无法正常打印了。不知道你f1有没有这个问题。
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-20 09:35:33 | 显示全部楼层
Ljiangwei 发表于 2025-8-19 21:28
测试了下,发现是h7系列的cache问题,必须将用作dma缓存buffer放在nocache才行。但是我printk使用的uart ...

不知道你是不是指下面的程序,printk 运行时还要能数据回环。
我使用 h750 测试了一下
PixPin_2025-08-20_09-35-05.gif

[C] 纯文本查看 复制代码
/*
 * Copyright (c) 2024 STMicroelectronics
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/printk.h>
#include <string.h>

#define RING_BUF_SIZE 1000
#define RX_BUF_SIZE 10

#define RECEIVE_TIMEOUT 0

#define STACK_SIZE 1024

#define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart)

static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);

/* uart configuration structure */
const struct uart_config uart_cfg = {.baudrate = 115200,
                                     .parity = UART_CFG_PARITY_NONE,
                                     .stop_bits = UART_CFG_STOP_BITS_1,
                                     .data_bits = UART_CFG_DATA_BITS_8,
                                     .flow_ctrl = UART_CFG_FLOW_CTRL_NONE};

/* define a ring buffer to get raw bytes*/
RING_BUF_DECLARE(ring_buf, RING_BUF_SIZE);

/* define uart rx buffer */
static __nocache __aligned(8) uint8_t rx_buffer[RX_BUF_SIZE];

/* define thread  stack size */
static K_THREAD_STACK_DEFINE(uart_rx_stack, STACK_SIZE);

/* struct uart_event async_evt */
static struct k_thread uart_rx_thread_data = {0};

void print_uart(char *buf, int len)
{
    for (int i = 0; i < len; i++)
    {
        // if ((buf[i] == '\n' || buf[i] == '\r'))
        // {
        // 	uart_poll_out(uart_dev, '\n');
        // }
        // else
        {
            uart_poll_out(uart_dev, buf[i]);
        }
    }
}

/* Data processing thread */
static void uart_rx_thread(void *p1, void *p2, void *p3)
{
    uint8_t rx_data[RX_BUF_SIZE];
    size_t len;

    while (1)
    {

        /* Check if there's data in the ring buffer */
        len = ring_buf_get(&ring_buf, rx_data, sizeof(rx_data));

        if (len > 0)
        {

            /* Process `len` bytes of data */
            print_uart(rx_data, len);
        }
    }
}

void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
    switch (evt->type)
    {
    case UART_RX_RDY:
        /* Data received; place into ring buffer */
        ring_buf_put(&ring_buf, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len);

        break;

    case UART_RX_DISABLED:
        /* Re-enable RX */
        uart_rx_enable(uart_dev, rx_buffer, sizeof(rx_buffer), RECEIVE_TIMEOUT);

        break;

    default:
        break;
    }
}

int main(void)
{
    if (!uart_dev)
    {
        printk("Failed to get UART device");
        return 1;
    }

    /* uart configuration parameters */
    int err = uart_configure(uart_dev, &uart_cfg);

    if (err == -ENOSYS)
    {
        printk("Configuration is not supported by device or driver,"
               " check the UART settings configuration\n");
        return -ENOSYS;
    }

    /* Configure uart callback */
    uart_callback_set(uart_dev, uart_cb, NULL);

    /* enable uart reception */
    uart_rx_enable(uart_dev, rx_buffer, sizeof(rx_buffer), RECEIVE_TIMEOUT);

    printk("\n Enter message to fill RX buffer size :\n");

    /* start uart data processing thread */
    k_tid_t tid = k_thread_create(&uart_rx_thread_data, uart_rx_stack,
                                  K_THREAD_STACK_SIZEOF(uart_rx_stack), uart_rx_thread, NULL,
                                  NULL, NULL, 5, 0, K_NO_WAIT);
    k_thread_name_set(tid, "RX_thread");

    while (1)
    {
        printk("uptime %lld\r\n", k_uptime_get());
        k_msleep(1000);
    }
}



[C] 纯文本查看 复制代码
&usart1 {
	pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
	pinctrl-names = "default";
	current-speed = <115200>;
	status = "okay";

    dmas = <&dmamux1 0 42 (STM32_DMA_PERIPH_TX | STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS)>,
           <&dmamux1 1 41 (STM32_DMA_PERIPH_RX | STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS | STM32_DMA_MODE_CYCLIC)>;
    dma-names = "tx", "rx";
	fifo-enable;
};
&dmamux1 {
    status = "okay";
};
&dma1 {
    status = "okay";
};


[C] 纯文本查看 复制代码
CONFIG_GPIO=y
CONFIG_DMA=y
CONFIG_UART_STM32=y
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_RING_BUFFER=y

CONFIG_NOCACHE_MEMORY=y


回复

使用道具 举报

2

主题

20

回帖

26

积分

新手上路

积分
26
发表于 2025-8-21 21:50:03 | 显示全部楼层
狠狠滴摸鱼冲浪 发表于 2025-8-20 09:35
不知道你是不是指下面的程序,printk 运行时还要能数据回环。
我使用 h750 测试了一下

感谢,我发现问题了,.conf里面配置了CONFIG_LOG=y。如果把这条配置删除,printk就能正常工作了,不知道为什么
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-22 08:49:44 | 显示全部楼层
Ljiangwei 发表于 2025-8-21 21:50
感谢,我发现问题了,.conf里面配置了CONFIG_LOG=y。如果把这条配置删除,printk就能正常工作了,不知道 ...

LOG 模式默认为 DEFERRED 模式,也就是会将LOG数据推送到 log 线程延迟输出日志。
改为 IMMEDIATE 模式,就好了

CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y

https://docs.zephyrproject.org/l ... g/index.html#printk
回复

使用道具 举报

3

主题

20

回帖

29

积分

新手上路

积分
29
发表于 2025-8-22 08:58:43 | 显示全部楼层
Ljiangwei 发表于 2025-8-21 21:50
感谢,我发现问题了,.conf里面配置了CONFIG_LOG=y。如果把这条配置删除,printk就能正常工作了,不知道 ...

或者配置
CONFIG_LOG=y
CONFIG_LOG_PRINTK=n
回复

使用道具 举报

2

主题

20

回帖

26

积分

新手上路

积分
26
发表于 2025-8-22 13:54:42 | 显示全部楼层
狠狠滴摸鱼冲浪 发表于 2025-8-22 08:58
或者配置
CONFIG_LOG=y
CONFIG_LOG_PRINTK=n

感谢,现在printk也可以正常工作了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-9-26 19:35 , Processed in 0.053101 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表