通过看题目,我们可以分析得出,这一届省赛的题目,主要考察了以下模块:LED、EEPROM的读与写、按键、数码管、PCF8591的AD转换、定时器以及对继电器和蜂鸣器的关闭。我一般拿到赛题,然后就会去分析里面所运用到的模块,然后把各个模块的模板写出来。然后最后像搭积木一样,来实现整体的功能。
以下就是我的模板:
首先创立bsp_led.h这个文件,往里写入:
#include"STC15F2K60S2.H"
void Led_Disp(unsigned char ucled);
然后创立bsp_led.c这个文件在里面写入:
/*
* 在对于传入的参数ucled,我们用正常逻辑来表示灯灭与亮,即1表示亮,0表示灭
*/
#include "bsp_led.h"
void Led_Disp(unsigned char ucled)
{
ucled =~ ucled;
P2 = P2 & 0X1F | 0X80;
P0 = ucled;
P2 &= 0x1F;
}
这两个模块都是接在iic总线上的,对于iic,在比赛过程中,我们是可以拿到它的底层驱动代码的。一共两个文件,bsp_iic.c和bsp_iic.h。我们往里再加入三个函数,以实现我们的模板。
iic.c文件如下:我们往里加入,EEPROM_Read,EEOROM_Write,PCF8591_ADc这三个函数。
/*
程序说明: IIC总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include"bsp_iic.h"
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
void IIC_Delay(unsigned char i)
{
do{_nop_();}
while(i--);
}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//发送应答
void IIC_SendAck(bit ackbit)
{
SCL = 0;
SDA = ackbit; // 0:应答,1:非应答
IIC_Delay(DELAY_TIME);
SCL = 1;
IIC_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//等待应答
bit IIC_WaitAck(void)
{
bit ackbit;
SCL = 1;
IIC_Delay(DELAY_TIME);
ackbit = SDA;
SCL = 0;
IIC_Delay(DELAY_TIME);
return ackbit;
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++)
{
SCL = 0;
IIC_Delay(DELAY_TIME);
if(byt & 0x80) SDA = 1;
else SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 1;
byt <<= 1;
IIC_Delay(DELAY_TIME);
}
SCL = 0;
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char i, da;
for(i=0; i<8; i++)
{
SCL = 1;
IIC_Delay(DELAY_TIME);
da <<= 1;
if(SDA) da |= 1;
SCL = 0;
IIC_Delay(DELAY_TIME);
}
return da;
}
//====================一下为我们加入的内容
void EEPROM_Read(unsigned char *EEPROM_String,unsigned char addr,unsigned char num)
{
IIC_Start();
IIC_SendByte(0xA0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xA1);
IIC_WaitAck();
while(num--)
{
*EEPROM_String++=IIC_RecByte();
if(num) IIC_SendAck(0);
else IIC_SendAck(1);
}
IIC_Stop();
}
void EEPROM_Write(unsigned char *EEPROM_String,unsigned char addr,unsigned char num)
{
IIC_Start();
IIC_SendByte(0xA0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while(num--)
{
IIC_SendByte(*EEPROM_String++);
IIC_WaitAck();
IIC_Delay(200);
}
IIC_Stop();
}
unsigned char Pcf8591_Adc(unsigned char channel_num_control)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(channel_num_control);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
return temp;
}
然后在bsp_iic.h 补上这三个函数的定义。
# include "STC15F2K60S2.H"
#include "intrins.h"
#define DELAY_TIME 5
#define Photo_Res_Channel 0x41
#define Adj_Res_Channel 0x43
void IIC_Start(void);
void IIC_Stop(void);
bit IIC_WaitAck(void);
void IIC_SendAck(bit ackbit);
void IIC_SendByte(unsigned char byt);
unsigned char IIC_RecByte(void);
unsigned char Pcf8591_Adc(unsigned char channel_num_contrl);
void EEPROM_Write(unsigned char* EEPROM_String, unsigned char addr, unsigned char num);
void EEPROM_Read(unsigned char* EEPROM_String, unsigned char addr, unsigned char num);
老套路创建一个bsp_key.h的文件,并在里面写入:
#include"STC15F2K60S2.H"
unsigned char Key_Read();
然后创建bsp_key.c文件,往里写入:
#include"bsp_key.h"
unsigned char Key_Read()
{
unsigned int key_new;
unsigned char key_value;
P44=0;P42=1;P35=1;P34=1;
key_new = P3 & 0X0F;
P44=1;P42=0;P35=1;P34=1;
key_new = (key_new<<4)|(P3 & 0X0F);
P44=1;P42=1;P35=0;P34=1;
key_new = (key_new<<4)|(P3 & 0X0F);
P44=1;P42=1;P35=1;P34=0;
key_new = (key_new<<4)|(P3 & 0X0F);
switch(~key_new)
{
case 0x8000: key_value=4;break;
case 0x4000: key_value=5;break;
case 0x2000: key_value=6;break;
case 0x1000: key_value=7;break;
case 0x0800: key_value=8;break;
case 0x0400: key_value=9;break;
case 0x0200: key_value=10;break;
case 0x0100: key_value=11;break;
case 0x0080: key_value=12;break;
case 0x0040: key_value=13;break;
case 0x0020: key_value=14;break;
case 0x0010: key_value=15;break;
case 0x0008: key_value=12;break;
case 0x0004: key_value=13;break;
case 0x0002: key_value=14;break;
case 0x0001: key_value=15;break;
default: key_value=0;break;
}
return key_value;
}
老套路创建bsp_seg.h,往里写入:
#include "STC15F2K60S2.H"
void Seg_Trans(unsigned char *seg_string,unsigned char *seg_buf);
void Seg_Disp(unsigned char*seg_buf,unsigned char pos);
然后创建bsp_seg.c,往里写入:
#include "bsp_seg.H"
void Seg_Trans(unsigned char *seg_string,unsigned char *seg_buf)
{
unsigned char i,j;
unsigned char temp;
for(i=0,j=0;j<8;j++)
{
switch(seg_string[i])
{
case '0': temp=0xc0; break;
case '1': temp=0xf9; break;
case '2': temp=0xa4; break;
case '3': temp=0xb0; break;
case '4': temp=0x99; break;
case '5': temp=0x92; break;
case '6': temp=0x82; break;
case '7': temp=0xf8; break;
case '8': temp=0x80; break;
case '9': temp=0x90; break;
case '-': temp=0xbf; break;
case ' ': temp=0xff; break;
default : temp=0xff; break;
}
seg_buf[j]=temp;
i++;
}
}
void Seg_Disp(unsigned char*seg_buf,unsigned char pos)
{
P0 = 0XFF;
P2 = P2 & 0X1F | 0Xe0;
P2 &= 0X1F;
P0 = 0X01 << pos;
P2 = P2 & 0X1F | 0Xc0;
P2 &= 0X1F;
P0 =seg_buf[pos];
P2 = P2 & 0X1F | 0Xe0;
P2 &= 0X1F;
}
定时器这个模板不用想,我们可以去那个烧写的软件里面找:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJhzh171-1647304468424)(C:\Users\Wind\AppData\Roaming\Typora\typora-user-images\image-20220314181650106.png)]
老套路 创建timer.h
#include"STC15F2K60S2.H"
void Timer1Init(void);
创建timer.c
#include"timer.h"
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1=1;
}
十分常见的一个模板,基本上每年赛题都会使用。创建一个bsp_init.h
#include"STC15F2K60S2.H"
void Cls_init();
创建bsp_init.c
#include " bsp_init.h"
void Cls_init()
{
P0 = 0XFF;
P2 = P2 & 0X1F | 0X80;
P2 &= 0X1F;
P0=0;
P2 = P2 & 0X1F | 0XA0;
P2 &= 0X1F;
}
说到主函数的编写,其实就是编写4个函数,一个中断服务函数,一个led显示函数(Led_Proc),一个数码管显示函数(Seg_Proc),一个按键扫描函数(Key_Proc),其中按键又是非常接近底层的,所以我们可以先从编写扫描按键函数来入手这个赛题。
我们创建main.c文件
#include "bsp_led.h"
#include "bsp_seg.h"
#include "bsp_iic.h"
#include "bsp_key.h"
#include "timer.h"
#include "bsp_init.h"
#include "stdio.h"// (sprintf用) //一定不要落下某些函数的头文件,好像编译器不会报错,但会导致工作不正常
unsigned int Key_Slow_Down;
unsigned int Seg_Slow_Down;
unsigned long tick;//模仿32中的滴答定时器,可以定49天
unsigned long led_tick;//led流转专用
//++++++++++++++++++++按键专用++++++++++++++++++++++
unsigned char key_value,key_old,key_down;
//++++++++++++++++++++数码管显示专用+++++++++++++++++
unsigned char seg_string[10],seg_buf[8];
unsigned char pos;
//++++++++++++++++++++led显示用++++++++++++++++++++
unsigned char ucled;
unsigned char sz_mode=0;//设置模式的跳转,0-2,0什么都不显示,1设置led运行模式,2设置该led模式下的流转时间
unsigned int led_running[4]={400,400,400,400};//led下流转时间的数组
unsigned char EEPROM_led_running[4]={0,0,0,0};//用于写入EEPROM的数组
unsigned char led_mode=1;//led模式
bit led_flag=1;//该位可以控制led的亮灭
bit xssz_mode;//亮度显示设置位
bit buling;//数码管闪动
unsigned char Bright_Level;//显示亮度等级设置
unsigned char Bright_Level_Sister,Bright_Disp_Ctrl;//显示亮度等级设置
code unsigned char led_disp_matrix[4][8]={
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,//第一种模式,L1-L8
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, //第二种模式,L8-L1
0x81, 0x42, 0x24, 0x18, 0x81, 0x42, 0x24, 0x18,//第三种模式,从两边到中间
0x18, 0x24, 0x42, 0x81, 0x18, 0x24, 0x42, 0x81//第四种模式,从中间到两边
};
unsigned char j;
void Led_Proc();
void Seg_Proc();
void Key_Proc();
void main()
{
Cls_Init();
Timer1Init();
EA=1;
EEPROM_Read(EEPROM_led_running,16,4);//向EEPROM中读取数据
if(EEPROM_led_running[1]>12||EEPROM_led_running[0]>12||EEPROM_led_running[2]>12||EEPROM_led_running[3]>12)
{
EEPROM_led_running[0]=6;
EEPROM_led_running[2]=6;
EEPROM_led_running[3]=6;
EEPROM_led_running[1]=6;
}//因为原始EEPROM有数据,所以只要里面存的数据大于1200就说明不是之前存的。
led_running[0] = EEPROM_led_running[0] * 100;
led_running[1] = EEPROM_led_running[1] * 100;
led_running[2] = EEPROM_led_running[2] * 100;
led_running[3] = EEPROM_led_running[3] * 100;
while(1)
{
Led_Proc();
Key_Proc();
Seg_Proc();
}
}
void tm1_isr()interrupt 3
{
if(++Key_Slow_Down==10) Key_Slow_Down=0;
if(++Seg_Slow_Down==500) Seg_Slow_Down=0; //减速
tick++;
if(tick%800==0)
{
buling =~ buling;
}
switch(Bright_Level)
{
case 1:Bright_Level_Sister = 0; break;
case 2:Bright_Level_Sister = 3; break;
case 3:Bright_Level_Sister = 8; break;
case 4:Bright_Level_Sister = 11; break;
}
Bright_Disp_Ctrl++;
if(Bright_Disp_Ctrl==13)
Bright_Disp_Ctrl=0;
if(Bright_Disp_Ctrl <= Bright_Level_Sister)//实现亮度大小切换
Led_Disp(ucled);//LED显示
else
Led_Disp(0);//led灭
Seg_Disp(seg_buf,pos);
if(++pos==8) pos=0;
}
void Key_Proc()
{
if(Key_Slow_Down) return ;
Key_Slow_Down=1;
key_value = Key_Read();
key_down = key_value & (key_value ^ key_old);//按键里面的精华,使得一直按着的话会无效
key_old=key_value;
switch(key_down)
{
case 4:
sz_mode++;
if(sz_mode==3)
{
sz_mode=0;
EEPROM_led_running[0]=led_running[0]/100;
EEPROM_led_running[1]=led_running[1]/100;
EEPROM_led_running[2]=led_running[2]/100;
EEPROM_led_running[3]=led_running[3]/100;
EEPROM_Write(EEPROM_led_running,16,4);
}
break;
case 5:
led_flag =~ led_flag;
break;
case 9:
switch(sz_mode)
{
case 1:
led_mode++;
if(led_mode==5)
led_mode=1;
break;
case 2:
led_running[led_mode-1]+=100;
if(led_running[led_mode-1]==1300)
led_running[led_mode-1]=1200;
break;
}
break;
case 8:
switch(sz_mode)
{
case 1:
led_mode--;
if(led_mode==0)
led_mode=1;
break;
case 2:
led_running[led_mode-1]-=100;
if(led_running[led_mode-1]==300)
led_running[led_mode-1]=400;
break;
}
break;
}
if((sz_mode==0)&&(key_old==8))
{
xssz_mode=1;
}
else
xssz_mode=0;
}
void Seg_Proc()
{
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;//减速程序
switch(sz_mode)
{
case 1:
case 2:
sprintf(seg_string,"-%d- %4d",(unsigned int)led_mode,(unsigned int)led_running[led_mode-1]);
break;
case 0:
sprintf(seg_string," ");
break;
}
if(buling==1)
{
switch(sz_mode)
{
case 1:
seg_string[1]=' ';
break;
case 2:
seg_string[4]=' ';
seg_string[5]=' ';
seg_string[6]=' ';
seg_string[7]=' ';
break;
}
}
Bright_Level = (Pcf8591_Adc(Adj_Res_Channel)/64+1);
if(xssz_mode==1)
{
sprintf(seg_string," -%d",(unsigned int) Bright_Level);
}
Seg_Trans(seg_string,seg_buf);
}
void Led_Proc()
{
if(led_flag)
{
if(tick-led_tick>=led_running[led_mode-1])
{
led_tick=tick;
ucled=led_disp_matrix[led_mode-1][j];
j++;
if(j==8)
{
if(++led_mode==5) led_mode=1;
j=0;
}
}
}
else
ucled=0;
}