一、简介
1、
当提到"51单片机"时,通常指的是Intel 8051系列单片机。这是一种非常流行的8位微控制器,最初由Intel公司在1980年代推出。它的名字"51"源于其型号的"80C51",其中"80"表示1980年代,而"C51"则表示是8051系列。
以下是一些关于51单片机的基本信息:
(1)、体系结构: 8051是一种基于哈佛结构的单片机,具有8位数据总线、16位地址总线和一组通用寄存器。它包含了CPU、RAM、ROM(或Flash Memory)、定时器/计数器、串行通信控制器(UART)等基本组件。
(2)、存储: 8051通常包括内部ROM(或Flash Memory)用于存储程序代码,以及RAM用于存储数据。外部存储器可以通过外部总线扩展。
(3)、时钟: 它工作在低至4MHz的时钟频率下,通常是晶振振荡器提供时钟信号。
(4)、输入/输出: 8051具有通用输入/输出引脚,可以通过编程配置为数字输入或输出。此外,它还有串行通信端口(UART)用于与其他设备通信。
(5)、中断: 8051支持中断,可以通过硬件或软件触发。中断是一种机制,允许程序在执行过程中响应外部事件。
(6)、定时器/计数器: 8051有一个或多个定时器/计数器,用于进行时间测量或生成定时脉冲。
(7)、应用领域: 8051系列单片机被广泛应用于嵌入式系统和各种控制应用,包括家电、汽车电子、工业自动化等。
(8)、编程语言: 8051可以使用汇编语言或高级语言(如C语言)进行编程。
需要注意的是,虽然Intel首先推出了8051系列,但由于其开放的架构,其他制造商也生产了兼容的版本,因此有许多不同厂商生产的51单片机
2、
二、环境
1、软件:
Keil5 编写代码
STC_ISP 将代码下载到单片机上
2、安装下载驱动:
3、编写一个程序,并下载到单片机上
打开keil5,新建一个项目,新建文件夹
编写程序,编译程序(设置创建文件(用于下载到单片机))
下载型号,安装文件
三、各个模块的使用
1、LED灯
1、电路
2、令一个灯亮
#include <REGX52.H>
void main(){
P2=0xFE; //1111 1110
while(1){} //为了防止程序不空转
}
3、闪烁
#include <REGX52.H>
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void main(){
while(1){
P2=0xFE; //1111 1110
Delay1ms(1000);
P2=0xFF; //1111 1111
Delay1ms(1000);
}
}
4、流水灯
#include <REGX52.H>
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void main(){
unsigned int flag=0;
P2=0xFE;//1111 1110
while(1){
flag=(P2&0x80)>>7;
Delay1ms(1000);
P2=(P2<<=1)|flag;
}
}
2、独立按钮
1、电路
2、按键抖动
3、按下后 改变灯的状态
#include <REGX52.H>
void main(){
while(1){
if(P3_5==0){
P2_0=0;
}else{
P2_0=1;
}
}
}
4、松开按钮后 改变灯的状态
#include <REGX52.H>
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void main(){
//unsigned int flag=0;
while(1){
if(P3_5==0){
Delay1ms(20);//检测按下
while(P3_5==0);
Delay1ms(20);//确认松手
P2_0=~P2_0;//状态取反
}
}
}
5、加法操作
#include <REGX52.H>
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void main(){
unsigned char LEDNum=0;
while(1){
if(P1_5==0){
Delay1ms(20);//检测按下
while(P1_5==0);
Delay1ms(20);//确认松手
LEDNum++;//自加一操作
P2=~LEDNum;
}
}
}
6、左移与右移
#include <REGX52.H>
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void main(){
unsigned char LEDNum=0;
P2=~0x01;
while(1){
if(P1_7==0){
Delay1ms(20);//检测按下
while(P1_7==0);
Delay1ms(20);//确认松手
LEDNum++;//加一
if(LEDNum>=8)//越界检查
LEDNum=0;
P2=~(0x01<<LEDNum);
}
if(P1_5==0){
Delay1ms(20);//检测按下
while(P1_5==0);
Delay1ms(20);//确认松手
LEDNum--;//减一
if(-1==LEDNum)//越界检查
LEDNum=7;
P2=~(0x01<<LEDNum);
}
}
}
3、数码管
电路图:
74HC1380译码器:
p01-p07
控制显示哪个数字
P22、P23、P24
控制74HC138译码器,来决定显示哪个数字
(p10-p13 控制显示哪个数字)
1、在指定的位置显示一个指定的数字
#include <REGX52.H>
#include "Delay.h"
unsigned char number_array[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void print_number(unsigned char index,unsigned char value){
if(value>10||value<0)return;
P2_2=0;
P2_3=0;
P2_4=0;//显示下个数字前先熄灯,消除残影
switch (index){
case 0:
P2_2=1;
P2_3=1;
P2_4=1;
break;
case 1:
P2_2=0;
P2_3=1;
P2_4=1;
break;
case 2:
P2_2=1;
P2_3=0;
P2_4=1;
break;
case 3:
P2_2=0;
P2_3=0;
P2_4=1;
break;
case 4:
P2_2=1;
P2_3=1;
P2_4=0;
break;
case 5:
P2_2=0;
P2_3=1;
P2_4=0;
break;
case 6:
P2_2=1;
P2_3=0;
P2_4=0;
break;
case 7:
P2_2=0;
P2_3=0;
P2_4=0;
break;
default:
break;
}
P0=number_array[value];
Delay(1);//显示一毫秒
}
void main(){
print_number(0,1);
while(1){}
}
2、在8个位置显示8个不同的数字
#include <REGX52.H>
#include "Delay.h"
unsigned char number_array[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void print_number(unsigned char index,unsigned char value){
if(value>10||value<0)return;
P2_2=0;
P2_3=0;
P2_4=0;//显示下个数字前先熄灯,消除残影
switch (index){
case 0:
P2_2=1;
P2_3=1;
P2_4=1;
break;
case 1:
P2_2=0;
P2_3=1;
P2_4=1;
break;
case 2:
P2_2=1;
P2_3=0;
P2_4=1;
break;
case 3:
P2_2=0;
P2_3=0;
P2_4=1;
break;
case 4:
P2_2=1;
P2_3=1;
P2_4=0;
break;
case 5:
P2_2=0;
P2_3=1;
P2_4=0;
break;
case 6:
P2_2=1;
P2_3=0;
P2_4=0;
break;
case 7:
P2_2=0;
P2_3=0;
P2_4=0;
break;
default:
break;
}
P0=number_array[value];
Delay(1);//显示一毫秒
}
void main(){
while(1){
print_number(0,1);
print_number(1,2);
print_number(2,3);
print_number(3,4);
print_number(4,5);
print_number(5,6);
print_number(6,7);
print_number(7,8);
}
}
4、LCD1602
LCD1602.c:
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
LCD1602.h:
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
main.c:
#include <REGX52.H>
#include "LCD1602.h"
void main(){
//P2=0xFE; //1111 1110
LCD_Init();
//LCD_ShowChar(2,2,'B');
//LCD_ShowString(1,2,"youyou");
//LCD_ShowNum(1,9,123,3);
//LCD_ShowSignedNum(1,13,-66,2);
//LCD_ShowHexNum(2,1,0xA8,2);
LCD_ShowBinNum(2,4,0xDD,8);
while(1){}
}
5、独立矩阵
简单表述下,按下一个按键就会联通两个接口。
举例:
按下S3, P17 和 P11连通
按下S10,P15 和 P12连通
类似cpu从内存中寻找数据。节省IO口
按下一个按键显示一个数字
按下按键1-16,然后在LCD1602上显示出来
matrixKey.c:
#include <REGX52.H>
#include "Delay.h"
unsigned char matrixKey(){
int checkNumber = 0;
char whichColumn = 0;
char whichLine = 0;
//第一步检查被按下的按钮是哪行一的
P1=0xF0;// 1111 0000
// p7 p6 p5 p4 p3 p2 p1 p0
if(0 == P1_4){
//p4列(即第4行)
Delay(20);//等待信号稳定
whichColumn=4;
}else if(0 == P1_5){
//p4列(即第3行)
Delay(20);//等待信号稳定
whichColumn=3;
}else if(0 == P1_6){
//p4列(即第2行)
Delay(20);//等待信号稳定
whichColumn=2;
}else if(0 == P1_7){
//p4列(即第1行)
Delay(20);//等待信号稳定
whichColumn=1;
}else{
//没有被按下
return 0;
}
//第二部检查被按下的按钮是哪一列的
P1=0x0F;//0000 1111
// p7 p6 p5 p4 p3 p2 p1 p0
if(0 == P1_0){
//p4列(即第4列)
while(0 == P1_0);
Delay(20);//等待其松手,后信号稳定
whichLine=4;
}else if(0 == P1_1){
//p4列(即第3列)
while(0 == P1_1);
Delay(20);//等待其松手,后信号稳定
whichLine=3;
}else if(0 == P1_2){
//p4列(即第2列)
while(0 == P1_2);
Delay(20);//等待其松手,后信号稳定
whichLine=2;
}else if(0 == P1_3){
//p4列(即第1列)
while(0 == P1_3);
Delay(20);//等待其松手,后信号稳定
whichLine=1;
}else{//impossible
//没有被按下
return 0;
}
return (whichColumn-1)*4+whichLine;
}
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "matrixKey.h"
void main(){
char which_key = 0;//零代表没有按键被按下
LCD_Init();
while(1){
which_key = matrixKey();
if(which_key){
LCD_ShowNum(1,1,which_key,2);
}
}
}
密码锁:
main.c:
#include <REGX52.H>
#include "LCD1602.h"
#include "matrixKey.h"
#define PASS_WORD 1212
void main(){
char which_key = 0;//零代表没有按键被按下
int input=0;
LCD_Init();
LCD_ShowString(1,1,"Password:");
LCD_ShowString(2,1,"0000");
while(1){
which_key = matrixKey();
if(which_key > 9){
return;//重置
}else if(which_key){
LCD_ShowNum(2,1,which_key,1);//回显
break;
}
}//等待输入
input+=which_key*1000;
while(1){
which_key = matrixKey();
if(which_key > 9){
return;//重置
}else if(which_key){
LCD_ShowNum(2,2,which_key,1);//回显
break;
}
}//等待输入
input+=which_key*100;
while(1){
which_key = matrixKey();
if(which_key > 9){
return;//重置
}else if(which_key){
LCD_ShowNum(2,3,which_key,1);//回显
break;
}
}//等待输入
input+=which_key*10;
while(1){
which_key = matrixKey();
if(which_key > 9){
return;//重置
}else if(which_key){
LCD_ShowNum(2,4,which_key,1);//回显
break;
}
}//等待输入
input+=which_key;
if(PASS_WORD == input){
LCD_Init();
LCD_ShowString(2,1,"OK");
}else{
LCD_ShowString(2,1,"ERROR");
}
while(1){}
}
6、计数器/计时器(晶振、中断)
1、电路(集成在芯片内部):
寄存器:
M1和M2决定使用4中工作模式的中的哪一个
计数输入位置:
晶振(频率是11.0592):
2、利用计时器使得小灯一秒一闪
#include <REGX52.H>
void timer0_init(){
//这个寄存器的名字叫 TMOD,用来设置计时器的工作模式
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01;
TF0=0; //打开通向中断的开关
TR0=1; //允许计数/计时
TH0=64535/256;
TL0=64535%256+1;
/*
TH0 TL0 总共可计数65535
65535 - 64535 =1000,即 1ms
(经过12的分频器,12M变成1M)
*/
// TH0=12000/256;
// TL0=12000%256+1;
ET0=1; //总断的分开关
EA=1; //中断的总开关
PT0=0; //设置中断的优先级别
}
unsigned int T0Count;
void Timer0_Routine() interrupt 1{
TH0=64535/256;
TL0=64535%256;
// TH0=12000/256;
// TL0=12000%256+1;
T0Count++;
if(T0Count>=1000){
T0Count=0;
P2_0=~P2_0;
}
}
void main(){
timer0_init();
while(1){}
}
3、使用按键控制流水灯的方向:
#include <REGX52.H>
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void timer0_init(){
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01;
TF0=0;
TR0=1;
TH0=64535/256;
TL0=64535%256+1;
// TH0=12000/256;
// TL0=12000%256+1;
ET0=1;
EA=1;
PT0=0;
}
unsigned int T0Count;
unsigned char direction=0;//0 left 1 right
void Timer0_Routine() interrupt 1{
unsigned int flag=0;
TH0=64535/256;
TL0=64535%256;
// TH0=12000/256;
// TL0=12000%256+1;
T0Count++;
if(T0Count>=1000){
T0Count=0;
switch(direction){
case 0:
flag=(P2&0x80)>>7;
P2=(P2<<=1)|flag;
break;
case 1:
flag=(P2&0x01)<<7;
P2=(P2>>=1)|flag;
break;
default:
break;
}
}
}
void main(){
P2=0xFE;//1111 1110
timer0_init();
while(1){
if(P1_7==0){
Delay1ms(20);
while(P1_7==0);
Delay1ms(20);
direction=0;
}
if(P1_5==0){
Delay1ms(20);
while(P1_5==0);
Delay1ms(20);
direction=1;
}
}
}
4、计时器
#include <REGX52.H>
unsigned char number_array[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned int T0Count;
unsigned char min=0;
unsigned char sec=0;
void Delay1ms(unsigned int xms){
unsigned char i, j;
while(xms){
i=2;
j=239;
do{
while(--j);
}while(--i);
xms-=1;
}
}
void print_number(unsigned char index,unsigned char value){
if(value>10||value<0)return;
P1_0=0;
P1_1=0;
P1_2=0;
P1_3=0;
switch (index){
case 0:
P1_3=1;
break;
case 1:
P1_2=1;
break;
case 2:
P1_1=1;
break;
case 3:
P1_0=1;
break;
default:
break;
}
P0=number_array[value];
Delay1ms(1);
P0=0x00;
}
void timer0_init(){
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01;
TF0=0;
TR0=1;
TH0=64535/256;
TL0=64535%256+1;
// TH0=12000/256;
// TL0=12000%256+1;
ET0=1;
EA=1;
PT0=0;
}
void Timer0_Routine() interrupt 1{
TH0=64535/256;
TL0=64535%256;
// TH0=12000/256;
// TL0=12000%256+1;
T0Count++;
if(T0Count>=1000){
T0Count=0;
sec++;
if(sec>=60){
min++;
sec=0;
}
if(min>=60){
min=0;
sec=0;
}
}
}
void main(){
timer0_init();
while(1){
print_number(0,min/10);
print_number(1,min%10);
print_number(2,sec/10);
print_number(3,sec%10);
}
}
使用软件配置代码:
7、串口(计时、中断、晶振、CH340C)
电路:
电脑用串口想单片机发送信息
#include <REGX52.H>
#include "Delay.h"
void UART_Init(void){
PCON|=0X80; //是能波特率倍速位 SMOD
SCON = 0X50; //串口以一模式工作 8位数据,可变波特率
TMOD&=0X0F; //
TMOD |=0X20; //设置计时器一 以8位重装的方式工作
TL1=0xF3; //设置定时初值
TH1=0xF3; //设置定时器重装值
ET1=0; //禁止定时器1 发起中断
TR1=1; //启动定时器1
}
void UART_SendByte(unsigned char Byte){
SBUF=Byte; //给传出的 SBUF 赋值
while(0==TI);
TI=0; //重置,等待下次的中断发起
}
void main(){
unsigned char count = 0;
UART_Init();
UART_SendByte(0x11);
while(1){
if(count>0xFF)count=0;
UART_SendByte(count++);
Delay(1000);
}
}
串口工具的使用:
关于4800HZ 的计算过程:
用于软件计算4800HZ 的TL1 TH1:
晶振(频率是11.0592):
#include <REGX52.H>
#include "Delay.h"
void UART_Init(void){
PCON|=0X80; //是能波特率倍速位 SMOD
SCON = 0X50; //串口以一模式工作 8位数据,可变波特率
TMOD&=0X0F; //
TMOD |=0X20; //设置计时器一 以8位重装的方式工作
// TL1=0xF3; //设置定时初值
// TH1=0xF3; //设置定时器重装值 //时钟频率为:12MHZ 时
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值 //时钟频率为:11.0592MHZ 时
ET1=0; //禁止定时器1 发起中断
TR1=1; //启动定时器1
EA=1;
ES=1;//打开串口中断开关
}
void UART_SendByte(unsigned char Byte){
SBUF=Byte; //给传出的 SBUF 赋值
while(0==TI);
TI=0; //重置,等待下次的中断发起
}
void UART_Rountine() interrupt 4
{
if(1==RI){
unsigned char x = SBUF;
RI=0;
P2=~x;
UART_SendByte(x);
}
}
void main(){
UART_Init();
while(1){}
}
8、LED点阵屏(74HC595D)
电路图:
OE 是74HC595的使能控制。
上面有横线,代表低电位有效,所以接线帽需要接GND。
1、显示静态画面(栅栏)
#include <REGX52.H>
#include "Delay.h"
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
unsigned char map_array[8]={0x50,0xFF,0x50,0xFF,0x50,0xFF,0x50,0x50};
void _74H595_WriteByte(unsigned char Byte){
unsigned char i;
for(i=0;i<8;i++){
SER=Byte&(0x80>>i);
SCK=1;//跳一下将SER的值输入D0,D0->D1 , D1->D2 ... D6->D7
SCK=0;
}
RCK=1;//跳一下,将值整体输入一次(到D9-D16)
RCK=0;
}
void main(){
unsigned char i = 0;
//初始化74HC595
SCK=0;
RCK=0;
while(1){
for(i=0; i<8; ++i){
_74H595_WriteByte(map_array[i]);
P0=~(0x80>>i);
Delay(1);
P0=0xFF;//消除残影
}
}
}
2、播放动态文字(JK)
#include <REGX52.H>
#include "Delay.h"
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
//code 可以让数组中的值存入 flash ,节省内存(ROM)空间,但其中的值不可改变
unsigned char code map_array[24]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x42,0x7E,0x40,0x00,0x7E,0x08,0x14,0x22,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
void _74H595_WriteByte(unsigned char Byte){
unsigned char i;
for(i=0;i<8;i++){
SER=Byte&(0x80>>i);
SCK=1;//跳一下将SER的值输入D0,D0->D1 , D1->D2 ... D6->D7
SCK=0;
}
RCK=1;//跳一下,将值整体输入一次(到D9-D16)
RCK=0;
}
void main(){
unsigned char i = 0;
unsigned char j = 0;
unsigned char unit_count = 0;
//初始化74HC595
SCK=0;
RCK=0;
while(1){
for(i=0; i<8; ++i){
_74H595_WriteByte(map_array[i+j]);
P0=~(0x80>>i);
Delay(1);
P0=0xFF;//消除残影
}
if(++unit_count < 10){
continue;
}else if(j++ < 16){
unit_count = 0;
}else{
unit_count = 0;
j = 0;
}
}
}
9、时钟 (DS1302)
电路图:
DS1302的使用说明:
1、计时器
#include <REGX52.H>
#include "LCD1602.h"
#define RTC_READ_SECONDS 0x81
#define RTC_READ_MINUTES 0x83
#define RTC_READ_HOUR 0x85
#define RTC_READ_DATE 0x87
#define RTC_READ_MONTH 0x89
#define RTC_READ_DAY 0x8B
#define RTC_READ_YEAR 0x8D
#define RTC_WRITE_SECOND 0x80
#define RTC_WRITE_MINUTES 0x82
#define RTC_WRITE_HOUR 0x84
#define RTC_WRITE_DATE 0x86
#define RTC_WRITE_MONTH 0x88
#define RTC_WRITE_DAY 0x8A
#define RTC_WRITE_YEAR 0x8C
//初始时间 秒......年
unsigned char set_time[8]={0x00,0x00,0x00,0x01,0x02,0x04,0x24};
//unsigned char set_time[8]={0x55,0x59,0x23,0x28,0x02,0x04,0x01};
//unsigned char set_time[8]={0x55,0x59,0x23,0x28,0x02,0x04,0x04};
//定义DS1302的三个线角
sbit DS1302_SCLK = P3^6;
sbit DS1302_IO = P3^4;
sbit DS1302_CE = P3^5;
//指令须在上升沿写入,所以需要先置0
void DS1302_Init(void){
DS1302_CE = 0;
DS1302_SCLK = 0;
}
void DS1302_WriteByte(unsigned char Command,Data){
unsigned char i;
DS1302_CE = 1;//打开使能,高电平开启
//写入8位的指令
for(i=0;i<8;++i){
DS1302_IO = Command&(0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;//每次上升沿,写入为 DS1302_IO 中的数据
}
//写入8位的数据
for(i=0;i<8;++i){
DS1302_IO = Data&(0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
DS1302_CE = 0;//关闭使能,高电平开启
}
unsigned char DS1302_ReadByte(unsigned char Command){
unsigned char i,Data = 0x00;
DS1302_CE = 1;
//写入8位的指令
for(i=0;i<8;++i){
DS1302_IO = Command&(0x01<<i);
DS1302_SCLK = 0;
DS1302_SCLK = 1;
/*
第一个读出数据的下降沿与最后一个写入指令的上升沿是紧挨着的,
所以需要保证在完成写入读指令后,
SCLK保持高电平,等做好读取准备的时候在将其赋值为0
*/
}
DS1302_IO = 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;//这里不置0,下次读就会出问题。我不知道具体是为什么(据说是因为 P3_4口 没有上拉电阻)
/*
这里写
DS1302_IO = 0;
DS1302_IO = 1;
或
DS1302_IO = 0;
都可以的,所以所需要强下拉下,后面才能正常使用
*/
//DS1302_IO = 1;
return Data;
}
void main(){
unsigned char Second = 0;
LCD_Init();
DS1302_Init();
LCD_ShowString(1,1,"20");
LCD_ShowString(1,5,"-");
LCD_ShowString(1,8,"-");
LCD_ShowString(2,3,"-");
LCD_ShowString(2,6,"-");
/*
如果读取时间为一个大于59并且不动的数,
则芯片有可能是处于写保护状态,在此处加上
DS1302_WriteByte(0x8E,0x00);
即可解除芯片写保护
*/
DS1302_WriteByte(0x8E,0x00);//解除芯片写保护
//写入初始时间
DS1302_WriteByte(RTC_WRITE_SECOND,set_time[0]);
DS1302_WriteByte(RTC_WRITE_MINUTES,set_time[1]);
DS1302_WriteByte(RTC_WRITE_HOUR,set_time[2]);
DS1302_WriteByte(RTC_WRITE_DATE,set_time[3]);
DS1302_WriteByte(RTC_WRITE_MONTH,set_time[4]);
//DS1302_WriteByte(RTC_WRITE_DAY,set_time[5]);
DS1302_WriteByte(RTC_WRITE_YEAR,set_time[6]);
while(1){
set_time[0] = DS1302_ReadByte(RTC_READ_SECONDS);
set_time[1] = DS1302_ReadByte(RTC_READ_MINUTES);
set_time[2] = DS1302_ReadByte(RTC_READ_HOUR);
set_time[3] = DS1302_ReadByte(RTC_READ_DATE);
set_time[4] = DS1302_ReadByte(RTC_READ_MONTH);
//set_time[5] = DS1302_ReadByte(RTC_READ_DAY);
set_time[6] = DS1302_ReadByte(RTC_READ_YEAR);
LCD_ShowHexNum(2,7,set_time[0],2);
LCD_ShowHexNum(2,4,set_time[1],2);
LCD_ShowHexNum(2,1,set_time[2],2);
LCD_ShowHexNum(1,9,set_time[3],2);
LCD_ShowHexNum(1,6,set_time[4],2);
//LCD_ShowHexNum(2,1,set_time[5],2);
LCD_ShowHexNum(1,3,set_time[6],2);
}
}
注:普中A2的P3_4接口,没有上拉电阻,向DS1302写完数据后需要将其置 0,才可正常进行下次的读操作。
2、可设置时间的时钟
10、嗡鸣器(ULN2003A)
达林顿晶体管(UNLN2003A):
独立按键使得LED亮起,并想0.2s的喇叭 50HZ的声音
#include <REGX52.H>
#include "Delay.h"
void bi(){
int count = 0;
for(count = 0; count < 100; ++count){
P2_5=0;
Delay(1);
P2_5=1;
Delay(1);
}
}
void main(){
while(1){
//open led
if(P3_1==0){
P2_0=~P2_0;
bi();
}else if(P3_0==0){
P2_1=~P2_1;
bi();
}else if(P3_2==0){
P2_2=~P2_2;
bi();
}else if(P3_3==0){
P2_3=~P2_3;
bi();
}
}
}
演奏小星星
#include <REGX52.H>
#include "LCD1602.h"
#define FULL_TIME 65535
unsigned char flag = 0;
unsigned long count_max = 0; //用于计算终结的时间
unsigned long count_now = 0; //用于计算终结的时间
unsigned char pitch = 1; //输入一个 1-36 的音高
unsigned long code dorimi[36] = {
61718, 61925, 62134, 62320, 62505, 62670, 62832, 62984, 63125, 63262, 63389, 63511,
63623, 63730, 63831, 63927, 64018, 63863, 64184, 64259, 64332, 64399, 64462, 64523,
64579, 64633, 64684, 64732, 64776, 64819, 64859, 64897, 64933, 64967, 64999, 65029
};
unsigned long code time_long[6] = {
125000, //125ms (1/16)
250000, //250ms (1/8)
500000, //500ms (1/4)
1000000, //1s (1/2)
2000000, //2s (1)
4000000, //4s (2)
};
unsigned char code small_star[] = {
12,2,12,2,
19,2,19,2,
21,2,21,2,
19,3,17,2,
17,2,
16,2,16,2,
14,2,14,2,
12,3,
19,2,19,2,
17,2,17,2,
16,2,16,2,
14,3,
19,2,19,2,
17,2,17,2,
16,2,16,2,
14,3,
12,2,12,2,
19,2,19,2,
21,2,21,2,
19,3,
17,2,17,2,
16,2,16,2,
14,2,14,2,
13,3
};
unsigned int TH0_s=0;
unsigned int TL0_s=0;
void timing_begin(){
//这个寄存器的名字叫 TMOD,用来设置计时器的工作模式
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01;
TF0=0; //打开通向中断的开关
TR0=1; //允许计数/计时
TH0_s=dorimi[pitch]/256;
TL0_s=dorimi[pitch]%256+1;
TH0=TH0_s;
TL0=TL0_s;
/*
TH0 TL0 总共可计数65535
65535 - 64535 =1000,即 1ms
(经过12的分频器,12M变成1M)
*/
// TH0=12000/256;
// TL0=12000%256+1;
ET0=1; //总断的分开关
EA=1; //中断的总开关
PT0=0; //设置中断的优先级别
flag=1; //计时开始标志
count_now = 0;
}
void Timer0_Routine() interrupt 1{
TH0=TH0_s;
TL0=TL0_s;
P2_5=~P2_5; //震动
count_now++;
if(count_now>=count_max){
TR0=0; //停止计时
flag=0; //计时结束标志
}
}
//放声
void bi(unsigned char pitch_cur,unsigned char time){//音高 | 和一个音执行的时长
unsigned int x = 0;
pitch = pitch_cur; //设置音高
count_max=time_long[time]/(65535-dorimi[pitch]); //计算需要执行多少的周期
timing_begin();
while(flag){}
}
void main(){
unsigned int i=0;
for (i=0;i<sizeof(small_star);i+=2){
bi(small_star[i],small_star[i+1]);
}
while(1){}
}
11、存储器(I2C、AT24C02)
AT24C02是内存256byte大小的存储器:
AT24C02的内部电路图:
I2C总线:
I2C的时序图
AT24C02的时序图:
保存一个字节到AT24C02中,然后读取出来
i2c.h
----------------------------------------------------------
#ifndef __I2C_H__
#define __I2C_H__
//起始条件
void I2C_Start(void);
//结束条件
void I2C_Stop(void);
//发送一个字节
void I2C_SendByte(unsigned char Byte);
//接受一个字节的数据
unsigned char I2C_ReceiveByte(void);
//发送应答
void I2C_SendAck(unsigned char AckBit);
//接受应答
unsigned char I2C_ReceiveAck(void);
#endif
----------------------------------------------------------
i2c.c
----------------------------------------------------------
#include <REGX52.H>
#include "i2c.h"
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
//起始条件
//SCL高电平期间,SDA由高到低
void I2C_Start(void){
I2C_SDA=1;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
}
//结束条件
//SCL高电平期间,SDA由低到高
void I2C_Stop(void){
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
//发送一个字节
//SDA的数据提前准备好,SCL在高电平时接受数据
void I2C_SendByte(unsigned char Byte){
unsigned char i;
for(i=0;i<8;i++){
I2C_SDA=Byte&(0x80>>i); //由高到低一位一位的传递
I2C_SCL=1;
I2C_SCL=0;
}
}
//接受一个字节的数据
//SDA做好接受数据的准备,SCL在高电平时接受数据
unsigned char I2C_ReceiveByte(void){
unsigned char i,Byte=0x00;
I2C_SDA=1; //断开连接,准备接受数据
for(i=0;i<8;i++){
I2C_SCL=1;
if(I2C_SDA){Byte|=(0x80>>i);} //由高到低一位一位的接受数据
I2C_SCL=0;
}
return Byte;
}
//发送应答
//在接受完一个字节的数据后进行,0是应答,1是非应答
void I2C_SendAck(unsigned char AckBit){
I2C_SDA=AckBit; //准备好要发送的应答
I2C_SCL=1; //开始发送
I2C_SCL=0; //发送结束
}
//接受应答
//在发送完一字节的数据后进行,0是应答,1是非应答
unsigned char I2C_ReceiveAck(void){
unsigned char AckBit;
I2C_SDA=1; //准备好接受应答
I2C_SCL=1; //开始接受
AckBit=I2C_SDA; //接受应答
I2C_SCL=0; //结束接受
return AckBit; //将接受到的应答返回
}
----------------------------------------------------------
at24c02.h
----------------------------------------------------------
#ifndef __AT24C02_H__
#define __AT24C02_H__
//写一个字节到指定的位置
void AT24C02_WriteByte(unsigned char WordAddress,Data);
//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif
----------------------------------------------------------
at24c02.c
----------------------------------------------------------
#include <REGX52.H>
#include "I2C.h"
#include "at24c02.h"
#define AT24C02_ADDRESS 0xA0
/*
发送的八位数据:
xxxx xxx x
前四位是设备编号 可配置地址(对应A0~A2) 最后一位是设置读写(0是写,1是读)
AT24C02的是1010 原理图中A0~A2是接地的
*/
//写一个字节到指定的位置
void AT24C02_WriteByte(unsigned char WordAddress,Data){
I2C_Start(); //开始
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck(); //写到AT24C02
I2C_SendByte(WordAddress);
I2C_ReceiveAck(); //写入的地址
I2C_SendByte(Data);
I2C_ReceiveAck(); //写入的具体数据
I2C_Stop(); //结束
}
//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress){
unsigned char Data;
//一阶段
I2C_Start(); //开始
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck(); //操作AT24C02
I2C_SendByte(WordAddress);
I2C_ReceiveAck(); //操作的地址
//二阶段
I2C_Start(); //开始
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck(); //读AT24C02
Data=I2C_ReceiveByte();
I2C_SendAck(1); //读取数据
I2C_Stop(); //结束
return Data;
}
----------------------------------------------------------
main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
#include "i2c.h"
#include "at24c02.h"
#include "Delay.h"
unsigned char Data;
void main(){
LCD_Init();
//LCD_ShowString(1,1,"Hello");
AT24C02_WriteByte(1,120);
Delay(5);
Data=AT24C02_ReadByte(1);
LCD_ShowNum(2,1,Data,3);
while(1){}
}
----------------------------------------------------------
2h计时器(到了时间嗡鸣器会响),时、分、秒、10毫秒(有暂停、保存功能)
print_number.h
----------------------------------------------------------
#ifndef __PRINT_NUMBER_H__
#define __PRINT_NUMBER_H__
void print_number(unsigned char index,unsigned char value);
void print_null();
#endif
----------------------------------------------------------
print_number.c
----------------------------------------------------------
#include <REGX52.H>
#include "Delay.h"
#include "print_number.h"
/*
0-9 为:0-9
10 为:空
11-21 为:带点的0-9
*/
unsigned char code number_array[21]={
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,
0x00,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF
};
//(index)位置 和 (value)数字
void print_number(unsigned char index,unsigned char value){
if(value>20||value<0||index>7||index<0)return;
P2_2=0;
P2_3=0;
P2_4=0;//显示下个数字前先熄灯,消除残影
switch (index){
case 0:
P2_2=1;
P2_3=1;
P2_4=1;
break;
case 1:
P2_2=0;
P2_3=1;
P2_4=1;
break;
case 2:
P2_2=1;
P2_3=0;
P2_4=1;
break;
case 3:
P2_2=0;
P2_3=0;
P2_4=1;
break;
case 4:
P2_2=1;
P2_3=1;
P2_4=0;
break;
case 5:
P2_2=0;
P2_3=1;
P2_4=0;
break;
case 6:
P2_2=1;
P2_3=0;
P2_4=0;
break;
case 7:
P2_2=0;
P2_3=0;
P2_4=0;
break;
default:
break;
}
P0=number_array[value];
//Delay(1);//显示一毫秒
}
void print_null(){
P2_2=0;
P2_3=0;
P2_4=0;//显示下个数字前先熄灯,消除残影
}
----------------------------------------------------------
i2c.h
----------------------------------------------------------
#ifndef __I2C_H__
#define __I2C_H__
//起始条件
void I2C_Start(void);
//结束条件
void I2C_Stop(void);
//发送一个字节
void I2C_SendByte(unsigned char Byte);
//接受一个字节的数据
unsigned char I2C_ReceiveByte(void);
//发送应答
void I2C_SendAck(unsigned char AckBit);
//接受应答
unsigned char I2C_ReceiveAck(void);
#endif
----------------------------------------------------------
i2c.c
----------------------------------------------------------
#include <REGX52.H>
#include "i2c.h"
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
//起始条件
//SCL高电平期间,SDA由高到低
void I2C_Start(void){
I2C_SDA=1;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
}
//结束条件
//SCL高电平期间,SDA由低到高
void I2C_Stop(void){
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
----------------------------------------------------------
at24c02.h
----------------------------------------------------------
#ifndef __AT24C02_H__
#define __AT24C02_H__
//写一个字节到指定的位置
void AT24C02_WriteByte(unsigned char WordAddress,Data);
//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif
----------------------------------------------------------
at24c02.c
----------------------------------------------------------
#include <REGX52.H>
#include "I2C.h"
#include "at24c02.h"
#define AT24C02_ADDRESS 0xA0
/*
发送的八位数据:
xxxx xxx x
前四位是设备编号 可配置地址 最后一位是设置读写(0是写,1是读)
*/
//写一个字节到指定的位置
void AT24C02_WriteByte(unsigned char WordAddress,Data){
I2C_Start(); //开始
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck(); //写到AT24C02
I2C_SendByte(WordAddress);
I2C_ReceiveAck(); //写入的地址
I2C_SendByte(Data);
I2C_ReceiveAck(); //写入的具体数据
I2C_Stop(); //结束
}
//从指定的位置读一个字节出来
unsigned char AT24C02_ReadByte(unsigned char WordAddress){
unsigned char Data;
//一阶段
I2C_Start(); //开始
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck(); //操作AT24C02
I2C_SendByte(WordAddress);
I2C_ReceiveAck(); //操作的地址
//二阶段
I2C_Start(); //开始
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck(); //读AT24C02
Data=I2C_ReceiveByte();
I2C_SendAck(1); //读取数据
I2C_Stop(); //结束
return Data;
}
----------------------------------------------------------
main.c
----------------------------------------------------------
#include <REGX52.H>
#include "i2c.h"
#include "at24c02.h"
#include "Delay.h"
#include "print_number.h"
unsigned char Data;
unsigned char time_begin_flag = 1;
//当前时间
unsigned char now_time_array[4] = {0, 0, 0, 0};
//hour munite second ms
//0-23 0-59 0-59 0-99
unsigned char print_number_array[8]={1,2,3,4,5,6,7,8};//当前时间显示表
/*
开始计时
*/
void timer0_init(){
//这个寄存器的名字叫 TMOD,用来设置计时器的工作模式
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01;
TF0=0; //打开通向中断的开关
TR0=1; //允许计数/计时
TH0=64535/256;
TL0=64535%256+1;
/*
TH0 TL0 总共可计数65535
65535 - 64535 =1000,即 1ms
(经过12的分频器,12M变成1M)
*/
// TH0=12000/256;
// TL0=12000%256+1;
ET0=1; //总断的分开关
EA=1; //中断的总开关
PT0=0; //设置中断的优先级别
}
//初始化
void init_time_print(){
unsigned char tens = 0;
unsigned char ones = 0;
//设置ram中的时刻表(从ROM中拿数据)
Data=AT24C02_ReadByte(0);
now_time_array[0]=Data;
Data=AT24C02_ReadByte(1);
now_time_array[1]=Data;
Data=AT24C02_ReadByte(2);
now_time_array[2]=Data;
Data=AT24C02_ReadByte(3);
now_time_array[3]=Data;
//设置显示的时刻表
//更新 hour
tens = now_time_array[1]/10;
ones = now_time_array[1]%10;
print_number_array[0]=tens;
print_number_array[1]=ones+11;
//更新 munite
tens = now_time_array[1]/10;
ones = now_time_array[1]%10;
print_number_array[2]=tens;
print_number_array[3]=ones+11;
//更新 s
tens = now_time_array[2]/10;
ones = now_time_array[2]%10;
print_number_array[4]=tens;
print_number_array[5]=ones+11;
//更新 ms
tens = now_time_array[3]/10;
ones = now_time_array[3]%10;
print_number_array[6]=tens;
print_number_array[7]=ones+11;
}
/*
一毫秒进入一次这个中断函数
*/
unsigned int T0Count=0;
unsigned char cur_number = 0;
unsigned char key_status[4]={0,0,0,0};
void Timer0_Routine() interrupt 1{
TH0=64535/256;
TL0=64535%256+1;
//数码管的操作
//每两毫秒进入一次,数码管函数
if(0==T0Count%2){//两毫秒进入一次循环
cur_number= T0Count%16;
print_number(cur_number/2,print_number_array[cur_number/2]);
}
//加时间的操作
//每10毫秒加一次时间
if(0==T0Count%10){
if(time_begin_flag){
now_time_array[3]++;
}
}
//加时间的操作
//每20ms检查一次按键的状态
//三个个按键的作用分别是:暂停、开始、保存、重置为0
if(0==T0Count%20){
if(0==P3_1 && key_status[0]==1){//暂停
time_begin_flag=0;
}
if(0==P3_0 && key_status[1]==1){//开始
time_begin_flag=1;
}
if(0==P3_2 && key_status[2]==1){//保存
TR0=0;//停止计时
//将RAM中的数据存入ROM
AT24C02_WriteByte(0,now_time_array[0]);
Delay(5);
AT24C02_WriteByte(1,now_time_array[1]);
Delay(5);
AT24C02_WriteByte(2,now_time_array[2]);
Delay(5);
AT24C02_WriteByte(3,now_time_array[3]);
Delay(5);
//初始化
init_time_print();
TR0=1;//开始计时
}
if(0==P3_3 && key_status[3]==1){//重置
TR0=0;//停止计时
//写入,将时间归零
AT24C02_WriteByte(0,0);
Delay(5);
AT24C02_WriteByte(1,0);
Delay(5);
AT24C02_WriteByte(2,0);
Delay(5);
AT24C02_WriteByte(3,0);
Delay(5);
//初始化
init_time_print();
TR0=1;//开始计时
}
key_status[0]=P3_1;
key_status[1]=P3_0;
key_status[2]=P3_2;
key_status[3]=P3_3;
if(T0Count==65519){//65520可用被40整除,40是8,10和20的最小公倍数
T0Count=0;
return;
}
}
T0Count++;
}
//嗡鸣器
void bi(){
int count = 0;
while(1){
P2_5=0;
Delay(1);
P2_5=1;
Delay(1);
}
}
void main(){
unsigned char tens = 0;
unsigned char ones = 0;
unsigned char now_time_array_3 = 0;
//初始化
init_time_print();
timer0_init();//开启计时函数
//now_time_array_3 = now_time_array[3];
//开始计时
while(1){
if(now_time_array_3 == now_time_array[3]){
continue; //时间没有跟新
}else{
now_time_array_3=now_time_array[3]; //更新为现在的ms数
}
if(now_time_array[3]>99){ //ms 进位了
now_time_array[3]=0;
now_time_array[2]++;
if(now_time_array[2]>59){ //s 进位了
now_time_array[2]=0;
now_time_array[1]++;
if(now_time_array[1]>59){ //munite 进位了
now_time_array[1]=0;
now_time_array[0]++;
//到2h时,嗡鸣器开始响亮
if(2==now_time_array[0]){
TR0=0;//停止计时
bi();
}
if(now_time_array[0]>23){ //hour 进位了
now_time_array[0]=0;
}
//更新 hour
tens = now_time_array[1]/10;
ones = now_time_array[1]%10;
print_number_array[0]=tens;
print_number_array[1]=ones+11;
}
//更新 munite
tens = now_time_array[1]/10;
ones = now_time_array[1]%10;
print_number_array[2]=tens;
print_number_array[3]=ones+11;
}
//更新s
tens = now_time_array[2]/10;
ones = now_time_array[2]%10;
print_number_array[4]=tens;
print_number_array[5]=ones+11;
}
//更新ms
tens = now_time_array[3]/10;
ones = now_time_array[3]%10;
print_number_array[6]=tens;
print_number_array[7]=ones+11;
}
}
----------------------------------------------------------
12、温度检测 (1-Wire(单总线)、DS18B20))
简介:
电路原理图:
AMR中的数据结构:
51中的电路结构:
时序图:
注:因为单总线没有时钟同步,所以在通信的过程中是不能被打断的。
显示当前温度:
OneWire.h
----------------------------------------------------------
#ifndef __ONE_Wire_H__
#define __ONE_Wire_H__
//初始化
unsigned char OneWire_Init(void);
//发送一位数据
void OneWire_SendBit(unsigned char Bit);
//接受从机的一位数据
unsigned char OneWire_ReceiveBit(void);
//发送一个字节的数据
void OneWire_SendByte(unsigned char Byte);
//接受一个字节的数据
unsigned char OneWire_ReceiveByte(void);
#endif
----------------------------------------------------------
OneWire.c
----------------------------------------------------------
#include <REGX52.H>
#include "OneWire.h"
//单总线就是靠这一根线来通讯的
sbit OneWire_DQ=P3^7;
//初始化
//初始化可以结束上条指定,开始新的指令
//初始化后,从机 会进行响应,没有从机的话就没有响应
//低电平,0,代表响应
//高电平,1,代表无响应
unsigned char OneWire_Init(void){
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1; //确认已经释放了对总线的释放
OneWire_DQ=0;
i = 247;while(--i); //Delay 500us,最少拉低480us,这里拉低了500us
OneWire_DQ=1; //准备好接受从机发送数据
i = 32;while(--i); //Delay 70us 要等待15-60us,从机拉低60-240us.
// 这里等待了70us,开始取从机发的数据
AckBit=OneWire_DQ; //这里是在接受数据
i=247;while(--i); //Dealy 500us 这里是在等待从机释放总线
return AckBit;
}
//发送一位数据
void OneWire_SendBit(unsigned char Bit){
unsigned char i;
OneWire_DQ=0;
i=4;while(--i); //Delay 10us 拉低10u后发送要传输的数据
OneWire_DQ=Bit;
i=24;while(--i); //Delay 50us
//在传0时 保证拉低的总时长大于60;也小于120
//在传1时 保证40us后从机可以读取到高电平;且使得整个时间片大于60us
OneWire_DQ=1;
}
//接受从机的一位数据
unsigned char OneWire_ReceiveBit(void){
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i=2;while(--i); //Delay 5us //拉低5us
OneWire_DQ=1; //准备好接受从机发送的数据
i=2;while(--i); //Delay 5us
Bit=OneWire_DQ; //等待5us后,接受数据
i=24;while(--i); //Delay 50us 使得整个过程大于60us
return Bit;
}
//发送一个字节的数据
void OneWire_SendByte(unsigned char Byte){
unsigned char i;
for(i=0;i<8;i++){
OneWire_SendBit(Byte&(0x01<<i));//由低到高一位一位的发送,和 I2C相反
}
}
//接受一个字节的数据
unsigned char OneWire_ReceiveByte(void){
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++){
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}//由低到高一位一位的接受数据
}
return Byte;
}
----------------------------------------------------------
ds18b20.h
----------------------------------------------------------
#ifndef __DS18B20_H__
#define __DS18B20_H__
//将当前的温度信息写入到 RAM中 暂存起来
void DS18B20_ConvertT(void);
//读取当前温度信息(从RAM中一个字节一个字节的读取)
float DS18B20_ReadT(void);
#endif
----------------------------------------------------------
ds18b20.c
----------------------------------------------------------
#include <REGX52.H>
#include "OneWire.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
//将当前的温度信息写入到 RAM中 暂存起来
void DS18B20_ConvertT(void){
OneWire_Init(); //初始化
OneWire_SendByte(DS18B20_SKIP_ROM); //跳过从机选择(ROM指令),直接进入功能指令
OneWire_SendByte(DS18B20_CONVERT_T); //将温度信息写入到 RAM中
}
//读取当前温度信息(从RAM中一个字节一个字节的读取)
float DS18B20_ReadT(void){
unsigned char TLSB,TMSB;
int Temp;
float T;
OneWire_Init();//初始化
OneWire_SendByte(DS18B20_SKIP_ROM);//跳过从机选择(ROM指令),直接进入功能指令
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);//读取RAM中的数据
TLSB=OneWire_ReceiveByte(); //读取第一个字节
TMSB=OneWire_ReceiveByte(); //读取第二个字节
//对读出的数据进行处理,将其转换位浮点型
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0;
return T;
}
----------------------------------------------------------
main.c
----------------------------------------------------------
#include <REGX52.H>
#include "ds18b20.h"
#include "LCD1602.h"
float t;
void main(){
LCD_Init();
LCD_ShowString(1,1,"Temperature:");
while(1){
DS18B20_ConvertT();
t=DS18B20_ReadT();
if(t>=0){
LCD_ShowChar(2,1,'+');
}else{
LCD_ShowChar(2,1,'-');
}
LCD_ShowNum(2,2,t,3);
LCD_ShowChar(2,5,'.');
LCD_ShowNum(2,6,(unsigned long)(t*10000)%10000,4);
}
}
----------------------------------------------------------
13、液晶屏的显示(LCD1602)
简介:
电路图:
时序图及指令集:
本程序使用的指令流程:
显示一个X
LCD1602.h
----------------------------------------------------------
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
//初始化函数
void LCD_Init();
//在LCD1602指定位置上显示一个字符
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
#endif
----------------------------------------------------------
LCD1602.c
----------------------------------------------------------
#include <REGX52.H>
//引脚配置
sbit LCD_RS=P2^6; //指令、数据的选择
sbit LCD_RW=P2^5; //读写选择
sbit LCD_EN=P2^7; //使能
//用于传数据
#define lCD_DataPort P0
//LCD1602执行命令需要时间
void LCD_Delay(){
unsigned char i,j;
i=2;
j=239;
do{
while(--j);
}while(--i);
}
//输入一条指令
void LCD_WriteCommand(unsigned char Command){
LCD_RS=0; //指令
LCD_RW=0; //写
lCD_DataPort=Command; //命令的内容
LCD_EN=1; //数据有效
LCD_Delay();
LCD_EN=0; //下降沿执行命令
LCD_Delay();
}
//写入字节的数据
void LCD_WriteData(unsigned char Data){
LCD_RS=1; //数据
LCD_RW=0; //写
lCD_DataPort=Data; //数据的内容
LCD_EN=1; //数据有效
LCD_Delay();
LCD_EN=0; //下降沿写入数据
LCD_Delay();
}
//设置光标位置
void LCD_SetCursor(unsigned char Line,unsigned char Column){
if(Line==1){
LCD_WriteCommand(0x80|(Column-1)); //首位位1,第二位 0表示在第一行显示。后四位表示字符所在的行的位置
}else if(Line==2){
LCD_WriteCommand(0x80|(Column-1+0x40)); //首位位1,第二位 1表示在第二行显示。后四位表示字符所在的行的位置
}
}
//初始化函数
void LCD_Init(){
LCD_WriteCommand(0x38); //八位数据接口,两行显示,5*8点阵
LCD_WriteCommand(0x0c); //显示开,光标关,闪烁关
LCD_WriteCommand(0x06); //数据读写操作后,光标自动加1,画面不动
LCD_WriteCommand(0x01); //清屏
}
//在LCD1602指定位置上显示一个字符
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char){
LCD_SetCursor(Line,Column); //设置光标位置(行列)
LCD_WriteData(Char); //显示字符
}
----------------------------------------------------------
main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
void main(){
//初始化函数
LCD_Init();
//在LCD1602指定位置上显示一个字符
LCD_ShowChar(1,1,'X');
while(1){}
}
----------------------------------------------------------
完整版:
LCD1602.h
----------------------------------------------------------
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
----------------------------------------------------------
LCD1602.c
----------------------------------------------------------
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
----------------------------------------------------------
14、直流电机(PWM)
简介:
电路图:
注:第二张图的二极管是为了防止 电感器件(马达)击穿三极管。
下面程序用的P01口,接线是5v 、01口。
PWM:
因为直流电机的电路上经过的电路比较大,所以调节其运行工具不能通过加电阻的方式。
于是就是使用了PWM的方式(在一定时间间隔中加如电流)
LED呼吸灯:
#include <REGX52.H>
sbit LED=P2^0;
void delay(unsigned int time){
while(time--){}
}
void main(){
unsigned int i,j,k = 0;
//变亮
while(1){
for(i=0;i<100;++i){
for(j=0;j<20;++j){
LED=0;
delay(i);
}
for(j=0;j<20;++j){
LED=1;
delay(100-i);
}
}
//变暗
for(i=0;i<100;++i){
for(j=0;j<20;++j){
LED=1;
delay(i);
}
for(j=0;j<20;++j){
LED=0;
delay(100-i);
}
}
}
}
3档马达,每按一次K1(独立按键),就加一当的转速。
#include <REGX52.H>
sbit motor=P1^0;
void Timer0Init(void) //100微秒@11.0592MHz
{
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01; //设置定时器模式
TL0 = 0xAE; //设置定时初值
TH0 = 0xFB; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //总断的分开关
EA=1; //中断的总开关
PT0=0; //设置中断的优先级别
}
//PWM
unsigned int T0Count;//计数,在0-99之间
unsigned int Compare;//在0-99之间
void Timer0_Routine() interrupt 1{
TL0 = 0xAE; //设置定时初值
TH0 = 0xFB; //设置定时初值
T0Count++;
if(T0Count<Compare){//10ms 进入一次
motor=1;
}else{
motor=0;
}
if(T0Count>100)T0Count=0;//重置
}
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void main(){
Compare = 0;
Timer0Init();
//变亮
while(1){
if(P3_1==0){
Delay(20);//检测按下
while(P3_1==0);
Delay(20);//确认松手
if(Compare<75){
Compare+=25;
}else{
Compare=0;
}
}
// Compare++;
// if(Compare==100){
// Compare=0;
// }
}
}
15、ADC、DAC(XPT2046)
ADC: 模拟信号转为数字信号
A:analog 模拟,
D:digital 数字
DAC:数字信号转为模拟信号
实现原理:
具体应用
(电路的接口连接)
(时序与指令)
ADC
显示模拟信号的值:
XPT2046.h
----------------------------------------------------------
#ifndef __XPT2046_H__
#define __XPT2046_H__
/*
XP AIN0
YP AIN1
UBAT AIN2
AUX AIN3
制字的控制命令
第7位 第6位 第5位 第4位 第3位 第2位 第1位 第0位
S A2 A1 A0 MODE SER/DFR PD1 PD0
S设置必须为1 ,它叫控制字首位
A2 A1 A0 :选择哪个作为输入端口 001(XP) 101(YP) 010(BAT) 110(AUX)
MODE :0 是12位的精度, 1 是8位精度
SER/DFR 1是单端输入配置 0是差分输入模式
PD1 PD0 00代表低功耗模式 11代表供电状态
*/
#define XPT2046_XP_8 0x9C
#define XPT2046_YP_8 0xDC
#define XPT2046_UBAT_8 0xAC
#define XPT2046_AUX_8 0xEC
#define XPT2046_XP_12 0x94
#define XPT2046_YP_12 0xD4
#define XPT2046_UBAT_12 0xA4
#define XPT2046_AUX_12 0xE4
unsigned int XPT2046_ReadAD(unsigned char Command);
#endif
----------------------------------------------------------
XPT2046.c
----------------------------------------------------------
#include <REGX52.H>
/*
可以在51的原理图中查找到这里的连接依据
CS是片选线
DCLK是时钟线
DIN是写命令的线
DOUT是读数据的线
*/
sbit XPT2046_CS=P3^5;
sbit XPT2046_DCLK=P3^6; //上升沿写入数据 下降沿读取数据
sbit XPT2046_DIN=P3^4;
sbit XPT2046_DOUT=P3^7;
unsigned int XPT2046_ReadAD(unsigned char command){
unsigned char i;
unsigned int ADVAlue=0;
XPT2046_DCLK=0; //为在上升沿写数据做准备
XPT2046_CS=0; //命令开始
//发送命令
for(i=0;i<8;++i){
XPT2046_DIN=command&(0x80>>i);
XPT2046_DCLK=1;//写入
XPT2046_DCLK=0;//为下次写入做准备
}
//接受数据
for(i=0;i<16;++i){
XPT2046_DCLK=1;
XPT2046_DCLK=0;//下降沿后开始读数据
if(XPT2046_DOUT){ADVAlue|=(0x8000>>i);}
}
XPT2046_CS=1;//命令结束
return ADVAlue;
}
----------------------------------------------------------
main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
#include "XPT2046.h"
#include "Delay.h"
void main(){
unsigned int value=0;
LCD_Init();
LCD_ShowString(1,1,"AD1");
LCD_ShowString(1,6,"NTC1");
LCD_ShowString(1,11,"GR1");
//变亮
while(1){
//分辨率为8 AD1 旋转
value=XPT2046_ReadAD(XPT2046_XP_8);
LCD_ShowNum(2,1,value/256,3);//16位中,后8位为0
Delay(10);
//分辨率为12
//ADValue=XPT2046_ReadAD(XPT2046_XP_12);
//LCD_ShowNum(2,1,ADValue/16,4);//16位中,后4位为0
//Delay(10);
//分辨率为8 NTC1 温度
value=XPT2046_ReadAD(XPT2046_YP_8);
LCD_ShowNum(2,6,value/256,3);//16位中,后8位为0
Delay(10);
//分辨率为8 GR1 光感
value=XPT2046_ReadAD(XPT2046_UBAT_8);
LCD_ShowNum(2,11,value/256,3);//16位中,后8位为0
Delay(10);
}
}
----------------------------------------------------------
DAC
呼吸灯(将PWM信号转为模拟信号)
#include <REGX52.H>
sbit DA=P2^1;
void Timer0Init(void) //100微秒@11.0592MHz
{
TMOD=TMOD&0xF0;
TMOD=TMOD|0x01; //设置定时器模式
TL0 = 0xF5; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //总断的分开关
EA=1; //中断的总开关
PT0=0; //设置中断的优先级别
}
//PWM
unsigned int T0Count;//计数,在0-99之间
unsigned int Compare;//在0-99之间
void Timer0_Routine() interrupt 1{
TL0 = 0xF5; //设置定时初值
TH0 = 0xFF; //设置定时初值
T0Count++;
if(T0Count<Compare){//10ms 进入一次
DA=0;
}else{
DA=1;
}
if(T0Count>100)T0Count=0;//重置
}
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void main(){
unsigned int i=0;
Compare = 0;
Timer0Init();
//变亮
while(1){
for(i=0,Compare=0;i<200;++i){
if(i<100)Compare+=1;
if(i>100)Compare-=1;
Delay(1);
}
}
}
16、红外线(遥控器)
简介:
电路:
(外部中断接口)
中断寄存器:
NEC编码:
遥控器键值:
在LCD上输出地址码、控制码,音量大小
用于计算时间
Timer0.h
----------------------------------------------------------
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0_Init(void);
//设置从几开始计数
void Timer0_SetCounter(unsigned int Value);
//获取到现在计数到多大了
unsigned int Timer0_GetCounter(void);
//设置是否开始计时
void Timer0_Run(unsigned char Flag);
#endif
----------------------------------------------------------
Timer0.c
----------------------------------------------------------
#include <REGX52.H>
void Timer0_Init(void){
TMOD &= 0xF0; //
TMOD |= 0x01; //设置计时模式,16位重载
TL0 = 0; //
TH0 = 0; //从头开始计数
TF0 = 0; //打开通向中断的开关
TR0 = 0; //未开始计时
}
//设置从几开始计数
void Timer0_SetCounter(unsigned int Value){
TH0=Value/256;
TL0=Value%256;
}
//获取到现在计数到多大了
unsigned int Timer0_GetCounter(void){
return (TH0<<8)|TL0;
}
//设置是否开始计时
void Timer0_Run(unsigned char Flag){
TR0=Flag;
}
----------------------------------------------------------
开启外部中断(INT0)
Int0.h
----------------------------------------------------------
#ifndef __INT0_H__
#define __INT0_H__
//开启外部中断0(INT0 P3_2 K3)
void Ini0_Init(void);
#endif
----------------------------------------------------------
Int0.c
----------------------------------------------------------
#include <REGX52.H>
//开启外部中断0
void Ini0_Init(void){
IT0=1; //0:低电平触发,1:下降沿触发
IE0=0; //中断标志位,可以不进行设置
//有中断来时,被设置位1,当响应后,会被硬件置0(下降沿触发模式中)
EX0=1; //中断使能
EA=1; //中断使能总开关
PX0=1; //选择最高优先级
}
/*
void Int0_Routine(void) interrupt 0{
Num++;
}
*/
----------------------------------------------------------
红外线(遥控器数据解析)
IR.h
----------------------------------------------------------
#ifndef __IR_H__
#define __IR_H__
void IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);
#endif
----------------------------------------------------------
IR.c
----------------------------------------------------------
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"
unsigned int IR_Time;
unsigned char IR_State;
unsigned char IR_Data[4];
unsigned char IR_pData;
unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;
unsigned char IR_Temp;
void IR_Init(void){
Timer0_Init();//开启计时
Ini0_Init();//开启中断 INT0
}
//中断函数(下降沿进入)
void Int0_Routine(void) interrupt 0{
/*
IR_State 0:等待命令
1:检测有无 Start的时序
2:解析地址码和命令
*/
/*
11.0592/12=0.9216
Start
9ms + 4.5ms = 13500 us
13500 x 0.9216 = 12442
Repeat
9ms + 2.25ms = 11250 us
11250 x 0.9216 = 10368
logical '0'
560 + 560 = 1120
1120 x 0.9216 = 1032
logical '1'
560 + 1690 = 2250
2250 x 0.9216 = 2061
*/
if(IR_State==0){ //等待命令
Timer0_SetCounter(0);
Timer0_Run(1); //开启计时
IR_State=1;
}
else if(IR_State==1){ //检测有无 Start的时序 或 repeat 时序
IR_Time=Timer0_GetCounter();
Timer0_SetCounter(0);
if(IR_Time>12442-500 && IR_Time<12442+500){//Start时序
IR_State=2;
}
else if(IR_Time>10368-500 && IR_Time<10368+500){//repeat时序
IR_RepeatFlag=1;
Timer0_Run(0);
IR_State=0;//等待下次命令
}
else{//识别不出来
IR_State=1;
}
}
else if(IR_State==2){ //解析地址码和命令
IR_Time=Timer0_GetCounter();
Timer0_SetCounter(0);
if(IR_Time>2061-200 && IR_Time<2061+200){//数据 1的时序
IR_Data[IR_pData/8]|=(0x01<<(IR_pData&7));
IR_pData++;
}
else if(IR_Time>1032-200 && IR_Time<1032+200){ //数据 0的时序
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));
IR_pData++;
}
else{//无法识别,回去寻找 Start的时序
IR_pData=0;
IR_State=1;
return;
}
if(IR_pData>=32){//获取到了完整的4个字节,进行校验
IR_pData=0;
if((IR_Data[0]==~IR_Data[1] && (IR_Data[2]==~IR_Data[3]))){//校验成功,装填数据
IR_Address=IR_Data[0];
IR_Command=IR_Data[2];
IR_DataFlag=1;
}
Timer0_Run(0);
IR_State=0;//回去等待下一条命令
}
}
}
unsigned char IR_GetDataFlag(void){//是否是重发命令
IR_Temp = IR_DataFlag;
IR_DataFlag=0;
return IR_Temp;
}
unsigned char IR_GetRepeatFlag(void){
IR_Temp = IR_RepeatFlag;
IR_RepeatFlag=0;
return IR_Temp;
}
unsigned char IR_GetAddress(void){
IR_Temp = IR_Address;
return IR_Temp;
}
unsigned char IR_GetCommand(void){
IR_Temp = IR_Command;
return IR_Temp;
}
----------------------------------------------------------
main.c
----------------------------------------------------------
#include <REGX52.H>
#include "LCD1602.h"
#include "IR.h"
unsigned char Address;
unsigned char Command;
unsigned int Num=0;
unsigned char* pData;
void main(){
LCD_Init();
IR_Init();//开启红外线的检测
LCD_ShowString(1,1,"Adr");
LCD_ShowString(1,5,"code");
while(1){
if(IR_GetDataFlag()||IR_GetDataFlag()){
Address=IR_GetAddress();
Command=IR_GetCommand();
LCD_ShowHexNum(2,1,Address,2);
LCD_ShowHexNum(2,5,Command,2);
if(Command==0x15){
Num--;
}
if(Command==0x09){
Num++;
}
LCD_ShowNum(2,10,Num,3);
}
}
}
----------------------------------------------------------
四、其他
1、keil uVision5中文乱码的解决办法!
2、STC51 IO接口
P1、P2、P3: 准双向输出配置
P0:开漏输出配置
看不懂的原图:
3、电平标准、全双工
3.5、常见的通信协议:
4、sfr 和 sbit
5、关键字 code 的使用
/**
*code 可以让数组中的值存入 flash ,
*节省内存(ROM)空间,
*但其中的值不可改变
*/
unsigned char code map_array[24]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x42,0x7E,0x40,0x00,0x7E,0x08,0x14,0x22,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
6、BCD码
两位本来可以表示 0-F,但它只使用 0-9 的部分