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

文章目录

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

前言

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

I2C简介

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

硬件连接

Hi3861的IO口中有两组I2C,分别是I2C0和I2C1,笔者在此使用的是I2C0,需要确定好自己使用的是1还是0,后面编程要用。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第2张图片
然后这两组又可以分别复用到两组IO,也就是说有四组I2C接口,复用引脚是GPIO9(I2C0 SCL)和GPIO10(I2C0 SDA)。OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第3张图片
所以笔者的硬件连接如下:
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第4张图片

编程实现

创建代码框架

结合前面两篇介绍的工程建立,首先在工程系统目录下新建一个文件夹,然后添加一个c文件和一个BUILD.gn的编译构建文件;新建完成后,开始搭建代码框架,本文借用上一篇的任务创建的框架,采用线程创建的方式来实现。
在这里插入图片描述
创建代码如下:

//任务实现函数
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的复用功能,选择自己所需的即可。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第5张图片
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函数,可以看见里面一共有六个函数。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第6张图片
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第7张图片
对比STM32的I2C可以发现,这四个函数STM32的底层函数及其相似。使用方式也基本是套娃。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第8张图片
不同的是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)。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第9张图片

OLED初始化配置

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

//OLED的初始化
void OLED_Init(void)
{
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_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 register
	OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
	OLED_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 display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_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/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_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接口发送函数。
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第10张图片
右边的API函数的三个参数分别是:1.I2C通道号,这个和前面初始化的通道号一致;2.从机的地址,笔者此处是0X78;3.发送的数据,这里需要注意,接口韩式定义的是一个结构体指针,在使用过程中需要将发送的数据做一个处理。
API调用举例如下:
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕_第11张图片

#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的介绍就记录到此,文中如有不妥之处欢迎指出。需要代码的同学可以私信笔者获取。

你可能感兴趣的:(OpenHarmony南向开发,经验分享,OpenHarmony,c语言,单片机,嵌入式)