【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)时序图

根据时序图控制SDA和SCL的高低,实现IIC通讯协议。

(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, &param);
        HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_7, &param);
}
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, &param);
        }
 else if(mode == 2)
        {
                param.mode = GPIOx_Pn_F0_INPUT;
                HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_7, &param);
        }
}
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)。但下面的印象非常深刻。

我当时的错误是这样的:

后来查了一下是因为少装了点啥。

解决:

(如果你装了当我没说...)

hb build -f 时报错

我当时的错误是这样的:

解决:

当时我问了群里的一个兄弟,昵称:家星。再次万分感谢。

后来我发现在官方配置里也提到过:

xshell串口格式乱

解决:文件 - 属性 - 终端 - 高级 - 用CR-LF接受LF®

六、结语

最后,再次感谢极术社区和全志平台,感谢各位群友给我的帮助,这也是我第一次写关于技术的文章,写的不好,希望大佬多多批评指正。如果有错误可以联系我嗷QQ:2403057219(24小时在线[狗头])

相关推荐
Net_Walke19 小时前
git 的常用命令
git·物联网·github·iot
老坛程序员1 天前
Mosquitto:MQTT Broker入门与分布式部署最佳实践
分布式·物联网·网络协议·iot
TDengine (老段)1 天前
TDengine 时序函数 IRATE 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine
TDengine (老段)1 天前
TDengine 时序函数 CSUM 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
TDengine (老段)4 天前
TDengine 聚合函数 HYPERLOGLOG 用户手册
大数据·数据库·物联网·哈希算法·时序数据库·iot·tdengine
TDengine (老段)5 天前
TDengine 聚合函数 HISTOGRAM 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
不会吉他的肌肉男不是好的挨踢男5 天前
docker 安装TDengine 并创建新用户
docker·容器·iot·tdengine
BAGAE15 天前
MODBUS 通信协议详细介绍
linux·嵌入式硬件·物联网·硬件架构·iot·嵌入式实时数据库·rtdbs
TDengine (老段)16 天前
TDengine 选择函数 TOP() 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
TDengine (老段)16 天前
TDengine 特殊函数 MODE() 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据