嵌入式学习之路2

嵌入式学习之路2

    • Day31(2025-12.21--2024-10.25)
        • [第 20 章 OLED](#第 20 章 OLED)
          • [20.1 SSD1315驱动芯片](#20.1 SSD1315驱动芯片)
          • [20.2 使用IIC协议驱动SSD1315芯片控制OLED显示字符串和数字](#20.2 使用IIC协议驱动SSD1315芯片控制OLED显示字符串和数字)
          • 附录
    • Day32(2025-12.23--2024-10.28)
      • [第 21 章 18B20](#第 21 章 18B20)
        • [21.1 1-Wire 通信协议](#21.1 1-Wire 通信协议)
        • [21.2 18B20 通信流程](#21.2 18B20 通信流程)
        • [21.3 使用1Wire协议控制18B20测试温度,并使用IIC协议将温度显示再OLED上](#21.3 使用1Wire协议控制18B20测试温度,并使用IIC协议将温度显示再OLED上)
      • [第 22 章 DS1302 实时时钟](#第 22 章 DS1302 实时时钟)
        • [22.1 通信协议(三线串行通信)](#22.1 通信协议(三线串行通信))
        • [22.2 DS1302](#22.2 DS1302)
        • [22.3 使用3Wire协议设置,读取DS1320上的时间并显示在OLED上](#22.3 使用3Wire协议设置,读取DS1320上的时间并显示在OLED上)
        • 附录
      • [第 23 章关于通信协议的总结](#第 23 章关于通信协议的总结)
  • 51智能车
    • Day33(2025-12.24--2024-10.29)
      • [第 1 章 架构设计](#第 1 章 架构设计)
      • [第 2 章 避障](#第 2 章 避障)
        • [2.1 基础准备](#2.1 基础准备)
        • [2.2 测距模块](#2.2 测距模块)
        • [2.3 电机模块](#2.3 电机模块)
        • [2.4 应用层](#2.4 应用层)
      • [3 遥控](#3 遥控)
        • [3.1 蓝牙模块](#3.1 蓝牙模块)
        • [3.2 应用层](#3.2 应用层)
      • [第 4 章 巡线](#第 4 章 巡线)
        • [4.1 光电反射传感器模块](#4.1 光电反射传感器模块)
        • [4.2 应用层](#4.2 应用层)
      • [第 5 章 模式切换](#第 5 章 模式切换)
        • [5.1 独立按键](#5.1 独立按键)
        • [5,2 模式切换](#5,2 模式切换)

Day31(2025-12.21--2024-10.25)

第 20 章 OLED
20.1 SSD1315驱动芯片

① 组成

介绍
MCU Interface 单片机接口
Command Decoder 命令解码器
Graphic Display Data RAM GDDRAM,图形显示数据内存
Common Driver 公共驱动,连接OLED的行,共64个引脚
Segment Driver 段驱动,连接OLED的列,共128个引脚

② I²C 总线协议

设备地址: 011110 + SA0 SA0默认是0

7bit地址+写标志(0) : 0x78

数据字节和控制字节:

数据字节 命令 显示数据

控制字节

== ③ GDDRAM==




内部结构:分为8个Page,每个Page 128 个Byte

寻址方式:

  1. 页寻址
  2. 水平寻址

  3. 垂直寻址

内存每个bit与屏幕像素的映射

影响因素: GDDRAM和SSD1315输出引脚之间(可以配置)

SSD1315输出引脚与屏幕之间

通过时序信号我们可以看出如何来写入数据或者读出数据

20.2 使用IIC协议驱动SSD1315芯片控制OLED显示字符串和数字
Com
c 复制代码
#ifndef __COM_UTIL_H__
#define __COM_UTIL_H__

#include <STC89C5xRC.H>
#include <INTRINS.H>

// 类型别名
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

/**
 * @brief 延时函数,指定延时多少毫秒
 *
 * @param count 指定多少毫秒
 */
void Delayms(u16 count);

#endif
c 复制代码
/*******************************************************************************
****文件路径         : \使用定时器中断控制LED实现流水灯效果\src\Com\Com_Util.c
****作者名称         : error: git config user.name & please set dead value or install git
****文件版本         : V1.0.0
****创建日期         : 2025-12-15 22:34:16
****简要说明         : 
****
****版权信息         : 2025 by ${git_name}, All Rights Reserved.
********************************************************************************/


#include "Com_Util.h"

// 延时函数,指定延时多少毫秒
/*******************************************************************************
****函数功能: 
****入口参数: count:
****出口参数: 
****函数备注: 
********************************************************************************/
void Delayms(u16 count)
{
    u8 data i, j;
    while (count)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j)
                ;
        } while (--i);
        count--;
    }
}
Dri

Dri_IIC

c 复制代码
#ifndef __DRI_IIC_H__
#define __DRI_IIC_H__

#include "Com_Util.h"
#define SCL P17
#define SDA P16

/**
 * @brief 发送起始信号
 * 
 */
void Dri_IIC_Start();

/**
 * @brief 发送一个字节的数据
 * 
 * @param byte 要发送的数据
 */
void Dri_IIC_SendByte(u8 byte);

/**
 * @brief 接收确认信号
 * 
 * @return bit 返回接收到确认信号
 */
bit Dri_IIC_ReceiveACK();

/**
 * @brief 接收一个字节的数据
 * 
 * @return u8 接收到的数据
 */
u8 Dri_IIC_ReceiveByte();

/**
 * @brief 发送确认信号
 * 
 * @param ack 确认信号
 */
void Dri_IIC_SendACK(bit ack);

/**
 * @brief 发送停止信号
 * 
 */
void Dri_IIC_Stop();

#endif /* __DRI_IIC_H__ */
c 复制代码
#include "Dri_IIC.h"

/**
 * @brief 发送起始信号
 *
 */
void Dri_IIC_Start()
{
    // 确保是空闲状态
    SCL = 1;
    SDA = 1;

    // 将SDA拉低,发送起始信号
    SDA = 0;

    // 拉低时钟,方便下一步操作
    SCL = 0;
}

/**
 * @brief 发送一个字节的数据
 *
 * @param byte 要发送的数据
 */
void Dri_IIC_SendByte(u8 byte)
{
    /*
        第一次发送(最高位):  (byte & 0b10000000) == 0 ? 0 : 1
        第二次发送(次高位):  (byte & 0b01000000) == 0 ? 0 : 1
        第三次发送(第三高位):  (byte & 0b0010000) == 0 ? 0 : 1
        ...
        最后一次发送(最低位):(byte & 0b0000001) == 0 ? 0 : 1
    */
    u8 i;
    for (i = 0; i < 8; i++)
    {
        SDA = (byte & (0x80 >> i)) == 0 ? 0 : 1;
        SCL = 1;
        SCL = 0;
    }
}

/**
 * @brief 接收确认信号
 *
 * @return bit 返回接收到确认信号
 */
bit Dri_IIC_ReceiveACK()
{
    bit ack;

    // 主设备释放SDA(将其拉高)
    SDA = 1;

    // 主设备读取SDA
    SCL = 1;
    ack = SDA;

    // 拉低SCL
    SCL = 0;

    return ack;
}

/**
 * @brief 接收一个字节的数据
 *
 * @return u8 接收到的数据
 */
u8 Dri_IIC_ReceiveByte()
{
    /*
        第一次: xxxxxxxx  | SDA(a) =  xxxxxxxa
        第二次: xxxxxxa0  | SDA(b) =  xxxxxxab
        第三次: xxxxxab0  | SDA(c) =  xxxxxabc
        ...
        第八次:abcdefg0  | SDA(h) =  abcdefgh
    */

    u8 byte = 0; // 保存接收到一个字节的数据
    u8 i;

    // 主设备释放SDA(拉高)
    SDA = 1;

    // 逐比特接收数据,共接收8次,先接收高位
    for (i = 0; i < 8; i++)
    {
        SCL = 1;

        // 先byte左移1位
        byte <<= 1;
        // 将SDA放在byte目前的最低位上
        byte |= SDA;

        SCL = 0;
    }

    // 返回
    return byte;
}

/**
 * @brief 发送确认信号
 *
 * @param ack 确认信号
 */
void Dri_IIC_SendACK(bit ack)
{
    SDA = ack;
    SCL = 1;
    SCL = 0;
}

/**
 * @brief 发送停止信号
 *
 */
void Dri_IIC_Stop()
{
    SDA = 0;
    SCL = 1;
    SDA = 1;
}

Dri_Timer0

c 复制代码
#ifndef __DRI_TIMER0_H__
#define __DRI_TIMER0_H__

#include <STDIO.H>
#include "Com_Util.h"

// 定义Timer0最多可注册回调函数的数量
#define MAX_CALLBACK_COUNT 4

// 给函数指针类型取别名
typedef void (*Timer0_Callback)(void);

/**
 * @brief 定时器0初始化
 */
void Dri_Timer0_Init();

/**
 * @brief 注册定时器0的回调函数
 * 
 * @param cb 回调函数的地址
 * @return 1表示注册成功,0表示注册失败
 */
bit Dri_Timer0_RegisterCallback(Timer0_Callback cb);

/**
 * @brief 解除指定的定时器0的回调函数
 * 
 * @param cb 要解除回调函数的地址
 * @return 1表示解除注册成功,0表示解除注册失败
 */
bit Dri_Timer0_DeregisterCallback(Timer0_Callback cb);

#endif /* __DRI_TIMER0_H__ */
c 复制代码
#include "Dri_Timer0.h"

// 定义函数指针数组,每个元素都是 Timer0_Callback 类型
static Timer0_Callback s_timer0_callbacks[MAX_CALLBACK_COUNT] = {NULL, NULL, NULL, NULL};

/**
 * @brief 定时器0初始化
 */
void Dri_Timer0_Init()
{
    // 1. 打开总开关和定时器0的开关
    EA = 1;
    ET0 = 1;

    // 2. 设置脉冲来源、工作模式、GATE
    TMOD &= 0xF0;
    TMOD |= 0x01; // GATE=0, C/T=0, M1、M0 = 01  16位脉冲计数器(不会自动重装载)

    // 3. 计算脉冲计数器的初始值
    TL0 = 64614;
    TH0 = 64614 >> 8;

    // 4. 设置定时器0的优先级(使用默认)
    // 5. 开始计数
    TR0 = 1;
}

/**
 * @brief 注册定时器0的回调函数
 *
 * @param cb 回调函数的地址
 * @return 1表示注册成功,0表示注册失败
 */
bit Dri_Timer0_RegisterCallback(Timer0_Callback cb)
{
    u8 i;

    // 如果回调函数已经注册过,直接返回0
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] == cb)
        {
            return 0;
        }
    }

    // 将回调函数添加到第一个不是NULL的位置
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] == NULL)
        {
            s_timer0_callbacks[i] = cb;
            return 1;
        }
    }

    return 0;
}

/**
 * @brief 解除指定的定时器0的回调函数
 *
 * @param cb 要解除回调函数的地址
 * @return 1表示解除注册成功,0表示解除注册失败
 */
bit Dri_Timer0_DeregisterCallback(Timer0_Callback cb)
{
    u8 i;
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] == cb)
        {
            s_timer0_callbacks[i] = NULL;
            return 1;
        }
    }

    return 0;
}

// 定义Timer0的中断服务程序
void Timer0_Handler() interrupt 1
{
    u8 i;

    // 重置脉冲计数器的初始值
    TL0 = 64614;
    TH0 = 64614 >> 8;

    // 依次调用回调函数
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] != NULL)
        {
            s_timer0_callbacks[i]();
        }
    }
}
Int
c 复制代码
#ifndef __INT_OLED_H__
#define __INT_OLED_H__

#include "Com_Util.h"
#include "Dri_IIC.h"

#define OLED_ADDR 0x78

//完成初始化
void Int_OLED_Init();

//清屏幕
void Int_OLED_Clear();

//指定位置显示指定字符串
void Int_OLED_DisplayString(u8 *str,u8 x, u8 y);

//在指定位置显示指定的数字
void Int_OLED_DisplayNumber(long num,u8 x, u8 y);

#endif /* __INT_OLED_H__ */
c 复制代码
# include "Int_OLED.h"

// 定义字模信息(大小写字母、数字、标点符号)
// 加上关键字code 只适合C51 会将数据存储到程序空间
static code u8 s_font_bitmaps[][16] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  0

    0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1

    0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2

    0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00,
    0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3

    0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00,
    0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4

    0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00,
    0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5

    0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00,
    0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6

    0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7

    0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8

    0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9

    0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00,
    0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10

    0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14

    0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04,
    0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15

    0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16

    0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17

    0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00,
    0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18

    0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00,
    0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19

    0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00,
    0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20

    0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00,
    0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21

    0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00,
    0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22

    0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
    0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23

    0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00,
    0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24

    0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25

    0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26

    0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27

    0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00,
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29

    0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00,
    0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30

    0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31

    0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00,
    0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32

    0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00,
    0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33

    0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34

    0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35

    0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36

    0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37

    0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38

    0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00,
    0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
    0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40

    0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41

    0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00,
    0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42

    0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00,
    0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44

    0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00,
    0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45

    0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08,
    0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46

    0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47

    0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00,
    0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48

    0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49

    0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50

    0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51

    0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00,
    0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53

    0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08,
    0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54

    0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00,
    0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55

    0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08,
    0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56

    0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00,
    0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57

    0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
    0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58

    0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59

    0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60

    0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61

    0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63

    0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65

    0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66

    0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67

    0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69

    0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71

    0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72

    0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73

    0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00,
    0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74

    0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75

    0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76

    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77

    0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79

    0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
    0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80

    0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81

    0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83

    0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84

    0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85

    0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
    0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86

    0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80,
    0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87

    0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88

    0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
    0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89

    0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90

    0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91

    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92

    0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93

    0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94
};

//发送多个数据字节
//bytes 表示要送的多个数据字节
//len 要发送的字节个数
//is_data 1是数据 0表示命令
static void Int_OLED_SendDataByte(u8 *bytes,u8 len,bit is_data)
{
    u8 i;
    //发送起始信号
    Dri_IIC_Start();

    //发送从设备地址和写标志,并接受确认信号
    Dri_IIC_SendByte(OLED_ADDR);
    Dri_IIC_ReceiveACK();

    //发送控制字节 发数据 0100 0000 发命令 0000 0000
    Dri_IIC_SendByte(is_data ? 0x40 : 0x00);
    Dri_IIC_ReceiveACK();

    //循环发送多个数据
    for ( i = 0; i < len; i++)
    {
        Dri_IIC_SendByte(bytes[i]);
        Dri_IIC_ReceiveACK();
    }
    
    //发送结束信号
    Dri_IIC_Stop();
}

//发命令
static void Int_OLED_WriteCommand(u8 *cmds, u8 len)
{
    Int_OLED_SendDataByte(cmds,len,0);
}

//发数据
static void Int_OLED_WriteData(u8 *cmds, u8 len)
{
    Int_OLED_SendDataByte(cmds,len,1);
}

//页寻址模式下,设置页地址和列地址
static void Int_OLED_SetAddress(u8 page_addr, u8 column_addr)
{
    u8 commands[3];
    //设置页寻址模式,默认是页寻址,可以省略

    //设置页地址
    commands[0] = 0xB0 + page_addr;


    //设置列地址

    commands[1] = column_addr & 0x0F;
    commands[2] = (column_addr>>4) + 0x10;

    //发送命令
    Int_OLED_WriteCommand(commands,3);
}

//完成初始化
void Int_OLED_Init()
{
    //将六个命令存储在一个字符数组中,这六个负责OLED的初始化
    u8 commands[6];
    //显示屏关闭
    commands[0]=0xAE;

    //列的重映射
    commands[1]=0xA1;

    //行的重映射
    commands[2]=0xC8;

    //
    commands[3] = 0x8D;
    commands[4] = 0x14;

    //显示屏开启
    commands[5]=0xAF;

    //将初始命令通过IIC协议发送到OLED的驱动芯片
    Int_OLED_WriteCommand(commands,6);

}

//通过水平寻址,清屏幕
void Int_OLED_Clear()
{
    //水平寻址,发送一系列的命令
    u8 commands[2]; 
    u8 Data[16];
    u8 i;
    for ( i = 0; i < 16; i++)
    {
        Data[i] = 0x00;
    }
    

    //设置寻址模式为水平寻址
    commands[0] = 0x20;
    commands[1] = 0x00;

    //设置起始页范围,因为起始值就是这个所以无需发送
    // commands[2] = 0x22;
    // commands[3] = 0x00;
    // commands[4] = 0x07;

    //设置起始列地址
    // commands[5] = 0x21;
    // commands[6] = 0x00;
    // commands[7] = 0xFF;

    //写入命令
    Int_OLED_WriteCommand(commands,2);

    //发送一系列的数据
    for ( i = 0; i < 64; i++)
    {
        Int_OLED_WriteData(Data,16);
    }
}

//指定位置显示指定字符串
void Int_OLED_DisplayString(u8 *str,u8 x, u8 y)
{
    u8 i;
    //写入字符串的上半部分 x是列,y是行
    //页寻址模式下设置页地址和列地址
    Int_OLED_SetAddress(y*2,x*8);
    //写入字符串,有效字符没有到达页边界
    for(i=0 ;str[i]!='\0'&& i+x<16;i++)
    {
        
        Int_OLED_WriteData(s_font_bitmaps[ str[i]- 32],8);
    }
    
    
    //写入字符串的下半部分
    Int_OLED_SetAddress((y*2)+1,x*8);
    for(i=0 ;str[i]!='\0'&& i+x<16;i++)
    {
        Int_OLED_WriteData(s_font_bitmaps[ str[i]- 32] + 8,8);
    }
}

//在指定位置显示指定的数字
void Int_OLED_DisplayNumber(long num,u8 x, u8 y)
{
    u8 str[11] = {0,0,0,0,0,0,0,0,0,0,0};
    sprintf(str,"%ld",num);
    Int_OLED_DisplayString(str,x,y);

}
main
c 复制代码
#include "Int_OLED.h"
#include "Com_Util.h"


void main()
{
    P46 = 0;
    Int_OLED_Init();
    Int_OLED_Clear();
    Int_OLED_DisplayString("hello GSX",0,0);
    Int_OLED_DisplayString("I'm Fine",0,1);
    Int_OLED_DisplayNumber(19990428,0,2);
    while (1);
}
附录

51单片存储类型关键字

以下是 8051 C51 编程中常用存储类型关键字及其作用的表格:

关键字 存储位置 访问方式 适用场景 特点
code 程序空间(ROM/Flash) 只读 存储常量、查找表 不能在运行时修改
data 内部 RAM(直接地址区) 快速访问 频繁使用的小型数据存储 访问速度快,节省代码空间
idata 内部 RAM(间接地址区) 间接访问 非常用的变量存储 可用整个内部 RAM
bdata 位寻址 RAM 位寻址 存储按位访问的标志位 可以逐个位访问
pdata 外部 RAM 的页地址区 间接访问 存储少量外部 RAM 数据 需通过页选择寄存器访问
xdata 外部 RAM 间接访问 存储大数据块 可访问外部扩展 RAM
hdata 高位外部 RAM 区 间接访问 存储大量数据 访问外部 RAM 的高位区域

相关单词

Graphic 图形

segment 段选

continuation 连续

缩写名词

LSB(Least Significant Bit)最低有效位

MSB(Most Significant Bit)最高有效位

51扩展板引脚参考

引脚 对应模块
P00 ~ P01 流水灯 / 数码管段选
P10 595 SER输入
P11 595 RCK输入
P12 595 SCK输入
P13 ~ P15 数码管位选
P16 I²C 数据线
P17 I²C 时钟线
P20 ~ P23 矩阵按键 1 ~ 4 行
P24 ~ P27 矩阵按键 1 ~ 4 列
P30/RxD 串口数据接收端
P31/TxD 串口数据发送端
P32 独立按键SW3 / 外部中断0 / Timer0计数启动
P33 独立按键SW4 / 外部中断1 / Timer1计数启动
P34 流水灯开关(1开) / Timer0脉冲源
P35 Timer1脉冲源 / 点阵LED开关(0开)
P36 数码管开关(0开)
P42 独立按键SW1 / 外部中断3
P43 独立按键 SW2 / 外部中断2
P46 蜂鸣器

Day32(2025-12.23--2024-10.28)

第 21 章 18B20


21.1 1-Wire 通信协议

== ① 概述==

异步、串行、半双工

== ② 初始化==

  1. 主设备发送复位脉冲(DQ=0),并至少持续480us
  2. 主设备释放DQ并等待15~60us (该时间之后,从设备才会发送存在脉冲)
  3. 主设备读取从设备存在脉冲(DQ数据),至少延时480us(等待时间+后面的采样时间)再进行后续的操作

== ③ 写操作(发送)==

c 复制代码
1. 先发低位,再发高位
2. 每发送一个bit的数据:
   2.1 主设备拉低DQ
   2.2 主设备要在15us内,将当前的bit信息发送到DQ
   2.3 发送1个bit的数据共需要持续60~120us
   2.4 发送完一个bit的数据需要将DQ释放,并至少持续1us

== ④ 读操作(接收)==

c 复制代码
1. 先接收低位,再接收高位
2. 每接收一个bit的数据:
   2.1 主设备将DQ拉低,至少持续1us,然后释放DQ
   2.2 延时15us,读取DQ上的数据(从设备会在DQ被释放15us之内发送DQ发送数据,此处延时10us,也可以)
   2.3 接收1个bit的数据共需要持续60~120us
21.2 18B20 通信流程

== ① 初始化==

== ② 发送 ROM 命令==

== ③ 发送功能命令==

21.3 使用1Wire协议控制18B20测试温度,并使用IIC协议将温度显示再OLED上
Com
c 复制代码
#ifndef __COM_UTIL_H__
#define __COM_UTIL_H__

#include <STC89C5xRC.H>
#include <INTRINS.H>

// 类型别名
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

/**
 * @brief 延时函数,指定延时多少毫秒
 *
 * @param count 指定多少毫秒
 */
void Delayms(u16 count);

//延时60us
void Delay60us(void);

//延时480us
void Delay480us(void);

//延时15us
void Delay15us(void);

//延时6us
void Delay6us(void);

//延时10us
void Delay10us(void);


#endif
c 复制代码
/*******************************************************************************
****文件路径         : \使用定时器中断控制LED实现流水灯效果\src\Com\Com_Util.c
****作者名称         : error: git config user.name & please set dead value or install git
****文件版本         : V1.0.0
****创建日期         : 2025-12-15 22:34:16
****简要说明         : 
****
****版权信息         : 2025 by ${git_name}, All Rights Reserved.
********************************************************************************/


#include "Com_Util.h"

// 延时函数,指定延时多少毫秒
/*******************************************************************************
****函数功能: 
****入口参数: count:
****出口参数: 
****函数备注: 
********************************************************************************/
void Delayms(u16 count)
{
    u8 data i, j;
    while (count)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j)
                ;
        } while (--i);
        count--;
    }
}


void Delay480us(void)	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 218;
	while (--i);
}

void Delay60us(void)	//@11.0592MHz
{
	unsigned char data i;

	i = 25;
	while (--i);
}


void Delay15us(void)	//@11.0592MHz
{
	unsigned char data i;

	i = 4;
	while (--i);
}

void Delay6us(void)	//@11.0592MHz
{
	_nop_();
}

void Delay10us(void)	//@11.0592MHz
{
	unsigned char data i;

	i = 2;
	while (--i);
}
Dir

Dri_1Wire

c 复制代码
#ifndef __DRI_1WIRE_H__
#define __DRI_1WIRE_H__

#include "Com_Util.h"
#define  DQ P44

//初始化 bit是从设备的回应信号
bit Dri_1Wire_Init();

//向从设备写入一个字节的数据
void Dri_1Wire_WriteByte(u8 byte);

//主设备读取一个字节的数据
u8 Dri_1Wire_ReadByte();

#endif /* __DRI_1WIRE_H__ */
c 复制代码
#include "Dri_1Wire.h"

//初始化 bit是从设备的回应信号
bit Dri_1Wire_Init()
{
    bit result;

    //主设备发送复位脉,拉低DQ,至少持续480us
    DQ =0;
    Delay480us();

    //主设备释放DQ ,并等待60us
    DQ  = 1;
    Delay60us();

    //主设备读取从设备的存在脉冲
    result = DQ;//DQ 是从设备发送的,我们只能控制主设备

    //延时至少420us
    Delay480us();

    return result;
}

//向从设备写入一个字节的数据
void Dri_1Wire_WriteByte(u8 byte)
{
    u8 i;
    //逐个bit发送
    for ( i = 0; i < 8; i++)
    {
        //主设备拉低DQ
        DQ  = 0;
        Delay10us();

        //主设备发送数据
        DQ = byte & 0x01;
        byte >>= 1;

        //整个发送过程至少持续60us
        Delay60us();

        //主设备释放DQ
        DQ = 1;

    }    
}

//主设备读取一个字节的数据
u8 Dri_1Wire_ReadByte()
{
    u8 i;
    u8 result= 0;
    u8 temp = 0x00;
    //逐个bit接收,先接收低位,再接受高位
    for ( i = 0; i < 8; i++)
    {
        //主设备拉低DQ 至少1us
        DQ = 0;

        //主设备释放DQ 交给从设备
        DQ = 1;

        //主设备等待10us,给从设备时间向DQ发送数据
        Delay10us();

        temp = DQ;
        temp <<= i;
        result |= temp;
        //接受的过程持续60us
        Delay60us();

        //读取完一个bit也拉高释放DQ
        DQ = 1;
    }
    return result;
}

Dri_IIC

c 复制代码
#ifndef __DRI_IIC_H__
#define __DRI_IIC_H__

#include "Com_Util.h"
#define SCL P17
#define SDA P16

/**
 * @brief 发送起始信号
 * 
 */
void Dri_IIC_Start();

/**
 * @brief 发送一个字节的数据
 * 
 * @param byte 要发送的数据
 */
void Dri_IIC_SendByte(u8 byte);

/**
 * @brief 接收确认信号
 * 
 * @return bit 返回接收到确认信号
 */
bit Dri_IIC_ReceiveACK();

/**
 * @brief 接收一个字节的数据
 * 
 * @return u8 接收到的数据
 */
u8 Dri_IIC_ReceiveByte();

/**
 * @brief 发送确认信号
 * 
 * @param ack 确认信号
 */
void Dri_IIC_SendACK(bit ack);

/**
 * @brief 发送停止信号
 * 
 */
void Dri_IIC_Stop();

#endif /* __DRI_IIC_H__ */
c 复制代码
#include "Dri_IIC.h"

/**
 * @brief 发送起始信号
 *
 */
void Dri_IIC_Start()
{
    // 确保是空闲状态
    SCL = 1;
    SDA = 1;

    // 将SDA拉低,发送起始信号
    SDA = 0;

    // 拉低时钟,方便下一步操作
    SCL = 0;
}

/**
 * @brief 发送一个字节的数据
 *
 * @param byte 要发送的数据
 */
void Dri_IIC_SendByte(u8 byte)
{
    /*
        第一次发送(最高位):  (byte & 0b10000000) == 0 ? 0 : 1
        第二次发送(次高位):  (byte & 0b01000000) == 0 ? 0 : 1
        第三次发送(第三高位):  (byte & 0b0010000) == 0 ? 0 : 1
        ...
        最后一次发送(最低位):(byte & 0b0000001) == 0 ? 0 : 1
    */
    u8 i;
    for (i = 0; i < 8; i++)
    {
        SDA = (byte & (0x80 >> i)) == 0 ? 0 : 1;
        SCL = 1;
        SCL = 0;
    }
}

/**
 * @brief 接收确认信号
 *
 * @return bit 返回接收到确认信号
 */
bit Dri_IIC_ReceiveACK()
{
    bit ack;

    // 主设备释放SDA(将其拉高)
    SDA = 1;

    // 主设备读取SDA
    SCL = 1;
    ack = SDA;

    // 拉低SCL
    SCL = 0;

    return ack;
}

/**
 * @brief 接收一个字节的数据
 *
 * @return u8 接收到的数据
 */
u8 Dri_IIC_ReceiveByte()
{
    /*
        第一次: xxxxxxxx  | SDA(a) =  xxxxxxxa
        第二次: xxxxxxa0  | SDA(b) =  xxxxxxab
        第三次: xxxxxab0  | SDA(c) =  xxxxxabc
        ...
        第八次:abcdefg0  | SDA(h) =  abcdefgh
    */

    u8 byte = 0; // 保存接收到一个字节的数据
    u8 i;

    // 主设备释放SDA(拉高)
    SDA = 1;

    // 逐比特接收数据,共接收8次,先接收高位
    for (i = 0; i < 8; i++)
    {
        SCL = 1;

        // 先byte左移1位
        byte <<= 1;
        // 将SDA放在byte目前的最低位上
        byte |= SDA;

        SCL = 0;
    }

    // 返回
    return byte;
}

/**
 * @brief 发送确认信号
 *
 * @param ack 确认信号
 */
void Dri_IIC_SendACK(bit ack)
{
    SDA = ack;
    SCL = 1;
    SCL = 0;
}

/**
 * @brief 发送停止信号
 *
 */
void Dri_IIC_Stop()
{
    SDA = 0;
    SCL = 1;
    SDA = 1;
}

Dri_Timer0

c 复制代码
#ifndef __DRI_TIMER0_H__
#define __DRI_TIMER0_H__

#include <STDIO.H>
#include "Com_Util.h"

// 定义Timer0最多可注册回调函数的数量
#define MAX_CALLBACK_COUNT 4

// 给函数指针类型取别名
typedef void (*Timer0_Callback)(void);

/**
 * @brief 定时器0初始化
 */
void Dri_Timer0_Init();

/**
 * @brief 注册定时器0的回调函数
 * 
 * @param cb 回调函数的地址
 * @return 1表示注册成功,0表示注册失败
 */
bit Dri_Timer0_RegisterCallback(Timer0_Callback cb);

/**
 * @brief 解除指定的定时器0的回调函数
 * 
 * @param cb 要解除回调函数的地址
 * @return 1表示解除注册成功,0表示解除注册失败
 */
bit Dri_Timer0_DeregisterCallback(Timer0_Callback cb);

#endif /* __DRI_TIMER0_H__ */
c 复制代码
#include "Dri_Timer0.h"

// 定义函数指针数组,每个元素都是 Timer0_Callback 类型
static Timer0_Callback s_timer0_callbacks[MAX_CALLBACK_COUNT] = {NULL, NULL, NULL, NULL};

/**
 * @brief 定时器0初始化
 */
void Dri_Timer0_Init()
{
    // 1. 打开总开关和定时器0的开关
    EA = 1;
    ET0 = 1;

    // 2. 设置脉冲来源、工作模式、GATE
    TMOD &= 0xF0;
    TMOD |= 0x01; // GATE=0, C/T=0, M1、M0 = 01  16位脉冲计数器(不会自动重装载)

    // 3. 计算脉冲计数器的初始值
    TL0 = 64614;
    TH0 = 64614 >> 8;

    // 4. 设置定时器0的优先级(使用默认)
    // 5. 开始计数
    TR0 = 1;
}

/**
 * @brief 注册定时器0的回调函数
 *
 * @param cb 回调函数的地址
 * @return 1表示注册成功,0表示注册失败
 */
bit Dri_Timer0_RegisterCallback(Timer0_Callback cb)
{
    u8 i;

    // 如果回调函数已经注册过,直接返回0
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] == cb)
        {
            return 0;
        }
    }

    // 将回调函数添加到第一个不是NULL的位置
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] == NULL)
        {
            s_timer0_callbacks[i] = cb;
            return 1;
        }
    }

    return 0;
}

/**
 * @brief 解除指定的定时器0的回调函数
 *
 * @param cb 要解除回调函数的地址
 * @return 1表示解除注册成功,0表示解除注册失败
 */
bit Dri_Timer0_DeregisterCallback(Timer0_Callback cb)
{
    u8 i;
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] == cb)
        {
            s_timer0_callbacks[i] = NULL;
            return 1;
        }
    }

    return 0;
}

