原创 【电子DIY设计】+基于HarmonyOS的OLED显示屏

2023-12-21 16:17 709 3 3 分类: 智能硬件


引言

本文主要介绍如何在 OpenHarmony3.2的基础上开发IIC接口的OLED显示屏的不同显示效果。

目前网络上有关鸿蒙嵌入式方面的参考文章比较少,大多为HarmonyOS 1.0的版本,现在随着HarmonyOS的逐渐发展壮大,稳定版本的OpenHarmony已经到了OpenHarmony 3.2,很多以前的API已经不再适用,所以本文将详细介绍如何开发基于 OpenHarmony3.2的IIC接口的OLED显示。

软硬件准备

硬件

润和HiSpark 3861开发板

IIC接口的OLED显示屏

软件

VSCode

DevEco Device Tool

OpenHarmony-v3.2-Release版本源码

功能介绍

使用IIC接口的OLED显示屏显示字符串"HarmonyOS"

使用IIC接口的OLED显示屏显示不同字号的字符串"HarmonyOS"

使用IIC接口的OLED显示屏显示中文“鸿蒙嵌入式”

功能开发

创建工程并获取源码

在VSCode中通过DevEco Device Tool创建OpenHarmony工程,本项目选用目前最新的稳定版本OpenHarmony-v3.2-Release 。SOC选用Hi3861,开发板会自动选择hi3861,路径根据自己实际情况选择即可。

工程配置完成后,点击确定,DevEco Device Tool会自动启动OpenHarmony3.2源码的下载

确定项目结构

在applications/sample/wifi-iot/app下创建文件夹OLED,在新建的文件夹下,放置我们的程序文件,主要为七个文件,分别是OLED.c、OLED.h、OLED_fonts.c、OLED_fonts.h、OLED_demo.c、OLED_conf.h和BUILD.gn。项目结构如下:

.

└── applications

└── sample

└── wifi-iot

└── app

└── OLED

│── OLED.c

│── OLED.h

│── OLED_fonts.c

│── OLED_fonts.h

│── OLED_demo.c

│── OLED_conf.h

└── BUILD.gn

 

实现流程

开启I2C功能

 

源码默认是关闭I2C功能的,需要我们在程序中手动打开,也就是设置CONFIG_I2C_SUPPORT=y。

修改文件:.device/soc/hisilicon/hi3861v100/sdk_liteos/build/config/usr_config.mk文件。在这个配置文件中打开I2C驱动宏

 

取消注释

CONFIG_I2C_SUPPORT=y

选择IIC 引脚

开发板上OLED显示屏使用的引脚为HI_IO_NAME_GPIO_13 、 HI_IO_NAME_GPIO_14,所以我们需要选择IIC的复用引脚。

 

在.device/soc/hisilicon/hi3861v100/sdk_liteos/app/wifiiot_app/init/app_io_init.c中,替换两行代码。

 hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_I2C1_SDA);

 

hi_io_set_func(HI_IO_NAME_GPIO_1, HI_IO_FUNC_GPIO_1_I2C1_SCL);

替换为

   

hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);

   

hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);

这样我们以后编程都可以直接使用HarmonyOS提供的抽象层函数了,不必每次都要定义IIC的IO复用引脚。

重要程序介绍

发送数据到OLED,驱动OLED其实就是通过IIC的发送,写入对应指令,来达到控制OLED显示的作用。

实际发送IIC的函数

static uint32_t

OLED

_SendData(uint8_t* data, size_t size)

{

    int id =

OLED

_I2C_IDX;

    return IoTI2cWrite(id,

OLED

_I2C_ADDR, data, size);

}

发送单个字节

static uint32_t

OLED

_WiteByte(uint8_t regAddr, uint8_t byte)

{

    uint8_t buffer[] = {regAddr, byte};

    return

OLED

_SendData(buffer, sizeof(buffer));

}

发送指令

void

OLED

_WriteCommand(uint8_t byte) {

   

OLED

_WiteByte(

OLED

_CTRL_CMD, byte);

}

发送不定长数据

void

OLED

_WriteData(uint8_t* buffer, size_t buff_size) {

    uint8_t data[

OLED

_WIDTH * 2] = {0};

    for (size_t i = 0; i < buff_size; i++) {

        data[i*2] =

OLED

_CTRL_DATA |

OLED

_MASK_CONT;

        data[i*2+1] = buffer;

    }

    data[(buff_size - 1) * 2] =

OLED

_CTRL_DATA;

   

OLED

_SendData(data, sizeof(data));

}

设定当前位置,我们为了设定显示位置,所以需要设置指针的位置。

void

OLED

_SetCursor(uint8_t x, uint8_t y) {

   

OLED

.CurrentX = x;

   

OLED

.CurrentY = y;

}


显示字符串

char

OLED

_DrawString(char* str, FontDef Font,

OLED

_COLOR color) {

    while (*str) {

        if (

OLED

_DrawChar(*str, Font, color) != *str) {

            return *str;

        }

        str++;

}

    return *str;

}

在屏幕缓冲区中绘制一个像素

;(也就是绘制一个像素点)

void

OLED

_DrawPixel(uint8_t x, uint8_t y,

OLED

_COLOR color) {

    if(x >=

OLED

_WIDTH || y >=

OLED

_HEIGHT) {

        return;

    }

    if(

OLED

.Inverted) {

        color = (

OLED

_COLOR)!color;

    }

    if(color == White) {

       

OLED

_Buffer[x + (y / 8) *

OLED

_WIDTH] |= 1 << (y % 8);

    } else {

       

OLED

_Buffer[x + (y / 8) *

OLED

_WIDTH] &= ~(1 << (y % 8));

    }

}

绘制区域

void

OLED

_DrawRegion(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t* data, uint32_t size, uint32_t stride)

{

    if (x + w >

OLED

_WIDTH || y + h >

OLED

_HEIGHT || w * h == 0) {

        printf("%dx%d @ %d,%d out of range or invalid!\r\n", w, h, x, y);

        return;

    }

    w = (w <=

OLED

_WIDTH ? w :

OLED

_WIDTH);

    h = (h <=

OLED

_HEIGHT ? h :

OLED

_HEIGHT);

    stride = (stride == 0 ? w : stride);

    uint8_t rows = size * 8 / stride;

    for (uint8_t i = 0; i < rows; i++) {

        uint32_t base = i * stride / 8;

        for (uint8_t j = 0; j < w; j++) {

            uint32_t idx = base + (j / 8);

            uint8_t byte = idx < size ? data[idx] : 0;

            uint8_t bit  = byte & (0x80 >> (j % 8));

           

OLED

_DrawPixel(x + j, y + i, bit ? White : Black);

        }

    }

}

显示中文

首先我们需要对文字取模,有很多取模工具,这里就不做介绍。这里着重介绍软件实现显示中文的效果。character数组存放汉字取模后的16进制数,这里选用字大小为16*16的,所以W和H都赋值为16,二维数组大小设定为32;

void

Oled

Chinese(void)

{

    const uint32_t W = 16, H = 16;

    uint8_t character[][32] = {

        {

           0x40,0x20,0x30,0x48,0x10,0xFC,0x02,0x88,0x9F,0xA8,0x64,0x88,0x24,0xA8,0x04,0x90,

           0x14,0x84,0x14,0xFE,0xE7,0x04,0x3C,0x24,0x29,0xF4,0x20,0x04,0x20,0x14,0x20,0x08,

        },{

           0x04,0x48,0x7F,0xFC,0x04,0x40,0x7F,0xFE,0x40,0x02,0x8F,0xE4,0x00,0x00,0x7F,0xFC,

           0x06,0x10,0x3B,0x30,0x05,0xC0,0x1A,0xA0,0x64,0x90,0x18,0x8E,0x62,0x84,0x01,0x00,

        },{

          0x01,0x00,0x21,0x08,0x21,0x08,0x3F,0xF8,0x22,0x28,0x22,0x20,0xFF,0x7C,0x22,0x44,

          0x22,0xA8,0x22,0x20,0x3E,0x20,0x22,0x20,0x22,0x50,0x22,0x48,0x3E,0x8E,0x21,0x04,

        },{

          0x04,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x80,0x02,0x80,0x02,0x80,

          0x04,0x40,0x04,0x40,0x08,0x20,0x08,0x20,0x10,0x10,0x20,0x10,0x40,0x0E,0x80,0x04,

        },

        {

            0x00,0x80,0x00,0xA0,0x00,0x90,0x00,0x84,0xFF,0xFE,0x00,0x80,0x00,0x80,0x3E,0x80,

            0x08,0x80,0x08,0x40,0x08,0x40,0x09,0x20,0x0E,0x22,0x70,0x12,0x20,0x0A,0x00,0x04

        }

    };

   

OLED

_Fill(Black);

    for (size_t i = 0; i < sizeof(character)/sizeof(character[0]); i++) {

       

OLED

_DrawRegion(i * W, 0, W, H, character, sizeof(character[0]), W);

    }

   

OLED

_UpdateScreen();

    sleep(1);

}

业务函数;这里主要是创建一个OLED显示的demo线程,线程的入口函数为OLEDTaskEntry。

void

OLED

Demo(void)

{

    osThreadAttr_t attr;

    attr.name = "

OLED

Task";

    attr.attr_bits = 0U;

    attr.cb_mem = NULL;

    attr.cb_size = 0U;

    attr.stack_mem = NULL;

    attr.stack_size = 10240;

    attr.priority = osPriorityBelowNormal7;

    if (osThreadNew(

OLED

Task

Entry

, NULL, &attr) == NULL) {

        printf("[

OLED

Demo] Falied to create

OLED

Task!\n");

    }

}

APP_FEATURE_INIT(

OLED

Demo);

11. OLEDDemo线程入口函数,这里我们首先初始化IIC,初始化OLED,然后令屏幕全黑,方便我们进行后续显示。在while循环中,我们首先显示字号为7x10的字符串HarmonyOS,然后延时一会,显示字号为11x18的字符串HarmonyOS,最后显示中文“鸿蒙嵌入式”。

void OLEDTaskEntry(void* arg)

{

    (void) arg;

    IoTI2cInit(0, OLED_I2C_BAUDRATE);//初始化IIC

    usleep(20*1000);

   

OLED

_Init();//初始化OLED

    while (1) {

   

OLED

_Fill(Black);//屏幕全黑

   

OLED

_SetCursor(0, 0);//指针设置在(0,0)

   

OLED

_DrawString("HarmonyOS", Font_7x10, White);

   

OLED

_UpdateScreen();

usleep(1000000);

OLED

_Fill(Black);//屏幕全黑

OLED

_SetCursor(0, 0);//指针设置在(0,0)

OLED

_DrawString("HarmonyOS", Font_11x18, White);

OLED

_UpdateScreen();

usleep(1000000);

   

OLED

_Fill(Black);//屏幕全黑

OLED

_SetCursor(0, 0);//指针设置在(0,0)

OledChinese();

   

OLED

_UpdateScreen();

    usleep(1000000);

    }

}

实现效果

显示字号为7x10的字符串HarmonyOS

显示字号为11x18的字符串HarmonyOS

显示汉字“鸿蒙嵌入式”。

总结

本文主要介绍如何通过在 OpenHarmony3.2的基础上开发IIC接口的OLED显示屏的不同显示效果。读者可以参考本文来开发自己的鸿蒙应用,显示符合自身需求的内容。

 

作者: 跋扈洋, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-3925799.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
3
关闭 站长推荐上一条 /1 下一条