前言
提示:代码全部参考官方指导书,在其基础上修改优化
指导书:
电子版指导书链接:
版本较老,建议购买最新版本(淘宝:国信长天科技)
提示:以下是本篇文章正文内容,下面案例可供参考
一、my代码模块
新建工程,文件夹,导入驱动文件,写自己的c.h文件
1. main.c
#include "initial.h"
#include "onewire.h"
#include "ds1302.h"
#include "iic.h"
#include <stdio.h>//sprintf函数
void key_proc();
void seg_proc();
void led_proc();
void uart_proc();
u16 wendu_val;
u8 time[3]={23,59,50};
u8 adc_val;
u8 write_buf[3]={0,0,0};
u8 read_buf[3]={0,0,0};
u8 dist_val;
u8 RX_buf[12],RX_num,pdata TX_buf[12];//超出data区(110),用pdata(xdata前一小段)
u8 key_dly,seg_dly;
unsigned long ms,key_time;
u8 led=0xff;
u8 seg_pos,seg_buf[10],seg_code[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
//一个小数点,结束符
u8 key_old;
u8 state;
bit key_long;//长按标志位
u8 key_cnt;
u8 key_down_old;
void main()
{
initial_sys();
do
wendu_val=rd_temperature()>>4;
while(wendu_val==85);//读掉初始的85°
set_rtc(time);
do
adc_val=pcf8591_adc();
while(adc_val==128);//读掉初始的2.51V(128)
eeprom_read(read_buf,0x00,3);//掉电读取
Timer1Init();//前面初始化完再显示正确数值
Timer0Init();
UartInit();
ET1=1;
ES=1;
EA=1;
while(1)
{
key_proc();
seg_proc();
led_proc();
uart_proc();
}
}
void Timer1(void) interrupt 3//主中断
{
ms++;
if(++key_dly==10)
key_dly=0;
if(++seg_dly==200)
seg_dly=0;
led_disp(led);
seg_disp(seg_pos,seg_code);
if(++seg_pos==8)
seg_pos=0;
}
void Uart0(void) interrupt 4//串口接收用中断,发送用查询
{
if(RI)
{
RX_buf[RX_num++]=SBUF;
RI=0;
}
}
void key_proc()
{
u8 key_val,key_down,key_up;
if(key_dly)
return;
key_dly=1;
key_val=key_scan();//三段式按键扫描
key_down=key_val&(key_val^key_old);//^异或
key_up=~key_val&(key_val^key_old);
key_old=key_val;
// if(key_down)//普通单击短按和长按
// {
// key_time=ms;
// key_long=0;
// }
// if(ms-key_time<=1000)//短按
// {
// switch(key_up)
// {
// case 0:break;
// case 4:
// if(++state==5)
// state=0;
// break;
// case 5:
// break;
// case 8:
// break;
// case 9:
// break;
// }
// }
// else//长按1s以上
// {
// if(!key_long)
// {
// key_long=1;//此if内容只执行一次
// switch(key_old)
// {
// case 8:
// break;
// }
// }
// }
if(key_down)//单击短按,双击或多击短按(以前没考过)和长按
{
key_cnt++;
if(key_cnt==1)
{
key_time=ms;
key_long=0;
}
key_down_old=key_down;
}
if(ms-key_time>300)//超过300ms判断单击还是双击
{
if(key_cnt==1)//单击。按键只按下一次
{
switch(key_down_old)
{
case 4:
if(state==0)
state=4;
else
state--;
break;
}
}
else if(key_cnt>1)//双击以上
{
switch(key_down_old)
{
case 4:
if(++state==5)
state=0;
break;
}
}
key_cnt=0;
}
if(ms-key_time>1000)//另外按键长按1s以上
{
if(!key_long)
{
key_long=1;
switch(key_old)
{
case 5:
state+=2;
if(state>4)
state=0;
break;
}
}
}
}
void seg_proc()
{
if(seg_dly)
return;
seg_dly=1;
wendu_val=rd_temperature();
dist_val=wave();
switch(state)
{
case 0:
sprintf(seg_buf,"1 %05.2f",(float)wendu_val/16.0);
break;
case 1:
read_rtc(time);
sprintf(seg_buf,"2 %02u%02u%02u",(u16)time[0],(u16)time[1],(u16)time[2]);
break;
case 2:
adc_val=pcf8591_adc();
pcf8591_dac(adc_val);
sprintf(seg_buf,"3 %4.2f",(float)adc_val/51.0);
break;
case 3:
sprintf(seg_buf,"4 %2u%2u%2u",(u16)read_buf[0],(u16)read_buf[1],(u16)read_buf[2]);
break;
case 4:
sprintf(seg_buf,"5 %03u",(u16)dist_val);
break;
}
seg_tran(seg_buf,seg_code);
}
void led_proc()
{
}
void uart_proc()//10届国赛
{
if(RX_num)//触发完RI中断
{
if(RX_buf[RX_num-1]=='\n')//正常接收
{
if(RX_buf[0]=='S' && RX_buf[1]=='T' && RX_buf[2]=='\r')//接收到ST/r/n
sprintf(TX_buf,"$%02u,%05.2f\r\n",(u16)dist_val,(float)wendu_val/16.0);
else if(RX_buf[0]=='P' && RX_buf[1]=='A' && RX_buf[2]=='R' && RX_buf[3]=='A' && RX_buf[4]=='\r')
sprintf(TX_buf,"#%02u,%2u\r\n",(u16)dist_val,(u16)wendu_val>>4);
else
sprintf(TX_buf,"ERROR1\r\n");
uart_send(TX_buf);
RX_num=0;
}
else//接收到错误指令
{
if(RX_num == 6)//收到六个字符,第六个字符不为\n
{
sprintf(TX_buf,"ERROR2\r\n");
uart_send(TX_buf);
RX_num = 0;
}
}
}
}
2. initial.c
包括点灯,系统初始化,数码管,按键(矩阵4x4,2x2,独立),超声波,串口
#include "initial.h"
sbit TX=P1^0;
sbit RX=P1^1;
void led_disp(u8 led)
{
P0=led;
P2=P2&0x1f|0x80;
P2&=0x1f;
}
void initial_sys()
{
P0=0xff;
P2=P2&0x1f|0x80;
P2&=0x1f;
P0=0x00;
P2=P2&0x1f|0xa0;
P2&=0x1f;
}
void seg_tran(u8* seg_buf,u8* seg_code)
{
u8 i,j=0,temp;
for(i=0;i<8;i++,j++)
{
switch(seg_buf[j])
{
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=0xff;break;
default:temp=0xff;
}
if(seg_buf[j+1]=='.')
{
temp&=0x7f;
j++;
}
seg_code[i]=temp;
}
}
void seg_disp(u8 seg_pos,u8* seg_code)
{
P0=0xff;//消隐
P2=P2&0x1f|0xe0;
P2&=0x1f;
P0=1<<seg_pos;
P2=P2&0x1f|0xc0;
P2&=0x1f;
P0=seg_code[seg_pos];
P2=P2&0x1f|0xe0;
P2&=0x1f;
}
//u8 key_scan()//4*4
//{
// u16 k;u8 key_val;
// P44=0;P42=1;P35=1;P34=1;
// k=P3;
// P44=1;P42=0;
// k=(k<<4)|(P3&0x0f);
// P42=1;P35=0;
// k=(k<<4)|(P3&0x0f);
// P35=1;P34=0;
// k=(k<<4)|(P3&0x0f);
// switch(~k)
// {
// case 0x8000:key_val=4;break;
// case 0x4000:key_val=5;break;
// case 0x2000:key_val=6;break;
// case 0x1000:key_val=7;break;
// case 0x0800:key_val=8;break;
// case 0x0400:key_val=9;break;
// case 0x0200:key_val=10;break;
// case 0x0100:key_val=11;break;
// case 0x0080:key_val=12;break;
// case 0x0040:key_val=13;break;
// case 0x0020:key_val=14;break;
// case 0x0010:key_val=15;break;
// case 0x0008:key_val=16;break;
// case 0x0004:key_val=17;break;
// case 0x0002:key_val=18;break;
// case 0x0001:key_val=19;break;
// default:key_val=0;
// }
// return key_val;
//}
u8 key_scan()//2*2
{
u8 k;u8 key_val;
P44=0;P42=1;
k=P3;
P44=1;P42=0;
k=(k<<4)|(P3&0x0f);
switch(~k)
{
case 0x80:key_val=4;break;
case 0x40:key_val=5;break;
case 0x08:key_val=8;break;
case 0x04:key_val=9;break;
default:key_val=0;
}
return key_val;
}
//u8 key_scan()//独立按键
//{
// if(P33==0)
// return(4);
// else if(P32==0)
// return(5);
// else if(P31==0)
// return(6);
// else if(P30==0)
// return(7);
// else
// return(0);
//}
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
}
void Timer0Init(void) //12微秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xF4; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 0; //定时器0停止计时
}
u8 wave()//超声波测距
{
u8 dist,num=10;//1个方波测不了,2个方波只能110cm,3个可以180cm...
//发的越少,越远越难接收到,5个方波基本能测满255cm,实际测量也最准
TX=0;
TL0 = 0xF4; //设置定时初值
TH0 = 0xFF;
TR0 = 1; //定时器0开始计时,发送40khz方波
while(num--)
{
while(!TF0);//等待溢出标志置1,中断方式能硬件清零,查询需手动清零
TX^=1;
TF0=0;
}
TR0 = 0;//定时器0停止,发送完毕,等待接收
TL0 = 0x00;
TH0 = 0x00;
TR0 = 1;//定时器0开始计时
while(RX && !TF0);//RX=0收到脉冲,或定时器溢出,超过65535us还没接收到
TR0=0;
if(TF0)//若溢出
{
TF0=0;
dist=255;
}
else
dist=((TH0<<8)+TL0)*0.017;//时间*速度(来回)
return dist;
}
void UartInit(void) //4800bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器2时钟为Fosc,即1T
T2L = 0x8F; //设定定时初值
T2H = 0xFD; //设定定时初值
AUXR |= 0x10; //启动定时器2
}
void uart_send(u8* str)//串口发送用查询,接收用中断
{
while(*str != '\0')
{
SBUF=*str;
while(TI==0);
TI=0;
str++;
}
}
3. onewire.c
包括温度传感器ds18b20
#include "onewire.h"
//
void Delay_OneWire(unsigned int t)
{
u8 i;
while(t--)
{
for(i=0;i<12;i++);//8~12倍皆可
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
// EA=0;//如果ds18b20数据显示发生跳变等情况,极有可能是通信过程被打断了,通信过程中可提前关闭中断
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
// EA=1;//通信完毕再开启中断。(非必要情况不写,会影响中断)
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
// EA=0;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
// EA=1;
return dat;
}
//
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;
}
unsigned int rd_temperature(void)
{
u8 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;
}
4. ds1302.c
包括时钟模块ds18b20
部分代码:(必要在通信过程前关中断,通信完再开中断,同上)
void set_rtc(u8* time)
{
u8 temp;
Write_Ds1302_Byte(0x8E,0x00);
temp=((time[0]/10)<<4)+time[0]%10;//时
Write_Ds1302_Byte(0x84,temp);
temp=((time[1]/10)<<4)+time[1]%10;//分
Write_Ds1302_Byte(0x82,temp);
temp=((time[2]/10)<<4)+time[2]%10;//秒
Write_Ds1302_Byte(0x80,temp);
Write_Ds1302_Byte(0x8E,0x80);
}
void read_rtc(u8* time)
{
u8 temp;
temp=Read_Ds1302_Byte(0x85);
time[0]=(temp>>4)*10+(temp&0x0f);//时
temp=Read_Ds1302_Byte(0x83);
time[1]=(temp>>4)*10+(temp&0x0f);//分
temp=Read_Ds1302_Byte(0x81);
time[2]=(temp>>4)*10+(temp&0x0f);//秒
}
5. iic.c
包括AD模块pcf8591,eeprom模块AT24C02
部分代码:
u8 pcf8591_adc()
{
u8 temp;
IIC_Start();
IIC_SendByte(0x90);//主机向从机发送写指令
IIC_WaitAck();//slave设备正常接收数据后,自动将SDA置0
IIC_SendByte(0x43);//dac开启时43,dac未开启03。允许DAC,ADC通道3
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);//主机向从机发送读指令
IIC_WaitAck();
temp=IIC_RecByte();
IIC_SendAck(1);//结束通信,不想应答(从设备),(不想接受从设备发来的信息)
IIC_Stop();
return temp;
}
void pcf8591_dac(u8 dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x43);//允许DAC,ADC通道3
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
void eeprom_write(u8* write_buf,u8 addr,u8 num)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while(num--)
{
IIC_SendByte(*write_buf++);
IIC_WaitAck();
IIC_Delay(200);
}
IIC_Stop();
}
void eeprom_read(u8* read_buf,u8 addr,u8 num)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
while(num--)
{
*read_buf++=IIC_RecByte();
if(num)
IIC_SendAck(0);
else
IIC_SendAck(1);
}
IIC_Stop();
}
二、注意点
1. sprintf
1.1 函数:sprintf
- 头文件:<stdio.h>
- 函数原型: int sprintf(char *str, char *farmat [,argument,…]);
- 功 能: 格式化输出到字符串中
- 参数: char *str 要输出的字符串
char *farmat [,argument,…] 要输入的格式 - 返回值: 返回字符串的字节数
程序例: 格式化输出到字符串中,并输出字符串
#include<stdio.h>
#include<math.h>
int main(void){
char buffer[80];
sprintf(buffer, "An approximation of Pi is %f", M_PI);
puts(buffer);
return 0;
}
运行结果:
An approximation of Pi is 3.141593
1.2 printf输出控制符
- %hd用来输出 short int 类型,hd 是 short decimal 的简写;
- %d用来输出 int 类型,d 是 decimal 的简写;
- %ld用来输出 long int 类型,ld 是 long decimal 的简写。
- %c:输出一个字符。c 是 character 的简写。
- %s:输出一个字符串。s 是 string 的简写。
- %f:输出一个小数。f 是 float 的简写。
在输出整数方面, 格式控制符和整数的符号是紧密相关的,具体就是:
- %d 以十进制形式输出有符号数;
- %u 以十进制形式输出无符号数;
- %o 以八进制形式输出无符号数;
- %x 以十六进制形式输出无符号数。
- printf函数并不支持“输出负的八进制或者十六进制数”。
小数的输出格式:
- %f 以十进制形式输出float类型;
- %lf 以十进制形式输出double类型;
- %e 以指数形式输出float类型,输出结果中的 e 小写;
- %E 以指数形式输出float类型,输出结果中的 E 大写;
- %le 以指数形式输出double类型,输出结果中的 e 小写;
- %lE 以指数形式输出double类型,输出结果中的 E 大写。
1.3 printf的格式控制的完整格式
% - 0 m.n l或h 格式字符
- %:表示格式说明的起始符号,不可缺少。
- -:有-表示左对齐输出,如省略表示右对齐输出。
- 0:有0表示指定空位填0,如省略表示指定空位不填。
- m.n:m指域宽,即对应的输出项在输出设备上所占的字符数(小数点也算一个字符数。若实际数值比m大,按实际数值显示,比如28.4567,%3.1,实际显示的是28.4)。n指精度,用于说明输出的实型数的小数位数。为指定n时,隐含的精度为n=6位。
- l或h:l对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型。
- 格式字符为上述d,u,f等
1.4 seg_buf[10]
8位数码管,seg_buf至少为seg_buf[9],因为字符串要以’\0’ 作为结束符。若有一个小数点,应为seg_buf[10],依次类推。
C系统在用字符数组存储字符串常量时会自动加一个’\0’作为结束符。例如“C program”共有9个字符。字符串是存放在一维数组中的,在数组中他占10个字节,最后一个字节‘\0’是系统自动加上的。
char c[ ] = {"I am happy"}; = char c[ ] = " I am happy"; = char c[ ] = {'I',' ','a','m',' ','h','a','p','p','y','\0'};
char c[10] = {"China"};
数组的前5个元素为:'C','h','i','n','a',第6个元素为'\0',后4个元素也设定为空字符‘\0'。
sprintf(seg_buf,"1 %05.2f",(float)wendu_val/16.0);
结果:seg_buf为字符串,第一个字符为1,第234字符为空格,第56789字符为25.32或09.45(低于10℃,小数点一位,小数点后两位,第一位填0),第10个字符为'\0'
2. ds18b20
2.1 避免初始85℃
datasheet:
器件原因,上电初值即为85℃,一次温度转换时间最长为750ms。上电初始不能让数码管显示85℃,应避免。
2.1.1 避免方法1:延时
初始化时,读取一次温度数据后延时750ms,之后再开定时器显示数码管
延时函数可直接在软件STC-ISP中生成
2.1.2 避免方法2:多次读取
开定时器前连续读取温度100次(经验次数100~200,可自行尝试)
2.1.3 避免方法3:效率读取
开定时器前连续读取温度,直至不为85℃
2.2 驱动延时修改
示例程序为51单片机代码,51单片机的机械周期=12 * 时钟周期,读指令比较慢。而15单片机的机械周期=时钟周期,速度比传统51单片机快12倍(时钟周期相同,均选择外部12M晶振)。故修改:
//单总线内部延时函数
void Delay_OneWire(unsigned int t)
{
u8 i;
while(t--)
{
for(i=0;i<12;i++);//8~12倍皆可
}
}
简单点修改:第一行加上t=t*12;即可,第二行为原来的while(t–);
2.3 防止中断打断时序
有时出现数据显示发生跳变等情况,不是板子有问题,代码有问题,中断处代码过多导致中断占用时间过多,执行主函数时,有些通信过程又被很快来到的中断打断了。整个代码一直在执行中断的内容,主函数内容很少被执行到。
方法1:优化代码。
方法2:通信过程提前关闭中断。(会影响中断)
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
EA=0;//如果ds18b20数据显示发生跳变等情况,极有可能是通信过程被打断了,通信过程中可提前关闭中断
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
EA=1;//通信完毕再开启中断。(非必要情况不写,会影响中断)
Delay_OneWire(5);
}
其余ds1302,adc等也可采取同样操作。
3. pcf8591
3.1 注意操作命令
假设adc开启通道3(滑动变阻器),dac未开启。则adc指令为:
IIC_SendByte(0x03);
假设adc开启通道3,dac也开启,即两者均开启,两者指令应一致:
ADC的:IIC_SendByte(0x43);//dac开启时43,dac未开启03。允许DAC,ADC通道3
DAC的:IIC_SendByte(0x43);//允许DAC,ADC通道3
3.2 adc避免初始128(2.51V)
方法同上ds18b20,但adc读第二次就能把正确数据读到。
方法1:定时器开启前加上一行:
adc_val=pcf8591_adc();//读掉初始的2.51V(128)
方法2:do…while结构
do
adc_val=pcf8591_adc();
while(adc_val==128);//读掉初始的2.51V(128)
4. eeprom
AT24C02是一个2K Bit的串行EEPROM存储器(掉电不丢失),内部含有256个字节。在24C02里面有一个8字节的页写缓冲器。
256*8
存储量只有256个字节,地址为0x00~0xff
AT24C02的存储容量为2K bit,内容分成32页,每页8Byte,共256Byte(一个字节8个Bit,总共2K Bit)
8指的是8字节的页写缓冲器,一次最大可以写入8个字节,存到8个地址中,存储量只有256个字节
void eeprom_write(u8* write_buf,u8 addr,u8 num)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
while(num--)
{
IIC_SendByte(*write_buf++);
IIC_WaitAck();
IIC_Delay(200);
}
IIC_Stop();
}
此为页写代码,num<=8
addr为页写存储的第一个地址,
若num=1,则只把buf[0]存入addr中,若num>1,则buf[0]存入addr中,buf[1]存入addr+1中,依次顺延
addr为0x00,addr+1就为0x01
*buf++相当于下标++,buf[0],buf[1]……
一个数据一个地址(一字节,8bit),下一个数据的地址自动变为下一位地址
存:按输入的地址往后依次存。
读:按输入的地址往后依次读。
若write_buf[3]={0,1,2},addr=0x00,num=3
运行eeprom_write后,write_buf[0]=0存入0x00,write_buf[1]=1存入0x01,write_buf[2]=2存入0x02。
若read_buf[3]={0,0,0},addr=0x00,num=3
运行eeprom_read后,read_buf[0]=0x00地址的内容0,read_buf[1]=0x01地址的内容1,read_buf[2]=0x02地址的内容2。
5. 数据超过ram容量
由于定义的变量太多(变量存在ram,程序存在rom),占用的内部ram已经超出(data慢110左右),发送报错
5.1 解决方法1:更改全部变量存储区域
点击keil中的魔法棒,调出Options for Target界面,在Memory Model中将默认的DATA区(内部RAM)改为XDATA区(外部拓展RAM),或者PDATA区(XDATA的前一小段)。
5.2 解决方法2:更改超出部分变量存储区域
data区的变量访问速度是最快的,xdata区访问速度比data慢很多,pdata为xdata中访问速度较快的前一小段区域。
u8 read_buf[3]={0,0,0};
u8 dist_val;
u8 RX_buf[12],RX_num,pdata TX_buf[12];//超出data区(110),用xdata或者pdata(xdata前一小段)
运行结果:
6. 优化代码
优化代码,代码执行效率高,存储量小,这个得靠平时积累。
-
官方参考代码规范便于学习的同时缺点也很明显,由于用了sprintf结构,占用的code非常大。
-
最简单的一个优化例子:>> 能代替 / 的情况下尽量代替,>>作为移位运算符,运算效率远高于 / 。乘除法的本质也就是移位加 / 减。
-
降低code最简单的就是少调用函数,能直接表明意思就直接表明,没必要再进行封装(封装为了易读和改写往往以牺牲效率为代价,好比与高级语言和汇编,汇编的效率是最最高的,很多时候关键的代码都得靠汇编编写)。
-
中断中执行的程序,能简洁尽量简洁,不然主程序无法执行,一直在执行中断中的程序。(可以自己计算,例如12M晶振,15单片机机械周期等于时钟周期,也就是1/12M s,一条指令需要多少机械周期,调用函数又需要多少机械周期,加在一起即为中断执行时间,而中断多长时间一次我们也知道)
-
能减少变量的使用尽量减少,能共用变量的尽量共用(buf缓冲数组不只是可以用作数码管,别的也可以调用)
-
遇到复杂的控制时,精准定时led做出相应变化,多用标志位(定义为bit型,unsigned char浪费空间)理清思路,最后再优化代码。
最后,理解好各个模块的工作原理非常重要,不要只会记住模板,得理解其含义,每个芯片的datasheet仔细看完,理解。
今年(2022)国赛题:
- 定时器0必须用作ne555频率测量,定时器1用作主定时1ms,超声波平时用的都是定时器0,这次只有定时器2能用,定时器2和0、1有个不一样的地方,没有溢出标志位,这样很多人甚至连超声波都没做出来,其实可以通过判断T2H和T2L是否同时达到某个数值即可,初值和达到的数值之差即可得出定时时间差。12us那边直接软件延时都能实现。
- 难点主要在于pwm波,定时器已用完,主定时器用更短的100us,200us?还是软件延时?都试过了,效果都不好,各种影响别的模块。最后居然发现板子上的uln2003芯片电机引脚根本没反应,不知道芯片原因还是什么。。。
总结
b站小蜜蜂老师:https://space.bilibili.com/397050828
小蜜蜂老师资源链接:https://www.xmf393.com/2019/06/11/lqbmcu/
如有错误,敬请指正。内容之后会陆续进行补充