一·项目介绍
本项目基于ADI为max32690微处理器提供的评估套件MAX32690EVKIT使用Zephyr UART驱动和display驱动,实现串口回显以及屏幕显示。
方案框图如下:

1.1硬件介绍
本项目基于以下硬件实现:
MAX32690 :MAX32690微控制器(MCU)是一款先进的片内系统(SoC),具有Arm® Cortex®-M4F CPU、大容量闪存和SRAM存储器以及最新一代Bluetooth® 5.2低功耗(LE)射频。该套件将处理能力与物联网应用程序所需的连接性结合在一起。
MAX32690EVKIT :Analog Devices (ADI)为MAX32690微控制器推出的官方评估套件。它提供了一个功能丰富的平台,用于评估这款高性能、超低功耗且支持蓝牙5.2的MCU,板上集成了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.初始化阶段

2.运行阶段
(1)UART中断回调处理(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×160的RGB565帧缓冲区(共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");
}
三 功能展示
串口回显:

屏幕显示:

四 总结
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》 系列活动,对于我来说是个很好的学习机会,理论结合实践。我们下期活动再见