// 定义Timer0的中断服务程序
void Timer0_Handler() interrupt 1
{
    u8 i;

    // 重置脉冲计数器的初始值
    TL0 = 64614;
    TH0 = 64614 >> 8;

    // 依次调用回调函数
    for (i = 0; i < MAX_CALLBACK_COUNT; i++)
    {
        if (s_timer0_callbacks[i] != NULL)
        {
            s_timer0_callbacks[i]();
        }
    }
}
Int

Int_18B20

c 复制代码
#ifndef __INT_18B20_H__
#define __INT_18B20_H__

#include "Dri_1Wire.h"


//获取测量的温度
char Int_18B20_GetTemperature();

//获取测量的温度
float Int_18B20_GetTemperatureFloat();


#endif /* __INT_18B20_H__ */
c 复制代码
#include "Int_18B20.h"


//获取测量的温度
char Int_18B20_GetTemperature()
{
    u8 ls_byte;
    u8 ms_byte;
    //初始化
    Dri_1Wire_Init();
    //ROM命令
    Dri_1Wire_WriteByte(0xCC);
    //发送温度转换命令
    Dri_1Wire_WriteByte(0x44);

    Dri_1Wire_Init();
    Dri_1Wire_WriteByte(0xCC);
    //发送读取暂存器命令
    Dri_1Wire_WriteByte(0xBE);

    ls_byte= Dri_1Wire_ReadByte();
    ms_byte= Dri_1Wire_ReadByte();

    ms_byte <<=4;
    ls_byte >>=4;
    ls_byte = ls_byte | ms_byte;
    return ls_byte;    
}

