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、X2 | 32.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;
}