蓝桥杯单片机备赛笔记

​ write by 黄铠杰

前言

退赛了,把笔记发出来。还有八篇省赛的代码没放出来,感觉比较同质化觉得没必要把。发现自己不喜欢做这个,及时脱离去玩fpga了。
这是过去摸鱼时间里备赛做的一些东西,基本上都是每天摸1小时出来做的。
太懒了,而且也觉得比较简单就没认真备赛。

一月二十三日

刚拿到板子,看了下大概的原理图和资料就开始做了。感觉就像是回到了大一的时候。废话不多说就开始弄了。

核心要先看懂关键的头文件知道端口对应原理的外设就差不多了。

先做了个流水灯的demo。

代码:

#include<STC15F2K60S2.H>
#include <intrins.h>
//flow led void delayms(int ms)
{int i=0,j=0; //用于计算延时for(i=0; i<ms; i++){for(j=0;j<921;j++);}
}  //延时int  led=0; //用于位移
void main()
{P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化P0=0XFE;led=0XFE;while(1){//led=_cror_(led,1);P0=_cror_(P0,1);delayms(1000);}
}

1、cror()函数

这个函数是在intrins.h里的 要用的时候注意声明就好。 cror循环右移 crol循环左移

2、疑惑

有一点比较让我疑惑的是,延时函数这里的选值不是很理解。之前在fpga上算出来按道理不应该这么小的,疑惑

还有就是这一段初始化的作用不理解,技术手册里找不到。

还做了一个独立按键的比较简单,就直接上代码了

#include <STC15F2K60S2.h>
#include <intrins.h>void delayms(int ms)
{int i=0,j=0; //用于计算延时for(i=0; i<ms; i++){for(j=0;j<921;j++);}
}  //延时#define key7 P30
#define press 1
int key_scan(void)
{if(key7==0){delayms(10); //延时10ms消抖if(key7==0){return press;}}else return 0;
}
void flow_led()
{P0=_cror_(P0,1);delayms(1000); //延时1s
}
void main(void)
{int key_value=0;P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化P0=0XFE;while(1){key_value=key_scan();if(key_value==press){flow_led();}}
}

一月二十四日

先将昨天存留下来的问题解决了。

1、初始化问题

昨天里做的时候发现了就是初始化那一行代码不是很理解。

P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;

就是上面这一段。今天看了原理图之后大概了解了。 因为单片机io口较少所以需要就是复用,先通过74HC138这个芯片来分出四个模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWNRePlA-1648216036017)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220124172458923.png)]

74hc138这个芯片的作用呢,就是相当于一个38译码器。分离出来的Y4/5/6/7C选通后可使用不同的外设。在开发的时候就需要设定此处来调整。

我们回过头再来看初始化代码:

P2=0XA0;P0=0X00;
P2=0X80;P0=0XFF;

第一行可以看出选通了高四位即1010,也就是相当于译出了Y5,选通了Y5对应的模块,也就是继电器与蜂鸣器模块。 第二句对端口0的操作则是关闭蜂鸣器。

第二行呢就是选通led所在的端口并将led设置为全灭。

从上述可以看出来我们平时在开发的时候就需要注意查看原理图结合着来进行编写程序。

2、系统延时问题

延时问题可以直接到stc的烧录软件里去抄就行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KllN7L0W-1648216036019)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220124173155737.png)]

3、四位数码管动态实验

简简单单 核心是切换显示 没有其他内容了

#include <STC15F2K60S2.H>
#include <intrins.h>void Delay1ms()       //@11.0592MHz
{unsigned char i, j;_nop_();_nop_();_nop_();i = 11;j = 190;do{while (--j);} while (--i);
}void delayms( int ms)
{int i=0;for(i=0;i<ms;i++){Delay1ms();}
}int number[10]={0XC0,0XF9,0XA4,0xB0,0X99,0X92,0X82,0XF8,0X80,0x90};void digital_show( int num)
{int thousand=0,hundred=0,ten=0,single=0;thousand=num/1000;hundred=(num-thousand*1000)/100;ten=(num-(thousand*10+hundred)*100)/10;single=num%10;P2=0XC0;P0=0X01;P2=0XE0;P0=number[thousand];delayms(2);P2=0XC0;P0=0X02;P2=0XE0;P0=number[hundred];delayms(2);P2=0XC0;P0=0X04;P2=0XE0;P0=number[ten];delayms(2);P2=0XC0;P0=0X08;P2=0XE0;P0=number[single];delayms(2);
}
void main(void)
{short int i=0;int num=4396;P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //关闭蜂鸣器并将led设置为熄灭P2=0XC0;P0=0X01;                                  //选中第一个数码管P2=0XE0;P0=0XFF;                                    //初始化数码管while(1){    digital_show(num);//delayms(1000);num++;}}

一月二十五日

1、矩阵键盘

好久没写矩阵键盘了。原理就是控制io高低电平先进行行扫描再进行列扫描。

先上代码

int raw_scan(void) //行扫描
{int temp=0;P42=0;P44=0;P3=0x0F;        //    0000 1111temp=P3;temp=temp&(0X0F);if(temp!=0X0F){temp=P3;switch(temp){case 0X0E: return 1;case 0X0D: return 2;case 0X0B: return 3;case 0X07: return 4; default: return 0;}}else return 0;
}
int line_scan( int line) //列扫描
{int temp,state;switch(line){case 0: return 0;case 1: P3=0X3E;P42=1;P44=1; break;     //0011 1110case 2: P3=0X3D;P42=1;P44=1; break;      //0011 1101case 3: P3=0X3B;P42=1;P44=1; break;      //0011 1011case 4: P3=0X37;P42=1;P44=1; break;      //0011 0111}delayms(5);state=((int)P44<<7)+((int)P42<<6)+((int)P35<<5)+((int)P34<<4);temp=state;temp=temp&0xF0;if(temp!=0XF0){temp=state;switch(temp){case 0xe0:return (line-1)*4+4;case 0xd0:return (line-1)*4+3;case 0xb0:return (line-1)*4+2;case 0x70:return (line-1)*4+1;default:return 0;}}else return 1;
}
void main(void)
{int line=0;int num=0;P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; P2=0XC0;P0=0X01;P2=0XFF;P0=0XFF;   while(1){line=raw_scan();num =line_scan(line);digital_show(num);}
}

省略了延时函数和数码管函数。

2、需要注意的一些问题

要注意语法上的错误,像case语句后要加break。否则很浪费时间。

还有就是需要把一些常用的模块的代码记熟了,到时候写的快一点,不用浪费时间。

一月二十七日

昨天摸鱼一天了,今天补一下工作量。写定时器和外部中断这两个小demo吧。

定时器与定时器中断

先讲一下定时器吧,因为用的是8051架构的单片机,所以说这个东西和微机就很像了。

手头的这块单片机内置了四个定时器,今天就单拿定时器0来写demo。定时器0或者定时器1有四种工作模式,十六位重装载、十六位非重装载、八位重装载、非重装载模式。直接使用十六位自动重装载模式来使用。

相关的寄存器有:TCON TMOD AUXR;具体的如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U1m3xigB-1648216036019)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220127162653069.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSpX7Ela-1648216036021)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220127162708049.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvU5pRlG-1648216036023)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220127162717009.png)]

