嵌入式学习记录- 9-1~10-3

9-1 LED点阵屏

2023 04 25

74HC695 串行输入并行输出的移位寄存器

74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。

C51的sr、sbit

sfr(special function register):特殊功能寄存器声明
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80

sbit(special bit):特殊位声明
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1
声明P0寄存器的第1位

LED点阵屏显示图形

#include <REGX52.H>
#include "Delay.h"

sbit RCK=P3^5;// 上升沿锁存 RCLK
sbit SCK=P3^6;// 上升沿移位 时钟 SERCLK
sbit SER=P3^4;// 数据输入

#define MATRIX_LED_PORT P0

/**
 * @brief 74HC595 写入一个字节
 * @param Byte 要写入的字节
 */
void _74HC595_WriteByte(unsigned char Byte){
    unsigned char i;
    for(i = 0;i < 8;i++){
        SER = Byte&(0x80 >> i);// 非零数据赋给sbit时为1
        SCK = 1;// 上升沿来临,写入数据SER到移位寄存器中
        SCK = 0;
    }
    RCK = 1;// 上升沿来临,数据输出
    RCK = 0;
}
/**
 * @brief LED点阵屏写入一列的数据
 * @param Column 要选择的列,0~7,0在最左边
 * @param Data 选择列显示的数据,0~0xFF,高位在上,1为亮
 */
void MatrixLED_ShowColumn(unsigned char Column,Data) {
    _74HC595_WriteByte(Data);
    MATRIX_LED_PORT = ~(0x80 >> Column);
    delay(1);
    MATRIX_LED_PORT = 0xFF;
}

int main() {
    SCK = 0;
    RCK = 0;
    while(1){
        MatrixLED_ShowColumn(0, 0x80);
        MatrixLED_ShowColumn(1, 0x40);
        MatrixLED_ShowColumn(2, 0x20);
        MatrixLED_ShowColumn(3, 0x10);
        MatrixLED_ShowColumn(4, 0x08);
        MatrixLED_ShowColumn(5, 0x04);
        MatrixLED_ShowColumn(6, 0x02);
        MatrixLED_ShowColumn(7, 0x01);
    }
}

9-2 LED显示屏显示动画

2023 04 25

main.c

#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
#include "Timer0.h"

unsigned char Animation[] = {
        0xFF,0x08,0x08,0x08,0xFF,0x00,0x0E,0x15,0x15,0x08,0x00,0x20,0x1E,0x01,0x00,0x20,
        0x1E,0x01,0x00,0x06,0x09,0x09,0x06,0x00,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,0x0
};
unsigned char n = 32;

unsigned char Offset;

int main() {
    MatrixLED_Init();
    Timer0Init();
    while(1){
        unsigned char i;
        for(i = 0;i < 8;i++){
            MatrixLED_ShowColumn(i, Animation[(i + Offset) % n]);
        }
    }
}

void timer0_Routine() interrupt 1{
    static unsigned int T0Count;
    TL0 = 0x66;		//设置定时初值
    TH0 = 0xFC;		//设置定时初值
    T0Count++;
    if(T0Count>= 300){
        if(Offset >= 31){
            Offset = 0;
        }else Offset++;
        T0Count = 0;
    }
}

MatrixLED.c

#include <REGX52.H>
#include "Delay.h"

sbit RCK=P3^5;// 上升沿锁存 RCLK
sbit SCK=P3^6;// 上升沿移位 时钟 SERCLK
sbit SER=P3^4;// 数据输入

#define MATRIX_LED_PORT P0

/**
 * @brief 74HC595 写入一个字节
 * @param Byte 要写入的字节
 */
void _74HC595_WriteByte(unsigned char Byte){
    unsigned char i;
    for(i = 0;i < 8;i++){
        SER = Byte&(0x80 >> i);// 非零数据赋给sbit时为1
        SCK = 1;// 上升沿来临,写入数据SER到移位寄存器中
        SCK = 0;
    }
    RCK = 1;// 上升沿来临,数据输出
    RCK = 0;
}
/**
 * @brief LED点阵屏写入一列的数据
 * @param Column 要选择的列,0~7,0在最左边
 * @param Data 选择列显示的数据,0~0xFF,高位在上,1为亮
 */
