前言:
从前面的学习中,新增了定时器模块,现在有延时函数,数码管显示,按键检测(包括矩阵键盘和独立按键),定时器模块
定时器模块如下:
#include <REGX52.H>
//定时器0配置
void Timer0_Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/*void Timer0_Routine() interrupt 1
{
static unsigned int T0count;
TL0 = 0x66;
TH0 = 0xFC;
T0count++;
if(T0count>=){
T0count=0;
}
}
*/
//定时器1配置
void Timer1_Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0x66; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1=1;
EA=1;
PT0=0;
}
/*
void Timer1_Routine() interrupt 3
{
static unsigned int T1count;
TL1 = 0x66;
TH1 = 0xFC;
T1count++;
if(T1count>=){
}
}
*/
一.串口
1.串口介绍
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
2.硬件电路
简单双向串口通信有两根通信线(发送端TXD和接收端RXD)
TXD与RXD要交叉连接
当只需单向的数据传输时,可以直接一根通信线
当电平标准不一致时,需要加电平转换芯片
3.电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
TTL电平:+5V表示1,0V表示0
RS232电平:-3 ~ -15V表示1,+3 ~ +15V表示0
RS485电平:两线压差+2 ~ +6V表示1,-2 ~ -6V表示0(差分信号)
4.接口及引脚定义
5.相关术语
全双工:通信双方可以在同一时刻互相传输数据
半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
单工:通信只能有一方发送到另一方,不能反向传输
异步:通信双方各自约定通信速率
同步:通信双方靠一根时钟线来约定通信速率
总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)
6.UART
STC89C52有1个UART
STC89C52的UART有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变
7.串口参数及时序图
波特率:串口通信的速率(发送和接收各数据位的间隔时间)
检验位:用于数据验证
停止位:用于数据帧间隔
8.串口模式图
SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器
9.串口相关寄存器
10.配置相关寄存器
void Uart1_Init(void) //4800bps@11.0592MHz
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x40; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF4; //设置定时初始值
TH1 = 0xF4; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
}
可以在stc-isp中计算波特率,生成代码
void Uart1_SendByte(unsigned char Byte){
SBUF=Byte;
while(TI==0);
TI=0;
}
通过串口发送完后TI自动置1,需要用软件复位
在循环中使用串口的时候,有可能会出现错误,解决方案是加一个延时函数
11.串口的模块化
把初始化和串口发送字符的函数放进模块即可
12.电脑通过串口控制单片机LED
1 串口初始化和发送的时候有差距,需要有中断,这需要将REN置0,并且加上中断相关的寄存器设置
void Uart1_Init(void)
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF4; //设置定时初始值
TH1 = 0xF4; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
EA=1;
ES=1;
}
并且定义中断函数
void Uart_Routine() interrupt 4
{
// EI与RI占用同一个通道,发送和接受都会触发这个中断函数,因此需要此判断
if(RI==1){
P2=~SBUF;
RI=0;
}
}
在中断函数中加入Uart1_SendByte函数即可向电脑返回数据
13.波特率的计算
定时器溢出率= 定时器时钟频率 / (12 × (2^SM1) × (256 - TL1))
在STC89C52RC中
溢出率=11.0592/(12*(2^0)*(256-0xF4))=0.0768
SMOD=1
波特率=溢出率/16=0.0048MHz=4800Hz
14.数据显示模式
HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
文本模式/字符模式:以原始数据编码(ASCLL码)后的形式显示
二.点阵屏
1.介绍
LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等
LED点阵屏分类
按颜色:单色、双色、全彩
按像素:8*8、16*16等(大规模的LED点阵通常由很多个小点阵拼接而成)
2.点阵屏的显示原理
LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已
LED点阵屏与数码管一样,有共阴和共阳两种接法,不同的接法对应的电路结构不同
LED点阵屏需要进行逐行或逐列扫描,才能使所有LED同时显示
单色(引脚乱序排序):
双色:
3.74HC595
74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展
其对应原理图如下:
扩展出D0~D7口,而后开发板点阵屏引脚对应关系如下:
4.C51的sfr、sbit
sfr(special function register):特殊功能寄存器声明
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80
sbit(special bit):特殊位声明
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1;
声明P0寄存器的第1位
可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作
5.LED点阵屏显示图形
首先定义74HC595的函数
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++){
SER_=Byte&(0x80>>i); //读位
SRCLK_=1; //移位
SRCLK_=0;
}
RCLK_=1; //锁存
RCLK_=0;
}
然后在主函数中要提前将SRCLK_,RCLK_置零
点阵屏显示函数
void MatrixLED_ShowColumn(unsigned char column,Data){
_74HC595_WriteByte(Data);
P0=~(0x80>>column);
}
其中74HC595控制显示每一列的LED,P0口控制显示的列
同数码管一样,点阵屏也需要消影,否则会影响旁边LED的显示
在原基础上加
Delay(1);
MATRIX_LED_PORT=0xFF;
6.用动画显示Hello!
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
#include "Timer.h"
unsigned char code Animation[]={ //用code可以减少占用内存,但不可更改
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //防止影响观感
0xFF,0x08,0x08,0xFF,0x00,0x0E,0x15,0x15,
0x15,0x09,0x00,0xFE,0x01,0x01,0x02,0x00,
0xFE,0x01,0x01,0x02,0x00,0x00,0x0E,0x11,
0x11,0x11,0x0E,0x00,0xFD,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //防止影响观感
};
unsigned char i,offset;
void main(){
Matrix_Init();
Timer0_Init();
while(1){
for(i=0;i<8;i++){
MatrixLED_Show(i,Animation[i+offset]);
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0count;
TL0 = 0x66;
TH0 = 0xFC;
T0count++;
if(T0count>=100){
T0count=0;
offset++;
if(offset>40)
offset=0;
}
}
三.DS1302时钟
1.介绍
DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能
RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
优点:
相较于定时器,DS1302可以在断电的时候也能在后台工作,重新启动时可以正确显示时间,不会像定时器那样占用CPU
(可惜的是,此开发板并未接备用电池,没有掉电运行的功能)
2.引脚定义及应用电路
引脚名 | 作用 |
---|---|
VCC2 | 主电源 |
VCC1 | 备用电池 |
GND | 电源地 |
X1、X2 | 32.768KHz晶振 |
CE | 芯片使能 |
IO | 数据输入/输出 |
SCLK | 串行时钟 |
3.内部结构框图
4.寄存器定义
5.时序定义
6.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)
在DS1302中数据储存的形式是以BCD码储存的,因此需要对其进行运算转为十进制数,否则会显示错误
7.DS1302模块化
#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_YEAR 0x8C
#define DS1302_DAY 0x8A
#define DS1302_WP 0x8E
unsigned char DS1302_Time[]={23,12,12,19,28,55,2};
//年月日时分秒,星期,写保护
unsigned char DS1302_Parameter[]={DS1302_YEAR,DS1302_MONTH,DS1302_DATE,DS1302_HOUR,DS1302_MINUTE,DS1302_SECOND,DS1302_DAY,DS1302_WP};
/**
* @brief DS1302初始化
* @param 无
* @retval 无
*/
void DS1302_Init(){DS1302_CE=0;DS1302_SCLK=0;}
/**
* @brief 写入一个字节
* @param Command 命令标识,用于判断写入的地址
Data 写入的字节
* @retval 无
*/
void DS1302_WriteByte(unsigned char Command,unsigned char Data){
unsigned char i;
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_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
/**
* @brief 读取一个字节
* @param Command命令标识
* @retval 读取出的字节
*/
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=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++){
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0;
return Data;
}
/**
* @brief BCD码转换
* @param Mod 转为()进制 10为转换为十进制,16为转换为16进制
Data 需要转换的数据
* @retval 转换后的结果
*/
unsigned char BCD_Transform(unsigned char Mod,unsigned char Data){
if(Mod==10){Data=Data/16*10+Data%16;}
if(Mod==16){Data=Data/10*16+Data%10;}
return Data;
}
/**
* @brief 设置时间
* @param 无
* @retval 无
*/
void DS1302_SetTime(){
unsigned char i;
DS1302_WriteByte(DS1302_Parameter[7],0x00);
for(i=0;i<7;i++){
DS1302_WriteByte(DS1302_Parameter[i],BCD_Transform(16,DS1302_Time[i]));
}
DS1302_WriteByte(DS1302_Parameter[7],0x80);
}
/**
* @brief 写入时间
* @param 无
* @retval 无
*/
void DS1302_ReadTime(){
unsigned char i;
for(i=0;i<7;i++){
DS1302_Time[i]=BCD_Transform(10,DS1302_ReadByte(DS1302_Parameter[i]));
}
}