Zephyr设备树理论知识请参考:Zephyr设备树保姆级上手教程【上】
本文主要是通过以下实例来讲解设备树节点如何编写
●给项目增加一个UART输出接口
●给项目增加一个LCD显示屏
实战中使用的是聆思命令行指令,可以参考《聆思CSK6开发工具简介》进行一键部署
实战1:如何编写UART1的串口节点
1、使用lisa zep creat创建UART 的Sample
sample选择路径:sample→boards→csk6→driver→uart→[uart_polling]
2、重新定义UART1
打开/uart_polling/boards路径下的csk6011a_nano.overlay文件进行串口配置,配置代码信息如下:
&pinctrl{
pinctrl_uart1_rx_default: uart1_rx_default{
pinctrls = <UART1_RXD_GPIOA_05>; //rx pin
};
pinctrl_uart1_tx_default: uart1_tx_default{
pinctrls = <UART1_TXD_GPIOA_04>; //tx pin
};
};
&uart1{
status = "okay";
current-speed = <9600>; //配置波特率
pinctrl-0 = <&pinctrl_uart1_rx_default &pinctrl_uart1_tx_default>;
pinctrl-names = "default";
};
想了解更多串口配置信息可以参考样本配置文件,配置讲解请参考文末视频
串口样本配置文件在聆思CSK6开发工具的安装目录下可以找到,路径为:开发工具安装目录/LISA/csk-sdk/zephyr/boards/arm/csk6011a_nano/csk6011a_nano.dts
修改串口后,相关配置项关联文件也需要修改:
> */zephyr/dts/arm/csk/csk6.dtsi
> */zepbyr/dts/bindings/serial/uart-controller.yaml
> */zepbyr/dts/bindings/pinctrl/pinctrl-device.yaml
> */zepbyr/dts/bindings/serial/listenai,csk-uart.yaml
> */zephyr/include/zephyr/dt-bindings/pinctrl/csk6-pinctrl.h
注意:/zephy让为ZEPHYR_BASE目录,可通过lisa info zep获得ZEPHYR_BASE目录路径
3、修改主程序main.c
将main.c中配置为“uart0”修改为“uart1”,配置修改后的信息参考如下,
主程序main.c路径:/uart_polling/src/main.c ,配置讲解请参考文末视频。
#include <kernel.h>
#include <zephyr.h>
#include <sys/printk.h>
#include <string.h>
#include <devicetree.h>
#include <logging/log.h>
#include <drivers/uart.h>
LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
#define UART1 DT_NODELABEL(uart1)
void main(void)
{
char rx_char;
const struct device *uart = device_get_binding("UART_1");
if(!device_is_ready(uart)){
LOG_ERR("device:%s is not ready!", uart->name);
return;
}
LOG_INF("devices %s is ready", uart->name);
while(1){
if(uart_poll_in(uart, &rx_char) == 0){/*read a char*/
uart_poll_out(uart, rx_char); /*send a char*/
}
}
return;
}
4、编译/烧录
- 进入实例目录:cd uart_polling
- 执行编译:lisa zep build -b csk6011a_nano
- 烧录指令:lisa zep flash
实战2:如何编写LCD显示屏节点(屏幕型号st7789)
实现路径参考
1、查看zephyr已有的LCD支持列表,确定可行性
2、根据yaml要求的硬件节点信息,结合实际硬件电路给出dts节点
3、打开驱动加载设备
st7789会抛出如下截图的API,应用层使用伪代码调用对应的API就可以调用st7789外设
伪代码调用:
> display_dev = device_get_binding(“sitronix_st7789v”);
> display_get_capabilities(display_dev, &capabilities);
> display_write(display_dev, x, y, &desc, buf);
基于sample模板实现驱动显示屏
1、选择同类设备的sample模板
创建命令:lisa zep create
此处使用的显示器模板选择路径:sample→boards→csk6→driver→display→ili9488
2、修改设备树文件
打开工程/ili9488/boards/路径,只保留一个overlay文件并填入以下的配置信息,修改名称为csk6011a_nano.overlay文件。
&csk6011a_nano_pinctrl{
pinctrl_spi0_sclk_default: spi0_sclk_default {
pinctrls = <&pinmuxb 1 6>;
};
pinctrl_spi0_mosi_default: spi0_mosi_default {
pinctrls = <&pinmuxb 10 6>;
};
pinctrl_spi0_miso_default: spi0_miso_default {
pinctrls = <&pinmuxa 17 6>;
};
pinctrl_spi0_cs_default: spi0_cs_default {
pinctrls = <&pinmuxb 0 6>;
};
};
&spi0 {
status = "okay";
pinctrl-0 = <&pinctrl_spi0_sclk_default &pinctrl_spi0_mosi_default &pinctrl_spi0_miso_default &pinctrl_spi0_cs_default>;
pinctrl-names = "default";
st7789v@0 {
compatible = "sitronix,st7789v";
label = "ST7789V";
status = "okay";
spi-max-frequency = <20000000>;
reg = <0>;
cmd-data-gpios = <&gpiob 2 1>;
reset-gpios = <&gpioa 15 1>;
width = <320>;
height = <240>;
x-offset = <0>;
y-offset = <0>;
// reg: 0xBB
vcom = <0x1e>;
// reg: 0xB7
gctrl = <0x35>;
// reg: 0xC3
vrhs = <0x0b>;
// reg: 0xC4
vdvs = <0x20>;
// reg: 0x36
mdac = <0xA3>;
// reg: 0x26
gamma = <0x01>;
// reg: 0x3a
colmod = <0x05>;
// reg: 0xc0
lcm = <0x2c>;
// reg: 0xb2
porch-param = [ 0c 0c 00 33 33 ];
// reg: 0xDF
cmd2en-param = [ 5a 69 02 01 ];
// reg: 0xD0
pwctrl1-param = [ a4 a1 ];
// reg: 0xE0
pvgam-param = [ d0 06 0b 07 07 24 2e 32 46 37 13 13 2d 33 ];
// reg: 0xE1
nvgam-param = [ d0 02 06 09 08 05 29 44 42 38 14 14 2a 30 ];
// reg: b0
ram-param = [ 00 F0 ];
// reg: b1
rgb-param = [ CD 08 14 ];
};
};
3、修改prj.con
路径为/ili9488/prj.conf
> CONFIG_HEAP_MEM_POOL_SIZE=102400
>
> #hardware
> CONFIG_DISPLAY=y
>
> CONFIG_ST7789V=y
> CONFIG_SPI=y
> CONFIG_GPIO=y
4、修改主程序main.c
/*
* Based on ili9488 sample:
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
#include <zephyr.h>
#include <device.h>
#include <drivers/display.h>
#define DISPLAY_DEV_NAME DT_LABEL(DT_INST(0, sitronix_st7789v))
enum corner {
TOP_LEFT,
TOP_RIGHT,
BOTTOM_RIGHT,
BOTTOM_LEFT
};
typedef void (*fill_buffer)(enum corner corner, uint8_t grey, uint8_t *buf,
size_t buf_size);
static void fill_buffer_argb8888(enum corner corner, uint8_t grey, uint8_t *buf,
size_t buf_size)
{
uint32_t color = 0;
switch (corner) {
case TOP_LEFT:
color = 0x00FF0000u;
break;
case TOP_RIGHT:
color = 0x0000FF00u;
break;
case BOTTOM_RIGHT:
color = 0x000000FFu;
break;
case BOTTOM_LEFT:
color = grey << 16 | grey << 8 | grey;
break;
}
for (size_t idx = 0; idx < buf_size; idx += 4) {
*((uint32_t *)(buf + idx)) = color;
}
}
static void fill_buffer_rgb888(enum corner corner, uint8_t grey, uint8_t *buf,
size_t buf_size)
{
uint32_t color = 0;
switch (corner) {
case TOP_LEFT:
color = 0x00FF0000u;
break;
case TOP_RIGHT:
color = 0x0000FF00u;
break;
case BOTTOM_RIGHT:
color = 0x000000FFu;
break;
case BOTTOM_LEFT:
color = grey << 16 | grey << 8 | grey;
break;
}
for (size_t idx = 0; idx < buf_size; idx += 3) {
*(buf + idx + 0) = color >> 16;
*(buf + idx + 1) = color >> 8;
*(buf + idx + 2) = color >> 0;
}
}
static uint16_t get_rgb565_color(enum corner corner, uint8_t grey)
{
uint16_t color = 0;
uint16_t grey_5bit;
switch (corner) {
case TOP_LEFT:
color = 0xF800u;
break;
case TOP_RIGHT:
color = 0x07E0u;
break;
case BOTTOM_RIGHT:
color = 0x001Fu;
break;
case BOTTOM_LEFT:
grey_5bit = grey & 0x1Fu;
/* shift the green an extra bit, it has 6 bits */
color = grey_5bit << 11 | grey_5bit << (5 + 1) | grey_5bit;
break;
}
return color;
}
static void fill_buffer_rgb565(enum corner corner, uint8_t grey, uint8_t *buf,
size_t buf_size)
{
uint16_t color = get_rgb565_color(corner, grey);
for (size_t idx = 0; idx < buf_size; idx += 2) {
*(buf + idx + 0) = (color >> 8) & 0xFFu;
*(buf + idx + 1) = (color >> 0) & 0xFFu;
}
}
static void fill_buffer_bgr565(enum corner corner, uint8_t grey, uint8_t *buf,
size_t buf_size)
{
uint16_t color = get_rgb565_color(corner, grey);
for (size_t idx = 0; idx < buf_size; idx += 2) {
*(uint16_t *)(buf + idx) = color;
}
}
static void fill_buffer_mono(enum corner corner, uint8_t grey, uint8_t *buf,
size_t buf_size)
{
uint16_t color;
switch (corner) {
case BOTTOM_LEFT:
color = (grey & 0x01u) ? 0xFFu : 0x00u;
break;
default:
color = 0;
break;
}
memset(buf, color, buf_size);
}
void main(void)
{
size_t x;
size_t y;
size_t rect_w;
size_t rect_h;
size_t h_step;
size_t scale;
size_t grey_count;
uint8_t *buf;
int32_t grey_scale_sleep;
const struct device *display_dev;
struct display_capabilities capabilities;
struct display_buffer_descriptor buf_desc;
size_t buf_size = 0;
fill_buffer fill_buffer_fnc = NULL;
LOG_INF("Display sample for %s", DISPLAY_DEV_NAME);
display_dev = device_get_binding(DISPLAY_DEV_NAME);
if (display_dev == NULL) {
LOG_ERR("Device %s not found. Aborting sample.",
DISPLAY_DEV_NAME);
return;
}
display_get_capabilities(display_dev, &capabilities);
if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED) {
rect_w = 16;
rect_h = 8;
} else {
rect_w = 2;
rect_h = 1;
}
h_step = rect_h;
scale = (capabilities.x_resolution / 8) / rect_h;
rect_w *= scale;
rect_h *= scale;
if (capabilities.screen_info & SCREEN_INFO_EPD) {
grey_scale_sleep = 10000;
} else {
grey_scale_sleep = 100;
}
buf_size = rect_w * rect_h;
if (buf_size < (capabilities.x_resolution * h_step)) {
buf_size = capabilities.x_resolution * h_step;
}
switch (capabilities.current_pixel_format) {
case PIXEL_FORMAT_ARGB_8888:
fill_buffer_fnc = fill_buffer_argb8888;
buf_size *= 4;
break;
case PIXEL_FORMAT_RGB_888:
fill_buffer_fnc = fill_buffer_rgb888;
buf_size *= 3;
break;
case PIXEL_FORMAT_RGB_565:
fill_buffer_fnc = fill_buffer_rgb565;
buf_size *= 2;
break;
case PIXEL_FORMAT_BGR_565:
fill_buffer_fnc = fill_buffer_bgr565;
buf_size *= 2;
break;
case PIXEL_FORMAT_MONO01:
case PIXEL_FORMAT_MONO10:
fill_buffer_fnc = fill_buffer_mono;
buf_size /= 8;
break;
default:
LOG_ERR("Unsupported pixel format. Aborting sample.");
return;
}
buf = k_malloc(buf_size);
if (buf == NULL) {
LOG_ERR("Could not allocate memory. Aborting sample.");
return;
}
(void)memset(buf, 0xFFu, buf_size);
buf_desc.buf_size = buf_size;
buf_desc.pitch = capabilities.x_resolution;
buf_desc.width = capabilities.x_resolution;
buf_desc.height = h_step;
for (int idx = 0; idx < capabilities.y_resolution; idx += h_step) {
display_write(display_dev, 0, idx, &buf_desc, buf);
}
buf_desc.pitch = rect_w;
buf_desc.width = rect_w;
buf_desc.height = rect_h;
fill_buffer_fnc(TOP_LEFT, 0, buf, buf_size);
x = 0;
y = 0;
display_write(display_dev, x, y, &buf_desc, buf);
fill_buffer_fnc(TOP_RIGHT, 0, buf, buf_size);
x = capabilities.x_resolution - rect_w;
y = 0;
display_write(display_dev, x, y, &buf_desc, buf);
fill_buffer_fnc(BOTTOM_RIGHT, 0, buf, buf_size);
x = capabilities.x_resolution - rect_w;
y = capabilities.y_resolution - rect_h;
display_write(display_dev, x, y, &buf_desc, buf);
display_blanking_off(display_dev);
grey_count = 0;
x = 0;
y = capabilities.y_resolution - rect_h;
LOG_INF("init success");
while (1) {
fill_buffer_fnc(BOTTOM_LEFT, grey_count, buf, buf_size);
display_write(display_dev, x, y, &buf_desc, buf);
++grey_count;
k_msleep(grey_scale_sleep);
#if CONFIG_TEST
if (grey_count >= 1024) {
break;
}
#endif
}
}
5、编译/烧录
- 编译指令:lisa zep build -b csk6011a_nano
- 烧录指令:lisa zep flash --runner pyocd
- 成功点亮屏幕并显示如下界面
实战视频
请观看视频进行详细了解,视频地址:Zephyr设备树保姆级上手教程【下】
1、如何编写UART节点
2、实现驱动显示屏
其他学习资源
Zephyr系列相关分享 | CSDN
环境搭建 | 聆思文档中心
芯片介绍 | 聆思文档中心
支持简介 | 聆思文档中心
更多视频课程
关于聆思
聆思科技是一家专注提供智能终端系统级(SoC)芯片的高科技企业,目前推出的CSK6系AI芯片已适配Zephyr RTOS。
如果你在学习、开发过程中有疑问或难题,希望与专家和更多的开发者交流,欢迎+V:listenai-csk
欢迎各位同学联系我们进行技术相关的探讨,也可以在评论区进行提问,大家一起进步吧!