【鸿蒙开发】Hi3861学习笔记- OLED示例
00. 目录
文章目录
- 00. 目录
- 01. I2C概述
- 02. I2C相关类型
- 2.1 hi_i2c_func
- 2.2 hi_i2c_data
- 2.3 hi_i2c_idx
- 03. I2C相关函数
- 3.1 hi_i2c_init
- 3.2 hi_i2c_deinit
- 3.3 hi_i2c_write
- 3.4 hi_i2c_read
- 3.5 hi_i2c_set_baudrate
- 04. 硬件设计
- 05. 软件设计
- 06. 实验现象
- 07. 附录
01. I2C概述
I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。
I2C 和 SPI 一样以主从的方式工作,不同于 SPI 一主多从的结构,它允许同时有多个主设备存在,每个连接到总线上的器件都有唯一的地址,主设备启动数据传输并产生时钟信号,从设备被主设备寻址,同一时刻只允许有一个主设备。如下图所示:
如下图所示为 I2C 总线主要的数据传输格式:
当总线空闲时,SDA 和 SCL 都处于高电平状态,当主机要和某个从机通讯时,会先发送一个开始条件,然后发送从机地址和读写控制位,接下来传输数据(主机发送或者接收数据),数据传输结束时主机会发送停止条件。传输的每个字节为8位,高位在前,低位在后。数据传输过程中的不同名词详解如下所示:
- 开始条件: SCL 为高电平时,主机将 SDA 拉低,表示数据传输即将开始。
- 从机地址: 主机发送的第一个字节为从机地址,高 7 位为地址,最低位为 R/W 读写控制位,1 表示读操作,0 表示写操作。一般从机地址有 7 位地址模式和 10 位地址模式两种,如果是 10 位地址模式,第一个字节的头 7 位 是 11110XX 的组合,其中最后两位(XX)是 10 位地址的两个最高位,第二个字节为 10 位从机地址的剩下8位,如下图所示:
- 应答信号: 每传输完成一个字节的数据,接收方就需要回复一个 ACK(acknowledge)。写数据时由从机发送 ACK,读数据时由主机发送 ACK。当主机读到最后一个字节数据时,可发送 NACK(Not acknowledge)然后跟停止条件。
- 数据: 从机地址发送完后可能会发送一些指令,依从机而定,然后开始传输数据,由主机或者从机发送,每个数据为 8 位,数据的字节数没有限制。
- 重复开始条件: 在一次通信过程中,主机可能需要和不同的从机传输数据或者需要切换读写操作时,主机可以再发送一个开始条件。
- 停止条件: 在 SDA 为低电平时,主机将 SCL 拉高并保持高电平,然后在将 SDA 拉高,表示传输结束。
02. I2C相关类型
2.1 hi_i2c_func
/**
* @ingroup iot_i2c
*
* I2C callback function. CNcomment:I2C回调函数。CNend
*/
typedef struct {
i2c_reset_func reset_func; /**< This function is called back when the communication with the slave device
is abnormal.CNcomment:I2C从异常处理函数CNend */
i2c_prepare_func prepare_func; /**< This function is called back before the I2C read/write operation to implement
the preparations before the I2C operation.
CNcomment:I2C操作前准备函数CNend */
i2c_restore_func restore_func; /**< After the I2C read/write operation is performed, this function is
called back to implement the recovery after the I2C operation.
CNcomment:I2C操作后恢复函数CNend */
} hi_i2c_func;
2.2 hi_i2c_data
/**
* @ingroup iot_i2c
*
* I2C TX/RX data descriptor. CNcomment:I2C发送/接收数据描述符。CNend
*/
typedef struct {
hi_u8* send_buf; /**< Data TX pointer. The user needs to ensure that no null pointer is transferred.
CNcomment:数据发送指针CNend */
hi_u32 send_len; /**< Length of sent data (unit: byte).
CNcomment:发送数据长度(单位:byte)CNend */
hi_u8* receive_buf; /**< Data RX pointer. CNcomment:数据接收指针CNend */
hi_u32 receive_len; /**< Length of received data (unit: byte).
CNcomment:接收数据长度(单位:byte)CNend */
} hi_i2c_data;
2.3 hi_i2c_idx
/**
* @ingroup iot_i2c
*
* I2C hardware index. CNComment:I2C硬件设备枚举。CNend
*/
typedef enum {
HI_I2C_IDX_0,
HI_I2C_IDX_1,
} hi_i2c_idx;
03. I2C相关函数
3.1 hi_i2c_init
/**
* @ingroup iot_i2c
* @brief Initializes the I2C controller. CNcomment:I2C初始化。CNend
*
* @par 描述:
* Initializes the I2C controller. CNcomment:I2C初始化。CNend
*
* @attention None
* @param id [IN] type #hi_i2c_idx,I2C hardware selection. CNcomment:I2C硬件设备选择。CNend
* @param baudrate [IN] type #hi_u32,I2C baudrate. CNcomment:I2C波特率。CNend
*
* @retval #0 Success.
* @retval #Other Failure. For details, see hi_errno.h.
* @par 依赖:
* @li hi_i2c.h:Declares the API.CNcomment:该接口声明所在的头文件。CNend
* @see hi_i2c_deinit。
*/
hi_u32 hi_i2c_init(hi_i2c_idx id, hi_u32 baudrate)
功能:
初始化 I2C 设备,并指定波特率
参数:
id:I2C 设备 ID
baudrate:I2C 通信波特率
返回值:
0 成功,1 失败
3.2 hi_i2c_deinit
/**
* @ingroup iot_i2c
* @brief Exits the I2C module.CNcomment:退出I2C模块。CNend
*
* @par 描述:
* Exits the I2C module. CNcomment:退出I2C模块。CNend
*
* @attention This API is called after hi_i2c_init is called. CNcomment:hi_i2c_init调用后再使用。CNend
* @param id [IN] type #hi_i2c_idx,I2C hardware selection. CNcomment:I2C硬件设备选择。CNend
*
* @retval #0 Success.
* @retval #Other Failure. For details, see hi_errno.h.
* @par 依赖:
* @li hi_i2c.h:Declares the API.CNcomment:该接口声明所在的头文件。CNend
* @see hi_i2c_init。
*/
hi_u32 hi_i2c_deinit(hi_i2c_idx id)
功能:
退出 I2C 模块
参数:
id:I2C 设备 ID
返回值:
0 成功,1 失败
3.3 hi_i2c_write
/**
* @ingroup iot_i2c
* @brief I2C data TX. CNcomment:I2C发送数据。CNend
*
* @par 描述:
* I2C data TX. CNcomment:I2C发送数据。CNend
*
* @attention Multiple tasks are not protected (multiple tasks are not supported). CNcomment:未作
多任务保护(不支持多任务)。CNend
* @param id [IN] type #hi_i2c_idx,I2C hardware selection. CNcomment:I2C硬件设备选择。CNend
* @param device_addr [IN] type #hi_u16,The device ID. High three bits of offset address of the I2C device on chipset.
CNcomment:设备号及设备片内偏移地址高3位(从设备哪个地方开始读)。CNend
* @param i2c_data [IN] type #const hi_i2c_data*,The data descriptor to be received. The structure member data sending
* pointer and data receiving pointer cannot be null.
CNcomment:待接收数据描述符,结构体成员数据发送指针和数据接收指针都不为空。CNend
*
* @retval #0 Success.
* @retval #Other Failure. For details, see hi_errno.h.
* @par 依赖:
* @li hi_i2c.h:Declares the API.CNcomment:该接口声明所在的头文件。CNend
* @see hi_i2c_writeread|hi_i2c_receive。
*/
hi_u32 hi_i2c_write(hi_i2c_idx id, hi_u16 device_addr, const hi_i2c_data *i2c_data)
功能:
将数据写入 I2C 设备
参数:
id:I2C 设备 id
device_addr:I2C 设备地址
i2c_data:指向要写入/接收的数据的指针,包括写入和读取数据长度等
返回值:
0 成功,1 失败
3.4 hi_i2c_read
/**
* @ingroup iot_i2c
* @brief I2C data RX. CNcomment:I2C接收数据。CNend
*
* @par 描述:
* I2C data RX. CNcomment:I2C接收数据。CNend
*
* @attention Multi-tasking is not supported. CNcomment:未作多任务保护(不支持多任务)。CNend
* @param id [IN] type #hi_i2c_idx,I2C hardware selection. CNcomment:I2C硬件设备选择。CNend
* @param device_addr [IN] type #hi_u16,The device ID. High three bits of offset address of the I2C device on chipset.
CNcomment:设备号及设备片内偏移地址高3位(从设备哪个地方开始读)。CNend
* @param i2c_data [OUT] type #const hi_i2c_data*,The data descriptor to be received. The structure member data sending
* pointer and data receiving pointer cannot be null.
CNcomment:待接收数据描述符,结构体成员数据发送指针和数据接收指针都不为空。CNend
*
* @retval #0 Success.
* @retval #Other Failure. For details, see hi_errno.h.
* @par 依赖:
* @li hi_i2c.h:Declares the API.CNcomment:该接口声明所在的头文件。CNend
* @see hi_i2c_write|hi_i2c_sendreceive。
*/
hi_u32 hi_i2c_read(hi_i2c_idx id, hi_u16 device_addr, const hi_i2c_data *i2c_data)
功能:
从 I2C 设备读取数据
参数:
id:I2C 设备 id
device_addr:I2C 设备地址
i2c_data:指向要写入/接收的数据的指针,包括写入和读取数据长度等
返回值:
0 成功,1 失败
3.5 hi_i2c_set_baudrate
/**
* @ingroup iot_i2c
* @brief Set I2C baudrate. CNcomment:I2C设置波特率。CNend
*
* @par 描述:
* Set I2C baudrate. CNcomment:I2C设置波特率。CNend
*
* @attention Multiple tasks are not protected (multiple tasks are not supported). CNcomment:未作
多任务保护(不支持多任务)。CNend
* @param id [IN] type #hi_i2c_idx,I2C hardware selection. CNcomment:I2C硬件设备选择。CNend
* @param baudrate [IN] type #hi_u32,I2C baudrate. CNcomment:I2C波特率。CNend
*
* @retval #0 Success.
* @retval #Other Failure. For details, see hi_errno.h.
* @par 依赖:
* @li hi_i2c.h:Declares the API.CNcomment:该接口声明所在的头文件。CNend
* @see hi_i2c_write|hi_i2c_receive。
*/
hi_u32 hi_i2c_set_baudrate(hi_i2c_idx id, hi_u32 baudrate)
功能:
设置 I2C 设备波特率
参数:
id:I2C 设备 id
baudrate:I2C 通信波特率
返回值:
0 成功,1 失败
04. 硬件设计
从上图可知,板载 OLED 接口均连接到指定 IO。
05. 软件设计
bsp_oled.h
#ifndef BSP_OLED_H
#define BSP_OLED_H
#include "cmsis_os2.h"
#include "hi_io.h"
#include "hi_gpio.h"
#define SSD1306_I2C_ADDR 0x78 // 器件的I2C从机地址
#define SSD1306_I2C_IDX 0 // 模块的I2C总线号
#define SSD1306_I2C_SPEED 100000 // 100KHz
#define SIZE 16
#define XLevelL 0x00
#define XLevelH 0x10
#define Max_Column 128
#define Max_Row 64
#define Brightness 0xFF
#define X_WIDTH 128
#define Y_WIDTH 64
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
//函数声明
void oled_wr_byte(uint8_t dat,uint8_t cmd);
void oled_display_on(void);
void oled_display_off(void);
void oled_set_pos(unsigned char x, unsigned char y);
void oled_init(void);
void oled_refresh_gram(void);
void oled_clear(void);
void oled_drawpoint(uint8_t x,uint8_t y,uint8_t t);
void oled_fill(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t dot);
void oled_fill_rectangle(uint8_t x0,uint8_t y0,uint8_t width,uint8_t height,uint8_t color);
void oled_drawline(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,uint8_t color);
void oled_drawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,uint8_t color);
void oled_fill_circle(uint8_t x0,uint8_t y0,uint8_t r,uint8_t color);
void oled_draw_bigpoint(uint8_t x0,uint8_t y0,uint8_t color);
void oled_draw_vline(uint8_t x0,uint8_t y0,uint8_t len,uint8_t color);
void oled_draw_hline(uint8_t x0,uint8_t y0,uint8_t len,uint8_t color);
void oled_fill_circle(uint8_t x0,uint8_t y0,uint8_t r,uint8_t color);
void oled_draw_ellipse(uint8_t x0, uint8_t y0, uint8_t rx, uint8_t ry,uint8_t color) ;
void oled_showchar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode);
void oled_shownum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size);
void oled_showstring(uint8_t x,uint8_t y,const uint8_t *p,uint8_t size);
void oled_showfontHZ(uint8_t x,uint8_t y,uint8_t pos,uint8_t size,uint8_t mode);
void oled_drawBMP(uint8_t x0, uint8_t y0,uint8_t x1, uint8_t y1,uint8_t BMP[]);
#endif
bsp_oled.c
#include "bsp_oled.h"
#include <unistd.h>
#include "hi_time.h"
#include "hi_i2c.h"
#include "oledfont.h"
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
static uint8_t OLED_GRAM[128][8];
//IIC发送数据
uint32_t iic_senddata(uint8_t *data, size_t size)
{
hi_i2c_data i2cData = {0};
i2cData.send_buf = data;
i2cData.send_len = size;
return hi_i2c_write(SSD1306_I2C_IDX, SSD1306_I2C_ADDR, &i2cData);
}
//IIC写命令
uint32_t write_iic_cmd(uint8_t cmd)
{
uint8_t buffer[] = {0x00, cmd};
return iic_senddata(buffer, sizeof(buffer));
}
//IIC写数据
uint32_t write_iic_data(uint8_t dat)
{
uint8_t buffer[] = {0x40, dat};
return iic_senddata(buffer, sizeof(buffer));
}
//向SSD1306写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void oled_wr_byte(uint8_t dat,uint8_t cmd)
{
if(cmd)
write_iic_data(dat);
else
write_iic_cmd(dat);
}
//更新显存到LCD
void oled_refresh_gram(void)
{
int i,n;
for(i=7;i>=0;i--) //修改刷新方向
{
oled_wr_byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
oled_wr_byte (0x00,OLED_CMD); //设置显示位置—列低地址
oled_wr_byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)oled_wr_byte(OLED_GRAM[n][i],OLED_DATA);
}
}
void oled_set_pos(unsigned char x, unsigned char y)
{
oled_wr_byte(0xb0+y,OLED_CMD);
oled_wr_byte(((x&0xf0)>>4)|0x10,OLED_CMD);
oled_wr_byte((x&0x0f)|0x01,OLED_CMD);
}
//开启OLED显示
void oled_display_on(void)
{
oled_wr_byte(0X8D,OLED_CMD); //SET DCDC命令
oled_wr_byte(0X14,OLED_CMD); //DCDC ON
oled_wr_byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void oled_display_off(void)
{
oled_wr_byte(0X8D,OLED_CMD); //SET DCDC命令
oled_wr_byte(0X10,OLED_CMD); //DCDC OFF
oled_wr_byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void oled_clear(void)
{
uint8_t i,n;
for(i=0;i<8;i++)
{
for(n=0;n<128;n++)
{
OLED_GRAM[n][i]=0;
}
}
oled_refresh_gram();//更新显示
}
//m^n函数
uint32_t oled_pow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
//画点
//x:0~127
//y:0~63
//t:1 填充 0,清空
void oled_drawpoint(uint8_t x,uint8_t y,uint8_t t)
{
uint8_t pos,bx,temp=0;
if(x>127||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;
}
//x1,y1,x2,y2 填充区域的对角坐标
//确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63
//dot:0,清空;1,填充
void oled_fill(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t dot)
{
uint8_t x,y;
for(x=x1;x<=x2;x++)
{
for(y=y1;y<=y2;y++)
{
oled_drawpoint(x,y,dot);
}
}
}
//填充矩形
//x0,y0:矩形的左上角坐标
//width,height:矩形的尺寸
//color:颜色
void oled_fill_rectangle(uint8_t x0,uint8_t y0,uint8_t width,uint8_t height,uint8_t color)
{
if(width==0||height==0)return;//非法.
oled_fill(x0,y0,x0+width-1,y0+height-1,color);
}
//画线
//x1,y1:起点坐标
//x2,y2:终点坐标
void oled_drawline(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,uint8_t color)
{
uint8_t t;
char xerr=0,yerr=0,delta_x,delta_y,distance;
char incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if(delta_x==0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平线
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//画线输出
{
oled_drawpoint(uRow,uCol,color);//画点
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
//画大点函数
//x0,y0:坐标
//color:颜色
//以(x0,y0)为中心,画一个9个点的大点
void oled_draw_bigpoint(uint8_t x0,uint8_t y0,uint8_t color)
{
uint8_t i,j;
uint8_t x,y;
if(x0>=1)x=x0-1;
else x=x0;
if(y0>=1)y=y0-1;
else y=y0;
for(i=y;i<y0+2;i++)
{
for(j=x;j<x0+2;j++)oled_drawpoint(j,i,color);
}
}
//画垂直线
//x0,y0:坐标
//len:线长度
//color:颜色
void oled_draw_vline(uint8_t x0,uint8_t y0,uint8_t len,uint8_t color)
{
if((len==0)||(x0>Max_Column-1)||(y0>Max_Row-1))return;
oled_fill(x0,y0,x0,y0+len-1,color);
}
//画水平线
//x0,y0:坐标
//len:线长度
//color:颜色
void oled_draw_hline(uint8_t x0,uint8_t y0,uint8_t len,uint8_t color)
{
if((len==0)||(x0>Max_Column-1)||(y0>Max_Row-1))return;
oled_fill(x0,y0,x0+len-1,y0,color);
}
//画实心圆
//x0,y0:坐标
//r半径
//color:颜色
void oled_fill_circle(uint8_t x0,uint8_t y0,uint8_t r,uint8_t color)
{
uint16_t i;
uint16_t imax = ((uint32_t)r*707)/1000+1;
uint16_t sqmax = (uint32_t)r*(uint32_t)r+(uint32_t)r/2;
uint16_t x=r;
oled_draw_hline(x0-r,y0,2*r,color);
for (i=1;i<=imax;i++)
{
if ((i*i+x*x)>sqmax)
{
// draw lines from outside
if (x>imax)
{
oled_draw_hline (x0-i+1,y0+x,2*(i-1),color);
oled_draw_hline (x0-i+1,y0-x,2*(i-1),color);
}
x--;
}
// draw lines from inside (center)
oled_draw_hline(x0-x,y0+i,2*x,color);
oled_draw_hline(x0-x,y0-i,2*x,color);
}
}
//画椭圆
//x0,y0:坐标
//rx:x方向半径
//ry:y方向半径
void oled_draw_ellipse(uint8_t x0, uint8_t y0, uint8_t rx, uint8_t ry,uint8_t color)
{
uint16_t OutConst, Sum, SumY;
uint8_t x,y;
uint8_t xOld;
uint8_t _rx = rx;
uint8_t _ry = ry;
if(rx>x0||ry>y0)return;//非法.
OutConst = _rx*_rx*_ry*_ry+(_rx*_rx*_ry>>1); // Constant as explaint above
// To compensate for rounding
xOld = x = rx;
for (y=0; y<=ry; y++)
{
if (y==ry)x=0;
else
{
SumY =((uint16_t)(rx*rx))*((uint32_t)(y*y)); // Does not change in loop
while(Sum = SumY + ((uint16_t)(ry*ry))*((uint32_t)(x*x)),(x>0) && (Sum>OutConst)) x--;
}
// Since we draw lines, we can not draw on the first iteration
if (y)
{
oled_drawline(x0-xOld,y0-y+1,x0-x,y0-y,color);
oled_drawline(x0-xOld,y0+y-1,x0-x,y0+y,color);
oled_drawline(x0+xOld,y0-y+1,x0+x,y0-y,color);
oled_drawline(x0+xOld,y0+y-1,x0+x,y0+y,color);
}
xOld = x;
}
}
//画矩形
//(x1,y1),(x2,y2):矩形的对角坐标
void oled_drawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,uint8_t color)
{
oled_drawline(x1,y1,x2,y1,color);
oled_drawline(x1,y1,x1,y2,color);
oled_drawline(x1,y2,x2,y2,color);
oled_drawline(x2,y1,x2,y2,color);
}
//在指定位置画一个指定大小的圆
//(x,y):中心点
//r :半径
void oled_draw_circle(uint8_t x0,uint8_t y0,uint8_t r,uint8_t color)
{
char a,b;
char di;
a=0;b=r;
di=3-(r<<1); //判断下个点位置的标志
while(a<=b)
{
oled_drawpoint(x0+a,y0-b,color); //5
oled_drawpoint(x0+b,y0-a,color); //0
oled_drawpoint(x0+b,y0+a,color); //4
oled_drawpoint(x0+a,y0+b,color); //6
oled_drawpoint(x0-a,y0+b,color); //1
oled_drawpoint(x0-b,y0+a,color);
oled_drawpoint(x0-a,y0-b,color); //2
oled_drawpoint(x0-b,y0-a,color); //7
a++;
//使用Bresenham算法画圆
if(di<0)di +=4*a+6;
else
{
di+=10+4*(a-b);
b--;
}
}
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 12/16/24
void oled_showchar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
{
uint8_t temp,t,t1;
uint8_t y0=y;
uint8_t csize=(size/8+((size%8)?1:0))*(size/2); //得到字体一个字符对应点阵集所占的字节数
chr=chr-' ';//得到偏移后的值
for(t=0;t<csize;t++)
{
if(size==12)temp=ascii_1206[chr][t]; //调用1206字体
else if(size==16)temp=ascii_1608[chr][t]; //调用1608字体
else if(size==24)temp=ascii_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;
}
}
}
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//num:数值(0~4294967295);
void oled_shownum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size)
{
uint8_t t,temp;
uint8_t enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
oled_showchar(x+(size/2)*t,y,' ',size,1);
continue;
}else enshow=1;
}
oled_showchar(x+(size/2)*t,y,temp+'0',size,1);
}
}
//显示字符串
//x,y:起点坐标
//size:字体大小
//*p:字符串起始地址
void oled_showstring(uint8_t x,uint8_t y,const uint8_t *p,uint8_t size)
{
while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
{
if(x>(X_WIDTH-(size/2))){x=0;y+=size;}
if(y>(Y_WIDTH-size)){y=x=0;oled_clear();}
oled_showchar(x,y,*p,size,1);
x+=size/2;
p++;
}
}
//显示汉字
//x,y:起点坐标
//pos:数组位置汉字显示
//size:字体大小
//mode:0,反白显示;1,正常显示
void oled_showfontHZ(uint8_t x,uint8_t y,uint8_t pos,uint8_t size,uint8_t mode)
{
uint8_t temp,t,t1;
uint8_t y0=y;
uint8_t csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数
if(size!=12&&size!=16&&size!=24)return; //不支持的size
for(t=0;t<csize;t++)
{
if(size==12)temp=FontHzk[pos][t]; //调用1206字体
else if(size==16)temp=FontHzk[pos][t]; //调用1608字体
else if(size==24)temp=FontHzk[pos][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;
}
}
}
}
//显示BMP图片128×64
//起始点坐标(x,y),x的范围0~127,y为页的范围0~7
void oled_drawBMP(uint8_t x0, uint8_t y0,uint8_t x1, uint8_t y1,uint8_t BMP[])
{
uint16_t j=0;
uint8_t x,y;
if(y1%8==0)y=y1/8;
else y=y1/8+1;
for(y=y0;y<y1;y++)
{
oled_set_pos(x0,y);
for(x=x0;x<x1;x++)
{
oled_wr_byte(BMP[j++],OLED_DATA);
}
}
}
//oled初始化
void oled_init(void)
{
uint32_t result;
hi_gpio_init();
// gpio_9 复用为 I2C_SCL
hi_io_set_pull(HI_IO_NAME_GPIO_9, HI_IO_PULL_UP);
hi_io_set_func(HI_IO_NAME_GPIO_9, HI_IO_FUNC_GPIO_9_I2C0_SCL);
// gpio_10 复用为 I2C_SDA
hi_io_set_pull(HI_IO_NAME_GPIO_10, HI_IO_PULL_UP);
hi_io_set_func(HI_IO_NAME_GPIO_10, HI_IO_FUNC_GPIO_10_I2C0_SDA);
result = hi_i2c_init(SSD1306_I2C_IDX, SSD1306_I2C_SPEED);
if (result != HI_ERR_SUCCESS)
{
printf("I2C SSD1306 Init status is 0x%x!!!\r\n", result);
return result;
}
usleep(100*1000);//100ms
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();
}
template.c
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "bsp_led.h"
#include "bsp_oled.h"
//LED任务
osThreadId_t LED_Task_ID; //led任务ID
void LED_Task(void)
{
led_init();//LED初始化
while (1)
{
LED(1);
usleep(200*1000); //200ms
LED(0);
usleep(200*1000); //200ms
}
}
//LED任务创建
void led_task_create(void)
{
osThreadAttr_t taskOptions;
taskOptions.name = "LEDTask"; // 任务的名字
taskOptions.attr_bits = 0; // 属性位
taskOptions.cb_mem = NULL; // 堆空间地址
taskOptions.cb_size = 0; // 堆空间大小
taskOptions.stack_mem = NULL; // 栈空间地址
taskOptions.stack_size = 1024; // 栈空间大小 单位:字节
taskOptions.priority = osPriorityNormal; // 任务的优先级
LED_Task_ID = osThreadNew((osThreadFunc_t)LED_Task, NULL, &taskOptions); // 创建任务1
if (LED_Task_ID != NULL)
{
printf("ID = %d, Create LED_Task_ID is OK!\n", LED_Task_ID);
}
}
//控制任务
osThreadId_t OLED_Task_ID; //任务ID
void OLED_Task(void)
{
oled_init();
oled_clear();
oled_showstring(0,0,"Hello World",12);
oled_showstring(0,15,"Hello World",16);
oled_showfontHZ(0,35,0,16,1);
oled_showfontHZ(1*16,35,1,16,1);
oled_refresh_gram();
while (1)
{
usleep(10*1000); //10ms
}
}
//任务创建
void oled_task_create(void)
{
osThreadAttr_t taskOptions;
taskOptions.name = "oledTask"; // 任务的名字
taskOptions.attr_bits = 0; // 属性位
taskOptions.cb_mem = NULL; // 堆空间地址
taskOptions.cb_size = 0; // 堆空间大小
taskOptions.stack_mem = NULL; // 栈空间地址
taskOptions.stack_size = 1024; // 栈空间大小 单位:字节
taskOptions.priority = osPriorityNormal; // 任务的优先级
OLED_Task_ID = osThreadNew((osThreadFunc_t)OLED_Task, NULL, &taskOptions); // 创建任务
if (OLED_Task_ID != NULL)
{
printf("ID = %d, Task Create OK!\n", OLED_Task_ID);
}
}
/**
* @description: 初始化并创建任务
* @param {*}
* @return {*}
*/
static void template_demo(void)
{
printf("-Hi3861开发板--OLED液晶显示实验\r\n");
led_task_create();
oled_task_create();//任务创建
}
SYS_RUN(template_demo);
06. 实验现象
实验现象:OLED 液晶显示字符