大概的就是上面。需要注意的就是各个位上的状态就行了。

然后就可以写初始化,通过TL0 TH0来设定计数值就行。完成初始化之后需要在主函数解除cpu的中断屏蔽使能。

代码如下:

#include <timer.h>void timer0_init()  //设置为16位自动重装载模式 时间位5ms
{AUXR |= 0X80;  //配置定时器0为不分频率TMOD &=0XF0;      //此处其实是确保tmod的高四位为零TH0=0X28;TL0=0X00;         //设定装载值TF0=0;TR0=1;               //允许计时器计数
}void Timer0Init(void)      //5毫秒@11.0592MHz
{AUXR |= 0x80;     //定时器时钟1T模式TMOD &= 0xF0;       //设置定时器模式TL0 = 0x00;       //设置定时初值TH0 = 0x28;        //设置定时初值TF0 = 0;       //清除TF0标志TR0 = 1;      //定时器0开始计时
}

第一个初始化是自己写的,第二个是直接用stc的烧录软件中生成的。

#include <STC15F2K60S2.H>
#include <seg.h>
#include <timer.h>void main()
{P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;  //关闭蜂鸣器关闭ledtimer0_init();EA=1;                                                         //cpu使能中断ET0=1;                                                        //开启定时器0中断;while(1){}
}int time_count=0;int num=0;
void timer0_isr() interrupt 1
{time_count++;if(time_count == 200){num++;time_count=0;}seg_show(num);
}

值得一提的就是主函数中的下面这两行

EA=1;                                                           //cpu使能中断
ET0=1;                                                     //开启定时器0中断;

功能注释中已经说明了

然后是设置中断函数,中断函数自行声明,声明后查询向量表设定优先级。

需要注意的就是,中断函数的局部变量在每次进入时都会重新生成,需要声明为全局变量。

外部中断

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1SnACGtE-1648216036023)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220127171238334.png)]

外部中断的就很简单了 设置EX0=1 IT0=1 跟着数据手册走就行了

#include <headfile.h>void Timer0Init(void)     //5毫秒@11.0592MHz
{AUXR |= 0x80;     //定时器时钟1T模式TMOD &= 0xF0;       //设置定时器模式TL0 = 0x00;       //设置定时初值TH0 = 0x28;        //设置定时初值TF0 = 0;       //清除TF0标志TR0 = 1;      //定时器0开始计时
}int time_count=0,num=0;
bit count_en=0;
void main()
{P2=0XA0;P0=0X00; P2=0X80;P0=0XFF; //关闭蜂鸣器与ledTimer0Init();IT0=1;EX0=1;                                           //使能外部中断并设置为下降沿EA=1; ET0=1;while(1){//seg_show(num);}
}void timer0_isr (void) interrupt 1
{time_count++;if(time_count==200){if(count_en == 1){num++;}else num=num;time_count=0;}seg_show(num);
}void exit0_int (void) interrupt 0
{if(P32==0){num++;count_en=~count_en;}
}

有一点需要注意的是 就是条件判断需要放到合理的位置。不然会出bug。

一月二十八日

ds18b20

今天写ds18b20的代码。

先上代码:

#include <headfile.h>sbit dq = P1^4 ;
void ds18b20_init(void)
{bit temp=1;while(temp){dq=0;delay10us(48);dq=1;delay10us(10);temp=dq;delay10us(42);}
//  seg_show(temp);
}void ds18b20_write( unsigned char dat)
{int i=0;for(i=0;i<8;i++){dq=0;dq=dat &0x01;delay10us(8);dq=1;dat=dat>>1;}
}
unsigned char ds18b20_read(void)
{int i=0;unsigned char dat =0x00;for(i=0;i<8;i++){dq=0;dat=dat>>1;//delayus(5);dq=1;//delayus(5);if(dq==1){dat|= 0x80;}delay10us(6);}return dat;
}
#include <headfile.h>
void Timer0Init(void);
unsigned char temp_low=0XAA;
void main()
{int temp=0,temperature=0;unsigned char low=0x00,high=0x00;int init_done;       //为1时完成初始化P2=0XA0;P0=0X00;P2=0X80;P0=0XFF; //初始化Timer0Init();EA=1;ET0=1;//seg_show(temp);while(1){ds18b20_init();ds18b20_write(0xcc);ds18b20_write(0x44);delay10us(50);       ds18b20_init();ds18b20_write(0xcc);ds18b20_write(0xbe);low=ds18b20_read();high=ds18b20_read();        temp=(high&0x0f);temp=temp<<4;low=(low &0xf0);low=_cror_(low,4);temperature= temp|low;//temp_low=0xa0;seg_show(temperature);}
}void Timer0Init(void)      //5毫秒@11.0592MHz
{AUXR |= 0x80;     //定时器时钟1T模式TMOD &= 0xF0;       //设置定时器模式TL0 = 0x00;       //设置定时初值TH0 = 0x28;        //设置定时初值TF0 = 0;       //清除TF0标志TR0 = 1;      //定时器0开始计时
}
void timer0_isr(void) interrupt 1
{
//      seg_show(temp_low);
}

关于ds18b20部分的代码通过看数据手册可以写出来,注意数据格式没什么太大的问题。

疑惑

反而是关于中断和主函数这里有问题,也就是逻辑编程前后台有一点问题。

当主函数调用了一个全局变量,中断服务函数如果也需要调用该变量的话,就会出现bug。在这块板子上具体体现的就是不会更改,推测了一下应该是因为数码管需要延时,延时时间超过了中断周期,导致一直卡死在中断中了。

但是这里也引出了就是前后台编程里会遇到的就是共享变量的问题了。

查阅了资料发现了就是说可以通过使用 volatile关键字 和临界区等操作来实现。后面玩操作系统估计会涉及到。

这里也提醒了后面写代码的时候注意前后台程序吧,中断服务函数应该是关注于标志变量的处理,处理函数应该放在主函数中会比较好。

一月二十九日

ds1302

写了ds1302的代码。ds1302其实说白了也是单总线通讯,和前面写的ds18b20不同的是这个模块多了的就是模块时钟线、复位线。写、读操作各自在sclk的上升沿与下降沿获取。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivybwXx7-1648216036024)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220130175303276.png)]

其实说白了就是读写寄存器的操作而已。值得一提的是就是ds1302是bcd码,写入与读出需要进行对应的编解码。bcd码就是用四位二进制表示十进制的各位。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KqovXmyK-1648216036024)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220130175655147.png)]

上述即为寄存器及其对应代表的意义。

