1. 简化138译码器对对应芯片的使能
void Init138(unsigned char n)
{
switch(n)
{
case 4: P2 = (P2 & 0x1f) | 0x80; break;
case 5: P2 = (P2 & 0x1f) | 0xa0; break;
case 6: P2 = (P2 & 0x1f) | 0xc0; break;
case 7: P2 = (P2 & 0x1f) | 0xe0; break;
}
}
2.
void Delay(unsigned int time)
{
while(time--);
while(time--);
}
延时函数要用 unsigned int, 不用 unsigned char,因为int为两个字节,延时时间会长一些。
3.动态数码管的编写
(1)
void Display(unsigned char value, unsigned char pos)
{
Init138(6);
P0 = 0x01 << pos;
Init138(7);
P0 = value;
}
选择哪位数码管显示什么内容
(2)
定义年份的时候最好不定义为一个四位的大数,因为可能超过类型大小,最好分为两个unsigned char的两位数
unsigned char month = 0,num1 = 20, num2 = 18;
void Display_Dongtai()
{
Display(smg[num1/10], 0);
Delay_smg(500);
Display(smg[num1%10], 1);
Delay_smg(500);
Display(smg[num2/10], 2);
Delay_smg(500);
Display(smg[num2%10], 3);
Delay_smg(500);
Display(smg[16], 4);
Delay_smg(500);
Display(smg[16], 5);
Delay_smg(500);
Display(smg[month/10], 6);
Delay_smg(500);
Display(smg[month%10], 7);
Delay_smg(500);
}
(3)
动态数码管显示变化的内容的时候,所用的延时函数和平常的延时也不一样
void delay(unsigned int t)
{
while(t--)
{
Display_Dongtai(); //在延时的同时也要执行当前数码管的状态
}
}
void main()
{
while(1)
{
Display_Dongtai();
month++;
if (month > 12)
{
month = 1;
num2++;
}
delay(100); //延时,体现数码管内容的变化
}
}
4.独立按键 (功能切换)
unsigned char flag = 0;
void KeyDown()
{
if (key7 == 0)
{
delay(100);
if (key7 == 0)
{
if(flag == 0)
{
led1 = 0;
flag = 1; //第一次按下后设置一个标志位
}
else if (flag == 1) //相当于是判断按键的第二次按下
{
led1 = 1;
flag = 0;
}
while(key7 == 0);//目的:使按键检测不那么快,按一下灯就亮,要不然按一下灯闪得很快
}
}
if (key6 == 0)
{
delay(100);
if (key6 == 0)
{
if (flag == 0) //key7按下后,key6不起作用,当然key6按下后,key7也不起作用,实现了功能选择
{
led2 = 0;
flag = 2;
}
else if (flag == 2)
{
led2 = 1;
flag = 0;
}
while(key6 == 0);
}
}
5.中断系统
(1)中断服务函数里面尽量少做事,一些可做可不做的事情放出去做,例如可以在中断服务函数里面做一个标志位的转化,把要做的功能引出去。
unsigned char flag = 0;
void ServiceInit0() interrupt 0
{
flag = 1;
}
void Ledint()
{
if (flag == 1)
{
led8 = 0;
delay(60000);
led8 = 1;
}
}
void Working()
{
led1 = 0;
delay(60000);
led1 = 1;
}
void main()
{
while(1)
{
Working();
Ledint();//类似于轮询的方法,即使检测到flag=1,也要等Working()函数执行完才执行Ledint()。
}
}
6.寄存器定时器
7.PWM(通过按键改变led的亮度)
unsigned char flag = 0;
void keydown()
{
if (key7 == 0)
{
delay(100);
if (key7 == 0)
{
switch (flag)
{
case 0:
ledvalue = 10;
TR0 = 1; //在按键第一次按下的时候再开启计数功能
flag = 1;
break;
case 1:
ledvalue = 50;
flag = 2;
break;
case 2:
ledvalue = 90;
flag = 3;
break;
case 3:
led1 = 1;
TR0 = 0;
flag = 0;
break;
}
while(key7 == 0);
}
}
}
void InitTime0() //此项目要求PWM频率为100HZ(周期为10ms=10000us),将其分为百分制,所以100*100,统一将定时器定时为100us
{
TMOD = 0x01;
TH0 = (65535 - 100) / 256;
TL0 = (65535 - 100) % 256;
ET0 = 1;
EA = 1;
//不急开启计数功能
}
void ServerTime0() interrupt 1
{
TH0 = (65535 - 100) / 256;
TL0 = (65535 - 100) % 256;
count++;
if (count == ledvalue)
{
led1 = 1;//到达设定值时,紧接着进入低电平阶段,执行相对无效状态(led灭)
}
if (count == 100)//一个周期结束马上回到高电平,所以在高电平期间执行有效状态(在此处就是led亮的时间)
{
led1 = 0;//执行有效状态
count = 0;
}
}
void main()
{
InitTime0();
HC138_4();
led1 = 1;
while(1)
{
keydown();
}
}
8.串口收发
#include <REGX52.H>
sfr AUXR = 0x8e;//蓝桥杯板子新增寄存器。需要新定义下
unsigned char uart_dat;
void uart_init()
{
TMOD = 0x20;//八位自动重装载模式
TH1 = 0xfd;//设置波特率为9600
TL1 = 0xfd;
TR1 = 1;//启动定时器1
SCON = 0x50;//串口参数为模式1和允许接收
AUXR = 0x00;//记住就行,难得解释
ES = 1;//使能串口中断
EA = 1;//使能总中断
}
void Uart_send(unsigned char dat)
{
SBUF = dat;
while(TI == 0);//等待发送数据完成(发送完成后TI = 1)
TI = 0;//清除发送完成标志
}
void ServerTime() interrupt 4
{ //在串口中断里面接收串口数据
if (RI == 1)
{
RI = 0;
uart_dat = SBUF;
Uart_send(uart_dat+1);
}
}
int main()
{
uart_init();
Uart_send(0x88);
while(1);
}
发送字符串:
void SendByte(unsigned char dat)
{
SBUF = dat;
while(TI == 0);
TI = 0;
}
void SendString(unsigned char *dat)
{
while(*dat != '\0')
{
SendByte(*dat++);
}
}
SendString("Welcome to XMF System!\r\n");
9.IO扩展与存储器映射扩展
存储器映射扩展:
IO口扩展:
void Init138(unsigned char n)
{
switch(n)
{
case 4: P2 = (P2 & 0x1f) | 0x80; break;
case 5: P2 = (P2 & 0x1f) | 0xa0; break;
case 6: P2 = (P2 & 0x1f) | 0xc0; break;
case 7: P2 = (P2 & 0x1f) | 0xe0; break;
}
}
Init138(4);
P0 = 0xf0;
可以替代为:
XBYTE[0x8000] = 0xf0;
10.DS18B20
#include <REGX52.H>
#include "onewire.h"
#include "absacc.h" //要用寄存器映射扩展就得用此头文件
//XBYTE[0x8000] = LED灯控制、 XBYTE[0xa000] = 蜂鸣器继电器控制
//XBYTE[0xc000] = 数码管位选、 XBYTE[0xe000] = 数码管段选
unsigned int T_dat = 0;
data unsigned char SMGNODot[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
data unsigned char SMGDot[10] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; //共阳
void DelaySMG(unsigned int t)
{
while(t--);
}
void Display_SMG(unsigned char pos, unsigned char value)
{
XBYTE[0xe000] = 0xff; //消影
XBYTE[0xc000] = 0x01 << pos;
XBYTE[0xe000] = value;
}
void Close_All_SMG()
{
XBYTE[0xc000] = 0xff;
XBYTE[0xe000] = 0xff;
}
void Display_ALL()
{
Display_SMG(7, SMGNODot[T_dat % 10]);
DelaySMG(400);
Display_SMG(6, SMGDot[(T_dat % 100) / 10]);
DelaySMG(400);
Display_SMG(5, SMGNODot[T_dat / 100]);
DelaySMG(400);
Display_SMG(4, 0xff);
DelaySMG(400);
Display_SMG(3, 0xff);
DelaySMG(400);
Display_SMG(2, 0xff);
DelaySMG(400);
Display_SMG(1, 0xff);
DelaySMG(400);
Display_SMG(0, 0xff);
DelaySMG(400);
Close_All_SMG();
}
void Delay_DS18B20(unsigned int t)
{
while(t--)
{
Display_ALL();
}
}
void Read()
{
unsigned char LSB, MSB;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_DS18B20(1000);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
LSB = Read_DS18B20();
MSB = Read_DS18B20();
init_ds18b20();
T_dat = 0x0000;
T_dat = MSB; //temp是16位int整型,msb、lsb是8为char型
T_dat <<= 8;
T_dat = T_dat | LSB; //整合到一起
if((T_dat & 0xf800) == 0x0000) //正温度的处理办法 与高五位进行判断
{
T_dat >>= 4; //取出温度结果的整数部分 右移四位,将小数移出去
T_dat = T_dat * 10; //放大10倍,然后加上小数部分
T_dat = T_dat + (LSB & 0x0f) * 0.625; // 取LSB的低四位(小数位)、分辨率0.0625也放大十倍
}
}
void main()
{
XBYTE[0x8000] = 0xff;
while(1)
{
Read();
Display_ALL();
}
}
举一个例子说明这个处理的具体过程:
假设DS18B20的温度采样结果是:LSB = 0x96,MSB = 0x01。
温度数据变量T_dat为16位无符号int整型,初始值为0x0000。
执行T_dat = MSB;语句后, T_dat 的值为:0x0001。
执行T_dat <<= 8;语句后, T_dat 的值为:0x0100。
执行T_dat = T_dat | LSB;语句后, T_dat 的值为:0x0196。
通过高5位的符号扩展位判断,进行正温度的处理算法,正常来说,应该是:
T_dat = 0x0196 × 0.0625 = 406 × 0.0625 = 25.375 摄氏度。
如果要求温度结果保留1位小数,为了简化在单片机中的运算,可以放大10倍进行整型处理:
首先将温度结果的整数部分取出: T_dat >>= 4;即 T_dat = 0x0019 = 25。
然后将整数部分放大10倍: T_dat = T_dat × 10 ;即 T_dat = 250。
然后将小数部分取出: LSB&0x0f,其结果为0x06。
再将小数部分乘以0.0625的10倍,即0x06 × 0.625 = 3.73。
最后将整数部分和小数部分相加: T_dat = 250 + 3.73 = 253。为什么?因为 T_dat 是整型。
对于温度数据253,在数码管显示的时候,在十位出加上一个小数点,就变成了:25.3。
这样可以让单片机避免很多浮点运算,而且数码管显示也会很简洁很方便。
如果要求温度结果只显示整数部分,那就不用这么啰嗦,直接显示整数部分即可:
T_dat >>= 4;即 T_dat = 0x0019 = 25 摄氏度。
11.DS1302
12.NE555定时器
#include <REGX52.H>
#include "smg.h"
unsigned int count_f;
unsigned char count_t;
unsigned int dat;
//**********************************************************************************************
void Init_Timer()
{
//定时器0用作计数,8位自动重装载
TH0 = 0xff; //来一次脉冲就溢出,P34:T0 定时/计数器0外部事件计数输入引脚
TL0 = 0xff; //P34与555定时器的single端连接在一起
//定时器1用作计时,50ms
TH1 = (65535 - 50000) / 256;
TL1 = (65535 - 50000) % 256;
TMOD = 0x16; //0001 0110
ET0 = 1;
ET1 = 1;
EA = 1;
TR0 = 1;
TR1 = 1;
}
void Servece_Time0() interrupt 1
{
count_f++; //来一个脉冲中断一次
}
void Servece_Time1() interrupt 3
{
TH1 = (65535 - 50000) / 256;
TL1 = (65535 - 50000) % 256;
count_t++;
if (count_t == 20)
{
count_t = 0;
dat = count_f; //dat保存之前值,又开始一轮新的计数
count_f = 0;
}
}
//**********************************************************************************************
void Display()
{
Display_smg(0, 0x8e);
Delay(100);
Display_smg(1, 0xff);
Delay(100);
Display_smg(2, 0xff);
Delay(100);
if (dat > 9999) //数字长度不足5位时,熄灭处理未用到的数码管
{
Display_smg(3, smg[dat / 10000]);
Delay(100);
}
if (dat > 999) //数字长度不足5位时,熄灭处理未用到的数码管
{
Display_smg(4, smg[(dat / 1000) % 10]);
Delay(100);
}
if (dat > 99) //数字长度不足5位时,熄灭处理未用到的数码管
{
Display_smg(5, smg[(dat / 100) % 10]);
Delay(100);
}
if (dat > 9) //数字长度不足5位时,熄灭处理未用到的数码管
{
Display_smg(6, smg[(dat / 10) % 10]);
Delay(100);
}
Display_smg(7, smg[dat % 10]);
Delay(100);
Display_ALL(0xff);
}
void main()
{
Init_Timer();
while(1)
{
Display();
}
}