//获取测量的温度
float Int_18B20_GetTemperatureFloat()
{
    u8 ls_byte;
    u8 ms_byte;
    //初始化
    Dri_1Wire_Init();

    //ROM命令
    Dri_1Wire_WriteByte(0xCC);

    //发送温度转换命令
    Dri_1Wire_WriteByte(0x44);

    //发送读取暂存器命令
    Dri_1Wire_Init();
    Dri_1Wire_WriteByte(0xCC);
    Dri_1Wire_WriteByte(0xBE);

    ls_byte= Dri_1Wire_ReadByte();
    ms_byte= Dri_1Wire_ReadByte();

    ms_byte <<=4;
    ls_byte >>=4;
    
    return (ls_byte | ms_byte) +(ls_byte & 0x0F)*0.0625;   
}

Int_OLED

c 复制代码
#ifndef __INT_OLED_H__
#define __INT_OLED_H__

#include "Com_Util.h"
#include "Dri_IIC.h"

#define OLED_ADDR 0x78

//完成初始化
void Int_OLED_Init();

//清屏幕
void Int_OLED_Clear();

//指定位置显示指定字符串
void Int_OLED_DisplayString(u8 *str,u8 x, u8 y);

//在指定位置显示指定的数字
void Int_OLED_DisplayNumber(long num,u8 x, u8 y);

