前言
首先,非常感谢极术社区和全志举办此次开发板申请试用活动。由于自己水平太低,拿到板子后不知道要干点啥,偶然看见一个大佬写的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, ¶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®
六、结语
最后,再次感谢极术社区和全志平台,感谢各位群友给我的帮助,这也是我第一次写关于技术的文章,写的不好,希望大佬多多批评指正。如果有错误可以联系我嗷QQ:2403057219(24小时在线[狗头])