直接上代码:

#include <headfile.h>sbit ce   = P1^3; //使能
sbit sda    = P2^3; //数据线
sbit sclk   =  P1^7; //clkvoid write_byte(unsigned char dat)
{int i=0;for(i=0;i<8;i++){sclk=0;sda=dat&0x01;dat=dat>>1;sclk=1;}
}void ds1302_write( unsigned char address,unsigned char dat)
{int i=0;ce=0;_nop_();_nop_();sclk=0;_nop_();ce=1;write_byte(address);write_byte((dat/10)<<4|dat%10);
}unsigned char ds1302_read(unsigned char address)
{unsigned char i,temp,data1,data2;ce=0;_nop_();sclk=0;_nop_();ce=1;write_byte(address);for(i=0;i<8;i++){sclk=0;       //在write_byte里已经将sclk置1了temp>>=1;if(sda==1) temp|=0x80;sclk=1;}sda=0;data1=temp/16;data2=temp%16;temp=data1*10+data2;return temp;
}
void ds1302_init(void)
{unsigned char init_reg = 0x80;unsigned char init_set[]={22,22,22,22,12,6,22};short int i = 0;ds1302_write(0x8e,0x00);for(i=0;i<7;i++){ds1302_write(init_reg,init_set[i]);init_reg+=2;}ds1302_write(0x8e,0x80);
}int ds1302_get(void)
{unsigned char data_reg=0x87;short int i=0;int dat,temp;dat=0;temp=0x0;temp=ds1302_read(data_reg);dat+=temp<<24;for(i=0;i<4;i++){temp=ds1302_read(data_reg);data_reg+=2;temp=temp<<((3-i)*8);dat=dat+temp;}ds1302_write(0x8e,0x80);return dat;
}
dat=ds1302_get();
dat=dat &(0x0000ff00);
dat=dat>>8;
display_year(dat);

这里的ds1302_get()函数是只写了获取年月日周几的数据,如果需要获取不同的数据可以直接更改寄存器就行。

返回的是一个int型的数据。int型是32位,每一种类型的数据都占8位。读取时通过与对应相于即可获得各种数据。

运算的时候还需要记得的就是获得对应类型的数据之后要记得向右位移相应的位数。

二月十四日

鸽了大概半个月的时间。回头按比赛给的库重新走一遍模块,然后开始写比赛的代码吧。

ds18b20

官方给出了单总线的读写操作和初始化的函数,在比赛的时候只需要关注怎么读取就行了。

从官方给出的datasheet可以看到,我们读取的顺序是:

初始化——skip rom——convert(温度转换)——skip rom——读取

对应的寄存器分别可以找得到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bU9ZATzv-1648216036025)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220214203601675.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RSv6R2AK-1648216036025)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220214203612887.png)]

值得一提的是,转换过后最好延时再进行下一步。

主函数代码如下:

#include <STC15F2K60S2.H>
#include <intrins.h>
#include <onewire.h>int num[10]={0XC0,0XF9,0XA4,0xB0,0X99,0X92,0X82,0XF8,0X80,0x90};
int  data_after_point;      //小数点后数据
int  data_before_point; //小数点前数据float temperature;
void Delay100us()       //@12.000MHz
{unsigned char i, j;i = 2;j = 39;do{while (--j);} while (--i);
}void Delay2ms()        //@12.000MHz
{unsigned char i, j;i = 24;j = 85;do{while (--j);} while (--i);
}
int flag=0;
void get_temp(void)
{   unsigned char tmp=0x00;    //用来存储中间变量unsigned char low,high;   //定义两个变量用来存储高八位与低八位init_ds18b20();Write_DS18B20(0XCC);Write_DS18B20(0X44);Delay100us();Delay100us();Delay100us();Delay100us();Delay100us();flag=init_ds18b20();Write_DS18B20(0XCC);Write_DS18B20(0XBE);low=Read_DS18B20();high=Read_DS18B20();high=_cror_(high,4);  //循环右移4位tmp= low &0xf0;tmp=_cror_(tmp,4);data_before_point=high+tmp;tmp=low&0x0f;data_after_point=tmp*625;temperature=data_before_point + tmp*0.0625;   //温度获取
}
void seg_show(void)
{unsigned char yi,er,san,si,wu,liu,qi;yi =data_before_point /100;er=(data_before_point-yi*100)/10;san=data_before_point%10;si=data_after_point/1000;wu=(data_after_point-si*1000)/100;liu=(data_after_point-si*1000-wu*100)/10;qi=data_after_point%10;P2=0XC0;P0=0X01;P2=0XE0;P0=num[yi];Delay2ms();P2=0XC0;P0=0X02;P2=0XE0;P0=num[er];Delay2ms();P2=0XC0;P0=0X04;P2=0XE0;P0=num[san]-0X80;Delay2ms();P2=0XC0;P0=0X08;P2=0XE0;P0=num[si];Delay2ms();P2=0XC0;P0=0X10;P2=0XE0;P0=num[wu];Delay2ms();P2=0XC0;P0=0X20;P2=0XE0;P0=num[liu];Delay2ms();P2=0XC0;P0=0X40;P2=0XE0;P0=num[qi];Delay2ms();
}
void main(void)
{P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;   //初始化代码 init_ds18b20();while (1){get_temp();seg_show();}
}

值得一提的是,官方给的底层库不是很正确,可能因为是在89c52的平台上的原因。需要对进行修改

具体修改就是更改延时函数,原本的延时函数如下:

void Delay_OneWire(unsigned int t)  STC89C52RC
{while(t--);
}

其实是错的,这样延时的话周期只有一微秒甚至没有。

应该要更改成以下:

void Delay_OneWire(unsigned int t)
{unsigned char i;while(t--){for(i=0;i<12;i++);}
}

后面基本就大同小异了。再总结一遍ds18b20的编写逻辑:

1.引入官方给的驱动库

2.更改驱动库中的延时函数

3.顺序: 初始化——跳过rom——转换——延时500us等待转换——初始化——跳过rom——读取

二月十五日

元宵节快乐,今天回过头来写ds1302的代码。

ds1302

官方给的代码没有什么问题,需要注意的就是ds1302的读写都是以bcd码的。所以需要在考点给的代码里加上bcd的编码和译码。需要注意的就是寄存器的地址了,其他的差不多。