#endif /* __INT_OLED_H__ */
c 复制代码
# include "Int_OLED.h"
#include "STDIO.H"

// 定义字模信息(大小写字母、数字、标点符号)
// 加上关键字code 只适合C51 会将数据存储到程序空间
static code u8 s_font_bitmaps[][16] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  0

    0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1

    0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2

    0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00,
    0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3

    0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00,
    0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4

    0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00,
    0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5

    0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00,
    0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6

    0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7

    0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8

    0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9

    0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00,
    0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10

    0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14

    0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04,
    0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15

    0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16

    0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17

    0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00,
    0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18

    0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00,
    0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19

    0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00,
    0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20

    0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00,
    0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21

    0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00,
    0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22

    0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
    0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23

    0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00,
    0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24

    0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25

    0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26

    0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27

    0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00,
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29

    0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00,
    0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30

    0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31

    0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00,
    0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32

    0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00,
    0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33

    0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34

    0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35

    0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36

    0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37

    0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38

    0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00,
    0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
    0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40

    0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41

    0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00,
    0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42

    0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00,
    0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44

    0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00,
    0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45

    0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08,
    0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46

    0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47

    0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00,
    0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48

    0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49

    0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50

    0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51

    0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00,
    0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53

    0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08,
    0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54

    0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00,
    0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55

    0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08,
    0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56

    0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00,
    0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57

    0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
    0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58

    0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59

    0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60

    0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61

    0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63

    0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65

    0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66

    0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67

    0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69

    0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71

    0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72

    0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73

    0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00,
    0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74

    0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75

    0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76

    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77

    0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79

    0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
    0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80

    0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81

    0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83

    0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84

    0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85

    0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
    0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86

    0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80,
    0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87

    0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88

    0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
    0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89

    0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90

    0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91

    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92

    0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93

    0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94
};

//发送多个数据字节
//bytes 表示要送的多个数据字节
//len 要发送的字节个数
//is_data 1是数据 0表示命令
static void Int_OLED_SendDataByte(u8 *bytes,u8 len,bit is_data)
{
    u8 i;
    //发送起始信号
    Dri_IIC_Start();

    //发送从设备地址和写标志,并接受确认信号
    Dri_IIC_SendByte(OLED_ADDR);
    Dri_IIC_ReceiveACK();

    //发送控制字节 发数据 0100 0000 发命令 0000 0000
    Dri_IIC_SendByte(is_data ? 0x40 : 0x00);
    Dri_IIC_ReceiveACK();

    //循环发送多个数据
    for ( i = 0; i < len; i++)
    {
        Dri_IIC_SendByte(bytes[i]);
        Dri_IIC_ReceiveACK();
    }
    
    //发送结束信号
    Dri_IIC_Stop();
}

//发命令
static void Int_OLED_WriteCommand(u8 *cmds, u8 len)
{
    Int_OLED_SendDataByte(cmds,len,0);
}

//发数据
static void Int_OLED_WriteData(u8 *cmds, u8 len)
{
    Int_OLED_SendDataByte(cmds,len,1);
}

//页寻址模式下,设置页地址和列地址
static void Int_OLED_SetAddress(u8 page_addr, u8 column_addr)
{
    u8 commands[3];
    //设置页寻址模式,默认是页寻址,可以省略

    //设置页地址
    commands[0] = 0xB0 + page_addr;


    //设置列地址

    commands[1] = column_addr & 0x0F;
    commands[2] = (column_addr>>4) + 0x10;

    //发送命令
    Int_OLED_WriteCommand(commands,3);
}

//完成初始化
void Int_OLED_Init()
{
    //将六个命令存储在一个字符数组中,这六个负责OLED的初始化
    u8 commands[6];
    //显示屏关闭
    commands[0]=0xAE;

    //列的重映射
    commands[1]=0xA1;

    //行的重映射
    commands[2]=0xC8;

    //
    commands[3] = 0x8D;
    commands[4] = 0x14;

    //显示屏开启
    commands[5]=0xAF;

    //将初始命令通过IIC协议发送到OLED的驱动芯片
    Int_OLED_WriteCommand(commands,6);

}

//通过水平寻址,清屏幕
void Int_OLED_Clear()
{
    //水平寻址,发送一系列的命令
    u8 commands[2]; 
    u8 Data[16];
    u8 i;
    for ( i = 0; i < 16; i++)
    {
        Data[i] = 0x00;
    }
    

    //设置寻址模式为水平寻址
    commands[0] = 0x20;
    commands[1] = 0x00;

    //设置起始页范围,因为起始值就是这个所以无需发送
    // commands[2] = 0x22;
    // commands[3] = 0x00;
    // commands[4] = 0x07;

    //设置起始列地址
    // commands[5] = 0x21;
    // commands[6] = 0x00;
    // commands[7] = 0xFF;

    //写入命令
    Int_OLED_WriteCommand(commands,2);

    //发送一系列的数据
    for ( i = 0; i < 64; i++)
    {
        Int_OLED_WriteData(Data,16);
    }
}

//指定位置显示指定字符串
void Int_OLED_DisplayString(u8 *str,u8 x, u8 y)
{
    u8 i;
    //写入字符串的上半部分 x是列,y是行
    //页寻址模式下设置页地址和列地址
    Int_OLED_SetAddress(y*2,x*8);
    //写入字符串,有效字符没有到达页边界
    for(i=0 ;str[i]!='\0'&& i+x<16;i++)
    {
        
        Int_OLED_WriteData(s_font_bitmaps[ str[i]- 32],8);
    }
    
    
    //写入字符串的下半部分
    Int_OLED_SetAddress((y*2)+1,x*8);
    for(i=0 ;str[i]!='\0'&& i+x<16;i++)
    {
        Int_OLED_WriteData(s_font_bitmaps[ str[i]- 32] + 8,8);
    }
}

//在指定位置显示指定的数字
void Int_OLED_DisplayNumber(long num,u8 x, u8 y)
{
    u8 str[11] = {0,0,0,0,0,0,0,0,0,0,0};
    sprintf(str,"%ld",num);
    Int_OLED_DisplayString(str,x,y);

}
main
c 复制代码
#include "Int_18B20.h"
#include "Int_OLED.h"
#include "Com_Util.h"
#include "STDIO.H"

// void main ()
// {
    
//     char temperature;
//     float temp1;
//     char temp_str[17];

//     P46 = 0;

//     Int_OLED_Init();
//     Int_OLED_Clear();

//     while (1)
//     {
//         temperature= Int_18B20_GetTemperature();
//         temp1 = Int_18B20_GetTemperatureFloat();
        

//         Int_OLED_DisplayString("temperature",0 ,0);

        

//         Int_OLED_DisplayNumber(temperature,0 ,1);
        