void MatrixLED_ShowColumn(unsigned char Column,Data) {
    _74HC595_WriteByte(Data);
    MATRIX_LED_PORT = ~(0x80 >> Column);
    delay(1);
    MATRIX_LED_PORT = 0xFF;
}

/**
 * @brief 点阵屏初始化
 */
void MatrixLED_Init(){
    SCK = 0;
    RCK = 0;
}

MatrixLED.h

#ifndef __MATRIXLED_H__
#define __MATRIXLED_H__

void _74HC595_WriteByte(unsigned char Byte);

void MatrixLED_ShowColumn(unsigned char Column, Data);

void MatrixLED_Init();

#endif

10-1 DS1302

2023 04 25

操作方法与移位寄存器相似,建议自行查看DS1302手册

类似于SPI通信接口

引脚名作用引脚名作用
VCC2主电源CE芯片使能
VCC1备用电池IO数据输入/输出
GND电源地SCLK串行时钟
X1、X232.768KHz晶振

BCD码

BCD码(Binary Coded Decimal‎),用4位二进制数来表示1位十进制数

例:0001 0011表示13,1000 0101表示85,0001 1010不合法

在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)

十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)

10-2 DS1302时钟

2023 04 25

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

unsigned char Second,i;

int main() {
    LCD_Init();
    DS1302_Init();
    LCD_ShowString(1, 1, "  -  -");
    LCD_ShowString(2, 1, "  :  :");

    DS1302_SetTime();
    while(1){
        DS1302_ReadTime();
        LCD_ShowNum(1, 1, DS1302_Time[0], 2);
        LCD_ShowNum(1, 4, DS1302_Time[1], 2);
        LCD_ShowNum(1, 7, DS1302_Time[2], 2);
        LCD_ShowNum(2, 1, DS1302_Time[3], 2);
        LCD_ShowNum(2, 4, DS1302_Time[4], 2);
        LCD_ShowNum(2, 7, DS1302_Time[5], 2);
    }
}

DS1302.c

#include <REGX52.H>

sbit DS1302_SCLK = P3^6;// 时钟
sbit DS1302_IO = P3^4;// 数据传输
sbit DS1302_CE = P3^5;// 使能

#define DS1302_SECOND   0x80
#define DS1302_MINUTE   0x82
#define DS1302_HOUR   0x84
#define DS1302_DATE   0x86
#define DS1302_MONTH   0x88
#define DS1302_DAY   0x8A
#define DS1302_YEAR   0x8C
#define DS1302_WP   0x8E // 写保护

// 年,月,日,时,分,秒,星期
unsigned char DS1302_Time[] = {23,4,25,19,59,55,2};

void DS1302_Init(){
    DS1302_CE=0;
    DS1302_SCLK=0;
}

/**
 * @brief 16进制数字转10进制,只有适用于两位数字
 * @param HexNumber 16进制数字
 * @return 十进制数字
 */
unsigned char DS1302_HexToDec(unsigned char HexNumber){
    return (HexNumber)/16*10 + HexNumber%16;
}

/**
 * @brief 10进制数字转16进制,只有适用于两位数字
 * @param DecNumber 16进制数字
 * @return 十进制数字
 */
unsigned char DS1302_DecToHex(unsigned char DecNumber){
    return (DecNumber)/10*16 + DecNumber%10;
}

/**
 * @brief DS1302 依据Command写入Data到DS1302内寄存器,在写入前需要关闭写保护
 * DS1302_WriteByte(DS1302_WP,0x00); 关闭写保护语句
 * DS1302_WriteByte(DS1302_WP,0x80); 开启写保护语句
 * @param Command 命令字
 * @param Data 需要写入的数据
 */
