Funpack5-3 - 基于MAX32690实现使用 Zephyr UART 驱动的串口回显以及屏幕显示
该项目使用了CodeFusion Studio,实现了MAX32690EVKIT的设计,它的主要功能为:基于Zephyr驱动的串口回显和屏幕显示。
标签
Funpack活动
Zephyr
屏幕显示
MAX32690
赵团团
更新2026-08-20
18

·项目介绍

本项目基于ADImax32690微处理器提供的评估套件MAX32690EVKIT使用Zephyr UART驱动display驱动,实现串口回显以及屏幕显示。

方案框图如下:

diagram-flowchart.png

1.1硬件介绍

本项目基于以下硬件实现:

MAX32690 MAX32690微控制器(MCU)是一款先进的片内系统(SoC),具有Arm® Cortex®-M4F CPU、大容量闪存和SRAM存储器以及最新一代Bluetooth® 5.2低功耗(LE)射频。该套件将处理能力与物联网应用程序所需的连接性结合在一起

MAX32690EVKIT Analog Devices (ADI)MAX32690微控制器推出的官方评估套件。它提供了一个功能丰富的平台,用于评估这款高性能、超低功耗且支持蓝牙5.2MCU板上集成了128x128像素的彩色TFT显示屏、立体声音频编解码器(带3.5mm接口)、板载HyperRAM以及一个带鞭状天线的蓝牙SMA连接器USB 2.0 Micro-B转串行UART

 

 

1.2功能概览

1.通过串口接收字符

2.接收到的字符进行大小写转换后再回显

3.使用Zephyr UART驱动

4.使用板载彩色TFT显示屏显示图片

1.3设计思路

实现项目功能概览的全部功能,实现步骤如下:

1.准备硬件和软件的开发环境

2.配置设备树

3.编写串口回显代码

4.编写屏幕显示代码

·功能实现

功能主要实现了两个功能:

UART交互:通过串口接收用户输入,将输入的小写字母转换为大写,大写字母转换为小写(大小写翻转),然后回显给用户。

显示屏初始化并显示图片:在程序启动时,向ST7735 TFT显示屏写入张图片,用于验证显示驱动是否正常工作。

2.1.软件流程

  1.初始化阶段

diagram-flowchart.png

2.运行阶段

1UART中断回调处理(serial_cb)

本函数在UART接收中断触发时执行:

