现在看看如何接外设,我这次用的是Pico2。这次接了一个SPI的屏,还有一个按键。屏幕用的是GC9A01 240x240 圆屏,按键就是普通的IO口按键。
1 SPI
首先还是配置设备树,在app.overlay。
/* * GC9A01 240x240 圆屏 @ Raspberry Pi Pico 2 * * 接线: * SCL -> GP18 (SPI0 SCK) SDA -> GP19 (SPI0 TX/MOSI) * CS -> GP17 (SPI0 CSn) DC -> GP20 * RES -> GP21 BLK -> GP22 (main.c 里直接 gpio 点亮) * VCC -> 3V3 GND -> GND * * pinctrl 组 spi0_default (boards/raspberrypi/common/rpi_pico-pinctrl-common.dtsi) * 正好就是 CSN_P17 + SCK_P18 + TX_P19。 */ #include <freq.h> #include <zephyr/dt-bindings/display/panel.h> #include <zephyr/dt-bindings/gpio/gpio.h> / { chosen { zephyr,display = &gc9a01_lcd; }; mipi_dbi_gc9a01 { compatible = "zephyr,mipi-dbi-spi"; status = "okay"; write-only; #address-cells = <1>; #size-cells = <0>; spi-dev = <&spi0>; dc-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; gc9a01_lcd: gc9a01@0 { compatible = "galaxycore,gc9x01x"; reg = <0>; mipi-max-frequency = <DT_FREQ_M(40)>; pixel-format = <PANEL_PIXEL_FORMAT_RGB_565>; width = <240>; height = <240>; display-inversion; }; }; }; &spi0 { status = "okay"; pinctrl-0 = <&spi0_default>; pinctrl-names = "default"; };prj.conf
# GC9A01 圆屏 (Pico2, Zephyr v4.1) CONFIG_DISPLAY=y CONFIG_GC9X01X=y CONFIG_SPI=y CONFIG_GPIO=y # 注意: 不要用 CONFIG_CHARACTER_FRAMEBUFFER / cfb_* —— CFB 只支持 # 1-bit 单色 (8 像素/字节), 用在 RGB565 彩色屏上会越界读内存导致花屏。 # 文字渲染用 main.c 里内置的 8x8 像素字库, 直接写应用帧缓冲。 CONFIG_PRINTK=y CONFIG_LOG=y CONFIG_MAIN_STACK_SIZE=4096CMakelist.txt
cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(gc9a01_demo LANGUAGES C) target_sources(app PRIVATE src/main.c)代码,核心代码就是
/* RGB565 显存, 存的是字节交换后的值 (大端上屏) */ static uint16_t fb[WIDTH * HEIGHT]; static struct display_buffer_descriptor desc = { .width = WIDTH, .height = HEIGHT, .pitch = WIDTH, .buf_size = sizeof(fb), }; static inline uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { uint16_t v = (uint16_t)(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)); return (uint16_t)((v << 8) | (v >> 8)); /* byteswap: LE buf -> BE wire */ } static void flush(void) { int ret = display_write(display, 0, 0, &desc, fb); if (ret < 0) { printk("display_write failed: %d\n", ret); } }先是在desc中写了屏幕的核心数据。然后最核心的就是分配了一块内存,平时在这块内存中填写图像数据,最后调用display_write写入。
3 I2C
添加工程在外部添加就可以了,增加环境变量。
export ZEPHYR_BASE=/root/code/zephyr
或者每次
source ~/code/zephyr/zephyr-env.sh创建~/proj/pico_i2c_scan
增加以下文件:
pico_i2c_scan/CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(pico_i2c_scan) target_sources(app PRIVATE src/main.c)prj.conf
CONFIG_I2C=y CONFIG_PRINTK=y # USB CDC ACM console CONFIG_USB_DEVICE_STACK=y CONFIG_USB_CDC_ACM=y CONFIG_SERIAL=y CONFIG_UART_CONSOLE=y CONFIG_UART_LINE_CTRL=y CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=ymain.c
#include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/drivers/i2c.h> #include <zephyr/usb/usb_device.h> #define I2C_NODE DT_NODELABEL(i2c1) int main(void) { int ret; ret = usb_enable(NULL); if (ret != 0) { printk("USB enable failed %d\n", ret); } printk("I2C scanner start\n"); const struct device *i2c_dev; i2c_dev = DEVICE_DT_GET(I2C_NODE); if (!device_is_ready(i2c_dev)) { printk("I2C device not ready\n"); return 0; } printk("I2C scanner start\n"); while (1) { printk("Scanning...\n"); for (uint8_t addr = 1; addr < 0x7f; addr++) { int ret; /* * 发送一个空write * 只检测ACK */ ret = i2c_write( i2c_dev, NULL, 0, addr); if (ret == 0) { printk( "Found device at 0x%02x\n", addr); } } k_sleep(K_SECONDS(5)); } return 0; }overlay
boards/rpi_pico.overlay
&i2c1 { status = "okay"; }; / { chosen { zephyr,console = &cdc_acm_uart0; zephyr,shell-uart = &cdc_acm_uart0; }; }; &zephyr_udc0 { cdc_acm_uart0: cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; }; };编译的命令是:
west build -p always -b rpi_pico ..