#include <stc15f2k60s2.h>
#include <intrins.h>
#include <ds1302.h>
int calender_data[7]=0;
unsigned char seg_num[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//0-9
void ds1302_init()
{unsigned char init_data[7]={22,22,22,22,10,5,22};//顺序是秒分时日月周几年份unsigned char i=0;        //计数unsigned char write_address=0x80;Write_Ds1302_Byte(0x8e,0x00); //清楚写保护位for(i=0;i<7;i++){Write_Ds1302_Byte(write_address,init_data[i]);write_address+=2;}Write_Ds1302_Byte(0x8e,0x80);
}
void get_data()
{unsigned char read_address=0x81;//读地址unsigned char i=0;unsigned char tmp=0;                     //中间变量for(i=0;i<7;i++){calender_data[i]=Read_Ds1302_Byte(read_address);read_address+=2;}
}void Delay2ms()        //@12.000MHz
{unsigned char i, j;i = 24;j = 85;do{while (--j);} while (--i);
}void seg_show()        //显示顺序为: 年月日秒
{unsigned char yi,er,san,si,wu,liu,qi,ba;yi=calender_data[6]/10;er=calender_data[6]%10;san=calender_data[4]/10;si=calender_data[4]%10;wu=calender_data[3]/10;liu=calender_data[3]%10;qi=calender_data[0]/10;ba=calender_data[0]%10;P2=0XC0;P0=0X01;P2=0XE0;P0=seg_num[yi];Delay2ms();P2=0XC0;P0=0X02;P2=0XE0;P0=seg_num[er];Delay2ms();P2=0XC0;P0=0X04;P2=0XE0;P0=seg_num[san];Delay2ms();P2=0XC0;P0=0X08;P2=0XE0;P0=seg_num[si];Delay2ms();P2=0XC0;P0=0X10;P2=0XE0;P0=seg_num[wu];Delay2ms();P2=0XC0;P0=0X20;P2=0XE0;P0=seg_num[liu];Delay2ms();P2=0XC0;P0=0X40;P2=0XE0;P0=seg_num[qi];Delay2ms();P2=0XC0;P0=0X80;P2=0XE0;P0=seg_num[ba];Delay2ms();
}
void main()
{P2=0XA0;P0=0X00; P2=0X80;P0=0XFF; //初始化ds1302_init();while(1){get_data();seg_show();}
}

如上

二月十六日

ne555频率采样

今天摸鱼状态,就把其实是昨晚上写好的频率采样的代码调整了一下。

这块板子上的ne555模块,从电路上看其实就是直接简单输出了一个频率可调的方波。调整频率通过电位器来实现。看原理图直接将输出硬件和p34相连,利用定时器0作计数器,定时器1作定时器,定时器1轮询中断累计固定时间计算频率。原理没什么难的,主要是需要理解计数器和定时器的使用。

先上计数器的代码:

void Timer0Init(void)
{AUXR |= 0x80;TMOD |= 0x05;    TL0 = 0x00;        //设置计数初值TH0 = 0x00;        //设置计数初值TF0 = 0;TR0 = 0;ET0 = 0;
}

这里需要关注的就是tmod这个寄存器的设置了,具体的可以看datasheet里去找各个位对应的功能。

void Timer1Init(void)        //2毫秒@12.000MHz
{AUXR |= 0x40;        //定时器时钟1T模式TMOD &= 0x0F;        //设置定时器模式TL1 = 0x40;        //设置定时初值TH1 = 0xA2;        //设置定时初值TF1 = 0;        //清除TF1标志TR1 = 1;        //定时器1开始计时ET1 = 1;
}

定时器1可以通过烧录软件来生成。

void Timer1Handle(void) interrupt 3
{P2=0X80;P0=0XFE;switch(time_count){
case0:  global_state=begin_count;TR0=1;time_count++; break;
case 1000:  global_state=end_count;                        time_count=0;TR0=0;freqcnt=(((unsigned int)TH0<<8)|(unsigned int )TL0)/2;  TH0=0;TL0=0; break;
default:     global_state=counting;                        time_count++;  break;}
}
void main()
{P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;Timer1Init();Timer0Init();EA=1;ET1=1;while(1){seg_show();}
}

一开始写的时候,是想着通过globalstate这个变量来传递消息,在主函数中执行处理,但是有bug。后来就直接丢到中断里跑了,发现效果很好,图省事就不管了。后期的话可能需要注意以下这一点。

二月十七日

今天开始陆陆续续有人返校了。起的有点晚,吃完午饭回实验室写一下epprom的代码。

epprom

干,我就是摸鱼王中王。四小时里只有一小时不到是工作时间。不能再这么下去了

epprom用的芯片是AT24C02,iic总线通讯。资料包给出了iic总线的驱动库。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QDia7IZS-1648216036026)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220217160501665.png)]

先来更改一下驱动库的代码

需要注意的就是,它的这个延时函数不对,需要自己添加一个6us的延时函数。并且更改。

void IIC_Delay(unsigned char i)
{do{_nop_();}while(i--);
}
void Delay6us()     //@12.000MHz
{unsigned char i;_nop_();_nop_();i = 15;while (--i);
}

然后查阅epprom芯片的datasheet。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBtuD8e9-1648216036027)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220217160633583.png)]

这里说的意思呢,就是epprom芯片有自己的设备地址,根据存储容量有不同的地址。其中最低一位为读/写位。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qmc5Jgpa-1648216036028)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220217161640816.png)]

根据使用的芯片来看,我们写操作应该是0xa0,读操作是0xa1;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YR1Zn3pK-1648216036029)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220217162608103.png)]

上面的这是写操作的时序,根据时序图来写就行。需要注意的就是要把记得校验。

