在上一篇配置Zephyr开发环境的基础上(【Zephyr】MCXN947 Zephyr开发环境搭建),我们开始上手第一个Zephyr 应用来学习Zephyr的使用。Zephyr 的源码的Demo 目录下有很多示例程序,我们可以使用这些示例程序进入Zephyr开发,在FreeRtos的基础环境开发中,习惯适配个shell 方便用于和cpu 交互使用,我们将之前适配的shell移植到Zephyr,Shell 的移植过程可以查看此贴(基础环境搭建之裸机环境添加shell),本次不做重点介绍。
开始之前我们先看下Zephyr 的软件结构图,从中可以看到我们本次shell 依赖的uart 的驱动在软件结构中的层次
因为要适配shell,主要依赖uart 的驱动,我们选取 zephyr\samples\drivers\uart\echo_bot 作为base,在此基础上进行修改适配shell,首先复制echo_bot 命名为shell,方便我门修改添加shell。
zephyr 的编译使用CMAKE来编译,我们修改shell/CMakeLists.txt 文件加入shell 源代码及依赖的ringbuffer 代码并将对应.h 路径加入到include path.
添加完源代码后在main函数入口添加uart 的配置操作,并注册uart 的中断回调函数,在回调内更新ringbuffer.
/* * Copyright (c) 2022 Libre Solar Technologies GmbH * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/drivers/uart.h> #include <littleshell.h> #include <ringbuffer.h> #include <string.h> /* change this to any other UART peripheral if desired */ #define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart) #define MSG_SIZE 32 /* queue to store up to 10 messages (aligned to 4-byte boundary) */ //K_MSGQ_DEFINE(uart_msgq, MSG_SIZE, 10, 4); static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE); static RingBuffer ringbuff; /* receive buffer used in UART ISR callback */ //static char rx_buf[MSG_SIZE]; //static int rx_buf_pos; uint8_t shell_uart_getchar(uint8_t * ch) { return RingBuffer_Read(&ringbuff,ch,1); } /* * Read characters from UART until line end is detected. Afterwards push the * data to the message queue. */ 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) { /* else: characters beyond buffer size are dropped */ /* Get date and write to ringbuffer */ RingBuffer_Write(&ringbuff,&c,1); } } /* * Print a null-terminated string character by character to the UART interface */ void print_uart(char *buf) { int msg_len = strlen(buf); for (int i = 0; i < msg_len; i++) { uart_poll_out(uart_dev, buf[i]); } } int main(void) { //char tx_buf[MSG_SIZE]; /* Init Ringbuffer */ static uint8_t uart_rx_buff[128] = {0}; RingBuffer_Init(&ringbuff,uart_rx_buff,128); if (!device_is_ready(uart_dev)) { printk("UART device not found!"); return 0; } /* configure interrupt and callback to receive data */ int ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL); if (ret < 0) { if (ret == -ENOTSUP) { printk("Interrupt-driven UART API support not enabled\n"); } else if (ret == -ENOSYS) { printk("UART device does not support interrupt-driven API\n"); } else { printk("Error setting UART callback: %d\n", ret); } return 0; } uart_irq_rx_enable(uart_dev); littleshell_main_entry(NULL); /* indefinitely wait for input from the user */ return 0; }
因为shell 中用大到了了"FSymTab" 来保存命令,需要在link 文件中添加对应的定义,我们使用的MCXN947板子使用了zephyr\include\zephyr\arch\arm\cortex_m\scripts\link.ld的链接文件,修改文件添加对应的section 定义。
这里分享一个小技巧,因为zephyr 仓库内的文件比较多,我们如何快速找到对应的link 文件,我们可以在west 编译时加上-v 选项,这样在终端上会输出详细的编译信息
从这些编译信息中我们可以快速找到编译使用的link file 文件。
上述修改完成后,我们使用west build -p auto -b frdm_mcxn947/mcxn947/cpu0 samples\drivers\uart\shell 命令来编译应用程序。
将生成的可执行程序下载到板子中运行发现shell 已经按照预取的运行
至此我们就将之前的裸机的shell 适配到zephyr 。