目录
明天就是省赛了,今天最后一天。
赛前预测:今年很有可能考DS18B20
关于常用程序封装,一下程序是自己在写这么多的省赛题后总结的经验,仅供参考。
自己关于比赛写程序的一些建议,不喜勿喷:
1、所有题目从前往后依次写,有些小bug没事,功能要稳定就行。
2、程序中建议不要使用delay,比如按键消抖,一旦使用delay很有可能影响其他部件的工作状态。
3、多设置标志位,在while(1)和定时器中断中判断标志位,再操作。
4、除了定时器中断和while(1),其他地方不建议放一些需要时间很多的操作。比如不建议在按键处理函数中调用读写温度等等。
5、关于一些操作的时间。独立按键2ms扫描一次;矩阵按键1ms扫描一次;LED、数码管、蜂鸣器、继电器放在定时器中断里2ms执行一次;DS18B20在没有说明采集时间间隔情况下,在man函数的while(1)里1s采集一次数据;AD100ms采集一次。DS1302在中断中执行。
上电全部初始化
void AllInit(void)
{
P2 = (P2 & 0x1f) | 0x80;
P0 = 0xff;
P2 = (P2 & 0x1f) | 0xc0;
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xa0;
P0 = 0x00;
P2 = P2 & 0x1f;
}
LED
void LED_work(u8 dat)
{
P0 = 0xff;
P2 = (P2 & 0x1f) | 0x80;
P0 = dat;
P2 = P2 & 0x1f;
}
数码管显示
//以下数组定义为全局变量
//存放共阳数码管的码字,比赛会提供
u8 code Nixie[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8,
0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e,
0xff, 0xbf}; //16 17
//存放数码管显示缓存
u8 Nixiebuff[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
//分别设置每个数码管显示什么
u8 smg1, smg2, smg3, smg4, smg5, smg6, smg7, smg8;
//数码管扫描,定时器中断调用
void Smg_Scan(void)
{
static u8 index = 0;
P2 = (P2 & 0x1f) | 0xc0; //打开Y6C
P0 = 0x01 << index;
P2 = (P2 & 0x1f) | 0xe0; //打开Y7C
P0 = 0xff;
P0 = Nixiebuff[index];
index ++;
index &= 0x07;
}
//更新数码管值,在while(1)或者定时器中断中调用都可以
void Smg_show()
{
//在这里设置每个数码管需要显示显示的数值,根据实际情况设置即可,数字对应上述数组Nixie中的元素
smg1 = 16;
smg2 = 16;
smg3 = 16;
smg4 = 16;
smg5 = 16;
smg6 = 16;
smg7 = 0;
smg8 = 0;
Nixiebuff[3] = Nixie[smg1];
Nixiebuff[2] = Nixie[smg2];
Nixiebuff[1] = Nixie[smg3];
Nixiebuff[0] = Nixie[smg4];
Nixiebuff[7] = Nixie[smg5];
Nixiebuff[6] = Nixie[smg6];
Nixiebuff[5] = Nixie[smg7];
Nixiebuff[4] = Nixie[smg8];
}
蜂鸣器buzzer
蜂鸣器只需操作P06这一个IO口就可以了。
注意,因为所有数据都是P0口发送,为了使其不受干扰,在准备使能控制蜂鸣器这个573前,先把P0全部置零,关闭
//打开蜂鸣器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xa0;
P06 = 1; //打开蜂鸣器
P2 = P2 & 0x1f;
//*****************************************
//关闭蜂鸣器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xa0;
P06 = 0; //关闭蜂鸣器
P2 = P2 & 0x1f;
继电器relay
和上述继电器一模一样,控制P04这个IO
//打开继电器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xa0;
P04 = 1; //打开继电器
P2 = P2 & 0x1f;
//*****************************************
//关闭继电器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xa0;
P04 = 0; //关闭继电器
P2 = P2 & 0x1f;
独立按键BTN
注意,在使用独立按键时需要将开发板上J5跳线帽接在BTN那一端
u8 KeySta[] = {1, 1, 1, 1}; //键值存储区
u8 Keybackup[] = {1, 1, 1, 1}; //键值备份区
sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;
//按键扫描函数,在定时器中断里调用
void Key_Scan(void)
{
static u8 Keybuff[] = {0xff, 0xff, 0xff, 0xff}; //按键缓冲区
u8 i = 0;
Keybuff[0] = (Keybuff[0] << 1) | S7;
Keybuff[1] = (Keybuff[1] << 1) | S6;
Keybuff[2] = (Keybuff[2] << 1) | S5;
Keybuff[3] = (Keybuff[3] << 1) | S4;
for(i = 0; i < 4; i++)
{
if(Keybuff[i] == 0xff) //按键松开
KeySta[i] = 1;
else if(Keybuff[i] == 0x00) //按键按下
KeySta[i] = 0;
else //键值不稳定
{}
}
}
//根据检测到的哪一个按键按下,执行相应的操作
void Key_drive(u8 key)
{
if(key == 0)
{
}
else if(key == 1)
{
}
else if(key == 2)
{
}
else if(key == 3)
{
}
}
//检测按键是否按下,在main函数while(1)中调用
void Key_press(void)
{
u8 i;
for(i = 0; i < 4; i ++)
{
if(KeySta[i] != Keybackup[i])
{
if(KeySta[i] == 0) //按键按下时操作
Key_drive(i);
Keybackup[i] = KeySta[i];
}
}
}
矩阵键盘KBD
矩阵键盘和独立按键类似,J5接KBD端。
注意:开发板上的P36和P37对应实际管脚是P42和P44。
//管脚定义,一定要注意前两个
sbit KeyOut1 = P4^4;
sbit KeyOut2 = P4^2;
sbit KeyOut3 = P3^5;
sbit KeyOut4 = P3^4;
sbit KeyIn1 = P3^0;
sbit KeyIn2 = P3^1;
sbit KeyIn3 = P3^2;
sbit KeyIn4 = P3^3;
//存放键码,KeyAction函数根据传递过去的键码对应哪一个按键按下,执行相应的操作
unsigned char code KeyMap[4][4] = {{0x01, 0x02, 0x00, 0x00},
{0x03, 0x04, 0x00, 0x00},
{0x05, 0x06, 0x00, 0x00},
{0x07, 0x08, 0x00, 0x00}};
//存放键值,用于消抖
unsigned char KeyBuff[4][4] = {{0xff, 0xff, 0xff, 0xff},
{0xff, 0xff, 0xff, 0xff},
{0xff, 0xff, 0xff, 0xff},
{0xff, 0xff, 0xff, 0xff}};
//存放每个按键是按下还是松开状态
unsigned char KeySta[4][4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};
//备份按键状态,用于判断按键状态是否发生改变
unsigned char KeyBackup[4][4] = {{1,1,1,1}, {1,1,1,1}, {1,1,1,1}, {1,1,1,1}};
//按键扫描函数,在定时器中断中调用,1ms调用一次
void KeyScan(void)
{
static u8 index = 0;
u8 i;
KeyBuff[0][index] = (KeyBuff[0][index] << 1) | KeyIn1;
KeyBuff[1][index] = (KeyBuff[1][index] << 1) | KeyIn2;
KeyBuff[2][index] = (KeyBuff[2][index] << 1) | KeyIn3;
KeyBuff[3][index] = (KeyBuff[3][index] << 1) | KeyIn4;
for(i = 0; i < 4; i ++)
{
if((KeyBuff[i][index] & 0x0f) == 0x00)
KeySta[i][index] = 0;
else if((KeyBuff[i][index] & 0x0f) == 0x0f)
KeySta[i][index] = 1;
}
index ++;
index &= 0x03;
switch(index)
{
case 0: KeyOut4 = 1; KeyOut1 = 0; break;
case 1: KeyOut1 = 1; KeyOut2 = 0; break;
case 2: KeyOut2 = 1; KeyOut3 = 0; break;
case 3: KeyOut3 = 1; KeyOut4 = 0; break;
}
}
//根据键码keycode判断哪一个按键按下,进行相应的操作。
void KeyAction(unsigned char keycode)
{
if(keycode == 0x01)
{
}
else if(keycode == 0x03)
{
}
else if(keycode == 0x02)
{
}
else if(keycode == 0x04)
{
}
else if(keycode == 0x05)
{
}
else if(keycode == 0x06)
{
}
else if(keycode == 0x07)
{
}
}
//检测按键是否按下吗,在main函数while(1)中调用
void KeyPress(void)
{
u8 i, j;
for(i = 0; i < 4; i ++)
{
for(j = 0; j < 4; j ++)
{
if(KeyBackup[i][j] != KeySta[i][j])
{
if(KeySta[i][j] == 0) //按键按下时操作
KeyAction(KeyMap[i][j]);
KeyBackup[i][j] = KeySta[i][j];
}
}
}
}
E2PROM和AD
根据官方提供的驱动添加读取AD值函数,读写E2PROM函数。
切记,写E2PROM后要等待5ms再写下一个字节。
读取E2PROM时不需要等待,直接连续读取就可以。
注意:somenop 中改为33个_nop()_
/*
程序说明: IIC总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include "sys.h"
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
somenop;
SDA = 0;
somenop;
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
somenop;
SDA = 1;
}
//应答位控制
void IIC_Ack(bit ackbit)
{
if(ackbit)
{
SDA = 0;
}
else
{
SDA = 1;
}
somenop;
SCL = 1;
somenop;
SCL = 0;
SDA = 1;
somenop;
}
//等待应答
bit IIC_WaitAck(void)
{
SDA = 1;
somenop;
SCL = 1;
somenop;
if(SDA)
{
SCL = 0;
IIC_Stop();
return 0;
}
else
{
SCL = 0;
return 1;
}
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(byt&0x80)
{
SDA = 1;
}
else
{
SDA = 0;
}
somenop;
SCL = 1;
byt <<= 1;
somenop;
SCL = 0;
}
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++)
{
SCL = 1;
somenop;
da <<= 1;
if(SDA)
da |= 0x01;
SCL = 0;
somenop;
}
return da;
}
//读取AD值
unsigned char Read_AIN(unsigned char chn)
{
unsigned char dat;
EA = 0;
IIC_Start(); //IIC总线起始信号
IIC_SendByte(0x90); //PCF8591的写设备地址
IIC_WaitAck(); //等待从机应答
IIC_SendByte(chn); //写入PCF8591的控制字节
IIC_WaitAck(); //等待从机应答
IIC_Stop(); //IIC总线停止信号
IIC_Start(); //IIC总线起始信号
IIC_SendByte(0x91); //PCF8591的读设备地址
IIC_WaitAck(); //等待从机应答
dat = IIC_RecByte(); //读取PCF8591通道3的数据
IIC_Ack(0); //产生非应答信号
IIC_Stop(); //IIC总线停止信号
EA = 1;
return dat;
}
//写E2PROM,一次写一个字节
void Write_E2PROM(unsigned char add, unsigned char dat)
{
EA = 0;
IIC_Start();
IIC_SendByte(0xa0); //发送器件地址
IIC_WaitAck();
IIC_SendByte(add); //发送操作地址
IIC_WaitAck();
IIC_SendByte(dat); //写一字节
IIC_WaitAck();
IIC_Stop();
somenop;
EA = 1;
}
//读E2PROM,一次读一个字节
unsigned char Read_E2PROM(unsigned char add)
{
unsigned char d;
IIC_Start();
IIC_SendByte(0xa0); //发送器件地址
IIC_WaitAck();
IIC_SendByte(add); //发送要操作的地址
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0xa1); //发送读操作
IIC_WaitAck();
d = IIC_RecByte(); //读一字节
IIC_Ack(0);
IIC_Stop();
return d;
}
DS18B20
DS18B20在官方驱动的基础上,增加开始温度转换函数和读取温度函数。
注意:DS18B20先读取出来的是低字节
/*
程序说明: 单总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051 12MHz
日 期: 2011-8-9
*/
#include "sys.h"
sbit DQ = P1^4; //单总线接口
//单总线延时函数
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0; i<8; i++);
}
}
//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//DS18B20设备初始化
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
//开始温度转换
void Trans_temper()
{
init_ds18b20(); //初始化
Write_DS18B20(0xcc); //跳过ROM,因为只有一个DS18B20
Write_DS18B20(0x44); //启动温度转换
}
//读取温度
int Gettemper()
{
unsigned char higt, low; //定义高八位低八位
unsigned int temper; //温度值
init_ds18b20(); //初始化
Write_DS18B20(0xcc); //跳过ROM,因为只有一个DS18B20
Write_DS18B20(0xbe);
low = Read_DS18B20();
higt = Read_DS18B20();
temper = ((unsigned int)higt << 8) | low;
return temper;
}
DS1302
DS1302在官方驱动基础上增加初始化函数。
注意:写DS1302前一定要取消写保护
/*
程序说明: DS1302驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include "sys.h"
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST = P1^3; // DS1302复位
void Write_Ds1302_Byte(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK=0;
SDA=temp&0x01;
temp>>=1;
SCK=1;
}
}
void Write_Ds1302( unsigned char address,unsigned char dat )
{
RST=0;
_nop_();
SCK=0;
_nop_();
RST=1;
_nop_();
Write_Ds1302_Byte(address);
Write_Ds1302_Byte(dat);
RST=0;
}
unsigned char Read_Ds1302 ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0;
_nop_();
SCK=0;
_nop_();
RST=1;
_nop_();
Write_Ds1302_Byte(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0;
_nop_();
RST=0;
SCK=0;
_nop_();
SCK=1;
_nop_();
SDA=0;
_nop_();
SDA=1;
_nop_();
return (temp);
}
void DS1302Init(void)
{
unsigned char timeinit[] = {0x23, 0x59, 0x55};
Write_Ds1302(0x8e, 0x00); //取消写保护
Write_Ds1302(0x84, timeinit[0]); //初始化时钟
Write_Ds1302(0x82, timeinit[1]); //初始化分钟
Write_Ds1302(0x80, timeinit[2]); //初始化秒钟
}
超声波
#define sonic_nop {_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();};
void SendSonic(void)
{
unsigned char i = 8;
while(i --)
{
TX = 1;
sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
TX = 0;
sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
}
}
//定时器0初始化
void Time0_Init(void)
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xcd; //设置定时初值1ms
TH0 = 0xd4; //设置定时初值1ms
TF0 = 0; //清除TF0标志
ET0 = 1; //允许定时器0中断
TR0 = 1; //定时器0开始计时
}
//定时器0中断程序
void Time0(void) interrupt 1
{
static uint t0 = 0;
static uchar index = 0;
t0 ++;
if(t0 == 125)
{
t0 = 0;
flag_125ms = 1;
Led_illume(0xfe << index);
index ++;
index &= 0x07;
}
}
void Timer1Init(void) //0微秒@11.0592MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0; //设置定时初值
TH1 = 0; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 0;
}
void main(void)
{
uint t, distance = 0;
All_Init();
Time0_Init();
Timer1Init();
EA = 1;
while(1)
{
if(flag_125ms)
{
flag_125ms = 0;
SendSonic(); //发送50KHz的超声波
TR1 = 1; //开启定时器1计时
while((RX == 1) && (TF1 == 0)); //如果接收到回波或者定时器1溢出
TR1 = 0; //关闭定时器1
if(TF1 == 1)
{
TF1 = 0;
distance = 999; //距离999
}
else
{
t = TH1;
t <<= 8;
t |= TL1;
distance = (uint)(t * 0.017);
}
Deal_distance(distance);
TH1 = 0;
TL1 = 0;
}
}
}