void epprom_write(unsigned char add,unsigned char dat)
{IIC_Start();IIC_SendByte(0xa0);IIC_WaitAck();IIC_SendByte(add);        //addIIC_WaitAck();IIC_SendByte(dat);IIC_WaitAck();IIC_Stop();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCo244AR-1648216036030)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220217164633094.png)]

unsigned char epprom_read(unsigned char add)
{unsigned char temp;IIC_Start();IIC_SendByte(0xa0);IIC_WaitAck();IIC_SendByte(add);IIC_WaitAck();IIC_Stop();IIC_Start();IIC_SendByte(0xa1);IIC_WaitAck();temp=IIC_RecByte();IIC_WaitAck();IIC_Stop();return temp;
}

然后在使用的时候,就需要注意写操作需要有2ms的延时等待写入。就可以了。

void main()
{int dat=0;unsigned char write_dat=0x22;P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;while(1){epprom_write(0X00,15);Delay2ms();dat=epprom_read(0x00);
//      Delay2ms();seg_show(dat);write_dat+=1;}
}

二月十九日

pcf8951

pcf8951主要功能是ad/da转换。这个芯片iic通信。需要注意iic官方协议需要修改一下。

这个还是挺简单的

大概思路就是:发送写地址——选择通道——发送读地址——收数据

#include <stc15f2k60s2.h>
#include <intrins.h>
#include <iic.h>unsigned char seg_num[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};void Delay2ms()        //@12.000MHz
{unsigned char i, j;i = 24;j = 85;do{while (--j);} while (--i);
}int read_8951(void)
{int tmp; //·µ»Ø±äÁ¿IIC_Start();IIC_SendByte(0x90); //·¢ËÍдµØÖ·IIC_WaitAck();IIC_SendByte(0x03); //Ñ¡ÔñͨµÀIIC_WaitAck();IIC_Stop();IIC_Start();IIC_SendByte(0x91);//·¢ËͶÁµØÖ·IIC_WaitAck();tmp=IIC_RecByte();IIC_Stop();return tmp;
}void seg_show( int num )
{unsigned char yi,er,san;yi=num/100;er=(num-yi*100)/10;san=num%10;P2=0XC0;P0=0X01; //µÚÒ»¸öÊýÂë¹ÜP2=0XE0;P0=seg_num[yi];Delay2ms();P2=0XC0;P0=0X02; //µÚ¶þ¸öÊýÂë¹ÜP2=0XE0;P0=seg_num[er];Delay2ms();P2=0XC0;P0=0X04; //µÚÈý¸öÊýÂë¹ÜP2=0XE0;P0=seg_num[san];
//  Delay2ms();
}void main()
{int ad_value;P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;while(1){ad_value=read_8951();Delay2ms();seg_show(ad_value);}}

二月二十三日

真题复现

freq=((unsigned int)TH0<<8)|(unsigned int)TL0;freq=freq/2;

在写频率的时候,发现了一个问题。

就是带符号int整除时,需要注意,int是十六位,但是它最高一位是符号位,带符号的除法时符号位是不会变的,所以会会导致结果有误差。

例如:

TH0=0x82;TL0=0XF2;freq=((((unsigned int)TH0)<<8)|((unsigned int)TL0));freq1=freq/2;

这里freq是int类型的数,82f2,最高位是1。那么此时去将它除以2,得到的结果是c179而不是4179。

所以在后面里需要注意的就是数据类型的使用了。

三月九日

惊了,摸鱼了快半个月,想不到;

把之前的东西补充了一下,按要求做好了。可能需要注意的就是要做好无消耗键盘还有区分长按短按的地方了。

长按短按直接通过利用定时器计数来实现就行。

然后就是状态机的使用了,注意后期debug的地方应该差不多。

先把按键相关的代码放一下:

char key_scan_1(void)
{unsigned char key7_press;static unsigned char key_last=0;unsigned char trg=0,key=0;key=P3 & 0x0e;key=key^0x0e;trg=key &(key ^key_last);key_last=key;return trg;
}

然后是整体的代码

#include <stc15f2k60s2.h>
#include <intrins.h>
#include <pcf8951.h>
#include <key.h>
unsigned char seg_num[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90 };
int timer1_count=0;
int key_count=0;
unsigned int freq;
unsigned char key_count_en;
void Delay2ms()     //@12.000MHz
{unsigned char i, j;i = 24;j = 85;do{while (--j);} while (--i);
}void Timer1Init(void)      //2毫秒@12.000MHz
{AUXR |= 0x40;     //定时器时钟1T模式TMOD &= 0x0F;       //设置定时器模式TL1 = 0x40;       //设置定时初值TH1 = 0xA2;        //设置定时初值TF1 = 0;       //清除TF1标志TR1 = 1;      //定时器1开始计时
}void counter0_init(void) //计数器0设置
{AUXR|= 0X80;TMOD|= 0X05;TL0=0X00;TH0=0X00;TF0=0;TR0=0;ET0=0;
}void seg_show_V( int V,int chanel)
{unsigned char yi,er,san,si,wu,liu,qi,ba;unsigned char lsb=0;int tmp;tmp=V;while(tmp!=0){lsb++;tmp/=10;}yi=0XC1;er=0;san=0;si=V/10000;wu=(V-si*10000)/1000;liu=(V-si*10000-wu*1000)/100;qi=(V-si*10000-wu*1000-liu*100)/10;ba=V%10;P2=0XC0;P0=0X80;P2=0XE0;P0=seg_num[ba];Delay2ms();P2=0XC0;P0=0X40;P2=0XE0;P0=seg_num[qi];Delay2ms();P2=0XC0;P0=0X20;P2=0XE0;P0=seg_num[liu]-0x80;Delay2ms();P2=0XC0;P0=0X04;P2=0XE0;P0=seg_num[chanel];Delay2ms();P2=0XC0;P0=0X02;P2=0XE0;P0=0xBF;Delay2ms();P2=0XC0;P0=0X01;P2=0XE0;P0=yi;Delay2ms();
}void seg_show_freq( int freq)
{unsigned char yi,er,san,si,wu,liu,qi,ba;unsigned char lsb=0;int tmp;tmp=freq;while(tmp!=0){lsb++;tmp/=10;}yi=0X8E;er=0;san=0;si=freq/10000;wu=(freq-si*10000)/1000;liu=(freq-si*10000-wu*1000)/100;qi=(freq-si*10000-wu*1000-liu*100)/10;ba=freq%10;if(lsb!=0){lsb--;P2=0XC0;P0=0X80;P2=0XE0;P0=seg_num[ba];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X40;P2=0XE0;P0=seg_num[qi];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X20;P2=0XE0;P0=seg_num[liu];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X10;P2=0XE0;P0=seg_num[wu];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X08;P2=0XE0;P0=seg_num[si];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X04;P2=0XE0;P0=seg_num[san];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X02;P2=0XE0;P0=seg_num[er];Delay2ms();}P2=0XC0;P0=0X01;P2=0XE0;P0=yi;Delay2ms();
}void seg_show_cycle( unsigned int cycle)
{unsigned char yi,er,san,si,wu,liu,qi,ba;unsigned char lsb=0;unsigned int tmp;tmp=cycle;tmp=10000/(tmp/10);cycle=tmp;while(tmp!=0){lsb++;tmp/=10;}yi=0XC8;er=0;san=0;si=cycle/10000;wu=(cycle-si*10000)/1000;liu=(cycle-si*10000-wu*1000)/100;qi=(cycle-si*10000-wu*1000-liu*100)/10;ba=cycle%10;if(lsb!=0){lsb--;P2=0XC0;P0=0X80;P2=0XE0;P0=seg_num[ba];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X40;P2=0XE0;P0=seg_num[qi];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X20;P2=0XE0;P0=seg_num[liu];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X10;P2=0XE0;P0=seg_num[wu];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X08;P2=0XE0;P0=seg_num[si];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X04;P2=0XE0;P0=seg_num[san];Delay2ms();}if(lsb!=0){lsb--;P2=0XC0;P0=0X02;P2=0XE0;P0=seg_num[er];Delay2ms();}P2=0XC0;P0=0X01;P2=0XE0;P0=yi;Delay2ms();}unsigned char key_value;void timer1_isr() interrupt 3
{switch(timer1_count){case 0: TR0=1;timer1_count+=1;break;case 1000:TR0=0;timer1_count=0;freq=((((unsigned int)TH0)<<8)|((unsigned int)TL0))/2;freq=freq; TH0=0;TL0=0;break;default: timer1_count+=1;  break;}if(key_count_en==1){key_count+=1;}else{key_count=0;}}#define state_freq     1
#define state_cycle     2
#define state_v             3
#define key7                    P30void main()
{unsigned int freq1,v,v_3;//频率、电压unsigned char state,led_en=1;unsigned char chanel=1,show_chanel,led_value=0;unsigned char key7_lastvalue;int press_time;int data_v=0,data_freq=0;P2=0XA0; P0=0X00; P2=0X80;P0=0XFF;freq1=freq/2;Timer1Init();counter0_init();EA=1;ET1=1;state=state_freq;while(1){v_3=read_pcf8951(0x03);    Delay2ms();
//      seg_show_freq(freq);
//      seg_show_cycle(freq);key_value=key_scan_1();if((key7==0) && (key7_lastvalue==1)){key_count_en=1;}else if((key7==0)&&(key7_lastvalue==0)){press_time=key_count;}else if((key7==1) &&(key7_lastvalue==0)){key_count_en=0;if(press_time>200){key_value=9;press_time=0;}else{key_value=1;}}key7_lastvalue=key7;switch(key_value){case 1:data_v=v_3;break;case 2:data_freq=freq;break;case 4:chanel=~chanel;break;case 8:state++;break;case 9:led_en=~led_en;break;case 0:state=state;if(state==2){chanel=1;}if(state>3){state=1;}break;}if(chanel==1){v=read_pcf8951(0x03); show_chanel=3;}else{v=read_pcf8951(0x01);show_chanel=1;              }Delay2ms();//seg_show_V(state);switch(state){case state_freq : led_value=4;//seg_show_freq(freq);break;case state_cycle: led_value=8;//seg_show_cycle(freq);break;case state_v    : led_value=16;//seg_show_V(v,show_chanel);break;}if(v>data_v){led_value+=1;}else {led_value=led_value;}if(freq > data_freq){led_value+=2;}else{led_value=led_value;}if(led_en == 1){P2=0x80;P0= (0XFF &(~led_value));}else{P2=0x80;P0=0xff;}led_value=0;}
}

三月十五日

做了第六套的题目,不是特别难。有意思的就是闪烁了,没其他的。

做第七套的题目。

三月十六日

做了第八套的题目。

做下来感觉不是特别难,把状态列好之后就基本上都能写好。值得一提的话就是闪烁的功能还有就是要注意主函数里的延时吧。

这里把闪烁的代码贴一下基本就差不多:

bit alarm_flag=0;
bit led =1;
void timer0_isr(void) interrupt 1
{count++;if(count%40==0){if(alarm_flag==1){count=0;led=~led;P2=0x80;P00=led;P01=1;P02=1;P03=1;P04=1;P05=1;P06=1;P07=1;}}
}

要注意数据类型以及初始值之类的。

未定态的取反是不稳定的

三月十九日

第九届的题属于是有点难,乍一看是挺简单的,但是框架写起来有点乱乱的。

先讲一下pwm,pwm驱动的方法就是利用定时器来生成pwm波来驱动led灯

定时器相关代码如下:

 pwm_count++;if(pwm_count<=duty){PWM=0;P2=0x00;P0=0xff;P2=0x80;P0=led_state;P2=0x00;}else if(pwm_count>duty){PWM=1;P2=0x00;P0=0xff;    P2=0x80;P0=0xff;P2=0x00;//P00=PWM;}else if(pwm_count==100){pwm_count=0;PWM=1;}

然后需要注意一下数码管的驱动了,如果直接放在主函数里运行会导致led闪烁(虽然我现在写的成品的代码也有led闪烁的现象,但是差不多)

后面写的时候需要注意一下这些地方,然后就是写框架的时候一定要注意先把大概的流程写了再埋头写代码,不然真的很折磨

然后就是变量这里,下次真的需要命名好,不然写起来真的恶心。

还有没解决的地方就是,双定时器这里没搞懂,放弃了直接用单定时器做了

导致了就是写起来特别别扭,堆屎山

下面直接上代码

#include <stc15f2k60s2.h>
#include <intrins.h>
#include <led.h>
#include <seg.h>
#include <pcf8951.h>
#include <key.h>
#include <epprom.h>void seg_show_level(unsigned level);
void seg_show_menu( unsigned char led_mode,int mode_freq,unsigned char shining_flag,unsigned char shining_bit);
void Timer1Init(void)       //100微秒@12.000MHz
{AUXR |= 0x40;     //定时器时钟1T模式TMOD &= 0x0F;       //设置定时器模式TMOD |= 0x10;     //设置定时器模式TL1 = 0x50;       //设置定时初值TH1 = 0xFB;        //设置定时初值TF1 = 0;       //清除TF1标志TR1 = 1;      //定时器1开始计时
}unsigned char freq=4,freq_set[5]={4,4,4,4,4};                //流转间隔为400ms
unsigned char led_state=0xff,led_mode=0,led_set_mode=1;i=0;//存储led状态
bit PWM=0;  //PWM
int timer1_count=0,led_run_count=0,shining_count=0;//定时器与led计数器
unsigned char pwm_count=0;//计数器
unsigned char duty=5,level=0;         //占空比与亮度等级
unsigned char ad_data=0;
unsigned char yi=0,er=1,san=1;
unsigned char num[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char seg_data[8];
unsigned char index=0,seg_show_en=0;
unsigned char key_value=0;
bit led_start=0;
bit shining_flag=0,level_show_flag=0;
unsigned char state_set=0; //设置界面状态变量
void timer1_isr(void) interrupt 3 //用来产生PWM
{TL1 = 0x50;       //设置定时初值TH1 = 0xFB;        //设置定时初值timer1_count++;shining_count++; //闪烁计数//key_value=10;if(shining_count%8000==0) //闪烁计数{shining_count=0;shining_flag=~shining_flag;}if(timer1_count%10==0)     //动态数码管显示{switch(state_set){case 0:for(i=0;i<8;i++){seg_data[i]=0xff;}led_set_mode=1;break;case 1:seg_show_en=1;seg_show_menu(led_set_mode,freq_set[led_set_mode]*100,shining_flag,1);break;case 2:seg_show_menu(led_set_mode,freq_set[led_set_mode]*100,shining_flag,2);break;}if((state_set ==0) && (level_show_flag==1)){seg_show_en=1;seg_show_level(level);}if(seg_show_en==1){P2=0xc0;P0=(0x80>>index);P2=0xe0;P0=seg_data[index];P2=0x00;index++;if(index==8) {index=0;}}}if(timer1_count%1000==0)     //读取rb2电位器电压{ad_data=get_pcf8951(0x03);if(ad_data<63){level=1;duty=5;}else if(ad_data<126){level=2;duty=30;}else if(ad_data<187){level=3;duty=60;}else{level=4;duty=90;}epprom_write(0xa1,freq_set[1]);epprom_write(0xa2,freq_set[2]);epprom_write(0xa3,freq_set[3]);epprom_write(0xa4,freq_set[4]);}if(timer1_count%(freq*1000)==0)//led模式流转{timer1_count=0;led_run_count++;if(led_start==1){if(led_run_count==21) {led_run_count=1;}if(led_run_count==1)    {led_state=0x7f;led_mode=1;freq=freq_set[1];}else if(led_run_count==9){led_state=0x7f;led_mode=2;freq=freq_set[2];}else if(led_run_count==13){led_state=0xe7;led_mode=3;freq=freq_set[3];}else if(led_run_count==17){led_state=0xe7;led_mode=4;freq=freq_set[4];}led_state=led_run(led_state,led_mode);}else{led_run_count=0;led_state=0xff;}}pwm_count++;if(pwm_count<=duty){PWM=0;P2=0x00;P0=0xff;P2=0x80;P0=led_state;P2=0x00;}else if(pwm_count>duty){PWM=1;P2=0x00;P0=0xff; P2=0x80;P0=0xff;P2=0x00;//P00=PWM;}else if(pwm_count==100){pwm_count=0;PWM=1;}//ET0=1;
}
void main()
{unsigned char test=0x7e;P2=0xa0;P0=0x00;P2=0x80;P0=0xff;freq_set[1]=epprom_read(0xa1);freq_set[2]=epprom_read(0xa2);freq_set[3]=epprom_read(0xa3);freq_set[4]=epprom_read(0xa4);Timer1Init();ET1=1;EA=1;while(1){key_value=key_scan();switch(key_value){case 1: led_start=~led_start; break;case 2:   state_set+=1;if(state_set==3){state_set=0;} break;}    switch(state_set){case 1:   switch(key_value){case 4:led_set_mode+=1;led_set_mode=(led_set_mode==5)?1:led_set_mode;break;case 8:led_set_mode-=1;led_set_mode=(led_set_mode==0)?4:led_set_mode;break;default:led_set_mode=led_set_mode;}break;case 2:switch(key_value){case 4:freq_set[led_set_mode]+=1;freq_set[led_set_mode]=(freq_set[led_set_mode]==13)?4:freq_set[led_set_mode];break;case 8:freq_set[led_set_mode]-=1;freq_set[led_set_mode]=(freq_set[led_set_mode]==3)?12:freq_set[led_set_mode];break;default:freq_set[led_set_mode]=freq_set[led_set_mode];}}if(P33==0){level_show_flag=1;}else{level_show_flag=0;}}
}void seg_show_level(unsigned level)
{unsigned char yi=0,er=0;er=level%100/10; yi=level%10;  seg_data[7]=0xff;seg_data[6]=0xff;seg_data[5]=0xff;seg_data[4]=0xff;seg_data[3]=0xff;seg_data[2]=0xff;seg_data[1]=num[er];seg_data[0]=num[yi];
}void seg_show_menu( unsigned char led_mode,int mode_freq,unsigned char shining_flag,unsigned char shining_bit)
{unsigned char freq[4]=0;freq[3]=mode_freq/1000;freq[2]=mode_freq/100%10;freq[1]=mode_freq%100/10; freq[0]=mode_freq%10;freq[3]=(freq[3]==0)?0xff:num[freq[3]];if(shining_flag==1){seg_data[7]=0xbf;seg_data[6]=num[led_mode];seg_data[5]=0xbf;seg_data[4]=0xff;seg_data[3]=freq[3];seg_data[2]=num[freq[2]];seg_data[1]=num[freq[1]];seg_data[0]=num[freq[0]];}else{switch(shining_bit){case 1:      seg_data[7]=0xff;seg_data[6]=0xff;seg_data[5]=0xff;seg_data[4]=0xff;seg_data[3]=freq[3];seg_data[2]=num[freq[2]];seg_data[1]=num[freq[1]];seg_data[0]=num[freq[0]];break;case 2:    seg_data[7]=0xbf;seg_data[6]=num[led_mode];seg_data[5]=0xbf;seg_data[4]=0xff;seg_data[3]=0xff;seg_data[2]=0xff;seg_data[1]=0xff;seg_data[0]=0xff;break;}}}

然后是再来回顾一下常用的模块的驱动吧

首先是按键

感觉做了几套题看见的大多是独立按键

独立按键的话如果不是特别的功能可以直接用下面一套代码

unsigned char key_scan(void)
{static unsigned char key_last;unsigned char trg,key;key=P3&0x0f;key=key^0x0f;trg=key&(key^key_last);key_last=key;return trg;
}

不需要死循环,还是比较好用的

接下来呢就是利用定时器中断驱动数码管

P2=0xc0;P0=(0x80>>index);
P2=0xe0;P0=seg_data[index];
P2=0x00;
index++;
if(index==8) {index=0;}

比较简单不多说

接下来就是DS1302这个模块的代码

ds1302官方给了库,可以直接套用

然后驱动代码呢

逻辑就是:写地址存,然后读,挺简单的直接上代码

void ds1302_init()
{unsigned char init_data[7]={22,22,22,22,10,5,22};//顺序是秒分时日月周几年份unsigned char i=0;        //计数unsigned char write_address=0x80;Write_Ds1302_Byte(0x8e,0x00); //清楚写保护位for(i=0;i<7;i++){Write_Ds1302_Byte(write_address,init_data[i]);write_address+=2;}Write_Ds1302_Byte(0x8e,0x80);
}
void get_data()
{unsigned char read_address=0x81;//读地址unsigned char i=0;unsigned char tmp=0;                     //中间变量for(i=0;i<7;i++){calender_data[i]=Read_Ds1302_Byte(read_address);read_address+=2;}
}

接下来呢,就是ds18b20

这玩意经常用,官方给的库,首先要改一下延时函数,

void Delay_OneWire(unsigned int t)  //STC89C52RC
{unsigned char i;while(t--){for (i=0;i<12;i++){}}
}

改成这样

然后使用的思路呢,就是:init——skip_rom——convert——init——skip_rom——read
3]0)?0xff:num[freq[3]];
if(shining_flag1)
{
seg_data[7]=0xbf;seg_data[6]=num[led_mode];seg_data[5]=0xbf;
seg_data[4]=0xff;seg_data[3]=freq[3];
seg_data[2]=num[freq[2]];seg_data[1]=num[freq[1]];seg_data[0]=num[freq[0]];
}
else
{
switch(shining_bit)
{
case 1: seg_data[7]=0xff;seg_data[6]=0xff;seg_data[5]=0xff;
seg_data[4]=0xff;seg_data[3]=freq[3];
seg_data[2]=num[freq[2]];seg_data[1]=num[freq[1]];seg_data[0]=num[freq[0]];
break;
case 2: seg_data[7]=0xbf;seg_data[6]=num[led_mode];seg_data[5]=0xbf;
seg_data[4]=0xff;seg_data[3]=0xff;
seg_data[2]=0xff;seg_data[1]=0xff;seg_data[0]=0xff;
break;
}
}

}


然后是再来回顾一下常用的模块的驱动吧首先是按键感觉做了几套题看见的大多是独立按键独立按键的话如果不是特别的功能可以直接用下面一套代码

unsigned char key_scan(void)
{
static unsigned char key_last;
unsigned char trg,key;
key=P3&0x0f;
key=key^0x0f;
trg=key&(key^key_last);
key_last=key;
return trg;
}


不需要死循环,还是比较好用的接下来呢就是利用定时器中断驱动数码管

P2=0xc0;P0=(0x80>>index);
P2=0xe0;P0=seg_data[index];
P2=0x00;
index++;
if(index==8) {index=0;}


比较简单不多说接下来就是DS1302这个模块的代码ds1302官方给了库,可以直接套用然后驱动代码呢逻辑就是:写地址存,然后读,挺简单的直接上代码

void ds1302_init()
{
unsigned char init_data[7]={22,22,22,22,10,5,22};//顺序是秒分时日月周几年份
unsigned char i=0; //计数
unsigned char write_address=0x80;
Write_Ds1302_Byte(0x8e,0x00); //清楚写保护位
for(i=0;i<7;i++)
{
Write_Ds1302_Byte(write_address,init_data[i]);
write_address+=2;
}
Write_Ds1302_Byte(0x8e,0x80);
}
void get_data()
{
unsigned char read_address=0x81;//读地址
unsigned char i=0;
unsigned char tmp=0; //中间变量
for(i=0;i<7;i++)
{
calender_data[i]=Read_Ds1302_Byte(read_address);
read_address+=2;
}
}


接下来呢,就是ds18b20这玩意经常用,官方给的库,首先要改一下延时函数,

void Delay_OneWire(unsigned int t) //STC89C52RC
{
unsigned char i;
while(t–)
{
for (i=0;i<12;i++)
{

 }
}

}


改成这样然后使用的思路呢,就是:init——skip_rom——convert——init——skip_rom——read

蓝桥杯单片机备赛笔记相关推荐

  1. 【蓝桥杯单片机备赛】3.【SMG】共阳共阴数码管模板整理及真题实战心得

    初学数码管的时候,曾经被数码管显示的亮度折磨过一段时间,现在来总结一遍自己对数码管的理解. 0 1 2 3 4 5 6 7 8 9 A B C D E F 0xC0 0xF9 0xA4 0xB0 0x ...

  2. 【蓝桥杯单片机备赛】8.【DS1302】学好了做个小天才智能手表玩玩

    学好了或许可以结合OLED.KEY.DS1302做个小闹钟哦 概述 一句话介绍DS1302:可以理解为一个电子手表,里面有31字节的RAM静态内存(只要有电,数据就不会丢失) 关键词:高性能 低功耗 ...

  3. 2021年 第12届 蓝桥杯【备赛直播公开课 —— 软件类(本科组、高职高专组)】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[2013年(第4届)~2021年(第12届)] 第11届 蓝桥杯-第1.2次模拟(软件类)真题-(2020年3月.4月)-官方讲解视频 说明:部分 ...

  4. 蓝桥杯java备赛Day3——跳马

    蓝桥杯java备赛A组--跳马 问题描述 问题描述: 一个8×8的棋盘上有一个马初始位置为(a,b),他想跳到(c,d),问是否可以?如果可以,最少要跳几步? 输入格式: a,b,c,d 输出格式: ...

  5. 蓝桥杯单片机省赛——第五届(模拟智能灌溉系统)

    蓝桥杯单片机省赛--第五届(模拟智能灌溉系统) 一.题目内容 二.程序源代码 1.主函数 2.ds1302驱动函数 3.iic驱动函数 注意:驱动代码需要改动一下,可以将原驱动代码按照下面的代码修改 ...

  6. 【第十三届蓝桥杯单片机省赛模拟冲刺01】

    第十三届蓝桥杯单片机省赛模拟冲刺 赛前一两周一定要多练习,把之前写过的程序在好好看看,把每个模块的代码都多敲几遍,把常考的每个模块部分都吃透,如果理解不了的趁时间还比较充足该背的背牢.同时在准备编程题 ...

  7. 第四届蓝桥杯单片机省赛 自动灌溉系统

    第四届蓝桥杯单片机省赛 自动灌溉系统 新手.所用驱动为大赛提供驱动 #include<stc15f2k60s2.h> #include "intrins.h"#defi ...

  8. 【蓝桥杯】第十三届蓝桥杯单片机国赛 代码程序

    第十三届蓝桥杯单片机国赛 程序 题目 hex文件 代码 工程文件 B站视频 更多资料 题目 历届的省赛和国赛的题目我已经在前面的文章(点击查看)里给大家分享了(网盘资源),需要的话,直接去下载,我在这 ...

  9. 第八届蓝桥杯单片机省赛----程序题

    main.c程序 #include<stc15f2k60s2.h> #include "ds18b20.h" #include <ds1302.h>#def ...

  10. 第十三届“蓝桥杯”单片机省赛——程序设计题

    第十三届"蓝桥杯"单片机省赛--程序设计题 在比赛时候编写,代码仅供参考 如有不足,多多指教 1.题目 2.代码 main.c程序 #include "reg52.h&q ...

最新文章

  1. python3连接mysql,python3连接MySQL数据库实例详解
  2. 软件测试论坛_浅谈软件测试的未来,我们该如何做好准备
  3. Raspberry Pi 3B 安装Miniconda
  4. Leetcode 122.买卖股票的最佳时机 II (每日一题 20210618)
  5. 坦克大战c语言程序贴吧,坦克大战!
  6. Linux学习笔记(十九)文件压缩
  7. Shell 判断数是否为某数的倍数
  8. 问题 G: 三个数比较大小
  9. 经典排序算法及其Python实现
  10. js当中null和{}区别
  11. 电磁兼容EMC标准 CISPR 22:EN 55022
  12. 51单片机——自动浇花系统(含全部代码)
  13. C# Winform鼠标样式设置方法
  14. 计算机数字键盘无法输入数字,键盘无法输入,键盘数字键打不出来
  15. php 项目创意,JavaScript实现“创意时钟”项目
  16. 真菌元胞自动机Python实现
  17. 【编程题】构造两两相邻数之和为奇数的矩阵
  18. 消费贷款用途证明怎样提供
  19. 山东省第五届ACM大学生程序设计竞赛 Colorful Cupcakes
  20. c+语言:1%3c%3c,C语言教程第4章1课件

热门文章

  1. nginx判断手机端还是电脑
  2. VUE源码解析(持续更新)
  3. win7原版镜像_(超详细)WIN7原版系统win镜像安装教程
  4. 设计模式——终结者模式
  5. 【计算机网络】—网络初识01
  6. 3dsMax建模,卡线学习笔记
  7. What Music简单的全网音乐播放器
  8. mPush实战笔记5完整测试
  9. telnet 回显 linux,telnet实现本地回显
  10. 金融数据类——外汇,CFD