//         sprintf(temp_str,"%.4f",temp1);
//         Int_OLED_DisplayString(temp_str,0,2);
//     }
// }

// 主函数
void main()
{
    char temp;
    float temp1;
    char temp1_str[17];
    P46= 0;

    // 初始化OLED并清屏
    Int_OLED_Init();
    Int_OLED_Clear();

    // 死循环
    while (1)
    {
        // 发去测温命令
        temp = Int_18B20_GetTemperature();
        temp1 = Int_18B20_GetTemperatureFloat();

        // 将温度信息显示在OLED
        Int_OLED_DisplayString("Tempearture: ", 0, 0);
        
        // temp整数
        Int_OLED_DisplayNumber(temp, 0, 1);

        // 小数
        sprintf(temp1_str, "%.4f", temp1);
        Int_OLED_DisplayString(temp1_str, 0, 2);
    }
}

第 22 章 DS1302 实时时钟

22.1 通信协议(三线串行通信)

DS1302 实时时钟芯片主要使用的是一个类似于SPI (Serial Peripheral Interface)

的三线通信协议来与微控制器进行通信。

== ① 发送(写操作)==

== ② 接收(读操作)==

22.2 DS1302



22.3 使用3Wire协议设置,读取DS1320上的时间并显示在OLED上
Com
c 复制代码
#ifndef __COM_UTIL_H__
#define __COM_UTIL_H__

#include <STC89C5xRC.H>
#include <INTRINS.H>

// 类型别名
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

/**
 * @brief 延时函数,指定延时多少毫秒
 *
 * @param count 指定多少毫秒
 */
void Delayms(u16 count);

// 延时10us
void Delay10us(void);

// 延时60us
void Delay60us(void);

// 延时480us
void Delay480us(void);

void Delay50us(void);	//@11.0592MHz

#endif
c 复制代码
#include "Com_Util.h"

// 延时函数,指定延时多少毫秒
void Delayms(u16 count)
{
    u8 data i, j;
    while (count)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j)
                ;
        } while (--i);
        count--;
    }
}

// 延时10us
void Delay10us(void)	//@11.0592MHz
{
	unsigned char data i;

	i = 2;
	while (--i);
}


// 延时60us
void Delay60us(void)	//@11.0592MHz
{
	unsigned char data i;

	i = 25;
	while (--i);
}

// 延时480us
void Delay480us(void)	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 218;
	while (--i);
}


void Delay50us(void)	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 20;
	while (--i);
}
Dri

Dri_1Wire

c 复制代码
#ifndef __DRI_1WIRE_H__
#define __DRI_1WIRE_H__

#include "Com_Util.h"

#define DQ P44

/**
 * @brief 初始化
 * 
 * @return bit 从设备的回应信号
 */
bit Dri_1Wire_Init();

/**
 * @brief 向从设备写入一个字节的数据
 * 
 * @param byte 要写入的字节
 */
void Dri_1Wire_WiteByte(u8 byte);

/**
 * @brief 读取一个字节的数据
 * 
 * @return u8 读取到字节
 */
u8 Dri_1Wire_ReadByte();


#endif /* __DRI_1WIRE_H__ */
c 复制代码
#include "Dri_1Wire.h"

/**
 * @brief 初始化
 *
 * @return bit 从设备的回应信号
 */
bit Dri_1Wire_Init()
{
    bit result;

    // 主设备发送复位脉冲,拉低DQ,至少持续480us
    DQ = 0;
    Delay480us();

    // 主设备释放DQ,并等待60us
    DQ = 1;
    Delay60us();

    // 主设备读取从设备的存在脉冲
    result = DQ;

    // 延时至少420us,此处延时480us 也可以 (60 + 420 = 480)
    Delay480us();

    return result;
}

/**
 * @brief 向从设备写入一个字节的数据
 *
 * @param byte 要写入的字节
 */
void Dri_1Wire_WiteByte(u8 byte)
{
    u8 i;

    // 循环,逐bit发送,先发低位再发高位
    for (i = 0; i < 8; i++)
    {
        // 先拉低DQ
        DQ = 0;

        // 在15us之内向DQ发送当前bit,此处选择延时10us
        Delay10us();
        DQ = byte & 0x01; // byte将最低位赋值给DQ
        byte >>= 1;       // byte右移1位

        // 发送1bit的持续时间是 60~120us, 此处延时60us
        Delay60us();

        // 发送完1bit数据,将DQ拉高
        DQ = 1;
    }
}

/**
 * @brief 读取一个字节的数据
 *
 * @return u8 读取到字节
 */
u8 Dri_1Wire_ReadByte()
{
    u8 byte = 0;
    u8 i;
    u8 b = 0;

    // 逐bit接收,先接收低位,再接收高位
    for (i = 0; i < 8; i++)
    {
        // 主设备拉低DQ,至少持续1us
        DQ = 0;
        // 主设备释放DQ,交给从设备
        DQ = 1;

        // 主设备等待15us,从设备会在15us之内向DQ发送数据, 这里延时10us
        Delay10us();
        b = DQ;
        b <<= i;
        byte |= b;

        // 接收的过程持续60~120us
        Delay60us();

        // 读取完1个bit,拉高DQ
        DQ = 1;
    }

    return byte;
}

Dri_3Wire

c 复制代码
#ifndef __DRI_3WIRE_H__
#define __DRI_3WIRE_H__

#include "Com_Util.h"

#define CE P41
#define SCLK P37
#define IO P40

/**
 * @brief 向1302写入一个字节(发送一个字节的数据)
 * 
 * @param byte 要写入的字节
 */
void Dri_3Wire_WriteByte(u8 byte);

/**
 * @brief 从1302中读取一个字节的数据(接收一个字节的数据)
 * 
 * @return u8 接收的字节
 */
u8 Dri_3Wire_ReadByte();


#endif /* __DRI_3WIRE_H__ */
c 复制代码
#include "Dri_3Wire.h"

/**
 * @brief 向1302写入一个字节(发送一个字节的数据)
 * 
 * @param byte 要写入的字节
 */
void Dri_3Wire_WriteByte(u8 byte)
{
    u8 i;

    SCLK = 0;
    // 循环逐比特发送,先发低位,再发高位
    for (i = 0; i < 8; i++)
    {
        IO = byte & 0x01;
        SCLK = 1;

        SCLK = 0;
        byte >>= 1;
    }
}

/**
 * @brief 从1302中读取一个字节的数据(接收一个字节的数据)
 * 
 * @return u8 接收的字节
 */
u8 Dri_3Wire_ReadByte()
{
    u8 byte = 0;
    u8 i;

    // 循环,逐bit接收,先收低位,再收高位
    for (i = 0; i < 8; i++)
    {
        if (IO)
        {
           byte |= (0x01 << i); 
        }

        SCLK = 1;
        SCLK = 0;
    }

    return byte;
}

Dri_IIC

c 复制代码
#ifndef __DRI_IIC_H__
#define __DRI_IIC_H__

#include "Com_Util.h"
#define SCL P17
#define SDA P16

/**
 * @brief 发送起始信号
 * 
 */
void Dri_IIC_Start();

/**
 * @brief 发送一个字节的数据
 * 
 * @param byte 要发送的数据
 */
void Dri_IIC_SendByte(u8 byte);

/**
 * @brief 接收确认信号
 * 
 * @return bit 返回接收到确认信号
 */
bit Dri_IIC_ReceiveACK();

/**
 * @brief 接收一个字节的数据
 * 
 * @return u8 接收到的数据
 */
u8 Dri_IIC_ReceiveByte();

/**
 * @brief 发送确认信号
 * 
 * @param ack 确认信号
 */
void Dri_IIC_SendACK(bit ack);

/**
 * @brief 发送停止信号
 * 
 */
void Dri_IIC_Stop();

#endif /* __DRI_IIC_H__ */
c 复制代码
#include "Dri_IIC.h"

/**
 * @brief 发送起始信号
 *
 */
void Dri_IIC_Start()
{
    // 确保是空闲状态
    SCL = 1;
    SDA = 1;

    // 将SDA拉低,发送起始信号
    SDA = 0;

    // 拉低时钟,方便下一步操作
    SCL = 0;
}

/**
 * @brief 发送一个字节的数据
 *
 * @param byte 要发送的数据
 */
void Dri_IIC_SendByte(u8 byte)
{
    /*
        第一次发送(最高位):  (byte & 0b10000000) == 0 ? 0 : 1
        第二次发送(次高位):  (byte & 0b01000000) == 0 ? 0 : 1
        第三次发送(第三高位):  (byte & 0b0010000) == 0 ? 0 : 1
        ...
        最后一次发送(最低位):(byte & 0b0000001) == 0 ? 0 : 1
    */
    u8 i;
    for (i = 0; i < 8; i++)
    {
        SDA = (byte & (0x80 >> i)) == 0 ? 0 : 1;
        SCL = 1;
        SCL = 0;
    }
}

/**
 * @brief 接收确认信号
 *
 * @return bit 返回接收到确认信号
 */
bit Dri_IIC_ReceiveACK()
{
    bit ack;

    // 主设备释放SDA(将其拉高)
    SDA = 1;

    // 主设备读取SDA
    SCL = 1;
    ack = SDA;

    // 拉低SCL
    SCL = 0;

    return ack;
}

/**
 * @brief 接收一个字节的数据
 *
 * @return u8 接收到的数据
 */
u8 Dri_IIC_ReceiveByte()
{
    /*
        第一次: xxxxxxxx  | SDA(a) =  xxxxxxxa
        第二次: xxxxxxa0  | SDA(b) =  xxxxxxab
        第三次: xxxxxab0  | SDA(c) =  xxxxxabc
        ...
        第八次:abcdefg0  | SDA(h) =  abcdefgh
    */

    u8 byte = 0; // 保存接收到一个字节的数据
    u8 i;

    // 主设备释放SDA(拉高)
    SDA = 1;

    // 逐比特接收数据,共接收8次,先接收高位
    for (i = 0; i < 8; i++)
    {
        SCL = 1;

        // 先byte左移1位
        byte <<= 1;
        // 将SDA放在byte目前的最低位上
        byte |= SDA;

        SCL = 0;
    }

    // 返回
    return byte;
}

/**
 * @brief 发送确认信号
 *
 * @param ack 确认信号
 */
void Dri_IIC_SendACK(bit ack)
{
    SDA = ack;
    SCL = 1;
    SCL = 0;
}

/**
 * @brief 发送停止信号
 *
 */
void Dri_IIC_Stop()
{
    SDA = 0;
    SCL = 1;
    SDA = 1;
}
Int

Int_18B20

