STC8 单片机驱动 I2C 屏幕:实现时间、日期与温湿度显示
在单片机项目中,“数据可视化” 是核心需求之一 —— 将时间、温湿度等关键信息实时显示在屏幕上,能让项目更具实用性。本文以STC8 系列单片机为核心,搭配 I2C 接口的 OLED 屏幕、RTC 实时时钟模块和温湿度传感器,手把手教你实现 “时间 + 日期 + 温湿度” 的一体化显示功能,从硬件选型到代码调试全程覆盖,新手也能轻松上手。
一、项目核心硬件清单(附选型理由)
STC8 系列单片机无硬件 I2C 外设,需通过软件模拟 I2C驱动外设,因此选择 I2C 接口的模块可减少引脚占用,简化接线。以下是经过实测的稳定硬件组合:
模块 | 推荐型号 | 关键参数 | 通信方式 | 选型理由 |
---|---|---|---|---|
主控单片机 | STC8A8K64U | 8051 内核,64KB Flash | - | 性价比高,IO 口充足,支持 5V/3.3V 供电 |
I2C 显示屏幕 | 128x64 OLED(SSD1306) | 0.96 英寸,对比度可调 | I2C | 功耗低、显示清晰,I2C 仅需 2 根线 |
实时时钟(RTC) | PCF8563 | 精度 ±2ppm,支持掉电走时 | I2C | 无需单片机计时,时间稳定性远超软件延时 |
温湿度传感器 | DHT11(入门)/SHT30(进阶) | DHT11:±2℃精度;SHT30:±0.3℃ | 单总线 / I2C | DHT11 接线简单,SHT30 精度更高 |
辅助元件 | 4.7kΩ 上拉电阻(2 个) | - | - | I2C 总线强制要求,保证通信稳定 |
二、硬件接线图(关键!避免接错)
STC8 的 I2C 引脚需自定义(软件模拟),建议优先选择 P2 口(电平稳定),接线时需注意 “共地”—— 所有模块的 GND 必须连接到单片机的 GND,否则会因电平紊乱导致通信失败。
![在这里插入图片描述]Alt
三、软件核心逻辑:从驱动到显示
项目软件分为 “底层驱动” 和 “上层显示逻辑” 两部分,底层驱动是基础,需先确保各外设能正常通信,再整合数据显示。以下代码基于 Keil C51 开发环境编写,关键函数已标注注释。
1. 第一步:实现通用 I2C 驱动(复用核心)
STC8 无硬件 I2C,需用软件模拟 I2C 时序(起始、停止、发送、接收),该驱动可同时供 OLED 和 DS3231 使用,避免重复代码。
代码文件:I2C.h
(头文件,定义引脚和函数声明)
#ifndef __I2C_H
#define __I2C_H
#include "config.h"
//========================================================================
// I2C设置
//========================================================================
#define I2C_Function(n) (n==0?(I2CCFG &= ~0x80):(I2CCFG |= 0x80)) //0:禁止 I2C 功能;1:使能 I2C 功能
#define I2C_ENABLE() I2CCFG |= 0x80 /* 使能 I2C 功能 */
#define I2C_DISABLE() I2CCFG &= ~0x80 /* 禁止 I2C 功能 */
#define I2C_Master() I2CCFG |= 0x40 /* 1: 设为主机 */
#define I2C_Slave() I2CCFG &= ~0x40 /* 0: 设为从机 */
#define I2C_SetSpeed(n) I2CCFG = (I2CCFG & ~0x3f) | (n & 0x3f) /* 总线速度=Fosc/2/(Speed*2+4) */
#define I2C_WDTA_EN() I2CMSAUX |= 0x01 /* 使能自动发送 */
#define I2C_WDTA_DIS() I2CMSAUX &= ~0x01 /* 禁止自动发送 */
#define I2C_ESTAI_EN(n) I2CSLCR = (I2CSLCR & ~0x40) | (n << 6) /* 使能从机接收START信号中断 */
#define I2C_ERXI_EN(n) I2CSLCR = (I2CSLCR & ~0x20) | (n << 5) /* 使能从机接收1字节数据中断 */
#define I2C_ETXI_EN(n) I2CSLCR = (I2CSLCR & ~0x10) | (n << 4) /* 使能从机发送1字节数据中断 */
#define I2C_ESTOI_EN(n) I2CSLCR = (I2CSLCR & ~0x08) | (n << 3) /* 使能从机接收STOP信号中断 */
#define I2C_SLRET() I2CSLCR |= 0x01 /* 复位从机模式 */
#define I2C_Address(n) I2CSLADR = (I2CSLADR & 0x01) | (n << 1) /* 从机地址 */
#define I2C_MATCH_EN() I2CSLADR &= ~0x01 /* 使能从机地址比较功能,只接受相匹配地址 */
#define I2C_MATCH_DIS() I2CSLADR |= 0x01 /* 禁止从机地址比较功能,接受所有设备地址 */
//========================================================================
// 定义声明
//========================================================================
#define DEV_ADDR 0xA0 //从机设备写地址
#define I2C_BUF_LENTH 8
#define I2C_ESTAI 0x40 /* 从机接收START信号中断 */
#define I2C_ERXI 0x20 /* 从机接收1字节数据中断 */
#define I2C_ETXI 0x10 /* 从机发送1字节数据中断 */
#define I2C_ESTOI 0x08 /* 从机接收STOP信号中断 */
typedef struct
{
u8 I2C_Speed; //总线速度=Fosc/2/(Speed*2+4), 0~63
u8 I2C_Enable; //I2C功能使能, ENABLE, DISABLE
u8 I2C_Mode; //主从模式选择, I2C_Mode_Master,I2C_Mode_Slave
u8 I2C_MS_WDTA; //主机使能自动发送, ENABLE, DISABLE
u8 I2C_SL_ADR; //从机设备地址, 0~127
u8 I2C_SL_MA; //从机设备地址比较使能, ENABLE, DISABLE
} I2C_InitTypeDef;
typedef struct
{
u8 isma; //MEMORY ADDRESS 接收判断标志
u8 isda; //DEVICE ADDRESS 接收判断标志
u8 addr; //ADDRESS 缓存
} I2C_IsrTypeDef;
extern u8 xdata I2C_Buffer[I2C_BUF_LENTH];
extern bit DisplayFlag;
void I2C_Init(I2C_InitTypeDef *I2Cx);
void I2C_WriteNbyte(u8 dev_addr, u8 mem_addr, u8 *p, u8 number);
void I2C_ReadNbyte(u8 dev_addr, u8 mem_addr, u8 *p, u8 number);
u8 Get_MSBusy_Status(void);
void SendCmdData(u8 cmd, u8 dat);
#endif
代码文件:I2C.c
(驱动实现,关键时序)
#include "I2C.h"
u8 xdata I2C_Buffer[I2C_BUF_LENTH];
//========================================================================
// 函数: void I2C_Init(I2C_InitTypeDef *I2Cx)
// 描述: I2C初始化程序.
// 参数: I2Cx: 结构参数,请参考I2C.h里的定义.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void I2C_Init(I2C_InitTypeDef *I2Cx)
{
if(I2Cx->I2C_Mode == I2C_Mode_Master)
{
I2C_Master(); //设为主机
I2CMSST = 0x00; //清除I2C主机状态寄存器
I2C_SetSpeed(I2Cx->I2C_Speed);
if(I2Cx->I2C_MS_WDTA == ENABLE) I2C_WDTA_EN(); //使能自动发送
else I2C_WDTA_DIS(); //禁止自动发送
}
else
{
I2C_Slave(); //设为从机
I2CSLST = 0x00; //清除I2C从机状态寄存器
I2C_Address(I2Cx->I2C_SL_ADR);
if(I2Cx->I2C_SL_MA == ENABLE) I2C_MATCH_EN(); //从机地址比较功能,只接受相匹配地址
else I2C_MATCH_DIS(); //禁止从机地址比较功能,接受所有设备地址
}
I2C_Function(I2Cx->I2C_Enable);
}
//========================================================================
// 函数: u8 Get_MSBusy_Status (void)
// 描述: 获取主机忙碌状态.
// 参数: none.
// 返回: 主机忙碌状态.
// 版本: V1.0, 2012-11-22
//========================================================================
u8 Get_MSBusy_Status(void)
{
return (I2CMSST & 0x80);
}
//========================================================================
// 函数: void Wait (void)
// 描述: 等待主机模式I2C控制器执行完成I2CMSCR.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void Wait()
{
while (!(I2CMSST & 0x40));
I2CMSST &= ~0x40;
}
//========================================================================
// 函数: void Start (void)
// 描述: I2C总线起始函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void Start()
{
I2CMSCR = 0x01; //发送START命令
Wait();
}
//========================================================================
// 函数: void SendData (char dat)
// 描述: I2C发送一个字节数据函数.
// 参数: 发送的数据.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void SendData(char dat)
{
I2CTXD = dat; //写数据到数据缓冲区
I2CMSCR = 0x02; //发送SEND命令
Wait();
}
//========================================================================
// 函数: void RecvACK (void)
// 描述: I2C获取ACK函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void RecvACK()
{
I2CMSCR = 0x03; //发送读ACK命令
Wait();
}
//========================================================================
// 函数: char RecvData (void)
// 描述: I2C读取一个字节数据函数.
// 参数: none.
// 返回: 读取数据.
// 版本: V1.0, 2020-09-15
//========================================================================
char RecvData()
{
I2CMSCR = 0x04; //发送RECV命令
Wait();
return I2CRXD;
}
//========================================================================
// 函数: void SendACK (void)
// 描述: I2C发送ACK函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void SendACK()
{
I2CMSST = 0x00; //设置ACK信号
I2CMSCR = 0x05; //发送ACK命令
Wait();
}
//========================================================================
// 函数: void SendNAK (void)
// 描述: I2C发送NAK函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void SendNAK()
{
I2CMSST = 0x01; //设置NAK信号
I2CMSCR = 0x05; //发送ACK命令
Wait();
}
//========================================================================
// 函数: void Stop (void)
// 描述: I2C总线停止函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void Stop()
{
I2CMSCR = 0x06; //发送STOP命令
Wait();
}
//========================================================================
// 函数: void SendCmdData (u8 cmd, u8 dat)
// 描述: I2C发送一个字节数据函数.
// 参数: 命令/数据.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void SendCmdData(u8 cmd, u8 dat)
{
I2CTXD = dat; //写数据到数据缓冲区
I2CMSCR = cmd; //设置命令
Wait();
}
//========================================================================
// 函数: void I2C_WriteNbyte(u8 dev_addr, u8 mem_addr, u8 *p, u8 number)
// 描述: I2C写入数据函数.
// 参数: dev_addr: 设备地址, mem_addr: 存储地址, *p写入数据存储位置, number写入数据个数.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void I2C_WriteNbyte(u8 dev_addr, u8 mem_addr, u8 *p, u8 number) /* DeviceAddress,WordAddress,First Data Address,Byte lenth */
{
Start(); //发送起始命令
SendData(dev_addr); //发送设备地址+写命令
RecvACK();
SendData(mem_addr); //发送存储地址
RecvACK();
do
{
SendData(*p++);
RecvACK();
}
while(--number);
Stop(); //发送停止命令
}
//========================================================================
// 函数: void I2C_ReadNbyte(u8 dev_addr, u8 mem_addr, u8 *p, u8 number)
// 描述: I2C读取数据函数.
// 参数: dev_addr: 设备地址, mem_addr: 存储地址, *p读取数据存储位置, number读取数据个数.
// 返回: none.
// 版本: V1.0, 2020-09-15
//========================================================================
void I2C_ReadNbyte(u8 dev_addr, u8 mem_addr, u8 *p, u8 number) /* DeviceAddress,WordAddress,First Data Address,Byte lenth */
{
Start(); //发送起始命令
SendData(dev_addr); //发送设备地址+写命令
RecvACK();
SendData(mem_addr); //发送存储地址
RecvACK();
Start(); //发送起始命令
SendData(dev_addr|1); //发送设备地址+读命令
RecvACK();
do
{
*p = RecvData();
p++;
if(number != 1) SendACK(); //send ACK
}
while(--number);
SendNAK(); //send no ACK
Stop(); //发送停止命令
}
2. 第二步:驱动关键外设(OLED+PCF8563+DHT11)
(1)OLED 屏幕驱动(PCF8563.h)
实现屏幕初始化、显示字符串 / 数字等基础功能,重点是 “固定显示区域”(避免全屏刷新导致闪烁)。
代码文件:PCF8563.h
(关键函数声明)
#ifndef __PCF8563_H__
#define __PCF8563_H__
#include "GPIO.h"
#include "NVIC.h"
#include "Switch.h"
#include "I2C.h"
// 设备地址
#define PCF8563_DEV_ADDR 0xa2
// 存储地址(寄存器地址): 时间(秒)存储地址
#define PCF8563_REG_SECOND 0x02
// 10进制数转BCD数:十位取出左移4位 + 个位 (得到BCD数)
#define WRITE_BCD(val) ((val / 10) << 4) + (val % 10)
// BCD数转10进制数:将高4位乘以10 + 低四位 (得到10进制数)
#define READ_BCD(val) (val >> 4) * 10 + (val & 0x0F)
// ======================时间日期
typedef struct {
u16 year;
u8 month;
u8 day;
u8 weekday;
u8 hour;
u8 minute;
u8 second;
} Clock_t;
// PCF8563初始化
void PCF8563_init();
// 设置时间
void PCF8563_set_clock(Clock_t temp);
// 获取时间
void PCF8563_get_clock(Clock_t *temp);
//=============================闹钟
typedef struct {
// 设置分\时\天\周,如果为-1,禁用此项
char minute ;
char hour ;
char day ;
char weekday;
} Alarm_t;
// 设置闹钟
void PCF8563_set_alarm(Alarm_t alarm);
// 启用闹钟
void PCF8563_enable_alarm();
// 禁用闹钟Alarm
void PCF8563_disable_alarm();
// 清理闹钟标记
void PCF8563_alarm_clear_flag();
//=============================定时器
// 国产芯片的HZ1有问题,不要使用,建议使用HZ64
typedef enum { HZ4096 = 0, HZ64 = 1, HZ1 = 2, HZ1_60 = 3} TimerFreq;
// 启动定时器
void PCF8563_enable_timer();
// 禁用定时器
void PCF8563_disable_timer();
// 清除定时器标志位
void PCF8563_clear_timer();
// 设置定时器,参数1:时钟频率 参数2:倒计时计算值,时间为:参数2/参数1
void PCF8563_set_timer(TimerFreq freq, u8 countdown);
// 闹钟中断处理函数,需要用户定义此函数
void PCF8563_on_alarm();
// 定时器中断处理函数,需要用户定义此函数
void PCF8563_on_timer();
#endif
(2)PCF8563RTC 驱动(获取时间 / 日期)
PCF8563的时间 / 日期存储在 0x00-0x06 寄存器中,需通过 I2C 读写这些寄存器,注意 “BCD 码转换”(寄存器值为 BCD 码,需转为十进制才能显示)。
代码片段:PCF8563.c
(读取时间核心函数)
#include "PCF8563.h"
// GPIO
static void GPIO_config(void) {
GPIO_InitTypeDef GPIO_InitStructure; //结构定义
// P32 P33 开漏输出
GPIO_InitStructure.Pin = GPIO_Pin_2 | GPIO_Pin_3; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_OUT_OD; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
// INT3: P37 准双向口
GPIO_InitStructure.Pin = GPIO_Pin_7; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
}
/**************** I2C初始化函数 *****************/
static void I2C_config(void)
{
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_Master; //主从选择 I2C_Mode_Master, I2C_Mode_Slave
I2C_InitStructure.I2C_Enable = ENABLE; //I2C功能使能, ENABLE, DISABLE
I2C_InitStructure.I2C_MS_WDTA = DISABLE; //主机使能自动发送, ENABLE, DISABLE
I2C_InitStructure.I2C_Speed = 13; //总线速度=Fosc/2/(Speed*2+4), 0~63
// 400k, 24M => 13
/*
总线速度=Fosc/2/(Speed*2+4), 0~63
400 k = =24 M/2/(Speed*2+4)
400 000 = 24 000 000 / 2 / (Speed*2+4)
*/
I2C_Init(&I2C_InitStructure);
NVIC_I2C_Init(I2C_Mode_Master,DISABLE,Priority_0); //主从模式, I2C_Mode_Master, I2C_Mode_Slave; 中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
I2C_SW(I2C_P33_P32); //I2C_P14_P15,I2C_P24_P25,I2C_P33_P32
}
// PCF8563初始化
void PCF8563_init() {
// 一定要
EAXSFR(); /* 扩展寄存器访问使能 */
GPIO_config();
I2C_config();
}
// 设置时间
void PCF8563_set_clock(Clock_t temp) {
u8 p[7], C;
// 8 4 2 1 8421码
// 7 6 5 4 3 2 1 0 位置,从右往左算
// y y y y x x x x 内容占位
// 秒的寄存器地址为: 0x02
// 秒p[0]: 第0~3位记录个位,第4~6位记录十位
p[0] = WRITE_BCD(temp.second);
// 分p[1]: 第0~3位,保存个数,第4到6位,保存十位
p[1] = WRITE_BCD(temp.minute);
// 时p[2]:第0~3位,保存个数,第4到5位,保存十位
p[2] = WRITE_BCD(temp.hour);
// 日p[3]:第0~3位,保存个数,第4到5位,保存十位
p[3] = WRITE_BCD(temp.day);
// 周p[4]:第0~2位,保存个数
p[4] = temp.weekday;
// 月_世纪p[5]: 第0~3位记录个位,第4位记录十位,第7位为0,世纪数为20xx,为1,世纪数为21xx
C = temp.year >= 2100 ? 1 : 0; // 第7位为0,世纪数为20xx,为1,世纪数为21xx
p[5] = WRITE_BCD(temp.month) + (C << 7);
// 年p[6]:第0~3位,保存个数,第4到7位,保存十位
// 2024 取出 24 中 2 和 4
// p[6] = (year % 10) + ((year % 100 / 10) << 4);
p[6] = WRITE_BCD(temp.year % 100);
// 写
// I2C_WriteNbyte(0xa2, 0x02, &p[0], 7);
I2C_WriteNbyte(PCF8563_DEV_ADDR, PCF8563_REG_SECOND, p, 7);
}
// 获取时间
void PCF8563_get_clock(Clock_t *temp) {
u8 p[7], C;
// 读
I2C_ReadNbyte(PCF8563_DEV_ADDR, PCF8563_REG_SECOND, p, 7);
// 8 4 2 1 8421码
// 7 6 5 4 3 2 1 0 位置,从右往左算
// y y y y x x x x 内容占位
// 秒的寄存器地址为: 0x02
// 秒p[0]: 第0~3位记录个位,第4~6位记录十位
temp->second = READ_BCD(p[0]);
// 分p[1]: 第0~3位,保存个数,第4到6位,保存十位
temp->minute = READ_BCD(p[1]);
// 时p[2]:第0~3位,保存个数,第4到5位,保存十位
temp->hour = READ_BCD(p[2]);
// 日p[3]:第0~3位,保存个数,第4到5位,保存十位
temp->day = READ_BCD(p[3]);
// 周p[4]:第0~2位,保存个数
temp->weekday = p[4];
// 月_世纪p[5] C: 第0~3位记录个位,第4位记录十位,第7位为0,世纪数为20xx,为1,世纪数为21xx
// 取出最高位
C = p[5] >> 7;
// p[5] 第7位置0
p[5] &= ~(1 << 7);
temp->month = READ_BCD(p[5]);
// 年p[6]:第0~3位,保存个数,第4到7位,保存十位
temp->year = READ_BCD(p[6]) + (C == 0 ? 2000 : 2100);
}
//=============================闹钟
// 设置闹钟
void PCF8563_set_alarm(Alarm_t alarm) {
u8 p[4];
// 默认第7位为0,默认启动的
// 分p[0]: 第0~3位,记录个数, 第4~6位记录十位, 第7位:置0启动, 置1禁用
if (alarm.minute == -1) {
p[0] = (1 << 7); // 禁用 1 << 7 ===> 0x80
} else {
p[0] = WRITE_BCD(alarm.minute);
}
// 时p[1]: 第0~3位,记录个数, 第4~5位记录十位, 第7位:置0启动, 置1禁用
p[1] = alarm.hour == -1 ? 0x80 : WRITE_BCD(alarm.hour);
// 日p[2]: 第0~3位,记录个数, 第4~5位记录十位, 第7位:置0启动, 置1禁用
p[2] = alarm.day == -1 ? 0x80 : WRITE_BCD(alarm.day);
// 周p[3]: 第0~2位,记录个数, 第7位:置0启动, 置1禁用
p[3] = alarm.weekday == -1 ? 0x80 : alarm.weekday ;
// 写数据
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x09, p, 4);
}
// 启用闹钟
void PCF8563_enable_alarm() {
u8 cfg;
//===================2.2 闹钟开启 寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(0xa2, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位 第3位:置0清除标志位,置1维持不变
cfg &= ~(1 << 3);
//c) 在原来配置基础上,启动闹钟,第1位:置0禁用,置1启动
cfg |= (1 << 1); // 置1启动
//d) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 禁用闹钟Alarm
void PCF8563_disable_alarm() {
u8 cfg;
//===================2.2 闹钟开启 寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(0xa2, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位 第3位:置0清除标志位,置1维持不变
cfg &= ~(1 << 3);
//c) 在原来配置基础上,启动闹钟,第1位:置0禁用,置1启动
cfg &= ~(1 << 1); // 置0禁用
//d) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 清理闹钟标记
void PCF8563_alarm_clear_flag() {
u8 cfg;
//===================寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位 第3位:置0清除标志位,置1维持不变
cfg &= ~(1 << 3);
//c) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
//=============================定时器
// 启动定时器
void PCF8563_enable_timer() {
u8 cfg;
//============2 定时器开启 寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位,第2位:置0清除标志位,置1维持不变
cfg &= ~(1 << 2);
//c) 在原来配置基础上,启动定时器,第0位:置0禁用,置1启用
cfg |= (1 << 0); // 第0位:置1启用
//d) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 禁用定时器
void PCF8563_disable_timer() {
u8 cfg;
//============2 定时器 寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位,第2位:置0清除标志位,置1维持不变
cfg &= ~(1 << 2);
//c) 在原来配置基础上,启动定时器,第0位:置0禁用,置1启用
cfg &= ~(1 << 0); // 第0位:置0禁用
//d) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 清除定时器标志位
void PCF8563_clear_timer() {
u8 cfg;
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位,第2位:置0清除标志位,置1维持不变
cfg &= ~(1 << 2);
//c) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 设置定时器,参数1:时钟频率 参数2:倒计时计算值,时间为:参数2/参数1
void PCF8563_set_timer(TimerFreq freq, u8 countdown) {
u8 p[2];
//============1 定时器设置 寄存器地址 0x0e
//a) 时钟频率
// 0x00: 4.096 khz 0x01: 64 hz 0x02: 1hz(咱们芯片用不了) 0x03: 1/60 hz
// 第7位为0,定时器禁用,第七位为1,定时器启用
p[0] = freq + (1 << 7);
//b) 计数值(0~255) ===》时间为: 计数值/时钟频率
p[1] = countdown;
// 写
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x0e, p, 2);
}
// INT3 中断回调函数
void exti_int3_call() {
u8 cfg;
//寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(0xa2, 0x01, &cfg, 1);
// 闹钟第1位和第3位为1,说明是闹钟触发了中断
if ((cfg & 0x02) && (cfg & 0x08)) {
PCF8563_on_alarm(); // 调用
// 清除标志位后,闹钟,才能重复使用
PCF8563_alarm_clear_flag();
}
if ((cfg & 0x01) && (cfg & 0x04)) { // 定时器第0位和第2位为1,说明是定时器触发了中断
PCF8563_on_timer(); // 调用
//============定时器清除标志位,才能重复 寄存器地址 0x01
PCF8563_clear_timer();
}
}
(3)DHT11 温湿度驱动(单总线)
DHT11 对时序要求严格,需精确控制 “拉低总线→等待响应→读取 40 位数据” 的过程,建议用_nop_()
微调延时。
代码片段:DHT11.h
(读取温湿度)
注意:由于DHT11.c里面有使用printf,所以,main函数一定要配置串口打印
#ifndef __DHT11_H__
#define __DHT11_H__
#include "GPIO.h"
#include "delay.h"
// 注意:由于DHT11.c里面有使用printf,所以,main函数一定要配置串口打印
#define DHT P46 // DHT11引脚
void DHT11_init();
// 返回-1:获取数据失败, 返回0:获取数据成功
char DHT11_get_humidity_temperature(int *humidity, float *temperature);
#endif
代码片段:DHT11.c
(读取温湿度)
#include "DHT11.h"
#define wait_level_change(level, min, max, desc) { cnt = 0; \
do { cnt++; Delay1us(); } while (DHT == level); \
if (cnt < min || cnt > max) { printf("%s err cnt = %d\n", desc, (int)cnt); return -1;} }
// XDATA内存模型 NOP5() DATA内存模型 NOP10()
void Delay1us(void) {
NOP5();
}
// GPIO
static void GPIO_config(void) {
GPIO_InitTypeDef GPIO_InitStructure; //结构定义
// P46
GPIO_InitStructure.Pin = GPIO_Pin_6; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P4, &GPIO_InitStructure);//初始化
}
void DHT11_init() {
GPIO_config();
}
// 返回-1:获取数据失败, 返回0:获取数据成功
char DHT11_get_humidity_temperature(int *humidity, float *temperature) {
u16 cnt = 0;
u8 num = 0;
char i, j;
u8 dat[5] = {0};
// printf("start\n");
// 1. 主机(stc8h) 拉低 18ms ~ 30ms
DHT = 0;
delay_ms(20);
DHT = 1;
// 2. 主机释放总时间 10us ~ 35us
cnt = 0;
do {
cnt++;
Delay1us();
} while(DHT == 1 && cnt < 50);
if (cnt < 10 || cnt > 35) {
printf("%s err cnt = %d\n", "主机释放总时间", (int)cnt);
return -1;
}
// 3. 响应低电平时间 78us ~ 88us
wait_level_change(0, 78, 88, "响应低电平时间");
// 4. 响应高电平时间 80us ~ 92us
wait_level_change(1, 80, 92, "响应高电平时间");
// 5. 收到主机起始信号后,传感器一次性从数据总线(SDA)串出40位数据,高位先出
// 40位数据,1字节8位,需要5个字节
for (i = 0; i < 5; i++) {
for (j = 0; j < 8; j++) {
// 5.1 信号低电平时间 50us ~ 58us
wait_level_change(0, 45, 58, "信号低电平时间");
// 5.2 真正的0、1数据 23us ~74 us
// 信号0 高电平时间 23us ~ 27us
// 信号1 高电平时间 68us ~ 74us
// wait_level_change(1, 23, 74, "真正的数据");
cnt = 0;
do {
Delay1us();
cnt++;
} while (DHT == 1);
dat[i] <<= 1;
if (cnt >= 68) {
dat[i] |= 0x01;
}
}
}
// 6. 数据校验
// 湿度高 8 位 湿度低 8 位 温度高 8 位 温度低 8 位 校验位
// dat[0] dat[1] dat[2] dat[3] dat[4]
// 6.1 校验位=湿度高位+湿度低位+温度高位+温度低位
if (dat[4] != (dat[0] + dat[1] + dat[2] + dat[3] )) {
printf("校验失败\n");
return - 1;
}
// 6.2 湿度高位为湿度整数部分数据,湿度低位为湿度小数部分数据,其中湿度小数部分为 0
*humidity = (int)dat[0];
//printf("湿度: %d %%\n", (int)dat[0]);
// 6.3 温度高位为温度整数部分数据,温度低位为温度小数部分数据(1位小数点)
// 且温度低位 Bit8 (最高位) 为 1 则表示负温度,否则为正温度
*temperature = dat[2] + (dat[3] & 0x7f) * 0.1;
if (dat[3] & 0x80) {
*temperature = -(*temperature);
}
//printf("温度: %.1f °\n", t);
return 0;
}
3. 第三步:主函数整合(数据采集 + 显示)
主函数逻辑很简单:初始化外设→循环采集数据→格式化显示,重点是 “定时刷新”(1 秒刷新一次,避免屏幕闪烁)。
代码文件:main.c
(核心逻辑)
#include "config.h"
#include "oled.h"
#include "bmp.h"
#include "GPIO.h"
#include "UART.h"
#include "NVIC.h"
#include "Switch.h"
#include "Delay.h"
#include "PCF8563.h"
#include "Exti.h"
#include "DHT11.h" // 注意:由于DHT11.c里面有使用printf,所以,main函数一定要配置串口打印
// GPIO
void GPIO_config(void) {
GPIO_InitTypeDef GPIO_InitStructure; //结构定义
// UART1: P30 P31 准双向口
GPIO_InitStructure.Pin = GPIO_Pin_0 | GPIO_Pin_1; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
// P32 P33 开漏输出
P3_MODE_OUT_OD(GPIO_Pin_2 | GPIO_Pin_3);
}
// 串口配置的函数定义
void UART_config(void) {
// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<
// 结构体类型 变量
COMx_InitDefine a; //结构定义
a.UART_Mode = UART_8bit_BRTx; //模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
a.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
a.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200
a.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLE
a.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLE
UART_Configuration(UART1, &a); //初始化串口1 UART1,UART2,UART3,UART4
NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
UART1_SW(UART1_SW_P30_P31); // 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}
/******************** INT配置 ********************/
void Exti_config(void)
{
EXTI_InitTypeDef Exti_InitStructure; //结构定义
// INT3
Exti_InitStructure.EXTI_Mode = EXT_MODE_RiseFall;//中断模式, EXT_MODE_RiseFall,EXT_MODE_Fall
Ext_Inilize(EXT_INT3,&Exti_InitStructure); //初始化
NVIC_INT3_Init(ENABLE,Priority_0); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}
// PCF8563_on_alarm和PCF8563_on_timer一定要定义,除非把函数调用注释
// 闹钟中断处理函数
void PCF8563_on_alarm() {
}
// 定时器中断处理函数
void PCF8563_on_timer() {
}
void main() {
Clock_t clk;
u8 buf[30];
char res;
float t;
int h;
// PCF8563初始化
PCF8563_init();
// 调用函数
GPIO_config();
UART_config();
Exti_config(); // 中断
DHT11_init();
OLED_Init();//初始化OLED
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
// 串口,要使用到中断,需要打开开关、
EA = 1;
//==================================写时间日期
clk.year = 2025;
clk.month = 4;
clk.day = 21;
// 0 星期天 1 星期一 6 星期六
clk.weekday = 0; // 星期几 (0~6范围)
clk.hour = 23;
clk.minute = 58;
clk.second = 55;
PCF8563_set_clock(clk); // 设置时间
while(1) {
PCF8563_get_clock(&clk); // 获取时间
sprintf(buf, "date: %02d-%02d-%02d", (int)clk.year, (int)clk.month, (int)clk.day);
OLED_ShowString(0, 0, buf, 16);
sprintf(buf, "time: %02d:%02d:%02d", (int)clk.hour, (int)clk.minute, (int)clk.second);
OLED_ShowString(0, 2, buf, 16);
res = DHT11_get_humidity_temperature(&h, &t);
if (res == 0) {
sprintf(buf, "temp: %.1f", t);
OLED_ShowString(0, 4, buf, 16);
sprintf(buf, "humidity: %d", h);
OLED_ShowString(0, 6, buf, 16);
}
delay_ms(250);
delay_ms(250);
delay_ms(250);
delay_ms(250);
}
}
oled.h 代码文件时我买i2c屏幕中的赠送的库函数
#ifndef __OLED_H
#define __OLED_H
#include "config.h"
//#define u8 unsigned char // 记得注释,不然,后面串口打印有问题
//#define u16 unsigned int
//#define u32 unsigned int
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
sbit OLED_SCL=P3^2;//SCL
sbit OLED_SDA=P3^3;//SDA
sbit OLED_RES =P1^2;//RES
//-----------------OLED端口定义----------------
#define OLED_SCL_Clr() OLED_SCL=0
#define OLED_SCL_Set() OLED_SCL=1
#define OLED_SDA_Clr() OLED_SDA=0
#define OLED_SDA_Set() OLED_SDA=1
#define OLED_RES_Clr() OLED_RES=0
#define OLED_RES_Set() OLED_RES=1
////OLED控制用函数
//void delay_ms(unsigned int ms);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
void OLED_WR_Byte(u8 dat,u8 cmd);
void OLED_Set_Pos(u8 x, u8 y);
void OLED_Display_On(void);
void OLED_Display_Off(void);
void OLED_Clear(void);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey);
u32 oled_pow(u8 m,u8 n);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey);
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 sizey);
void OLED_ShowChinese(u8 x,u8 y,u8 no,u8 sizey);
void OLED_DrawBMP(u8 x,u8 y,u8 sizex, u8 sizey,u8 BMP[]);
void OLED_Init(void);
#endif
oled.c 代码文件时我买i2c屏幕中的赠送的库函数
#include "oled.h"
#include "oledfont.h"
#include "Delay.h"
#include "I2C.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
//void delay_ms(unsigned int ms)
//{
// unsigned int a;
// while(ms)
// {
// a=1800;
// while(a--);
// ms--;
// }
// return;
//}
//反显函数
void OLED_ColorTurn(u8 i)
{
if(i==0)
{
OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
}
if(i==1)
{
OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
}
}
//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
if(i==0)
{
OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
OLED_WR_Byte(0xA1,OLED_CMD);
}
if(i==1)
{
OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
OLED_WR_Byte(0xA0,OLED_CMD);
}
}
//延时
void IIC_delay(void)
{
u8 t=1;
while(t--);
}
//起始信号
void I2C_Start(void)
{
OLED_SDA_Set();
OLED_SCL_Set();
IIC_delay();
OLED_SDA_Clr();
IIC_delay();
OLED_SCL_Clr();
}
//结束信号
void I2C_Stop(void)
{
OLED_SDA_Clr();
OLED_SCL_Set();
IIC_delay();
OLED_SDA_Set();
}
//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
OLED_SDA_Set();
IIC_delay();
OLED_SCL_Set();
IIC_delay();
OLED_SCL_Clr();
IIC_delay();
}
//写入一个字节
void Send_Byte(u8 dat)
{
u8 i;
for(i=0;i<8;i++)
{
OLED_SCL_Clr();//将时钟信号设置为低电平
if(dat&0x80)//将dat的8位从最高位依次写入
{
OLED_SDA_Set();
}
else
{
OLED_SDA_Clr();
}
IIC_delay();
OLED_SCL_Set();
IIC_delay();
OLED_SCL_Clr();
dat<<=1;
}
}
//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
/*I2C_Start();
Send_Byte(0x78);
I2C_WaitAck();
if(mode){Send_Byte(0x40);}
else{Send_Byte(0x00);}
I2C_WaitAck();
Send_Byte(dat);
I2C_WaitAck();
I2C_Stop(); */
if(mode) { // 0x78 0x40
I2C_WriteNbyte(0x78,0x40, &dat, 1);
} else { // 0x78 0x00
I2C_WriteNbyte(0x78 , 0x00, &dat, 1);
}
}
//坐标设置
void OLED_Set_Pos(u8 x, u8 y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_WR_Byte((x&0x0f),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)
{
u8 i,n;
for(i=0;i<8;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(0,OLED_DATA);
} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//sizey:选择字体 6x8 8x16
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey)
{
u8 c=0,sizex=sizey/2;
u16 i=0,size1;
if(sizey==8)size1=6;
else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
c=chr-' ';//得到偏移后的值
OLED_Set_Pos(x,y);
for(i=0;i<size1;i++)
{
if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);
if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);//6X8字号
else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);//8x16字号
// else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用户添加字号
else return;
}
}
//m^n函数
u32 oled_pow(u8 m,u8 n)
{
u32 result=1;
while(n--)result*=m;
return result;
}
//显示数字
//x,y :起点坐标
//num:要显示的数字
//len :数字的位数
//sizey:字体大小
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey)
{
u8 t,temp,m=0;
u8 enshow=0;
if(sizey==8)m=2;
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+(sizey/2+m)*t,y,' ',sizey);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(sizey/2+m)*t,y,temp+'0',sizey);
}
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 sizey)
{
u8 j=0;
while (chr[j]!='\0')
{
OLED_ShowChar(x,y,chr[j++],sizey);
if(sizey==8)x+=6;
else x+=sizey/2;
}
}
//显示汉字
void OLED_ShowChinese(u8 x,u8 y,u8 no,u8 sizey)
{
u16 i,size1=(sizey/8+((sizey%8)?1:0))*sizey;
for(i=0;i<size1;i++)
{
if(i%sizey==0) OLED_Set_Pos(x,y++);
if(sizey==16) OLED_WR_Byte(Hzk[no][i],OLED_DATA);//16x16字号
// else if(sizey==xx) OLED_WR_Byte(xxx[c][i],OLED_DATA);//用户添加字号
else return;
}
}
//显示图片
//x,y显示坐标
//sizex,sizey,图片长宽
//BMP:要显示的图片
void OLED_DrawBMP(u8 x,u8 y,u8 sizex, u8 sizey,u8 BMP[])
{
u16 j=0;
u8 i,m;
sizey=sizey/8+((sizey%8)?1:0);
for(i=0;i<sizey;i++)
{
OLED_Set_Pos(x,i+y);
for(m=0;m<sizex;m++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
//初始化
void OLED_Init(void)
{
OLED_RES_Clr();
delay_ms(200);
OLED_RES_Set();
OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}
bmp.h 是专门存放照片文件的,在买屏幕的时候,官方会有写的可以参考学习一下,还有画一个图案实现在屏幕上。
#ifndef __BMP_H
#define __BMP_H
unsigned char code BMP1[] =
{
0x00,0x03,0x05,0x09,0x11,0xFF,0x11,0x89,0x05,0xC3,0x00,0xE0,0x00,0xF0,0x00,0xF8,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x28,0xFF,0x11,0xAA,0x44,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x01,0x38,0x44,0x82,0x92,
0x92,0x74,0x01,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x44,0xFF,0x01,0x7D,
0x7D,0x7D,0x01,0x7D,0x7D,0x7D,0x7D,0x01,0x7D,0x7D,0x7D,0x7D,0x7D,0x01,0xFF,0x00,
0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3F,0x03,0x03,
0xF3,0x13,0x11,0x11,0x11,0x11,0x11,0x11,0x01,0xF1,0x11,0x61,0x81,0x01,0x01,0x01,
0x81,0x61,0x11,0xF1,0x01,0x01,0x01,0x01,0x41,0x41,0xF1,0x01,0x01,0x01,0x01,0x01,
0xC1,0x21,0x11,0x11,0x11,0x11,0x21,0xC1,0x01,0x01,0x01,0x01,0x41,0x41,0xF1,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x11,0x11,0x11,0x11,0x11,0xD3,0x33,
0x03,0x03,0x3F,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xE0,0x00,0x00,
0x7F,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x7F,0x00,0x00,0x01,0x06,0x18,0x06,
0x01,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x40,0x40,0x7F,0x40,0x40,0x00,0x00,0x00,
0x1F,0x20,0x40,0x40,0x40,0x40,0x20,0x1F,0x00,0x00,0x00,0x00,0x40,0x40,0x7F,0x40,
0x40,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x40,0x30,0x0C,0x03,0x00,0x00,
0x00,0x00,0xE0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x06,0x06,
0x06,0x06,0x04,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,0x04,0x84,0x44,0x44,0x44,
0x84,0x04,0x04,0x04,0x84,0xC4,0x04,0x04,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,
0x04,0x04,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,0x04,0x04,0x04,0x04,0x84,0x44,
0x44,0x44,0x84,0x04,0x04,0x84,0x44,0x44,0x44,0x84,0x04,0x04,0x04,0x04,0x06,0x06,
0x06,0x06,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x10,0x18,0x14,0x12,0x11,0x00,0x00,0x0F,0x10,0x10,0x10,
0x0F,0x00,0x00,0x00,0x10,0x1F,0x10,0x00,0x00,0x00,0x08,0x10,0x12,0x12,0x0D,0x00,
0x00,0x18,0x00,0x00,0x0D,0x12,0x12,0x12,0x0D,0x00,0x00,0x18,0x00,0x00,0x10,0x18,
0x14,0x12,0x11,0x00,0x00,0x10,0x18,0x14,0x12,0x11,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x7F,0x03,0x0C,0x30,0x0C,0x03,0x7F,0x00,0x00,0x38,0x54,0x54,0x58,0x00,0x00,
0x7C,0x04,0x04,0x78,0x00,0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xAA,0xAA,0xAA,
0x28,0x08,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x03,0x0C,0x30,0x0C,0x03,0x7F,
0x00,0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00,0x7F,0x02,0x04,0x08,0x10,0x7F,0x00,/********************************/
};
#endif