void DS1302_WriteByte(unsigned char Command,Data){
    unsigned char i;
    DS1302_CE = 1;

    // 写入命令
    for(i = 0;i < 8;i++){
        DS1302_IO=Command&(0x01 << i);// 从第0位开始输入

        // 由于单片机运算速度较慢,这里不需要延时,如果时较快的单片机需要考虑硬件能承受的最大时钟最快频率(在手册中都会有对操作的规定)
        DS1302_SCLK = 1;
        DS1302_SCLK = 0;
    }

    // 写入数据
    for(i = 0;i < 8;i++){
        DS1302_IO=Data&(0x01 << i);
        DS1302_SCLK = 1;
        DS1302_SCLK = 0;
    }

    DS1302_CE = 0;
}

/**
 * @brief 根据Command读取DS1302内寄存器数据
 * @param Command 命令字
 * @return 读取到的数据
 */
unsigned char DS1302_ReadByte(unsigned char Command){
    unsigned char i,Data = 0x00;
    Command |= 0x01;// 设置为读取(免得宏定义那么多东西)
    DS1302_CE = 1;

    // 写入命令
    for(i = 0;i < 8;i++){
        DS1302_IO=Command&(0x01 << i);
        DS1302_SCLK = 1;
        DS1302_SCLK = 0;
    }

    // 读出数据
    for(i = 0;i < 8;i++){
        DS1302_SCLK = 0;// 下降沿时DS1302给出数据
        if(DS1302_IO) Data |= (0x01 << i);
        DS1302_SCLK = 1;
    }

    DS1302_SCLK = 0;
    DS1302_CE = 0;
    DS1302_IO = 0;// 将IO口置零否则下次读出的数据会有问题

    return Data;
}

/**
 * @brief 将Time数组内时间写入到DS1302
 */
void DS1302_SetTime(){
    DS1302_WriteByte(DS1302_WP,0x00);// 关闭写保护

    DS1302_WriteByte(DS1302_YEAR, DS1302_DecToHex(DS1302_Time[0]));
    DS1302_WriteByte(DS1302_MONTH, DS1302_DecToHex(DS1302_Time[1]));
    DS1302_WriteByte(DS1302_DATE, DS1302_DecToHex(DS1302_Time[2]));
    DS1302_WriteByte(DS1302_HOUR, DS1302_DecToHex(DS1302_Time[3]));
    DS1302_WriteByte(DS1302_MINUTE, DS1302_DecToHex(DS1302_Time[4]));
    DS1302_WriteByte(DS1302_SECOND, DS1302_DecToHex(DS1302_Time[5]));
    DS1302_WriteByte(DS1302_DAY, DS1302_DecToHex(DS1302_Time[6]));

    DS1302_WriteByte(DS1302_WP,0x80);// 开启写保护
}

/**
 * @brief 将时间赋值到Time数组
 */
void DS1302_ReadTime(){
    DS1302_Time[0] = DS1302_HexToDec(DS1302_ReadByte(DS1302_YEAR));
    DS1302_Time[1] = DS1302_HexToDec(DS1302_ReadByte(DS1302_MONTH));
    DS1302_Time[2] = DS1302_HexToDec(DS1302_ReadByte(DS1302_DATE));
    DS1302_Time[3] = DS1302_HexToDec(DS1302_ReadByte(DS1302_HOUR));
    DS1302_Time[4] = DS1302_HexToDec(DS1302_ReadByte(DS1302_MINUTE));
    DS1302_Time[5] = DS1302_HexToDec(DS1302_ReadByte(DS1302_SECOND));
    DS1302_Time[6] = DS1302_HexToDec(DS1302_ReadByte(DS1302_DAY));
}

DS1302.h

#ifndef __DS1302_H__
#define __DS1302_H__

extern unsigned char DS1302_Time[];// extern如果不是数组必须要加

void DS1302_Init();
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime();
void DS1302_ReadTime();

#endif

10-3 DS1302可调时钟

2023 04 25

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"

unsigned char KeyNum,MODE,TimeSetSelect,Flag;
unsigned char DatesByMonth[] = {23,28,31,30,31,30,31,31,30,31,30,31};