c 复制代码
#ifndef __INT_18B20_H__
#define __INT_18B20_H__

#include "Dri_1Wire.h"

/**
 * @brief 获取测量的温度
 * 
 * @return char 测量的温度
 */
char Int_18B20_GetTempearture();


/**
 * @brief 获取测量的温度
 *
 * @return char 测量的温度(浮点数)
 */
float Int_18B20_GetTempeartureFloat();

#endif /* __INT_18B20_H__ */
c 复制代码
#include "Int_18B20.h"

/**
 * @brief 获取测量的温度
 *
 * @return char 测量的温度
 */
char Int_18B20_GetTempearture()
{
    u8 ls_byte;
    u8 ms_byte;

    // 1. 让18B20测温 ---------------------------------------------------------
    // 初始化
    Dri_1Wire_Init();
    // ROM命令:跳过
    Dri_1Wire_WiteByte(0xCC);
    // 发送测温命令,让18b20将测温结果存入暂存器
    Dri_1Wire_WiteByte(0x44);

    // 2. 读取18B20温度暂存器中的值(上次的测温结果)-------------------------------
    // 初始化
    Dri_1Wire_Init();
    // ROM命令:跳过
    Dri_1Wire_WiteByte(0xCC);
    // 发送读暂存器命令,读取测温结果
    Dri_1Wire_WiteByte(0xBE);

    // 接收数据
    ls_byte = Dri_1Wire_ReadByte();
    ms_byte = Dri_1Wire_ReadByte();

    // 处理温度信息
    return (ms_byte << 4) | (ls_byte >> 4);
}


/**
 * @brief 获取测量的温度
 *
 * @return char 测量的温度(浮点数)
 */
float Int_18B20_GetTempeartureFloat()
{
    u8 ls_byte;
    u8 ms_byte;

    // 1. 让18B20测温 ---------------------------------------------------------
    // 初始化
    Dri_1Wire_Init();
    // ROM命令:跳过
    Dri_1Wire_WiteByte(0xCC);
    // 发送测温命令,让18b20将测温结果存入暂存器
    Dri_1Wire_WiteByte(0x44);

    // 2. 读取18B20温度暂存器中的值(上次的测温结果)-------------------------------
    // 初始化
    Dri_1Wire_Init();
    // ROM命令:跳过
    Dri_1Wire_WiteByte(0xCC);
    // 发送读暂存器命令,读取测温结果
    Dri_1Wire_WiteByte(0xBE);

    // 接收数据
    ls_byte = Dri_1Wire_ReadByte();
    ms_byte = Dri_1Wire_ReadByte();

    // 处理温度信息
    return ((ms_byte << 4) | (ls_byte >> 4)) + (ls_byte & 0x0F) * 0.0625; 
}

Int_DS1302

c 复制代码
#ifndef __INT_DS1302_H__
#define __INT_DS1302_H__

#include "Com_Util.h"
#include "Dri_3Wire.h"

// 定义相关寄存器的地址+写标识,如果读+1
#define SECOND_REG 0x80
#define MINUTE_REG 0x82
#define HOUR_REG 0x84
#define DATE_REG 0x86
#define MONTH_REG 0x88
#define DAY_REG 0x8A
#define YEAR_REG 0x8C
#define WP_REG 0x8E

// 定义表示日期的结构体
typedef struct 
{
    u8 year;
    u8 month;
    u8 day_week;
    u8 day_month;
    u8 hour;
    u8 minute;
    u8 second;
} Struct_Date;

/**
 * @brief DS1302初始化
 */
void Int_DS1302_Init();


/**
 * @brief 设置日期
 * 
 * @param date 表示日期的结构体变量
 */
void Int_DS1302_SetDate(Struct_Date date);

/**
 * @brief 获取当前的日期时间 
 * 
 * @return Struct_Date 日期时间的结构体
 */
Struct_Date Int_DS1302_GetDate();

#endif /* __INT_DS1302_H__ */
c 复制代码
#include "Int_DS1302.h"

// 设置寄存器的值,写入
static void Int_DS1302_SetRegValue(addr, u8 value)
{
    // 打开读写使能
    CE = 1;

    // 写入控制字节
    Dri_3Wire_WriteByte(addr);
    // 写入数据字节
    Dri_3Wire_WriteByte(value);

    // 关闭读写使能
    CE = 0;
}

// 读取寄存器的值,返回读取的寄存器值
static u8 Int_DS1302_GetRegValue(addr)
{
    u8 value;

    // 打开读写使能
    CE = 1;

    // 写入控制字节
    Dri_3Wire_WriteByte(addr);
    // 读取数据字节
    value = Dri_3Wire_ReadByte();

    // 关闭读写使能
    CE = 0;

    return value;
}

/**
 * @brief DS1302初始化
 */
void Int_DS1302_Init()
{
    CE = 0;
    SCLK = 0;
    IO = 0;
}

/**
 * @brief 设置日期
 *
 * @param date 表示日期的结构体变量
 */
void Int_DS1302_SetDate(Struct_Date date)
{
    // 关闭写保护
    Int_DS1302_SetRegValue(WP_REG, 0x00);

    // 将秒信号写入寄存器
    Int_DS1302_SetRegValue(SECOND_REG, ((date.second / 10) << 4) | (date.second % 10));
    // 将分钟信号写入寄存器
    Int_DS1302_SetRegValue(MINUTE_REG, ((date.minute / 10) << 4) | (date.minute % 10));
    // 将小时信号写入寄存器
    Int_DS1302_SetRegValue(HOUR_REG, ((date.hour / 10) << 4) | (date.hour % 10));
    // 将每月几号信号写入寄存器
    Int_DS1302_SetRegValue(DATE_REG, ((date.day_month / 10) << 4) | (date.day_month % 10));
    // 将星期几信号写入寄存器
    Int_DS1302_SetRegValue(DAY_REG, date.day_week);
    // 将月信息写入寄存器
    Int_DS1302_SetRegValue(MONTH_REG, ((date.month / 10) << 4) | (date.month % 10));
    // 将年信息写入寄存器
    Int_DS1302_SetRegValue(YEAR_REG, ((date.year / 10) << 4) | (date.year % 10));

    // 打开写保护
    Int_DS1302_SetRegValue(WP_REG, 0x80);
}

/**
 * @brief 获取当前的日期时间
 *
 * @return Struct_Date 日期时间的结构体
 */
Struct_Date Int_DS1302_GetDate()
{
    // 定义结构体变量
    Struct_Date date;
    u8 temp;

    // 年
    temp = Int_DS1302_GetRegValue(YEAR_REG + 1);
    date.year = (temp >> 4) * 10 + (temp & 0x0F);

    // 月
    temp = Int_DS1302_GetRegValue(MONTH_REG + 1);
    date.month = (temp >> 4) * 10 + (temp & 0x0F);

    // 日(每月第几天)
    temp = Int_DS1302_GetRegValue(DATE_REG + 1);
    date.day_month = (temp >> 4) * 10 + (temp & 0x0F);

    // 星期几
    temp = Int_DS1302_GetRegValue(DAY_REG + 1);
    date.day_week = temp;

    // 时
    temp = Int_DS1302_GetRegValue(HOUR_REG + 1);
    date.hour = (temp >> 4) * 10 + (temp & 0x0F);

    // 分钟
    temp = Int_DS1302_GetRegValue(MINUTE_REG + 1);
    date.minute = (temp >> 4) * 10 + (temp & 0x0F);

    // 秒
    temp = Int_DS1302_GetRegValue(SECOND_REG + 1);
    date.second = (temp >> 4) * 10 + (temp & 0x0F);

    return date;
}

Int_OLED

c 复制代码
#ifndef __INT_OLED_H__
#define __INT_OLED_H__

#include "Com_Util.h"
#include "Dri_IIC.h"

// 设备地址 0111100 + 写标志 0
#define OLED_ADDR 0x78


/**
 * @brief OLED初始化
 * 
 */
void Int_OLED_Init();

/**
 * @brief 清屏
 * 
 */
void Int_OLED_Clear();

/**
 * @brief 在指定位置显示指定的字符串
 * 
 * @param str 字符串 
 * @param x   x坐标位置
 * @param y   y坐标位置
 */
void Int_OLED_DisplayString(u8 *str, u8 x, u8 y);

/**
 * @brief 在指定位置显示指定的数字
 * 
 * @param num   数字
 * @param x     x坐标位置
 * @param y     y坐标位置
 */     
void Int_OLED_DisplayNumber(long num, u8 x, u8 y);

#endif /* __INT_OLED_H__ */
c 复制代码
#include <STDIO.H>
#include "Int_OLED.h"

