模块化编程
在写代码的时候,总会有些重复出现的代码,这些代码不需要再进行修改,我们就可以把这些代码存在一个文件里面,避免main文件里出现大量重复代码,提高程序的可阅读性。
.c文件:函数、变量的定义
.h文件:可被外部调用的函数、变量的声明
自定义的变量、函数在调用前要有定义或声明(同一个.c)
使用到的自定义函数的.c文件需要添加到工程参与编译,
使用到的.h文件则要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)
<>就是从安装目录里翻要的东西。
#include就是预编译–>把头文件(.h文件里面的代码拷一份过来)。
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);
#endif
更多的模块化编程例子可以看这里,在本次51单片机的学习过程中用到的全部模块化了的小工具都打包在这了
矩阵按键
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,而在开发板的右下区域,我们可以看见一个4x4规格的按键矩阵,这就是矩阵按键。
通过扫描的方式,可以读出任意按键的状态,即在按键数量较多时,通过矩阵方式进行组合可以减少IO口的占用。
原本的挨个检测变成行列检测,只需要读取行和列就可以定位到你,这种减少方式会在检测点多的时候更加显著。
在51单片机中,按行扫描会导致一些引脚冲突造成的异常,因此建议用按列扫描。
相关模块化的代码看前面链接(MATRIXKEY.H.)
矩阵键盘密码锁
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"
unsigned char KeyNum;
unsigned int Password,Count;
void main()
{
LCD_Init();
LCD_ShowString(1,1,"Password:");
while(1)
{
KeyNum=MatrixKey();
if(KeyNum)
{
if(KeyNum<=10)
{
if(Count<4)
{
Password*=10;
Password+=KeyNum%10;
Count++;
}
LCD_ShowNum(2,1,Password,4);
}
if(KeyNum==11)
{
if(Password==2345) /
{
LCD_ShowString(1,14,"OK ");
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
}
else
{
LCD_ShowString(1,14,"ERR");
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
}
}
if(KeyNum==12)
{
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
}
}
}
}
头文件看前面链接
定时器
51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
定时器/计数器和单片机的 CPU 是相互独立的。定时器/计数器工作的过程是自动完成的,不需要 CPU 的参与。
定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1(简而言之,定时器就是根据脉冲信号,计脉冲数。)。 有了定时器/计数器之后,简单的重复加1的工作可以交给定时器/计数器处理。CPU 转而处理一些复杂的事情。
定时器流水灯
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main()
{
P2=0xFE;
Timer0Init();
while(1)
{
KeyNum=Key();
if(KeyNum)
{
if(KeyNum==1)
{
LEDMode++;
if(LEDMode>=2)LEDMode=0;
}
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18;
TH0 = 0xFC;
T0Count++;
if(T0Count>=500)
{
T0Count=0;
if(LEDMode==0)
P2=_crol_(P2,1);
if(LEDMode==1)
P2=_cror_(P2,1);
}
}
串口
通过串口(UART)让单片机与计算机进行通信。51单片机发送数据,计算机通过串行口接收数据;或者由计算机发送数据,单片机接收数据。
串口通信
发送数据的过程是:先把要传的数据写入发送缓冲区SBUF,以一定的波特率,通过发送数据源串口TXD,把数据发送出去。接收数据的过程是:数据通过RXD,以一定的波特率,写入SBUF。
单片机不管是发送数据还是接收数据,首先都应该对串口进行初始化。
初始化包括串口控制寄存器SCON、电源及波特率选择寄存器PCON、辅助寄存器AUXR的赋值,波特率的确定,中断是否开放。
串口发送程序
#include <REGX52.H>
void UartInit() //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
ET1 = 0; //禁止定时器1中断
}
void Uartsend(unsigned char byte)//发送
{
SBUF=byte;//把数据写入发送缓冲区SBUF
//数据发送完成的标志是TI=1;所以等待数据传送完
while(TI==0);
TI=0;//软件清零
}
int main()
{
UartInit();
Uartsend('a');
while(1)
{
}
}
LED点阵屏
LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。
LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已;
LED点阵屏与数码管一样,有共阴和共阳两种接法,不同的接法对应的电路结构不同;
LED点阵屏需要进行逐行或逐列扫描,才能使所有LED同时显示;
首先由SER端口输入串行数据8位。SRCLK时钟初始化后为低电平,当SRCLK受到一个上升沿信号时,串行数据就向移位寄存器中下移移位,直至8个上升沿信号,8位串行数据全部进入移位寄存器。RCLK时钟初始化后为低电平,当RCLK受到一个上升沿信号时,移位寄存器的8位数据全部并行移位至数据存储缓冲器,再经OA-OH进行输出(实现3线8 位串行输入、并行输出);数据输入的时候是高位先输入,低位后输入。
如果移位寄存器8位填满后,继续进行SRCLK受到一个上升沿信号,数据进行移位,OH’此时若连接下一片芯片的SER,则数据就会移至下一片芯片,从而实现数据多位扩展。
LED点阵屏显示动画
#include <REGX52.H>
#include "Delay.h"
#include "MatrixLED.h"
unsigned char code Animation[]={
0x3C,0x42,0xA9,0x85,0x85,0xA9,0x42,0x3C,
0x3C,0x42,0xA1,0x85,0x85,0xA1,0x42,0x3C,
0x3C,0x42,0xA5,0x89,0x89,0xA5,0x42,0x3C,
};
void main()
{
unsigned char i,Offset=0,Count=0;
MatrixLED_Init();
while(1)
{
for(i=0;i<8;i++)
{
MatrixLED_ShowColumn(i,Animation[i+Offset]);
}
Count++;
if(Count>15)
{
Count=0;
Offset+=8;
if(Offset>16)
{
Offset=0;
}
}
}
}
DS1302实现时钟
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
void TimeShow(void)
{
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,4,DS1302_Time[4],2);
LCD_ShowNum(2,7,DS1302_Time[5],2);
}
void TimeSet(void)
{
if(KeyNum==2)
{
TimeSetSelect++;
TimeSetSelect%=6;
}
if(KeyNum==3)
DS1302_Time[TimeSetSelect]++;
if(DS1302_Time[0]>99){DS1302_Time[0]=0;}
if(DS1302_Time[1]>12){DS1302_Time[1]=1;}
if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)
{
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
{
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0)
{
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
}
}
if(DS1302_Time[3]>23){DS1302_Time[3]=0;}
if(DS1302_Time[4]>59){DS1302_Time[4]=0;}
if(DS1302_Time[5]>59){DS1302_Time[5]=0;}
}
if(KeyNum==4)
{
DS1302_Time[TimeSetSelect]--;
if(DS1302_Time[0]<0){DS1302_Time[0]=99;}
if(DS1302_Time[1]<1){DS1302_Time[1]=12;}
if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
}
}
if(DS1302_Time[3]<0){DS1302_Time[3]=23;}
if(DS1302_Time[4]<0){DS1302_Time[4]=59;}
if(DS1302_Time[5]<0){DS1302_Time[5]=59;}
}
imeSetFlashFlag
if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1," ");}
else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4," ");}
else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7," ");}
else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1," ");}
else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4," ");}
else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7," ");}
else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
}
void main()
{
LCD_Init();
DS1302_Init();
Timer0Init();
LCD_ShowString(1,1," - - ");
LCD_ShowString(2,1," : : ");
DS1302_SetTime();
while(1)
{
KeyNum=Key();
if(KeyNum==1)
{
if(MODE==0){MODE=1;TimeSetSelect=0;}
else if(MODE==1){MODE=0;DS1302_SetTime();}
}
switch(MODE)
{
case 0:TimeShow();break;
case 1:TimeSet();break;
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18;
TH0 = 0xFC;
T0Count++;
if(T0Count>=500)
{
T0Count=0;
TimeSetFlashFlag=!TimeSetFlashFlag;
}
}