在蓝桥杯单片机中,由于外设的引脚都固定,为了在最短的时间内编写准确且实用的底层,我们有必要熟练一种模板从而把更多时间精力放到题目功能的实现上。
目录
新建工程
新建文件夹(考号命名)后在该目录下新建“Driver”和“User”子文件夹,在MDK中新建工程到“User”目录下,添加芯片包“IAP15F2K60S2”.
魔术棒“Output”勾选生产HEX文件,“C51”添加Driver路径。旁边的品字形进行文件名的修改(考试中“Target1”命名为考号)
在MDK新建两个页面均保存到“Driver”文件夹下,一个命名为init.c,一个为init.h,在品字形添加init.c,在init..c内右击选择“Inser'#include <STC15F2K60S2.H>'.将该头文件放到init..h,该.c文件引用init..h文件。
在模块对应的.c文件夹下引入自身的.h文件和“reg52.h”必要的话还有“intrins.h”
模块引脚定义在.c,下面就是函数定义。
.h文件一般仅放函数声明。
在User下面右击添加“Add Nem Item to Group 'User'... ”选择C文件,命名为main.c,建立主函数。
模板框架
1.头文件声明。2.变量声明。3.键盘处理函数(Key)。4.信息显示函数(Seg)。5.其他显示函数(LED)。6.定时器初始化(Tim)。7.中断服务函数。8.主函数。
初始化关闭LED、蜂鸣器、继电器
Init.c文件仅含System_Init()函数,在程序一开始调用,起关闭LED、蜂鸣器、继电器作用。
void System_Init()
{
P0 = 0xff;//关闭LED,LED共阳,置一熄灭
P2 = P2 & 0x1f | 0x80;//打开锁存器:P2=100xxxxx,Y4=0,Y4C=1
P2 &= 0x1f;//关闭锁存器:P2=000xxxxx,
P0 = 0x00;//关闭继电器、蜂鸣器:低电平驱动,ULN2003反相,置零不工作
P2 = P2 & 0x1f | 0xa0;//打开锁存器:P2=101xxxxx,Y5=0,Y5C=1
P2 &= 0x1f;//关闭锁存器:P2=000xxxxx
}
主函数基本变量
unsigned char Key_Slow_Down;//按键减速变量
unsigned char Key_Val,Key_Old,Key_Up,Key_Down;//按键扫描读取变量
unsigned int Seg_Slow_Down;//数码管减速变量
unsigned char Seg_Pos;//数码管扫描变量
unsigned char Seg_Buf[8]={1,10,6,4,5,6,7,7};
//数码管段选数组,表示从左到右以此为:1 不显示 6 4 5 6 7 7
unsigned char Seg_Point[8]={0,0,0,0,0,0,0,0};//数码管小数点数组,0表示不显示小数点,1表示显示
LED
Led.c包含点亮LED、蜂鸣器、继电器的函数。
/*addr表示点亮哪一个LED;enable为1使能,为0失能.例如Led_Disp(1,1)表示第二个LED点亮*/
void Led_Disp(unsigned char addr,enable)
{
static unsigned char temp = 0x00;//局部静态变量,仅初始化一次。
static unsigned char temp_old = 0xff;
if(enable)
temp |= 0x01 << addr;//第addr位置1
else
temp &= ~(0x01 << addr);//第addr位置0
if(temp != temp_old)//只有temp值变化才进入下面的程序
{
P0 = ~temp;//addr位取反,对应位点亮或熄灭
P2 = P2 & 0x1f | 0x80;//打开控制LED的锁存器
P2 &= 0x1f;//关闭控制LED的锁存器
temp_old = temp;//更新temp_old变量
}
}
蜂鸣器、继电器函数:
void beep_disp(unsigned char flag)
{
static unsigned char temp = 0xff;
static unsigned char temp_old = 0x00;
if(flag)
temp |= 0x40;
else
temp &= ~0x40;
if(temp != temp_old)
{
P0 = ~temp;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old = temp;
}
}
void relay_disp(unsigned char flag)
{
static unsigned char temp = 0xff;
static unsigned char temp_old = 0x00;
if(flag)
temp |= 0x10;
else
temp &= ~0x10;
if(temp != temp_old)
{
P0 = ~temp;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old = temp;
}
}
若使用以上代码,继电器和蜂鸣器的使用会相互影响。修改如下(增加MOTOR引脚输出PWM):
static unsigned char temp_1 = 0x00;
static unsigned char temp_old_1 = 0xff;
void Beep(unsigned char flag)
{
if(flag)
temp_1 |= 0x40;
else
temp_1 &= ~0x40;
if(temp_1 != temp_old_1)
{
P0 = temp_1;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old_1 = temp_1;
}
}
void Relay(unsigned char flag)
{
if(flag)
temp_1 |= 0x10;
else
temp_1 &= ~0x10;
if(temp_1 != temp_old_1)
{
P0 = temp_1;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old_1 = temp_1;
}
}
void pulse(unsigned char flag)
{
if(flag)
temp_0 |= 0x20;
else
temp_0 &= ~0x20;
if(temp_0 != temp_old_0)
{
P0 = temp_0;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old_0 = temp_0;
}
}
在main.c中:继电器、蜂鸣器驱动函数在定时器0中每1ms扫描一次,可在其他代码块中修改标志量控制模块状态
bit Relay_Flag;//继电器使能标志位
bit Beep_Enable_Flag;//闹钟功能使能标志位
void Timer0Server() interrupt 1
{
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//键盘减速专用
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//数码管减速专用
if(++Seg_Pos == 8) Seg_Pos = 0;//数码管显示专用
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
Relay(Relay_Flag);
Beep(Relay_Flag & Beep_Enable_Flag);
}
KEY
Key.c包含按键扫描函数
unsigned char Key_Read()
{
unsigned char temp = 0;
if(P33 == 0) temp = 4;
if(P32 == 0) temp = 5;
if(P31 == 0) temp = 6;
if(P30 == 0) temp = 7;
return temp;
}
键盘读取处理函数在main.c中
void Key_Proc()
{
if(Proc_Flag != 1) return;
Proc_Flag = 0;//键盘减速程序
Key_Val = Key_Read();//读取按键的值
Key_Down = Key_Val & (Key_Old ^ Key_Val);//检测按键的下降沿
Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//检测按键的上升沿
Key_Old = Key_Val;//更新按键旧值
}
switch(Key_Down)//如果下降沿有效,触发按键操作
{
case 4://显示界面切换
break;
case 5://输出模式切换
break;
case 6://LED 指示灯功能控制
break;
case 7://数码管显示功能控制
break;
}
}
//其他使用
if(Key_Down != 0) //按下任意按键
if(Key_Old == 4) //长按“4按键”
SEG
Seg.c包含数码管写入函数
/*共阳极段选,从0xc0到0x90依次表示0~9,0xff表示全部熄灭,0x88表示小数点*/
unsigned char seg_dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x88};
/*段选*/
unsigned char seg_wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
/*数码管写入函数*/
void Seg_Disp(unsigned char wela,dula,point)
{
P0 = 0xff;//消隐,段码全部熄灭
P2 = P2 & 0x1f | 0xe0;//打开控制段选的锁存器:P2=111xxxxx,Y7=0
P2 &= 0x1f;//关闭控制段选的锁存器
P0 = seg_wela[wela];//单个数码管位码
P2 = P2 & 0x1f | 0xc0;打开控制位选的锁存器:P2=110xxxxx,Y6=0
P2 &= 0x1f;//关闭控制位选的锁存器
P0 = seg_dula[dula];//单个数码管段码
if(point)//判断当前位是否需要加小数点
P0 &= 0x7f;//需要显示小数点,最高位dp置0并不影响其他位,01111111&xxxxxxxx
P2 = P2 & 0x1f | 0xe0;///打开控制段选的锁存器:P2=111xxxxx,Y7=0
P2 &= 0x1f; //关闭控制段选的锁存器
}
数码管显示函数在main.c
在给Seg_Buf[]赋值时:char型变量、数组直接“=”赋值;如果是float型变量,整数部分进行char型强制转换,小数部分进行int型强制转换。
......
千位赋值=变量/1000%10
百位赋值=变量/100%10
十位赋值=变量/10%10
个位赋值=变量%10
两位小数十分位赋值=(变量*100)/10%10
两位小数百分位赋值=(变量*100)%10
void Seg_Proc()
{
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;//数码管减速程序
//AD、DS18B20读取在该处进行。如:Voltage = Ad_Read(0x03) / 51.0;//实时获取电压值
switch(Seg_Disp_Mode)
{
case 0://温度显示界面
Seg_Buf[0] = 11;//标识符C
Seg_Buf[4] = (unsigned char)Temperature / 10 % 10;
Seg_Buf[5] = (unsigned char)Temperature % 10;
Seg_Buf[6] = (unsigned int)(Temperature * 100) / 10 % 10;
Seg_Buf[7] = (unsigned int)(Temperature * 100) % 10;
Seg_Point[5] = 1;//使能小数点
break;
case 1://参数设置界面
Seg_Buf[0] = 12;//标识符P
Seg_Buf[4] = Seg_Buf[5] = 10;//熄灭第五、第六个数码管
Seg_Buf[6] = Temperature_Params / 10 % 10;
Seg_Buf[7] = Temperature_Params % 10;
Seg_Point[5] = 0;//关闭小数点
break;
}
注:拿到题目首先观察数码管显示界面,如果某个位或小数点之前点亮,切换界面后不点亮,在后面的界面显示程序中要熄灭该位。
高位熄灭
如果某个待显示变量变化幅度大(如NE555的输出频率变化为几十到几千),高位需要熄灭使用while语句即可实现:
unsigned char i = 3;//高位熄灭专用变量
while(Seg_Buf[i] == 0) //数码管高位为0,进入下面的程序
{
Seg_Buf[i] = 10;//数码管高位熄灭
if(++i == 7) break; //保证最低位不熄灭 避免程序卡死
}
以上程序的表示监视数码管第三位到第六位(第七位若为0,表示该变量此时为0,可显示):i=3若万位为0,进入语句熄灭该位...当i=6若十位为0,进入语句该位熄灭之后i自增为7跳出语句执行后续程序,所以最多监视第六位
定时器0中断部分
/* 定时器0中断初始化函数 */
void Timer0Init(void) //12.000MHz晶振,定时1ms
{
AUXR &= 0x7F; //设置定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时器初值
TH0 = 0xFC; //设置定时器初值
TF0 = 0; //清除TF0标志位
TR0 = 1; //定时器0开始计时
ET0 = 1; //定时器中断0打开
EA = 1; //总中断打开
}
/* 定时器0中断服务函数*/
void Timer0Server() interrupt 1
{
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//10ms按键读取扫描一次
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//500ms数码管显示扫描一次
if(++Uart_Slow_Down == 200) Uart_Slow_Down = 0;//200ms串口扫描一次
if(++Seg_Pos == 8) Seg_Pos = 0;//数码管0-7位循环扫描
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);//数码管显示
Led_Disp(Seg_Pos,ucLed[Seg_Pos]);//其他显示函数
}
主函数
void main()
{
Sys_Init();
Timer0Init();//初始化
while(1)
{
Key_Proc();//三大函数循环扫描
Seg_Proc();
Led_Proc();
}
}