// 定义字模信息(大小写字母、数字、标点符号)
static code u8 s_font_bitmaps[][16] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  0

    0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x33, 0x30, 0x00, 0x00, 0x00, //! 1

    0x00, 0x10, 0x0C, 0x06, 0x10, 0x0C, 0x06, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //" 2

    0x40, 0xC0, 0x78, 0x40, 0xC0, 0x78, 0x40, 0x00,
    0x04, 0x3F, 0x04, 0x04, 0x3F, 0x04, 0x04, 0x00, // # 3

    0x00, 0x70, 0x88, 0xFC, 0x08, 0x30, 0x00, 0x00,
    0x00, 0x18, 0x20, 0xFF, 0x21, 0x1E, 0x00, 0x00, //$ 4

    0xF0, 0x08, 0xF0, 0x00, 0xE0, 0x18, 0x00, 0x00,
    0x00, 0x21, 0x1C, 0x03, 0x1E, 0x21, 0x1E, 0x00, //% 5

    0x00, 0xF0, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00,
    0x1E, 0x21, 0x23, 0x24, 0x19, 0x27, 0x21, 0x10, //& 6

    0x10, 0x16, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //' 7

    0x00, 0x00, 0x00, 0xE0, 0x18, 0x04, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x07, 0x18, 0x20, 0x40, 0x00, //( 8

    0x00, 0x02, 0x04, 0x18, 0xE0, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x20, 0x18, 0x07, 0x00, 0x00, 0x00, //) 9

    0x40, 0x40, 0x80, 0xF0, 0x80, 0x40, 0x40, 0x00,
    0x02, 0x02, 0x01, 0x0F, 0x01, 0x02, 0x02, 0x00, //* 10

    0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x01, 0x01, 0x1F, 0x01, 0x01, 0x01, 0x00, //+ 11

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0xB0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, //, 12

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, //- 13

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, //. 14

    0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x18, 0x04,
    0x00, 0x60, 0x18, 0x06, 0x01, 0x00, 0x00, 0x00, /// 15

    0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x00, 0x0F, 0x10, 0x20, 0x20, 0x10, 0x0F, 0x00, // 0 16

    0x00, 0x10, 0x10, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // 1 17

    0x00, 0x70, 0x08, 0x08, 0x08, 0x88, 0x70, 0x00,
    0x00, 0x30, 0x28, 0x24, 0x22, 0x21, 0x30, 0x00, // 2 18

    0x00, 0x30, 0x08, 0x88, 0x88, 0x48, 0x30, 0x00,
    0x00, 0x18, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // 3 19

    0x00, 0x00, 0xC0, 0x20, 0x10, 0xF8, 0x00, 0x00,
    0x00, 0x07, 0x04, 0x24, 0x24, 0x3F, 0x24, 0x00, // 4 20

    0x00, 0xF8, 0x08, 0x88, 0x88, 0x08, 0x08, 0x00,
    0x00, 0x19, 0x21, 0x20, 0x20, 0x11, 0x0E, 0x00, // 5 21

    0x00, 0xE0, 0x10, 0x88, 0x88, 0x18, 0x00, 0x00,
    0x00, 0x0F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // 6 22

    0x00, 0x38, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
    0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // 7 23

    0x00, 0x70, 0x88, 0x08, 0x08, 0x88, 0x70, 0x00,
    0x00, 0x1C, 0x22, 0x21, 0x21, 0x22, 0x1C, 0x00, // 8 24

    0x00, 0xE0, 0x10, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x00, 0x00, 0x31, 0x22, 0x22, 0x11, 0x0F, 0x00, // 9 25

    0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, //: 26

    0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, //; 27

    0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00,
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, //< 28

    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, //= 29

    0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00,
    0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, //> 30

    0x00, 0x70, 0x48, 0x08, 0x08, 0x08, 0xF0, 0x00,
    0x00, 0x00, 0x00, 0x30, 0x36, 0x01, 0x00, 0x00, //? 31

    0xC0, 0x30, 0xC8, 0x28, 0xE8, 0x10, 0xE0, 0x00,
    0x07, 0x18, 0x27, 0x24, 0x23, 0x14, 0x0B, 0x00, //@ 32

    0x00, 0x00, 0xC0, 0x38, 0xE0, 0x00, 0x00, 0x00,
    0x20, 0x3C, 0x23, 0x02, 0x02, 0x27, 0x38, 0x20, // A 33

    0x08, 0xF8, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x11, 0x0E, 0x00, // B 34

    0xC0, 0x30, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x07, 0x18, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // C 35

    0x08, 0xF8, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // D 36

    0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x23, 0x20, 0x18, 0x00, // E 37

    0x08, 0xF8, 0x88, 0x88, 0xE8, 0x08, 0x10, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, // F 38

    0xC0, 0x30, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00,
    0x07, 0x18, 0x20, 0x20, 0x22, 0x1E, 0x02, 0x00, // G 39

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
    0x20, 0x3F, 0x21, 0x01, 0x01, 0x21, 0x3F, 0x20, // H 40

    0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // I 41

    0x00, 0x00, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x00,
    0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, 0x00, // J 42

    0x08, 0xF8, 0x88, 0xC0, 0x28, 0x18, 0x08, 0x00,
    0x20, 0x3F, 0x20, 0x01, 0x26, 0x38, 0x20, 0x00, // K 43

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x20, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, // L 44

    0x08, 0xF8, 0xF8, 0x00, 0xF8, 0xF8, 0x08, 0x00,
    0x20, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x20, 0x00, // M 45

    0x08, 0xF8, 0x30, 0xC0, 0x00, 0x08, 0xF8, 0x08,
    0x20, 0x3F, 0x20, 0x00, 0x07, 0x18, 0x3F, 0x00, // N 46

    0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x0F, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0F, 0x00, // O 47

    0x08, 0xF8, 0x08, 0x08, 0x08, 0x08, 0xF0, 0x00,
    0x20, 0x3F, 0x21, 0x01, 0x01, 0x01, 0x00, 0x00, // P 48

    0xE0, 0x10, 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00,
    0x0F, 0x18, 0x24, 0x24, 0x38, 0x50, 0x4F, 0x00, // Q 49

    0x08, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x03, 0x0C, 0x30, 0x20, // R 50

    0x00, 0x70, 0x88, 0x08, 0x08, 0x08, 0x38, 0x00,
    0x00, 0x38, 0x20, 0x21, 0x21, 0x22, 0x1C, 0x00, // S 51

    0x18, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x18, 0x00,
    0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // T 52

    0x08, 0xF8, 0x08, 0x00, 0x00, 0x08, 0xF8, 0x08,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // U 53

    0x08, 0x78, 0x88, 0x00, 0x00, 0xC8, 0x38, 0x08,
    0x00, 0x00, 0x07, 0x38, 0x0E, 0x01, 0x00, 0x00, // V 54

    0xF8, 0x08, 0x00, 0xF8, 0x00, 0x08, 0xF8, 0x00,
    0x03, 0x3C, 0x07, 0x00, 0x07, 0x3C, 0x03, 0x00, // W 55

    0x08, 0x18, 0x68, 0x80, 0x80, 0x68, 0x18, 0x08,
    0x20, 0x30, 0x2C, 0x03, 0x03, 0x2C, 0x30, 0x20, // X 56

    0x08, 0x38, 0xC8, 0x00, 0xC8, 0x38, 0x08, 0x00,
    0x00, 0x00, 0x20, 0x3F, 0x20, 0x00, 0x00, 0x00, // Y 57

    0x10, 0x08, 0x08, 0x08, 0xC8, 0x38, 0x08, 0x00,
    0x20, 0x38, 0x26, 0x21, 0x20, 0x20, 0x18, 0x00, // Z 58

    0x00, 0x00, 0x00, 0xFE, 0x02, 0x02, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x00, //[ 59

    0x00, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x01, 0x06, 0x38, 0xC0, 0x00, //\ 60

    0x00, 0x02, 0x02, 0x02, 0xFE, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x40, 0x40, 0x7F, 0x00, 0x00, 0x00, //] 61

    0x00, 0x00, 0x04, 0x02, 0x02, 0x02, 0x04, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //^ 62

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, //_ 63

    0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //` 64

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x19, 0x24, 0x22, 0x22, 0x22, 0x3F, 0x20, // a 65

    0x08, 0xF8, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x3F, 0x11, 0x20, 0x20, 0x11, 0x0E, 0x00, // b 66

    0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0x20, 0x11, 0x00, // c 67

    0x00, 0x00, 0x00, 0x80, 0x80, 0x88, 0xF8, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0x10, 0x3F, 0x20, // d 68

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x1F, 0x22, 0x22, 0x22, 0x22, 0x13, 0x00, // e 69

    0x00, 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x18,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // f 70

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x6B, 0x94, 0x94, 0x94, 0x93, 0x60, 0x00, // g 71

    0x08, 0xF8, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // h 72

    0x00, 0x80, 0x98, 0x98, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // i 73

    0x00, 0x00, 0x00, 0x80, 0x98, 0x98, 0x00, 0x00,
    0x00, 0xC0, 0x80, 0x80, 0x80, 0x7F, 0x00, 0x00, // j 74

    0x08, 0xF8, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x3F, 0x24, 0x02, 0x2D, 0x30, 0x20, 0x00, // k 75

    0x00, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x20, 0x20, 0x3F, 0x20, 0x20, 0x00, 0x00, // l 76

    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x3F, 0x20, 0x00, 0x3F, 0x20, 0x00, 0x3F, // m 77

    0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x20, 0x3F, 0x21, 0x00, 0x00, 0x20, 0x3F, 0x20, // n 78

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x00, // o 79

    0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00,
    0x80, 0xFF, 0xA1, 0x20, 0x20, 0x11, 0x0E, 0x00, // p 80

    0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x0E, 0x11, 0x20, 0x20, 0xA0, 0xFF, 0x80, // q 81

    0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x20, 0x20, 0x3F, 0x21, 0x20, 0x00, 0x01, 0x00, // r 82

    0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x33, 0x24, 0x24, 0x24, 0x24, 0x19, 0x00, // s 83

    0x00, 0x80, 0x80, 0xE0, 0x80, 0x80, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x1F, 0x20, 0x20, 0x00, 0x00, // t 84

    0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00,
    0x00, 0x1F, 0x20, 0x20, 0x20, 0x10, 0x3F, 0x20, // u 85

    0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
    0x00, 0x01, 0x0E, 0x30, 0x08, 0x06, 0x01, 0x00, // v 86

    0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x80,
    0x0F, 0x30, 0x0C, 0x03, 0x0C, 0x30, 0x0F, 0x00, // w 87

    0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x20, 0x31, 0x2E, 0x0E, 0x31, 0x20, 0x00, // x 88

    0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80,
    0x80, 0x81, 0x8E, 0x70, 0x18, 0x06, 0x01, 0x00, // y 89

    0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
    0x00, 0x21, 0x30, 0x2C, 0x22, 0x21, 0x30, 0x00, // z 90

    0x00, 0x00, 0x00, 0x00, 0x80, 0x7C, 0x02, 0x02,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x40, //{ 91

    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, //| 92

    0x00, 0x02, 0x02, 0x7C, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, //} 93

    0x00, 0x06, 0x01, 0x01, 0x02, 0x02, 0x04, 0x04,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //~ 94
};

/**
 * @brief 发送多个数据字节
 *
 * @param bytes 要发送的多个字节
 * @param len 要发送的字节个数
 * @param is_data 1表示数据0表示命令
 */
static void Int_OLED_SendDataByte(u8 *bytes, u8 len, bit is_data)
{
    u8 i;

    // 发送起始信号
    Dri_IIC_Start();

    // 发送从设备地址和写标志并接收ACK信号
    Dri_IIC_SendByte(OLED_ADDR);
    Dri_IIC_ReceiveACK();

    // 发送控制字节   发数据 01000000; 发命令 00000000
    Dri_IIC_SendByte(is_data ? 0x40 : 0x00);
    Dri_IIC_ReceiveACK();

    // 循环发送多个数据字节
    for (i = 0; i < len; i++)
    {
        Dri_IIC_SendByte(bytes[i]);
        Dri_IIC_ReceiveACK();
    }

    // 发送停止信号
    Dri_IIC_Stop();
}

// 发送命令
static void Int_OLED_WriteCommand(u8 *cmds, u8 len)
{
    Int_OLED_SendDataByte(cmds, len, 0);
}

