前言
本人为2023年蓝桥杯单片机设计与开发省赛一等奖国赛三等奖获奖选手。
能力并不是很强,比我强的同学也大有所在,不过也是希望能给即将参加蓝桥杯比赛的同学提供一些力所能及的帮助或者启示。
学习之路老师推荐:bilibili:小蜜蜂老师的干货铺,bilibili:Alice_西风
其实无论学习什么都要学会总结笔记,别人做的再好,你也很难记得住。
看完课程之后可以试着总结一些通用的底层代码并加以记忆。这样考试的时候就可以提前写出来个模板出来,再往里面填逻辑代码不就行了~
基础代码
初始化
#include "Init.h"
void Init_System()
{
P0=0xff;
P2=P2&0x1f|0x80;
P2&=0x1f;
P0=0x00;
P2=P2&0x1f|0xa0;
P2&=0x1f;
}
矩阵按键
#include "Key.h"
unsigned char Key_Read()
{
unsigned char temp;
//AUXR&=0xef; //关闭T2R,即定时器2 避免串口和矩阵按键互相干扰
P44=0;P42=1;P35=1;P34=1;
if(P33==0)temp=4;
if(P32==0)temp=5;
if(P31==0)temp=6;
if(P30==0)temp=7;
P44=1;P42=0;P35=1;P34=1;
if(P33==0)temp=8;
if(P32==0)temp=9;
if(P31==0)temp=10;
if(P30==0)temp=11;
P44=1;P42=1;P35=0;P34=1;
if(P33==0)temp=12;
if(P32==0)temp=13;
if(P31==0)temp=14;
if(P30==0)temp=15;
P44=1;P42=1;P35=1;P34=0;
if(P33==0)temp=16;
if(P32==0)temp=17;
if(P31==0)temp=18;
if(P30==0)temp=19;
//P3=0xff; //清除
//AUXR|=0x10; //打开T2R,即定时器2(串口波特率发生所用的定时器)
return temp;
}
数码管
#include "Seg.h"
code unsigned char seg_dula[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x8e,0x89,0x92,0xc6};
code 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&=0x1f;
P0=seg_wela[wela];
P2=P2&0x1f|0xc0;
P2&=0x1f;
P0=seg_dula[dula];
if(point)
P0&=0x7f;
P2=P2&0x1f|0xe0;
P2&=0x1f;
}
LED、BEEP、Relay
#include "Led.h"
void Led_Disp(unsigned char addr,flag)
{
static unsigned char temp_l,temp_old_l;
if(flag)
temp_l|=0x01<<addr;
else
temp_l&=~0x01<<addr;
if(temp_l!=temp_old_l)
{
P0=~temp_l;
P2=P2&0x1f|0x80;
P2&=0x1f;
temp_old_l=temp_l;
}
}
void BEEP(unsigned char flag)
{
if(flag)
temp|=0x40;
else
temp&=~0x40;
if(temp!=temp_old)
{
P0=temp;
P2=P2&0x1f|0xa0;
P2&=0x1f;
temp_old=temp;
}
}
void Realy(unsigned char flag)
{
if(flag)
temp|=0x10;
else
temp&=~0x10;
if(temp!=temp_old)
{
P0=temp;
P2=P2&0x1f|0xa0;
P2&=0x1f;
temp_old=temp;
}
}
串口
#include "Uart.h"
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8???,?????
AUXR |= 0x01; //??1?????2???????
AUXR &= 0xFB; //?????12T??
T2L = 0xE6; //???????
T2H = 0xFF; //???????
AUXR |= 0x10; //???2????
ES=1;
EA=1;
}
extern putchar(char c)
{
SBUF=c;
while(TI==0);
TI=0;
return c;
}
超声波
#include "utlrasound.h"
#include "intrins.h"
sbit Tx=P1^0;
sbit Rx=P1^1;
void Delay12us() //@12.000MHz
{
unsigned char i;
_nop_();
_nop_();
i = 38;
while (--i);
}
void Ut_Ware_Init()
{
unsigned char i;
for(i=0;i<8;i++)
{
Tx=1;
Delay12us();
Tx=0;
Delay12us();
}
}
unsigned char Ut_Ware_Data()
{
unsigned int time;
CMOD=0x00;
CH=CL=0;
Ut_Ware_Init();
CR=1;
while((Rx==1)&&(CF==0));
CR=0;
if(CF==0)
{
time=CH<<8|CL;
return time*0.017;
}
else
{
CF=0;
return 0;
}
}
时钟DS1302
#include "ds1302.h"
#include "reg52.h"
#include "intrins.h"
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST=P1^3;
..........
void Set_Rtc(unsigned char* ucRtc)
{
unsigned char i;
Write_Ds1302_Byte(0x8e,0x00);
for(i=0;i<3;i++)
Write_Ds1302_Byte(0x84-2*i,ucRtc[i]);
Write_Ds1302_Byte(0x8e,0x80);
}
void Get_Rtc(unsigned char* ucRtc)
{
unsigned char i;
for(i=0;i<3;i++)
ucRtc[i]=Read_Ds1302_Byte(0x85-2*i);
}
光敏电阻和EEPROM
#include "iic.h"
#include "reg52.h"
#include "intrins.h"
sbit sda=P2^1;
sbit scl=P2^0;
........
void Da_Wirte(unsigned char dat)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x41);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
unsigned char Ad_Read(unsigned char addr)
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
void EEPROM_Write(unsigned char* EEPROM_String,unsigned char addr,num)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
while(num--)
{
I2CSendByte(*EEPROM_String++);
I2CWaitAck();
I2C_Delay(200);
}
I2CStop();
}
void EEPROM_Read(unsigned char* EEPROM_String,unsigned char addr,num)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
while(num--)
{
*EEPROM_String++=I2CReceiveByte();
if(num) I2CSendAck(0);
I2CSendAck(1);
}
I2CStop();
}
温度
#include "onewire.h"
#include "reg52.h"
sbit DQ=P1^4;
........
float Temp_Read()
{
unsigned char low,high;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
return (high<<8|low)/16.0;
}
C51类型.h文件模板
#ifndef __DS1302_H
#define __DS1302_H
#endif
功能划分
LED频率亮灭
例:当按下按键4时,LED0以0.2秒为闪烁,亮灭10次
xdata unsigned long int Ms_Tick;//系统计时器
case 4:
........
Measurement_Complete_Flag = 1;//拉高测量完成标志位
........
break;
ucLed[0] = Measurement_Complete_Flag?Led_Star_Flag?1:0:0;
//按键按下时Measurement_Complete_Flag为真,当定时器为0.2秒把Led_Star_Flag置为真时,
//定时器中写
/* 计时相关 */
Ms_Tick++;
if(Measurement_Complete_Flag)
{
if(!(Ms_Tick % 1000))
{
Led_Star_Flag ^= 1;
if(++Count == 6) //亮5次灭5次
{
Count = 0;
Measurement_Complete_Flag = 0;
}
}
}
频率输出
void Timer0_Init(void) //1??@12.000MHz
{
AUXR &= 0x7F; //?????12T??
TMOD &= 0xF0; //???????
TMOD |= 0x05;
TL0 = 0; //???????
TH0 = 0; //???????
TF0 = 0; //??TF0??
TR0 = 1; //???0????
}
void Timer1Server() interrupt 3
{
if(++Key_slow_down==20)Key_slow_down=0;
if(++Seg_slow_down==500)Seg_slow_down=0;
if(++Uart_slow_down==200)Uart_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]);
if(++Timer_1000ms==1000)
{
TR0=0;
Timer_1000ms=0;
Freq=TH0<<8|TL0;
TH0=TL0=0;
TR0=1;
}
}
Seg_Buf[3]=Freq/10000%10;
Seg_Buf[4]=Freq/1000%10;
Seg_Buf[5]=Freq/100%10;
Seg_Buf[6]=Freq/10%10;
Seg_Buf[7]=Freq%10;
pos=3; //定义起始扫描变量
while(Seg_Buf[pos]==0) //高位为零时熄灭
{
Seg_Buf[pos]=10;
if(++pos==(7-(int)Hz_flag))break;
}
读取温度
//temp为float类型
temp=Temp_Read();
Seg_Buf[4]=(unsigned char)temp/10%10;
Seg_Buf[5]=(unsigned char)temp%10;
Seg_Buf[6]=(unsigned int)(temp*100)/10%10;
Seg_Buf[7]=(unsigned int)(temp*100)%10;
Seg_Point[5]=1;
读取电压
//d_val为float类型
d_val=Ad_Read(0x43)/51.0; //0x43为光敏,0x41为电阻,/51.0为数据转换
Seg_Point[5]=1;
Seg_Buf[5]=(unsigned char)d_val%10;
Seg_Buf[6]=(unsigned int)(d_val*100)/10%10;
Seg_Buf[7]=(unsigned int)(d_val*100)%10;
写入电压
//写入0.4V,需要*51进行数据转换
Da_Write(51*0.4);
写入EEPROM
//当写入数据为数组时,中间变量务必是8的倍数,最后变量为写入数据长度
EEPROM_Write(dat,0,2);
//当写入数据为变量时
EEPROM_Write(&a,8,1);
读取EEPROM
//读取的数据写入数组时,中间变量务必是8的倍数,最后变量为写入数据长度
EEPROM_Read(dat,0,2);
//当读取的数据为变量时
EEPROM_Write(&a,8,1);
时钟初始化
unsigned char ucRtc_disp[3]={0x16,0x59,0x50};
Set_Rtc(ucRtc_disp);
时钟写入
Get_Rtc(ucRtc_disp);
串口
void Uart_Proc()
{
if(Uart_Slow_Down) return;
Uart_Slow_Down = 1;
if(Uart_Recv_Index>0) //接收到数据
{
if(Uart_Recv_Index==4||Uart_Recv_Index==6) //数据长度
{
if(Uart_Recv[0] == 'S' && Uart_Recv[1] == 'T' && Uart_Recv[2] == '\r' && Uart_Recv[3] == '\n')
printf("$%d,%.2f\r\n",(unsigned int)distance,temperature);
else if(Uart_Recv[0] == 'P' && Uart_Recv[1] == 'A' && Uart_Recv[2] == 'R' && Uart_Recv[3] == 'A' && Uart_Recv[4] == '\r' && Uart_Recv[5] == '\n')
printf("#%d,%d\r\n",(unsigned int)Parm_Ctrol[0],(unsigned int)Parm_Ctrol[1]);
else printf("ERROR\r\n");
}
else
{
printf("ERROR\r\n");
}
if(Uart_Flag == 1)
{
Uart_Flag = 0;
printf("ERROR\r\n");
}
Uart_Flag = Uart_Recv_Index = 0;
}
}
长按短按
if(Key_Down == 12 || Key_Down == 13) //假设按键12、13
Key_Flag = 1;
if(Timer_1000Ms < 1000 && Key_Flag == 1) //短按
{
switch(Key_Up)
{
case 12: //短按12
break;
case 13: //短按13
break;
}
}
else //长按
{
switch(Key_Old)
{
case 12:
break;
case 13:
break;
}
}
数组储存历史数据
unsigned char Ultra_Data[2];//超声波测距数据储存数组 0-本次测量 1-上次测量
unsigned char Historical_Data[10];//历史储存数据
unsigned char Historical_Data_Index;//历史储存数据索引
switch(Key_Down)
{
case 4:
Ultra_Data[1] = Ultra_Data[0];//储存上一次测量值
Ultra_Data[0] = Rd_Ulra();//读取这次测量值
Measurement_Complete_Flag = 1;//拉高测量完成标志位
Historical_Data[Historical_Data_Index] = Ultra_Data[1];//保存历史值
if(++Historical_Data_Index == 9)
Historical_Data_Index = 0;
break;
}
PWM
例:若当前测量到的频率数据大于频率参数,通过竞赛板上的电机驱动引脚N MOTOR(J3-6)输出 1KHz 80%占空比的脉冲信号:否则该引脚输出 1KHz 20%占空比的脉冲信号。
unsigned char PWM_Count;//脉宽频率计数值 1KHZ = 1MS
unsigned char PWM_Level;//占空比数据储存变量
PWM_Level = ucLed[3]?8:2;
/* 定时器2中断初始化函数 */
void Timer2Init(void) //100微秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0x9C; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= (1<<2); //定时器中断2打开
}
void Timer2Server() interrupt 12
{
if(++PWM_Count == 10) PWM_Count = 0;//脉冲周期为1MS
Pulse(PWM_Count < PWM_Level);//PWM输出
}
定时器2
void Timer2_Init(void) //100微秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0x9C; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= (1<<2); //******加上这句*******//
}
void Timer2Server() interrupt 12
{
}
主函数main模板
#include "Init.h"
#include "Key.h"
#include "Seg.h"
#include "Led.h"
#include "Uart.h"
#include "stdio.h"
#include "onewire.h"
#include "iic.h"
unsigned char Key_val,Key_down,Key_up,Key_old;
unsigned char Key_slow_down;
unsigned char Seg_Buf[8]={10,10,10,10,10,10,10,10};
unsigned char Seg_Point[8]={0,0,0,0,0,0,0,0};
unsigned char Seg_Pos;
unsigned char Seg_slow_down;
unsigned char ucLed[8]={0,0,0,0,0,0,0,0};
unsigned char Uart_Recv[10];
unsigned char Uart_Read[10];
unsigned char Uart_recv_index;
unsigned char Uart_slow_down;
void Key_Porc()
{
if(Key_slow_down)return;
Key_slow_down=1;
Key_val=Key_Read();
Key_down=Key_val&(Key_old^Key_val);
Key_up=~Key_val&(Key_old^Key_val);
Key_old=Key_val;
}
void Seg_Porc()
{
if(Seg_slow_down)return;
Seg_slow_down=1;
}
void Led_Porc()
{
}
void Uart_Porc()
{
if(Uart_slow_down)return;
Uart_slow_down=1;
}
void Timer1_Init(void) //1??@12.000MHz
{
AUXR &= 0xBF; //?????12T??
TMOD &= 0x0F; //???????
TL1 = 0x18; //???????
TH1 = 0xFC; //???????
TF1 = 0; //??TF1??
TR1 = 1; //???1????
ET1=1;
EA=1;
}
void Timer1Server() interrupt 3
{
if(++Key_slow_down==20)Key_slow_down=0;
if(++Seg_slow_down==500)Seg_slow_down=0;
if(++Uart_slow_down==200)Uart_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]);
}
void UartServer() interrupt 4
{
if(RI==1)
{
RI=0;
Uart_Recv[Uart_recv_index]=SBUF;
Uart_recv_index++;
}
if(Uart_recv_index > 6) //存储6个数据到数组
{
Uart_recv_index = 0;
//功能
}
}
void main()
{
Init_System();
Timer1_Init();
Timer0_Init();
UartInit();
while(1)
{
Key_Porc();
Seg_Porc();
Led_Porc();
Uart_Porc();
}
}