Files
License_plate_recognition/oled_ssd1306.c
2025-10-18 16:05:39 +08:00

295 lines
8.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// OLED显示屏简化版驱动源文件
#include <stdio.h> // 标准输入输出
#include <stddef.h> // 标准类型定义
#include "iot_gpio.h" // OpenHarmony HALIoT硬件设备操作接口-GPIO
#include "iot_i2c.h" // OpenHarmony HALIoT硬件设备操作接口-I2C
#include "iot_errno.h" // OpenHarmony HALIoT硬件设备操作接口-错误代码定义
#include "hi_io.h" // 海思 Pegasus SDKIoT硬件设备操作接口-IO
// 字库头文件
#include "oled_fonts.h"
// OLED显示屏简化版驱动接口文件
#include "oled_ssd1306.h"
// 定义一个宏,用于计算数组的长度
#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
// 定义一个宏用于标识I2C0
#define OLED_I2C_IDX 0
// 定义一个宏用于标识I2C0的波特率传输速率
#define OLED_I2C_BAUDRATE (400 * 1000) // 400KHz
// 定义一个宏用于标识OLED的宽度
#define OLED_WIDTH (128)
// 定义一个宏用于标识SSD1306显示屏驱动芯片的设备地址
#define OLED_I2C_ADDR 0x78
// 定义一个宏,用于标识写命令操作
#define OLED_I2C_CMD 0x00 // 0000 0000 写命令
// 定义一个宏,用于标识写数据操作
#define OLED_I2C_DATA 0x40 // 0100 0000(0x40) 写数据
// 定义一个宏用于标识100ms的延时
#define DELAY_100_MS (100 * 1000)
// 定义一个结构体,表示要发送或接收的数据
typedef struct
{
// 要发送的数据的指针
unsigned char *sendBuf;
// 要发送的数据长度
unsigned int sendLen;
// 要接收的数据的指针
unsigned char *receiveBuf;
// 要接收的数据长度
unsigned int receiveLen;
} IotI2cData;
/// @brief 向OLED写一个字节
/// @param regAddr 写入命令还是数据 OLED_I2C_CMD / OLED_I2C_DATA
/// @param byte 写入的内容
/// @retval 成功返回IOT_SUCCESS失败返回IOT_FAILURE
static uint32_t I2cWiteByte(uint8_t regAddr, uint8_t byte)
{
// 定义字节流
uint8_t buffer[] = {regAddr, byte};
IotI2cData i2cData = {0};
i2cData.sendBuf = buffer;
i2cData.sendLen = sizeof(buffer) / sizeof(buffer[0]);
// 发送字节流
return IoTI2cWrite(OLED_I2C_IDX, OLED_I2C_ADDR, i2cData.sendBuf, i2cData.sendLen);
}
/// @brief 向OLED写一个命令字节
/// @param cmd 写入的命令字节
/// @return 成功返回IOT_SUCCESS失败返回IOT_FAILURE
static uint32_t WriteCmd(uint8_t cmd)
{
return I2cWiteByte(OLED_I2C_CMD, cmd);
}
/// @brief 向OLED写一个数据字节
/// @param cmd 写入的数据字节
/// @return 成功返回IOT_SUCCESS失败返回IOT_FAILURE
uint32_t WriteData(uint8_t data)
{
return I2cWiteByte(OLED_I2C_DATA, data);
}
/// @brief 初始化SSD1306显示屏驱动芯片
uint32_t OledInit(void)
{
// 构造初始化代码
static const uint8_t initCmds[] = {
0xAE, // 显示关闭
0x00, // 页寻址模式时设置列地址的低4位为0000
0x10, // 页寻址模式时设置列地址的高4位为0000
0x40, // 设置起始行地址为第0行
0xB0, // 页寻址模式时设置页面起始地址为PAGE0
0x81, // 设置对比度
0xFF, // 对比度数值
0xA1, // set segment remap
0xA6, // 设置正常显示。0对应像素熄灭1对应像素亮起
0xA8, // --set multiplex ratio(1 to 64)
0x3F, // --1/32 duty
0xC8, // Com scan direction
0xD3, // -set display offset
0x00, //
0xD5, // set osc division
0x80, //
0xD8, // set area color mode off
0x05, //
0xD9, // Set Pre-Charge Period
0xF1, //
0xDA, // set com pin configuartion
0x12, //
0xDB, // set Vcomh
0x30, //
0x8D, // set charge pump enable
0x14, //
0xAF, // 显示开启
};
// 初始化GPIO-13
IoTGpioInit(HI_IO_NAME_GPIO_13);
// 设置GPIO-13引脚功能为I2C0_SDA
hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
// 初始化GPIO-14
IoTGpioInit(HI_IO_NAME_GPIO_14);
// 设置GPIO-14引脚功能为I2C0_SCL
hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);
// 用指定的波特速率初始化I2C0
IoTI2cInit(OLED_I2C_IDX, OLED_I2C_BAUDRATE);
// 发送初始化代码初始化SSD1306显示屏驱动芯片
for (size_t i = 0; i < ARRAY_SIZE(initCmds); i++)
{
// 发送一个命令字节
uint32_t status = WriteCmd(initCmds[i]);
if (status != IOT_SUCCESS)
{
return status;
}
}
// OLED初始化完成返回成功
return IOT_SUCCESS;
}
/// @brief 设置显示位置
/// @param x x坐标1像素为单位
/// @param y y坐标8像素为单位。即页面起始地址
/// @return 无
void OledSetPosition(uint8_t x, uint8_t y)
{
//设置页面起始地址
WriteCmd(0xb0 + y);
// 列0~127
// 第0列0x00列二进制00000000。低地址0000即0x00。高地址0000(需要|0x10)0000|0x10=0x10。
// 第127列0x7f列二进制01111111。低地址1111即0x0F。高地址0111(需要|0x10)0111|0x10=0x17。
// 设置显示位置列地址的低4位
// 直接取出列地址低4位作为命令代码的低4位命令代码的高4位为0000
WriteCmd(x & 0x0f);
// 设置显示位置列地址的高4位
// 取出列地址高4位作为命令代码的低4位命令代码的高4位必须为0001
// 实际编程时列地址的高4位和0x10二进制00010000进行按位或即得到命令代码
WriteCmd(((x & 0xf0) >> 4) | 0x10);
}
/// @brief 全屏填充
/// @param fillData 填充的数据1字节
/// @return 无
void OledFillScreen(uint8_t fillData)
{
// 相关变量用于遍历page和列
uint8_t m = 0;
uint8_t n = 0;
// 写入所有页的数据
for (m = 0; m < 8; m++)
{
//设置页地址0~7
WriteCmd(0xb0 + m);
// 设置显示位置为第0列
WriteCmd(0x00); //设置显示位置:列低地址(0000)
WriteCmd(0x10); //设置显示位置:列高地址(0000)
// 写入128列数据
// 在一个页中数据按列写入一次一列对应发送过来的1字节数据
for (n = 0; n < 128; n++)
{
// 写入一个字节数据
WriteData(fillData);
}
}
}
/// @brief 清屏函数
/// @return 无
void OledClearScreen(void)
{
OledFillScreen(0x00); // 用0x00填充整个屏幕实现清屏
}
/// @brief 显示一个字符
/// @param x: x坐标1像素为单位
/// @param y: y坐标8像素为单位
/// @param ch: 要显示的字符
/// @param font: 字库
void OledShowChar(uint8_t x, uint8_t y, uint8_t ch, Font font)
{
// 数组下标
uint8_t c = 0;
// 循环控制
uint8_t i = 0;
// 得到数组下标
// 空格的ASCII码32在字库中的下标是0。字库中的字符-空格即相应的数组下标
c = ch - ' ';
// 显示字符
if (font == FONT8x16) // 8*16的点阵一个page放不下
{
// 显示字符的上半部分
// 设置显示位置
OledSetPosition(x, y);
// 逐个字节写入16个数组元素的前8个
for (i = 0; i < 8; i++)
{
WriteData(F8X16[c * 16 + i]);
}
// 显示字符的下半部分
// 设置显示位置为下一个PAGE
OledSetPosition(x, y + 1);
// 逐个字节写入16个数组元素的后8个
for (i = 0; i < 8; i++)
{
WriteData(F8X16[c * 16 + 8 + i]);
}
}
else // 6*8的点阵在一个page中
{
// 设置显示位置
OledSetPosition(x, y);
// 逐个字节写入数组第二维的6个数组元素
for (i = 0; i < 6; i++)
{
WriteData(F6x8[c][i]);
}
}
}
/// @brief 显示一个字符串
/// @param x: x坐标1像素为单位
/// @param y: y坐标8像素为单位
/// @param str: 要显示的字符串
/// @param font: 字库
void OledShowString(uint8_t x, uint8_t y, const char *str, Font font)
{
// 字符数组(字符串)下标
uint8_t j = 0;
// 检查字符串是否为空
if (str == NULL)
{
printf("param is NULL,Please check!!!\r\n");
return;
}
// 遍历字符串,显示每个字符
while (str[j])
{
// 显示一个字符
OledShowChar(x, y, str[j], font);
// 设置字符间距
x += 8;
// 如果下一个要显示的字符超出了OLED显示的范围则换行
if (x > 120)
{
x = 0;
y += 2;
}
// 下一个字符
j++;
}
}