// 发数据
static void Int_OLED_WriteData(u8 *datas, u8 len)
{
    Int_OLED_SendDataByte(datas, len, 1);
}

// 页寻址模式下,设置页地址和列地址
static void Int_OLED_SetAddress(u8 page_addr, u8 column_addr)
{
    u8 commands[3];

    // 1. 设置为页寻址模式,默认就是也寻址,此步可以省略

    // 2. 设置页地址
    commands[0] = 0xB0 + page_addr;

    // 3. 设置列地址
    commands[1] = column_addr & 0x0F; // 发送低4位
    commands[2] = 0x10 + (column_addr >> 4);

    // 写入命令
    Int_OLED_WriteCommand(commands, 3);
}

/**
 * @brief OLED初始化
 *
 */
void Int_OLED_Init()
{
    // 定义6个元素的字符数组保存命令
    u8 commands[6];

    // 显示屏关闭
    commands[0] = 0xAE;

    // 行和列的重映射
    commands[1] = 0xC8;
    commands[2] = 0xA1;

    // 增加电荷泵
    commands[3] = 0x8D;
    commands[4] = 0x14;

    // 显示屏开启
    commands[5] = 0xAF;

    // 向OLED驱动芯片写入命令
    Int_OLED_WriteCommand(commands, 6);
}

/**
 * @brief 清屏
 *
 */
void Int_OLED_Clear()
{
    u8 i = 0;
    u8 datas[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

    // 1.发送一系列的命令-------------------------------------
    u8 commands[2];
    // 1.1 设置寻址模式为水平寻址
    commands[0] = 0x20;
    commands[1] = 0x00;
    // 1.2 设置页范围, 默认就是 0b000 ~ 0b111,此处无需设置
    // 1.3 设置列范围, 默认就是 0b0000000 ~ 0b1111111,此处无需设置
    // 写入命令
    Int_OLED_WriteCommand(commands, 2);

    // 2.发送一系列的数据-------------------------------------
    // 每次发16个字节,分64次
    for (i = 0; i < 64; i++)
    {
        Int_OLED_WriteData(datas, 16);
    }
}

/**
 * @brief 在指定位置显示指定的字符串
 *
 * @param str 字符串
 * @param x   x坐标位置
 * @param y   y坐标位置
 */
void Int_OLED_DisplayString(u8 *str, u8 x, u8 y)
{
    u8 i;

    // 1.写入字符串的上半部分 ----------------------------------
    // 1.1 页寻址模式下设置页地址和列地址
    Int_OLED_SetAddress(y * 2, x * 8);
    // 1.2 循环写入数据 有效字符且没有到达页边界
    for (i = 0; str[i] != '\0' && x + i < 16; i++)
    {
        // 写入该字符的字模信息的前8个元素
        Int_OLED_WriteData(s_font_bitmaps[str[i] - 32], 8);
    }

    // 2.写入字符串的下半部分 ----------------------------------
    Int_OLED_SetAddress(y * 2 + 1, x * 8);
    // 1.2 循环写入数据 有效字符且没有到达页边界
    for (i = 0; str[i] != '\0' && x + i < 16; i++)
    {
        // 写入该字符的字模信息的后8个元素
        Int_OLED_WriteData(s_font_bitmaps[str[i] - 32] + 8, 8);
    }
}

/**
 * @brief 在指定位置显示指定的数字
 *
 * @param num   数字
 * @param x     x坐标位置
 * @param y     y坐标位置
 */
void Int_OLED_DisplayNumber(long num, u8 x, u8 y)
{
    u8 str[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    sprintf(str, "%ld", num);
    Int_OLED_DisplayString(str, x, y);
}
main
c 复制代码
#include <STDIO.H>
#include "Com_Util.h"
#include "Int_OLED.h"
#include "Int_18B20.h"
#include "Int_DS1302.h"

char *weeks[7] = {
    "Mon",
    "Tue",
    "Wed",
    "Thu",
    "Fri",
    "Sat",
    "Sun"
};

// 主函数
void main()
{
    char tmp;
    Struct_Date curr_date;
    char first_line[17];
    char second_line[17];
    // 定义结构体变量
    Struct_Date start_date = {24, 10, 1, 28, 17, 22, 55};

    // 初始化OLED并清屏
    Int_OLED_Init();
    Int_OLED_Clear();

    // 初始化1302
    Int_DS1302_Init();

    // 设置起始的时间
    Int_DS1302_SetDate(start_date);

    // 死循环
    while (1)
    {
        // 获取温度信息
        tmp = Int_18B20_GetTempearture();
        // 获取时间信息
        curr_date = Int_DS1302_GetDate();

        // 显示第一行
        sprintf(first_line, "20%02d/%02d/%02d %s", (int)curr_date.year, (int)curr_date.month, (int)curr_date.day_month, weeks[curr_date.day_week-1]);
        Int_OLED_DisplayString(first_line, 0, 0);

        // 显示第二行
        sprintf(second_line, "%02d:%02d:%02d Tmp:%d", (int)curr_date.hour, (int)curr_date.minute, (int)curr_date.second, (int)tmp);
        Int_OLED_DisplayString(second_line, 0, 1);

    }
}
c 复制代码
秒寄存器
分钟寄存器
小时寄存器
Date寄存器(每月第几天)
Day寄存器(每周第几天)
月寄存器
年寄存器
写保护寄存器
附录
c 复制代码
wire		线
temperature	温度

51扩展板引脚参考

引脚 对应模块
P00 ~ P01 流水灯 / 数码管段选
P10 595 SER输入
P11 595 RCK输入
P12 595 SCK输入
P13 ~ P15 数码管位选
P16 I²C 数据线
P17 I²C 时钟线
P20 ~ P23 矩阵按键 1 ~ 4 行
P24 ~ P27 矩阵按键 1 ~ 4 列
P30/RxD 串口数据接收端
P31/TxD 串口数据发送端
P32 独立按键SW3 / 外部中断0 / Timer0计数启动
P33 独立按键SW4 / 外部中断1 / Timer1计数启动
P34 流水灯开关(1开) / Timer0脉冲源
P35 Timer1脉冲源 / 点阵LED开关(0开)
P36 数码管开关(0开)
P42 独立按键SW1 / 外部中断3
P43 独立按键 SW2 / 外部中断2
P44 1-wire总线
P46 蜂鸣器

第 23 章关于通信协议的总结

51智能车

Day33(2025-12.24--2024-10.29)

第 1 章 架构设计

第 2 章 避障

2.1 基础准备
  1. 实现GPIO驱动代码
  2. 将定时器0改成定时器2, 保证回调函数在数组中连续存储
  3. 使用定时器2改造蜂鸣器
2.2 测距模块


① 硬件设计

P32引脚连接ECHO引脚, ECHO开始输出高电平,定时器0开始计数,ECHO高电平变电平的时候下降沿触发外部中断0

② 软件设计

c 复制代码
1. 初始化
   两个引脚设置为0; 
   注册定时器2的回调函数实现两次测距间隔大于100ms
   定时器0相关配置,不触发中断只用来计数
   外部中断0相关配置,优先级要高
   
2. 获取测距结果
   死循环中不断调用不断获取
2.3 电机模块

51芯片通过控制驱动芯片,芯片驱动电机



① 硬件设计

c 复制代码
1. 左电机
   前进:A1提供方波输入作为高电平,A2输入低电平
   后退:A2提供方波输入作为高电平,A1输入低电平
   
2. 右电机(与左电机镜像放置)
   前进:B2提供方波输入作为高电平,B1输入低电平
   后退:B1提供方波输入作为高电平,B2输入低电平

② 软件设计

c 复制代码
1. 利用定时器回调函数,实现方波每40ms一个周期,根据速度值不同决定每个周期的高电平占比
2. 分别设置左电机和右电机的速度,速度不会突变,而是逐渐变化
3. 更新速度的函数需要在死循环中不断调用
2.4 应用层

如果前方障碍物小于200mm,转弯

3 遥控

3.1 蓝牙模块



① 蓝牙芯片 ECB01C

蓝牙芯片接收到单片机发送的数据:

  1. 如果是AT命令,自己处理
  2. 如果不是AT命令,透传给手机

蓝牙芯片接收到手机发送的数据:

直接透传给单片机

② 软件设计

  1. 单片机向蓝牙发送AT命令,修改蓝牙名称,只需要执行一次
  2. 单片机通电先检测蓝牙状态,如果蓝牙状态正常继续执行后续逻辑,如果蓝牙不正常不执行后续逻辑。
3.2 应用层

使用手机上的小程序向单片机发送单字节命令(命令先给蓝牙芯片,蓝牙芯片通过串口转发给单片机)

第 4 章 巡线

4.1 光电反射传感器模块

① 硬件设计

c 复制代码
光电反射传感器发出红外线,并接收反射回来的红外线,不同颜色的物体对红外线的反射效果不同,白色反射效果最强,黑色最弱
光电反射传感器接收到的反射光线越强,电流通过性越好(阻值越小)
如果光电反射传感器下面是黑线,向比较器输入的电压较大,最终向单片机引脚输入高电平,LED灭

② 软件设计

c 复制代码
车头向左偏,右边的传感器检测到黑线,得到正值的误差
车头向右偏,左边的传感器检测到黑线,得到负值的误差
4.2 应用层

① 使用误差纠偏

② 使用PID算法纠偏

Proportional: 比例、**Integral: **积分、**Derivative: **微分。

第 5 章 模式切换

5.1 独立按键
5,2 模式切换
相关推荐
码农小韩7 小时前
基于Linux的C++学习——指针
linux·开发语言·c++·学习·算法
微露清风7 小时前
系统性学习C++-第十九讲-unordered_map 和 unordered_set 的使用
开发语言·c++·学习
wdfk_prog7 小时前
[Linux]学习笔记系列 -- [fs]seq_file
linux·笔记·学习
行业探路者8 小时前
二维码标签是什么?主要有线上生成二维码和文件生成二维码功能吗?
学习·音视频·语音识别·二维码·设备巡检
li星野8 小时前
OpenCV4X学习—核心模块Core
人工智能·opencv·学习
@zulnger8 小时前
python 学习笔记(多线程和多进程)
笔记·python·学习
凉、介9 小时前
SylixOS 中的 Unix Socket
服务器·c语言·笔记·学习·嵌入式·sylixos
好奇龙猫9 小时前
【人工智能学习-AI-MIT公开课第 19. 架构:GPS、SOAR、包容架构】
人工智能·学习·架构
saoys9 小时前
Opencv 学习笔记:绘制动态随机直线(附实时展示)
笔记·opencv·学习