oled.c代码解释
#include "oled.h"
#include "i2c.h"
#include "oledfont.h" // 包含OLED字体相关的头文件
// 初始化命令数组,用于初始化OLED屏幕
uint8_t CMD_Data[]={
0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F,
0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1, 0xDA, 0x12,
0xD8, 0x30, 0x8D, 0x14, 0xAF};
// 向OLED写入初始化命令的函数
void WriteCmd(void)
{
uint8_t i = 0;
// 循环遍历初始化命令数组
for(i=0; i<27; i++)
{
// 通过I2C接口向OLED设备写入单个初始化命令
HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,CMD_Data+i,1,0x100);
}
}
// 向设备写控制命令的函数
void OLED_WR_CMD(uint8_t cmd)
{
// 通过I2C接口向OLED设备写入单个控制命令
HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,&cmd,1,0x100);
}
// 向设备写数据的函数
void OLED_WR_DATA(uint8_t data)
{
// 通过I2C接口向OLED设备写入单个数据
HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x40,I2C_MEMADD_SIZE_8BIT,&data,1,0x100);
}
// 初始化OLED屏幕的函数
void OLED_Init(void)
{
// 延时200ms,等待OLED设备稳定
HAL_Delay(200);
// 调用写入初始化命令的函数
WriteCmd();
}
// 清屏函数
void OLED_Clear(void)
{
uint8_t i,n;
// 循环操作8页
for(i=0;i<8;i++)
{
// 设置当前页地址
OLED_WR_CMD(0xb0+i);
// 设置列低地址
OLED_WR_CMD (0x00);
// 设置列高地址
OLED_WR_CMD (0x10);
// 循环清空每一列的数据
for(n=0;n<128;n++)
OLED_WR_DATA(0);
}
}
// 开启OLED显示的函数
void OLED_Display_On(void)
{
// 发送设置DCDC命令
OLED_WR_CMD(0X8D);
// 开启DCDC
OLED_WR_CMD(0X14);
// 开启显示
OLED_WR_CMD(0XAF);
}
// 关闭OLED显示的函数
void OLED_Display_Off(void)
{
// 发送设置DCDC命令
OLED_WR_CMD(0X8D);
// 关闭DCDC
OLED_WR_CMD(0X10);
// 关闭显示
OLED_WR_CMD(0XAE);
}
// 设置OLED显示位置的函数
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
// 设置页地址
OLED_WR_CMD(0xb0+y);
// 设置列高地址
OLED_WR_CMD(((x&0xf0)>>4)|0x10);
// 设置列低地址
OLED_WR_CMD(x&0x0f);
}
// 点亮OLED屏幕所有像素的函数
void OLED_On(void)
{
uint8_t i,n;
// 循环操作8页
for(i=0;i<8;i++)
{
// 设置当前页地址
OLED_WR_CMD(0xb0+i); // 设置页地址(0~7)
// 设置列低地址
OLED_WR_CMD(0x00); // 设置显示位置 - 列低地址
// 设置列高地址
OLED_WR_CMD(0x10); // 设置显示位置 - 列高地址
// 循环点亮每一列的像素
for(n=0;n<128;n++)
OLED_WR_DATA(1);
} // 更新显示
}
// 计算m的n次方的函数
unsigned int oled_pow(uint8_t m,uint8_t n)
{
unsigned int result=1;
// 循环n次进行累乘
while(n--)result*=m;
return result;
}
// 在OLED上显示数字的函数
// x,y :起始坐标
// len :数字的位数
// size:字体大小
// mode:模式 0,填充模式;1,叠加模式
// num:数值(0~4294967295);
void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2)
{
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)
{
// 如果当前位为0且还未显示有效数字,则显示空格
OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
continue;
}else enshow=1;
}
// 显示当前位的数字
OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
}
}
// 在指定位置显示一个字符,包括部分字符
// x:0~127
// y:0~63
// mode:0,反白显示;1,正常显示
// size:选择字体 16/12
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{
unsigned char c=0,i=0;
// 得到偏移后的值
c=chr-' ';
if(x>128-1){x=0;y=y+2;}
if(Char_Size ==16)
{
// 设置显示位置
OLED_Set_Pos(x,y);
// 写入字符的上半部分数据
for(i=0;i<8;i++)
OLED_WR_DATA(F8X16[c*16+i]);
// 设置显示位置到下一行
OLED_Set_Pos(x,y+1);
// 写入字符的下半部分数据
for(i=0;i<8;i++)
OLED_WR_DATA(F8X16[c*16+i+8]);
}
else {
// 设置显示位置
OLED_Set_Pos(x,y);
// 写入字符数据
for(i=0;i<6;i++)
OLED_WR_DATA(F6x8[c][i]);
}
}
// 显示一个字符串的函数
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size)
{
unsigned char j=0;
// 循环遍历字符串中的每个字符
while (chr[j]!='\0')
{
// 显示当前字符
OLED_ShowChar(x,y,chr[j],Char_Size);
// 横坐标右移
x+=8;
if(x>120){x=0;y+=2;}
j++;
}
}
// 显示中文字符的函数
// hzk 用取模软件得出的数组
void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no)
{
uint8_t t,adder=0;
// 设置显示位置
OLED_Set_Pos(x,y);
// 写入中文字符的上半部分数据
for(t=0;t<16;t++)
{
OLED_WR_DATA(Hzk[2*no][t]);
adder+=1;
}
// 设置显示位置到下一行
OLED_Set_Pos(x,y+1);
// 写入中文字符的下半部分数据
for(t=0;t<16;t++)
{
OLED_WR_DATA(Hzk[2*no+1][t]);
adder+=1;
}
}
main.c代码解释
#include "main.h"
#include "i2c.h"
#include "gpio.h"
#include "oled.h"
// 未使用的私有类型定义区域(目前为空)
// 可用于定义自定义的数据类型等
// 目前没有实际定义内容
// 未使用的私有宏定义区域(目前为空)
// 可用于定义一些宏,方便代码的修改和复用
// 目前没有实际定义内容
// 未使用的私有变量定义区域(目前为空)
// 可用于定义在main函数中使用的私有变量
// 目前没有实际定义内容
// 系统时钟配置函数声明
void SystemClock_Config(void);
// 主函数,程序的入口点
int main(void)
{
// MCU初始化,包括重置所有外设、初始化Flash接口和Systick
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化GPIO外设
MX_GPIO_Init();
// 初始化I2C1外设
MX_I2C1_Init();
// 初始化OLED屏幕
OLED_Init();
// 清空OLED屏幕
OLED_Clear();
// 在(0, 0)位置显示数字123456,数字位数为6,字体大小为16
OLED_ShowNum(0,0,123456,6,16);
// 在(0, 2)位置显示字符串"Hello World!",字体大小为16
OLED_ShowString(0,2,"Hello World!",16);
// 在(0, 4)位置显示第一个中文字符(根据索引0获取字符数据)
OLED_ShowCHinese(0,4,0);
// 在(16, 4)位置显示第二个中文字符(根据索引1获取字符数据)
OLED_ShowCHinese(16,4,1);
// 在(32, 4)位置显示第三个中文字符(根据索引2获取字符数据)
OLED_ShowCHinese(32,4,2);
// 无限循环,程序的主循环
while (1)
{
// 目前循环内没有实际操作,可添加其他功能代码
}
}
// 系统时钟配置函数
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置RCC振荡器,使用HSE(高速外部时钟)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
// 使能HSE
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
// HSE预分频值为1
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
// 使能HSI(高速内部时钟)
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
// 使能PLL(锁相环)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
// PLL的时钟源为HSE
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
// PLL的倍频系数为9
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
// 配置RCC振荡器,如果配置失败则调用错误处理函数
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// 配置CPU、AHB和APB总线时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
// 系统时钟源为PLL时钟
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
// AHB时钟分频系数为1
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
// APB1时钟分频系数为2
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
// APB2时钟分频系数为1
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
// 配置时钟,如果配置失败则调用错误处理函数
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
// 错误处理函数,在发生错误时被调用
void Error_Handler(void)
{
// 禁用中断
__disable_irq();
// 进入无限循环,使程序停在这里
while (1)
{
}
}
// 断言失败处理函数(用于调试,目前为空实现)
// 当assert_param函数检查到错误参数时调用
// 可用于报告错误发生的源文件名和行号
void assert_failed(uint8_t *file, uint32_t line)
{
// 可添加代码用于报告文件名和行号
// 目前没有实际实现
}
oled.h代码解释
#ifndef __OLED_H__
// 防止头文件被重复包含的宏定义
#define __OLED_H__
#include "stm32f1xx_hal.h"
// 包含STM32F1系列的HAL库头文件,提供硬件抽象层的功能
//ʹÓÃÓ²¼þI2C1
///
#define OLED_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
// 定义宏,用于使能GPIOA的时钟。这里可能存在一个小错误,
// 因为后续使用的是GPIOB相关引脚,而这里使能的是GPIOA的时钟,
// 或许应该是使能GPIOB的时钟(__HAL_RCC_GPIOB_CLK_ENABLE())
#define GPIOx_OLED_PORT GPIOB
// 定义宏,表示OLED相关引脚所在的GPIO端口为GPIOB
#define OLED_SCK_PIN GPIO_PIN_8
// 定义宏,表示OLED的时钟信号(SCK)所连接的引脚为GPIOB的第8号引脚
#define OLED_SCK_ON() HAL_GPIO_WritePin(GPIOx_OLED_PORT, OLED_SCK_PIN, GPIO_PIN_SET)
// 定义宏,将OLED的SCK引脚设置为高电平
#define OLED_SCK_OFF() HAL_GPIO_WritePin(GPIOx_OLED_PORT, OLED_SCK_PIN, GPIO_PIN_RESET)
// 定义宏,将OLED的SCK引脚设置为低电平
#define OLED_SCK_TOGGLE() HAL_GPIO_TogglePin(GPIOx_OLED_PORT, OLED_SCK_PIN)
// 定义宏,翻转OLED的SCK引脚电平
#define OLED_SDA_PIN GPIO_PIN_9
// 定义宏,表示OLED的数据信号(SDA)所连接的引脚为GPIOB的第9号引脚
#define OLED_SDA_ON() HAL_GPIO_WritePin(GPIOx_OLED_PORT, OLED_SDA_PIN, GPIO_PIN_SET)
// 定义宏,将OLED的SDA引脚设置为高电平
#define OLED_SDA_OFF() HAL_GPIO_WritePin(GPIOx_OLED_PORT, OLED_SDA_PIN, GPIO_PIN_RESET)
// 定义宏,将OLED的SDA引脚设置为低电平
#define OLED_SDA_TOGGLE() HAL_GPIO_TogglePin(GPIOx_OLED_PORT, OLED_SDA_PIN)
// 定义宏,翻转OLED的SDA引脚电平
///
// 函数声明:向OLED写入初始化命令的函数
void WriteCmd(void);
// 函数声明:向OLED设备写控制命令的函数
void OLED_WR_CMD(uint8_t cmd);
// 函数声明:向OLED设备写数据的函数
void OLED_WR_DATA(uint8_t data);
// 函数声明:初始化OLED屏幕的函数
void OLED_Init(void);
// 函数声明:清屏函数
void OLED_Clear(void);
// 函数声明:开启OLED显示的函数
void OLED_Display_On(void);
// 函数声明:关闭OLED显示的函数
void OLED_Display_Off(void);
// 函数声明:设置OLED显示位置的函数
void OLED_Set_Pos(uint8_t x, uint8_t y);
// 函数声明:点亮OLED屏幕所有像素的函数
void OLED_On(void);
// 函数声明:在OLED上显示数字的函数
void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2);
// 函数声明:在指定位置显示一个字符的函数
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size);
// 函数声明:显示一个字符串的函数
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size);
// 函数声明:显示中文字符的函数
void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no);
#endif
oledfont.h代码解释
#ifndef __OLEDFONT_H__
#define __OLEDFONT_H__
#include "stm32f1xx_hal.h"
// 部分汉字点阵数据,每个汉字由32个字节表示,用于在OLED等设备上显示汉字
// 每个汉字的点阵数据分为上下两部分,各16个字节,组合起来形成16x16的点阵
const unsigned char Hzk[][32]=
{
// 汉字“角”的点阵数据
{
// 上半部分点阵数据,每一个字节的8位对应点阵的一行中的8个点
0x20,0x10,0xE8,0x24,0x27,0x24,0x24,0xE4,0x24,0x34,0x2C,0x20,0xE0,0x00,0x00,0x00,
// 下半部分点阵数据
0x80,0x60,0x1F,0x09,0x09,0x09,0x09,0x7F,0x09,0x09,0x49,0x89,0x7F,0x00,0x00,0x00
}, /*"角",0*/
/* (16 X 16 , 宋体 )*/
// 汉字“度”的点阵数据
{
0x00,0x00,0xFC,0x24,0x24,0x24,0xFC,0x25,0x26,0x24,0xFC,0x24,0x24,0x24,0x04,0x00,
0x40,0x30,0x8F,0x80,0x84,0x4C,0x55,0x25,0x25,0x25,0x55,0x4C,0x80,0x80,0x80,0x00
}, /*"度",1*/
/* (16 X 16 , 宋体 )*/
// 汉字“值”的点阵数据
{
0x00,0x80,0x60,0xF8,0x07,0x04,0xE4,0xA4,0xA4,0xBF,0xA4,0xA4,0xE4,0x04,0x00,0x00,
0x01,0x00,0x00,0xFF,0x40,0x40,0x7F,0x4A,0x4A,0x4A,0x4A,0x4A,0x7F,0x40,0x40,0x00
}, /*"值",2*/
/* (16 X 16 , 宋体 )*/
// 汉字“电”的点阵数据
{
0x00,0x00,0xF8,0x88,0x88,0x88,0x88,0xFF,0x88,0x88,0x88,0x88,0xF8,0x00,0x00,0x00,
0x00,0x00,0x1F,0x08,0x08,0x08,0x08,0x7F,0x88,0x88,0x88,0x88,0x9F,0x80,0xF0,0x00
}, /*"电",3*/
/* (16 X 16 , 宋体 )*/
// 汉字“压”的点阵数据
{
0x00,0x00,0xFE,0x02,0x82,0x82,0x82,0x82,0xFA,0x82,0x82,0x82,0x82,0x82,0x02,0x00,
0x80,0x60,0x1F,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x44,0x58,0x40,0x40,0x00
}, /*"压",4*/
/* (16 X 16 , 宋体 )*/
// 汉字“值”的点阵数据(这里重复定义了“值”,可能是为了方便调用)
{
0x00,0x80,0x60,0xF8,0x07,0x04,0xE4,0xA4,0xA4,0xBF,0xA4,0xA4,0xE4,0x04,0x00,0x00,
0x01,0x00,0x00,0xFF,0x40,0x40,0x7F,0x4A,0x4A,0x4A,0x4A,0x4A,0x7F,0x40,0x40,0x00
}, /*"值",5*/
/* (16 X 16 , 宋体 )*/
// 字符“:”的点阵数据
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}, /*":",6*/
/* (16 X 16 , 宋体 )*/
};
#endif