void Init();
void TimeShow();
void TimeSet();

int main() {
    Init();
    while(1){
        KeyNum = Key();
        if(KeyNum == 1) {
            MODE ^= 1;
            if(MODE == 0){
                DS1302_SetTime();
                TimeSetSelect = 0;
            }
        };
        switch(MODE){
            case 0: DS1302_ReadTime();TimeShow();break;
            case 1: TimeSet();break;
        }
    }
}

/**
 * @brief 初始化
 */
void Init(){
    LCD_Init();
    DS1302_Init();
    Timer0Init();
    LCD_ShowString(1, 1, "  -  -");
    LCD_ShowString(2, 1, "  :  :");

    DS1302_SetTime();
}

/**
 * @brief 实时时钟模式
 */
void TimeShow(){
    if(MODE == 1 && Flag){
        switch(TimeSetSelect){
            case 0: LCD_ShowString(1,1,"  ");break;
            case 1: LCD_ShowString(1,4,"  ");break;
            case 2: LCD_ShowString(1,7,"  ");break;
            case 3: LCD_ShowString(2,1,"  ");break;
            case 4: LCD_ShowString(2,4,"  ");break;
            case 5: LCD_ShowString(2,7,"  ");break;
        }
        if(TimeSetSelect != 0) LCD_ShowNum(1, 1, DS1302_Time[0], 2);
        if(TimeSetSelect != 1) LCD_ShowNum(1, 4, DS1302_Time[1], 2);
        if(TimeSetSelect != 2) LCD_ShowNum(1, 7, DS1302_Time[2], 2);
        if(TimeSetSelect != 3) LCD_ShowNum(2, 1, DS1302_Time[3], 2);
        if(TimeSetSelect != 4) LCD_ShowNum(2, 4, DS1302_Time[4], 2);
        if(TimeSetSelect != 5) LCD_ShowNum(2, 7, DS1302_Time[5], 2);
    }else{
        LCD_ShowNum(1, 1, DS1302_Time[0], 2);
        LCD_ShowNum(1, 4, DS1302_Time[1], 2);
        LCD_ShowNum(1, 7, DS1302_Time[2], 2);
        LCD_ShowNum(2, 1, DS1302_Time[3], 2);
        LCD_ShowNum(2, 4, DS1302_Time[4], 2);
        LCD_ShowNum(2, 7, DS1302_Time[5], 2);
    }

}

/**
 * @brief 设置时间模式
 */
void TimeSet() {
    unsigned char MaxDate = 31;
    switch(KeyNum){
        case 2:{
            TimeSetSelect++;
            TimeSetSelect%=6;
            break;
        }
        case 3:{
            DS1302_Time[TimeSetSelect]--;
            break;
        }
        case 4:{
            DS1302_Time[TimeSetSelect]++;
            break;
        }
    }
    DS1302_Time[0] = (DS1302_Time[0] + 100) % 100;// 年限制
    DS1302_Time[1] = (DS1302_Time[1] - 1 + 12) % 12 + 1;// 月限制
    {
        MaxDate = DatesByMonth[DS1302_Time[1]];
        if (MaxDate == 28 && DS1302_Time[1] != 0 && DS1302_Time[1] % 4 == 0) MaxDate = 29;
        DS1302_Time[2] = (DS1302_Time[2] - 1 + MaxDate) % MaxDate + 1;//日限制

    }
    DS1302_Time[3] = (DS1302_Time[3] + 24) % 24;// 时限制
    DS1302_Time[4] = (DS1302_Time[4] + 60) % 60;// 分限制
    DS1302_Time[5] = (DS1302_Time[5] + 60) % 60;// 秒限制
    TimeShow();
}

/**
 * @brief 中断函数,用于时间设置模式闪烁
 */
void timer0_Routine() interrupt 1{
    static unsigned int T0Count;
    TL0 = 0x66;		//设置定时初值
    TH0 = 0xFC;		//设置定时初值
    T0Count++;
    T0Count%=1000;
    Flag = T0Count / 500;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值