【前言】
此前使用STM32或者python等驱动过OLED屏,但是在Zephyr上还没有实现过,本篇将跟大家分享如何在Zephry中使用I2C,并实现驱动OLED屏。
【启用驱动程序】
1.通过将以下行添加到应用程序配置文件prj.conf中来启用I2C驱动程序。
CONFIG_I2C=y #启动I2C CONFIG_TFM_SECURE_UART=n //在534x 因为与I2C设备地址有冲突,所以需要禁用。 CONFIG_TFM_LOG_LEVEL_SILENCE=y
2、在源代码文件中包含I2C API的头文件
#include <zephyr/drivers/i2c.h>
3、展开配置文件->Devicetree并创建一个覆盖文件。
其内容如下:
&i2c1 { status = "okay"; pinctrl-0 = <&i2c1_default>; pinctrl-1 = <&i2c1_sleep>; pinctrl-names = "default", "sleep"; ssd1306: ssd1306@3c { compatible = "i2c-device"; status = "okay"; reg = <0x3c>; }; clock-frequency = <I2C_BITRATE_FAST>; }; &pinctrl { /omit-if-no-ref/ i2c1_default: i2c1_default { group1 { psels = <NRF_PSEL(TWIM_SCL, 1, 14)>, <NRF_PSEL(TWIM_SDA, 1, 15)>; }; }; /omit-if-no-ref/ i2c1_sleep: i2c1_sleep { group1 { psels = <NRF_PSEL(TWIM_SCL, 1, 14)>, <NRF_PSEL(TWIM_SDA, 1, 15)>; low-power-enable; }; }; };
做好这些准备后,我移植了一个OLED的驱动到工程中,对相应的收发函数进行修改就行了。
4、在oled.c中添加定义节点标识符
#define I2C1_NODE DT_NODELABEL(ssd1306)
5、检索API特定的设备结构。
宏调用I2C_DT_SPEC_GET()返回结构i2c_dt_spec,其中包含I2C总线的设备指针以及目标地址。
static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C1_NODE);
6、使用device_is_ready()验证设备是否可以使用。在oled.c中添加一个函数,用于OLED初始化时调用:
int oled_dev_init(void) { if (!device_is_ready(dev_i2c.bus)) { printk("Device %s is not ready\n", dev_i2c.bus->name); return -1; } return 0; }
此函数在OLED初始时先调用。
7、修改发送函数,由于SSD1306只需要发送数据,所以只需要修改发送函数就行了:
void OLED_WR_Byte(unsigned char dat,unsigned char cmd) { unsigned char i2c_tx_buff[2]; if(cmd) { i2c_tx_buff[0] = 0x40; } else { i2c_tx_buff[0] = 0x00; } i2c_tx_buff[1] = dat; i2c_write_dt(&dev_i2c, i2c_tx_buff, 2); }
8、修改整屏刷新的函数:
void OLED_Display(void) { OLED_WR_Byte(0x21, 0); // 设置列地址 OLED_WR_Byte(0x00, 0); // 起始列地址 OLED_WR_Byte(SSD1306_WIDTH - 1, 0); // 结束列地址 OLED_WR_Byte(0x22, 0); // 设置页地址 OLED_WR_Byte(0x00, 0); // 起始页地址 OLED_WR_Byte(OLED_PAGE_SIZE - 1, 0); // 结束页地址 Flash_OLED_Buffer[0] = 0x40; // uint8_t i2c_tx_buff[SSD1306_WIDTH * OLED_PAGE_SIZE +1]; // i2c_tx_buff[0] = 0x40; // memcpy(&i2c_tx_buff[1], OLED_buffer, SSD1306_WIDTH * OLED_PAGE_SIZE); // i2c_master_write(SSD1306_ADDR, Flash_OLED_Buffer, SSD1306_WIDTH * OLED_PAGE_SIZE +1); i2c_write_dt(&dev_i2c, Flash_OLED_Buffer, SSD1306_WIDTH * OLED_PAGE_SIZE + 1); }
到此OLED的驱动就移植好了,源码见我的附件。
【测试】
在main.c中添加对oled的头文件的引用,并添加打印函数:
int main(void) { int ret; bool led_state = true; printk("start !\n"); if (!gpio_is_ready_dt(&led)) { return 0; } ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return 0; } OLED_Init(); OLED_Clear(0); GUI_ShowString(0, 0, "hello world", 16, 1); OLED_Display(); while (1) { ret = gpio_pin_toggle_dt(&led); if (ret < 0) { return 0; } led_state = !led_state; // printf("LED state: %s\n", led_state ? "ON" : "OFF"); k_msleep(SLEEP_TIME_MS); } return 0; }
接上OLED屏后,就可以看到打印出来对应的效果:
附工程源码: