【XR806开发板试用】软件模拟IIC驱动OLED显示图片&&自己遇到的坑
-
前言
首先,非常感谢极术社区和全志举办此次开发板申请试用活动。由于自己水平太低,拿到板子后不知道要干点啥,偶然看见一个大佬写的I2C外设使用及控制OLED屏显示,文章中使用了硬件IIC控制OLED,正好我手里也有一块OLED,那就直接试试模拟IIC(虽然有硬件IIC,软件IIC显得比较鸡肋[狗头])。同时感谢群里的好兄弟们,在我遇到坑的时候帮我解决了很多问题,非常感谢。(由于我水平比较低,可能会存在错误,希望各位大佬可以批评指正)
一、设备及其连接
1、设备
OLED:1.3寸四脚OLED(IIC)
驱动板:XR806(废话[狗头])
2、连接OLED XR806 VCC--------------3V3 GND--------------GND SDA--------------PB07 SCL--------------PB06
二、文件结构及相关配置
1、文件结构my_oled ├── BUILD.gn ├── i2c │ ├── i2c.c │ └── i2c.h ├── oled │ ├── oled.c │ ├── oledfont.h │ └── oled.h └── src └── main.c
2、BUILD.gn修改
(1)device/xradio/xr806/ohosdemo/BUILD.gn配置group("ohosdemo") { deps = [ #"hello_demo:app_hello", #"iot_peripheral:app_peripheral", #"wlan_demo:app_WlanTest", #"led:app_led", "my_oled:app_oled", ] }
(2)device/xradio/xr806/ohosdemo/my_oled/BUILD.gn配置
import("//device/xradio/xr806/liteos_m/config.gni") #必须,config中定义了头文件>路径和关键宏定义 static_library("app_oled") { #必须,所有应用工程必须是app_打头 configs = [] sources = [ "src/main.c", "oled/oled.c", "i2c/i2c.c", ] cflags = board_cflags #必须,board_cflags是在config.gni中定义的关键宏定义 include_dirs = board_include_dirs #必须,board_include_dirs是>在config.gni中定义的文件路径 include_dirs += [ "./i2c", "./oled", "//utils/native/lite/include", "//kernel/liteos_m/kernel/arch/include", "//base/iot_hardware/peripheral/interfaces/kits", #根据实际情况添加头文件路径 ] }
三、代码
代码有点多,只能放出来一部分,完整的代码可以在下面的链接中下载QWQ.(代码有点乱,注释比较少,如果我写的不清楚,欢迎随时来问我嗷QWQ,文末有联系方式[狗头])
链接:https://pan.baidu.com/s/1LaegCeUGTuxnlHhk2lN4cw
提取码:1217
1、模拟IIC
(1)时序图
(2)模拟IIC代码(部分)
i2c.h#ifndef __I2C_H #define __I2C_H #include "driver/chip/hal_gpio.h" #define SDA_IN() PB7_OUT_OR_IN_SELECT(2) #define SDA_OUT() PB7_OUT_OR_IN_SELECT(1) //IO操作函数 #define IIC_SCL_HIGH HAL_GPIO_WritePin(GPIO_PORT_B, GPIO_PIN_6, GPIO_PIN_HIGH) #define IIC_SCL_LOW HAL_GPIO_WritePin(GPIO_PORT_B, GPIO_PIN_6, GPIO_PIN_LOW) #define IIC_SDA_HIGH HAL_GPIO_WritePin(GPIO_PORT_B, GPIO_PIN_7, GPIO_PIN_HIGH) #define IIC_SDA_LOW HAL_GPIO_WritePin(GPIO_PORT_B, GPIO_PIN_7, GPIO_PIN_LOW) #define READ_SDA HAL_GPIO_ReadPin(GPIO_PORT_B, GPIO_PIN_7) void i2c_init(void); void IIC_Start(void); //发送IIC开始信号 void IIC_Stop(void); //发送IIC停止信号 void IIC_Send_Byte(uint8_t txd); //IIC发送一个字节 uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节 uint8_t IIC_Wait_Ack(void); //IIC等待ACK信号 void IIC_Ack(void); //IIC发送ACK信号 void IIC_NAck(void); //IIC不发送ACK信号 #endif
i2c.c(部分)
#include <stdio.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "driver/chip/hal_gpio.h" #include "i2c.h" GPIO_InitParam param; void gpio_output_init(void) { param.driving = GPIO_DRIVING_LEVEL_1; param.mode = GPIOx_Pn_F1_OUTPUT; param.pull = GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_6, ¶m); HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_7, ¶m); } void PB6_output_ctl(uint8_t level) { HAL_GPIO_WritePin(GPIO_PORT_B, GPIO_PIN_6, level ? GPIO_PIN_HIGH : GPIO_PIN_LOW); } void PB7_output_ctl(uint8_t level) { HAL_GPIO_WritePin(GPIO_PORT_B, GPIO_PIN_7, level ? GPIO_PIN_HIGH : GPIO_PIN_LOW); } void PB7_OUT_OR_IN_SELECT(uint8_t mode ) // 1输出2输入 { if(mode == 1) { param.mode = GPIOx_Pn_F1_OUTPUT; HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_7, ¶m); } else if(mode == 2) { param.mode = GPIOx_Pn_F0_INPUT; HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_7, ¶m); } } void i2c_init() { gpio_output_init(); PB6_output_ctl(1); //输出高 PB7_output_ctl(1); } //产生起始信号 void IIC_Start(void) { SDA_OUT(); //sda线输出 IIC_SDA_HIGH; IIC_SCL_HIGH; OS_MSleep(0.004);; IIC_SDA_LOW; OS_MSleep(0.004); IIC_SCL_LOW;//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void IIC_Stop(void) { SDA_OUT();//sda线输出 IIC_SCL_LOW; IIC_SDA_LOW; OS_MSleep(0.004); IIC_SCL_HIGH; IIC_SDA_HIGH;//发送I2C总线结束信号 OS_MSleep(0.004); } ...
2、OLED代码
OLED部分是直接修改了一下我以前在stm32上用的代码。
oled.h#ifndef __OLED_H #define __OLED_H #include "i2c.h" #define u8 unsigned char #define u32 unsigned int #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode); void OLED_Clear(void); void OLED_Init(void); void OLED_Refresh_Gram(void); void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size); void OLED_ShowChinese(u8 x,u8 y,u8 chr,u8 len,u8 size,u8 mode); void OLED_Showpic(u8 x,u8 y,u8 chr,u8 size,u8 mode); #endif
oledfont.h里是字库,还有图片
oled.c(部分)void OLED_Init(void) { i2c_init(); OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示 OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率 OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率 OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数 OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64) OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移 OLED_WR_Byte(0X00,OLED_CMD); //默认为0 OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数. OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置 OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭 OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式 OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127; OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置 OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置 OLED_WR_Byte(0x81,OLED_CMD); //对比度设置 OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮) OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期 OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2; OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率 OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc; OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示 OLED_WR_Byte(0xAF,OLED_CMD); //开启显示 OLED_Clear(); } /*********************************/ void OLED_DrawPoint(u8 x,u8 y,u8 t) { u8 pos,bx,temp=0; if(x>131||y>63)return;//超出范围了. pos=7-y/8; bx=y%8; temp=1<<(7-bx); if(t)OLED_GRAM[x][pos]|=temp; else OLED_GRAM[x][pos]&=~temp; } /********* 在指定位置显示一个字符,包括部分字符节 *************/ //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 12/16/24 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode) { u8 temp,t,t1; u8 y0=y; u8 csize=(size/8+((size%8)?1:0))*(size/2); //得到字体一个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值 for(t=0;t<csize;t++) { if(size==12)temp=asc2_1206[chr][t]; //调用1206字体 else if(size==16)temp=asc2_1608[chr][t]; //调用1608字体 else if(size==24)temp=asc2_2412[chr][t]; //调用2412字体 else return; //没有的字库 for(t1=0;t1<8;t1++) { if(temp&0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp<<=1; y++; if((y-y0)==size) { y=y0; x++; break; } } } }
3、主函数代码
main.c#include <stdio.h> #include "ohos_init.h" //(7) #include "kernel/os/os.h" #include "iot_gpio.h" //(8) #include "i2c.h" #include "oled.h" static OS_Thread_t g_main_thread; #define GPIO_ID_PA21 21 static void MainThread(void *arg) { printf("delay test start\r\n"); IoTGpioInit(GPIO_ID_PA21); //(3) IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT); //(4) OLED_Init(); OLED_Showpic(1,1,0,60,1); OLED_Refresh_Gram(); while (1) { IoTGpioSetOutputVal(GPIO_ID_PA21, 1); //(5) OS_MSleep(500); IoTGpioSetOutputVal(GPIO_ID_PA21, 0); //(6) OS_MSleep(500); } } void LEDMain(void) //(2) { printf("i2c Test Start\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(LEDMain);
四、运行结果
编译下载后运行如图:
原图是这个恐龙QIQ。
虽然最后显示的有点恐怖,但也算成功了,哈哈哈哈。
五、踩过的坑
说起来配置环境那直接痛苦面具,我遇到了很多错误,但当时就想着赶快配置好,大部分都没记录来(抱头痛哭T_T)。但下面的印象非常深刻。
make menuconfig错误
我当时的错误是这样的:
后来查了一下是因为少装了点啥。
解决:
(如果你装了当我没说...)hb build -f 时报错
我当时的错误是这样的:
解决:
xshell串口格式乱
解决:文件 - 属性 - 终端 - 高级 - 用CR-LF接受LF(R)
六、结语
最后,再次感谢极术社区和全志平台,感谢各位群友给我的帮助,这也是我第一次写关于技术的文章,写的不好,希望大佬多多批评指正。如果有错误可以联系我嗷QQ:2403057219(24小时在线[狗头])
Copyright © 2023 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号