> 文档中心 > OpenHarmony学习笔记——I2C驱动0.96OLED屏幕

OpenHarmony学习笔记——I2C驱动0.96OLED屏幕

文章目录

  • 前言
  • I2C简介
  • 硬件连接
  • 编程实现
    • 创建代码框架
    • 初始化并复用GPIO
    • 初始化I2C0
    • 初始化OLED
      • 从机地址
      • OLED初始化配置
      • 功能代码
  • 总结

前言

前面介绍了一些关于在Hi3861上使用OpenHarmony的系统框架,别看Hi3861的IO口数量不多,但是其资源是相当丰富的,单片机有的复用功能他都有I2C、SPI、UART、ADC、PWM都支持,本文将在之前的框架基础上用Hi3861使用I2C协议驱动0.96寸的OLED屏幕显示图片、字符、数字以及汉字。
在这里插入图片描述

I2C简介

I2C是南向开发中使用频率很高的一个协议,有关它的简介笔者在之前的文章中有过介绍,大家可以去参考——树莓派4B学习笔记——IO通信篇(I2C)。在这里只需要知道以下几点:
1.I2C有一个时钟线SCL,用来连接主从,使得主从机的时钟同步;还有一个数据线SDA,用来串行传输数据;
2.I2C通信中没有片选,选中从机是通过地址来判断的;
由于Hi3861提供了I2C的API接口,所以底层的数据传输方式、起始信号、结束信号、应答信号这些东西不需要去纠结,在操作器件时使用API的接口格式即可。

硬件连接

Hi3861的IO口中有两组I2C,分别是I2C0和I2C1,笔者在此使用的是I2C0,需要确定好自己使用的是1还是0,后面编程要用。
在这里插入图片描述
然后这两组又可以分别复用到两组IO,也就是说有四组I2C接口,复用引脚是GPIO9(I2C0 SCL)和GPIO10(I2C0 SDA)。请添加图片描述
所以笔者的硬件连接如下:
在这里插入图片描述

编程实现

创建代码框架

结合前面两篇介绍的工程建立,首先在工程系统目录下新建一个文件夹,然后添加一个c文件和一个BUILD.gn的编译构建文件;新建完成后,开始搭建代码框架,本文借用上一篇的任务创建的框架,采用线程创建的方式来实现。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕
创建代码如下:

//任务实现函数static void OLEDTask(void){}//任务创建函数static void OLEDExampleEntry(void){    osThreadAttr_t attr;    attr.name = "OLEDTask";//任务名    attr.attr_bits = 0U;    attr.cb_mem = NULL;    attr.cb_size = 0U;    attr.stack_mem = NULL;    attr.stack_size = I2C_TASK_STACK_SIZE;//堆栈大小    attr.priority = I2C_TASK_PRIO;    if (osThreadNew((osThreadFunc_t)OLEDTask, NULL, &attr) == NULL)    { printf("Falied to create OLEDTask!\n");    }    printf("Create I2CTask Successful!!\n");}APP_FEATURE_INIT(OLEDExampleEntry);

初始化并复用GPIO

完成了代码框架的搭建后,需要根据硬件的GPIO接线方式对GPIO9以及GPIO10进行初始化,参考前面的点灯流程可以知道,Hi3861的IO初始化和STM32类似,首先初始化GPIO,然后复用为I2CSCL和SDA,初始化的命令已经在gpio_ex.h中标明,每个IO的复用功能,选择自己所需的即可。
在这里插入图片描述
GPIO9与GPIO10的初始化代码如下:

在任务函数中添加此代码,初始化GPIOGpioInit();    //GPIO_10复用为I2C0_SDA    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_10, WIFI_IOT_IO_FUNC_GPIO_10_I2C0_SDA);    //GPIO_9复用为I2C0_SCL    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_9, WIFI_IOT_IO_FUNC_GPIO_9_I2C0_SCL);    //baudrate: 400kbps

初始化I2C0

上面提到过Hi3861中有两组I2C,这里笔者使用的I2C0,根据使用STM32的经验,肯定是需要指向和初始化的。这里官方已经封装好了API接口函数,调用“wifiiot_i2c.h”和“wifiiot_i2c_ex.h”即可看见里面的API函数,可以看见里面一共有六个函数。
在这里插入图片描述
在这里插入图片描述
对比STM32的I2C可以发现,这四个函数STM32的底层函数及其相似。使用方式也基本是套娃。
在这里插入图片描述
不同的是Hi3861的I2C接口初始化时需要设定传输速率,这里笔者选择了400Kbps,初始化代码如下:

  //baudrate: 400kbps    I2cInit(WIFI_IOT_I2C_IDX_0, 400000);    I2cSetBaudrate(WIFI_IOT_I2C_IDX_0, 400000);    printf("I2C Test Start\n");

初始化OLED

截止到上一步,Hi3861作为主机的初始化已经完成了,接下来需要主机利用总线发送指令初始化从机用来实现功能。

从机地址

如下图所示,笔者使用的OLED地址是0X78,I2C的地址最后一位是读写标志位,由于OLED作为从机,主机不需要读取数据,只需要向OLED写入指令和数据即可,所以我们在编程时,可以直接使用这个地址0x78即可。(写操作是最低位是0)。
在这里插入图片描述

OLED初始化配置

整个初始化过程需要配置的指令数据很多,笔者在此不做赘述,直接使用了中景园的代码套娃,注意源代码中GPIO初始化的部分需要删掉。
初始化代码如下:

//OLED的初始化void OLED_Init(void){OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panelOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control registerOLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current BrightnessOLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常OLED_WR_Byte(0xA6,OLED_CMD);//--set normal displayOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 dutyOLED_WR_Byte(0xD3,OLED_CMD);//-set display offsetShift Mapping RAM Counter (0x00~0x3F)OLED_WR_Byte(0x00,OLED_CMD);//-not offsetOLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequencyOLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/SecOLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge periodOLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 ClockOLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configurationOLED_WR_Byte(0x12,OLED_CMD);OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomhOLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect LevelOLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)OLED_WR_Byte(0x02,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disableOLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disableOLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) OLED_Clear();OLED_WR_Byte(0xAF,OLED_CMD);}

功能代码

完成上述初始化后,屏幕已经可以正常显示了,接下来需要编写执行函数,显示图片,汉字字符串这些。这些功能说到底还是调用I2C写API,给从机数据,让它显示内容。这里笔者还是用了中景园的代码,由于I2C底层代码的改动,需要将源代码中的IIC接收和发送部分修改成API函数的模式才能正常使用。也就是说需要将下面左边的发送字节的函数修改为右边的API接口发送函数。
在这里插入图片描述
右边的API函数的三个参数分别是:1.I2C通道号,这个和前面初始化的通道号一致;2.从机的地址,笔者此处是0X78;3.发送的数据,这里需要注意,接口韩式定义的是一个结构体指针,在使用过程中需要将发送的数据做一个处理。
API调用举例如下:
在这里插入图片描述

#define NT3H1X_SLAVE_ADDRESS 0x55 //器件地址static bool writeTimeout(  uint8_t *data, uint8_t dataSend) {    uint32_t status = 0;    WifiIotI2cData nt3h1101_i2c_data1 = {0};//定义结构体变量    nt3h1101_i2c_data1.sendBuf = data;//赋值    nt3h1101_i2c_data1.sendLen = dataSend;//数据长度    status = I2cWrite(WIFI_IOT_I2C_IDX_1, (NT3H1X_SLAVE_ADDRESS<<1)|0x00, &nt3h1101_i2c_data1);    if (status != 0)    { printf("===== Error: I2C write status1 = 0x%x! =====\r\n", status); return 0;    }    usleep(300000);    return 1;}

参考这个例子,可以修改出是和此OLED的代码,这里笔者参考了此篇博文——Hispark-3861_oled显示。
代码如下:

u32 my_i2c_write(WifiIotI2cIdx id, u16 device_addr, u32 send_len){    u32 status;    WifiIotI2cData oled_i2c_data = { 0 };    oled_i2c_data.sendBuf = g_send_data;    oled_i2c_data.sendLen = send_len;    status = I2cWrite(id, device_addr, &oled_i2c_data);    if (status != 0) { printf("===== Error: I2C write status = 0x%x! =====\r\n", status); return status;    }    return 0;}/// IIC Write Command/void Write_IIC_Command(unsigned char IIC_Command){    g_send_data[0] = 0x00;    g_send_data[1] = IIC_Command;    my_i2c_write(WIFI_IOT_I2C_IDX_0, 0x78, 2);}/// IIC Write Data/void Write_IIC_Data(unsigned char IIC_Data){    g_send_data[0] = 0x40;    g_send_data[1] = IIC_Data;    my_i2c_write(WIFI_IOT_I2C_IDX_0, 0x78, 2);}void OLED_WR_Byte(unsigned dat,unsigned cmd){if(cmd){   Write_IIC_Data(dat);   }else{   Write_IIC_Command(dat);}}

修改这个接口函数之后,其他的功能代码直接copy中景园的即可。
笔者的最后的任务函数如下:

static void OLEDTask(void){    GpioInit();    //GPIO_10复用为I2C0_SDA    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_10, WIFI_IOT_IO_FUNC_GPIO_10_I2C0_SDA);    //GPIO_9复用为I2C0_SCL    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_9, WIFI_IOT_IO_FUNC_GPIO_9_I2C0_SCL);    //baudrate: 400kbps    I2cInit(WIFI_IOT_I2C_IDX_0, 400000);    I2cSetBaudrate(WIFI_IOT_I2C_IDX_0, 400000);    printf("I2C Test Start\n");   OLED_Init();OLED_ColorTurn(0);//0正常显示,1 反色显示    OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示usleep(500000);    while (1)    { OLED_Clear();OLED_ShowString(1,0,"ABC",8);//6*8 “ABC”OLED_ShowString(1,8,"ABC",12);//6*12 “ABC”    OLED_ShowString(1,20,"ABC",16);//8*16 “ABC” OLED_ShowString(1,36,"openharmony",16);    OLED_Refresh();usleep(500000); OLED_ShowPicture(1,0,128,64,BMP1);OLED_Refresh(); usleep(500000); OLED_ShowPicture(1,0,128,64,BMP2);OLED_Refresh(); usleep(500000); OLED_ShowPicture(1,0,128,64,BMP3);OLED_Refresh(); usleep(500000); OLED_ShowPicture(1,0,128,64,BMP4);OLED_Refresh(); usleep(500000);    }}

然后是编译,下载,这些就步骤不做记载了。
最后来看看笔者移植后的效果:
在这里插入图片描述

总结

有关Hi3861使用I2C驱动0.96寸OLED的介绍就记录到此,文中如有不妥之处欢迎指出。需要代码的同学可以私信笔者获取。

一家人vip视频