更新UART中断状态(调用 uart_irq_update

检查是否有接收数据准备好(调用 uart_irq_rx_ready

循环从FIFO读取字符数据

如果读取到行结束符(\n  \r)且缓冲区有内容,则:

在字符串末尾添加结束符 \0

将完整字符串放入消息队列(k_msgq_put

重置接收缓冲区位置(rx_buf_pos = 0

如果读取到普通字符且缓冲区未满,则:

检查字符是否为英文字母

如果是大写字母('A'-'Z'),转换为小写(加32

如果是小写字母('a'-'z'),转换为大写(减32

将转换后的字符存入接收缓冲区

超出缓冲区大小的字符被丢弃

2)字符输出功能(print_uart)

本函数实现字符串的串口发送:

计算字符串长度

循环逐字符调用 uart_poll_out 发送

支持发送任意以 \0 结尾的字符串

3)显示初始化与更新功能(main函数中实现)

本功能在系统启动时执行:

通过 device_get_binding("ST7735") 获取显示设备

分配128×160RGB565帧缓冲区(共20480像素)

将帧缓冲区填充为图片

配置显示描述符(宽度、高度、行距等)

调用 display_write 将帧缓冲区数据写入显示屏

检查写入操作是否成功并打印状态信息

4)主循环消息处理(main函数中的while循环)

本循环持续运行处理用户输入:

阻塞等待消息队列中的数据(k_msgq_get

接收到用户输入的字符串后:

发送"Echo: "前缀

发送转换后的用户输入字符串

发送换行回车符(\r\n)0

等待下一条用户输入

2.2实现过程

项目基于CodeFusion Studio(CFS)进行开发,CodeFusion Studio(CFS是一款基于Microsoft的Visual Studio Code(VS Code)的嵌入式软件开发平台。CodeFusion Studio 为嵌入式处理器和 MCU 提供一流的开发工具,为新手提供直观的工具,同时为专业嵌入式开发者启用高级功能。

1. 配置设备树

Zephyr引入了DeviceTree这一成熟的方案,像Linux一样,各个半导体厂商自己出DeviceTree来描述自己的产品,并且自己提供各个外设的驱动代码。用户只需调用Zephyr标准驱动,底层就会根据DeviceTree自动找到对应厂商的驱动代码,然后编译进固件中,所以需要正确配置设备树。由于 EV 套件设计将 MAX32690 的可用 SPI 引脚分配给音频编解码器,因此 TFT 的 SPI 引脚被连接到通用 IO 引脚上。固件需要通过直接设置、清除和读取这些 IO 引脚(也就是 Bit-banging)来模拟所需的 SPI 信号,所以设备树要做特殊配置。

chosen {
zephyr,code-partition = &m4_flash;
zephyr,flash = &flash0;
zephyr,coredump-partition = &coredump_partition;
zephyr,display = &st7735;
zephyr,sram = &m4_sram;
};

mipi_dbi {
compatible = "zephyr,mipi-dbi-spi";
spi-dev = <&spibb0>;
#address-cells = <1>;
#size-cells = <0>;

status = "okay";

st7735: st7735@0 {
compatible = "sitronix,st7735r";
mipi-max-frequency = <DT_FREQ_M(6)>;
mipi-mode = "MIPI_DBI_MODE_SPI_3WIRE";
label = "ST7735";
reg = <0>;
width = <130>;
height = <132>;
x-offset = <0>;
y-offset = <0>;
madctl = <0xc8>;
colmod = <0x05>;
vmctr1 = <0x51>;
pwctr1 = [02 02];
pwctr2 = [c5];
pwctr3 = [0d 00];
pwctr4 = [8d 1a];
pwctr5 = [8d ee];
frmctr1 = [02 35 36];
frmctr2 = [02 35 36];
frmctr3 = [02 35 36 02 35 36];
gamctrp1 = [0a 1c 0c 14 33 2b 24 28 27 25 2c 39 00 05 03 0d];
gamctrn1 = [0a 1c 0c 14 33 2b 24 28 27 25 2d 3a 00 05 03 0d];
};
};

spibb0: spibb0 {
compatible = "zephyr,spi-bitbang";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
clk-gpios = <&gpio2 25 (GPIO_ACTIVE_HIGH | MAX32_GPIO_VSEL_VDDIOH)>;
mosi-gpios = <&gpio2 24 (GPIO_ACTIVE_HIGH | MAX32_GPIO_VSEL_VDDIOH)>;
cs-gpios = <&gpio2 11 (GPIO_ACTIVE_LOW | MAX32_GPIO_VSEL_VDDIOH)>;
};

reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;

m4_sram: memory@20000000 {
/* This partition is chosen for sram. */
compatible = "mmio-sram";
reg = <0x20000000 DT_SIZE_K(768)>;
};
};
};

2. 图片数据填充

RGB565是16位色,每个像素只需要2个字节,直接能节省33%的存储空间和内存占用 尤其是在嵌入式系统、单片机、智能手表、小型LCD屏,以及需要高帧率的小游戏开发中,设备的内存和处理器资源有限,RGB565既能保证视觉效果(人眼几乎看不出差异),又能大幅提升运行效率,是性价比最高的选择。

static const uint16_t logo_rgb565[20480]= {
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,
  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001F,  0x001...};

3.UART中断处理过程 (serial_cb)

触发条件:用户通过串口发送数据

处理流程:更新UART中断状态,检查是否有接收数据,循环从FIFO读取每个字符

对每个字符进行处理:,大写字母(A-Z):转换为小写,big++ 计数,小写字母(a-z):转换为大写,small++ 计数,统计总数:total++,存入接收缓冲区 rx_buf

检测到行结束符(\n 或 \r)时:在缓冲区末尾添加字符串结束符 \0,将完整字符串放入消息队列 uart_msgq,重置缓冲区位

void serial_cb(const struct device *dev, void *user_data)
{
    uint8_t c;

    if (!uart_irq_update(uart_dev)) {
        return;
    }

    if (!uart_irq_rx_ready(uart_dev)) {
        return;
    }

    /* read until FIFO empty */
    while (uart_fifo_read(uart_dev, &c, 1) == 1) {
        if ((c == '\n' || c == '\r') && rx_buf_pos > 0) {
            /* terminate string */
            rx_buf[rx_buf_pos] = '\0';

            /* if queue is full, message is silently dropped */
            k_msgq_put(&uart_msgq, &rx_buf, K_NO_WAIT);

            /* reset the buffer (it was copied to the msgq) */
            rx_buf_pos = 0;
        } else if (rx_buf_pos < (sizeof(rx_buf) - 1)) {
             if (c >= 'A' && c <= 'Z') {
                c=c+32;
                big++;
             }
            else if (c >= 'a' && c <= 'z') {
                c=c-32;
                small++;
             }
             total++;
            rx_buf[rx_buf_pos++] = c;
        }
        /* else: characters beyond buffer size are dropped */
    }
}

4.将图片数据填充到屏幕

    const struct device *display;
    display = device_get_binding("ST7735"); /* match the label in DTS */


    if (!display) {
        printk("Failed to find display device\n");
        return 0;
    }
    if (!device_is_ready(display)) {
        printk("Display device not ready\n");
        return 0;
    }
  struct display_buffer_descriptor desc = {
        .pitch = W,
        .width = W,
        .height = H,
        .buf_size = sizeof(logo_rgb565),
    };
    int rc = display_write(display, 0, 0, &desc, logo_rgb565);
    if (rc) {
        printk("display_write failed: %d\n", rc);
    } else {
        printk("Wrote frame to display\n");
    }

三 功能展示

串口回显:

96816d73569cba479f9eb2b2b7cfc77d.png

屏幕显示:

四 总结

1.遇到的难题

本次项目中最主要的问题是能否正确的配置设备树,而设MAX32690EVKIT设计将 MAX32690 的可用 SPI 引脚分配给音频编解码器,因此 TFT 的 SPI 引脚被连接到通用 IO 引脚上,所以固件需要通过直接设置、清除和读取这些 IO 引脚(也就是 Bit-banging)来模拟所需的 SPI 信号,故配置设备树比较麻烦,不能简单的配置SPI,而在ADI的MSDK中对此有详细的配置说明,比对着这些最终成功的驱动了套件的屏幕。

2.心得体会

第一次基于max32系列芯片进行开发,也是第一次接触zephyr系统。从STM32到MAX32690,从裸机到Zephyr,这个过程就像学会了一门新的语言Analog Devices的CFS(Code Fusion Studio) 后,图形化界面极大降低了上手难度。虽然开始很痛苦,但掌握之后,视野豁然开朗——原来嵌入式开发可以这样优雅、可移植、可维护特别是看到自己写的UART回显程序能正常运行,并且可以在TFT屏上刷出Logo,那种成就感是值得的。

最后感谢电子森林与得捷电子联合推出的 《Funpack》 系列活动,对于我来说是个很好的学习机会,理论结合实践。我们下期活动再见



附件下载
st7735.zip
团队介绍
电子爱好者
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号