蓝桥杯单片机备用笔记
write by 黄铠杰
前言
退出比赛后,发出笔记。还有八个省级比赛的代码没有发布。我觉得没有必要同质化。发现自己不喜欢这样做,及时离开fpga了。 这是在过去的钓鱼时间里做的一些事情,基本上每天摸一个小时。 太懒了,也觉得比较简单,没有认真备战。
一月二十三日
刚拿到板子,看了一下大概的原理图和资料就开始做了。感觉就像回到了大一。我开始胡说八道。
核心要先了解关键的头文件,了解端口对应原理的外设。
先做个流水灯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(); } } }
一月二十四日
先解决昨天留下的问题。
一、初始化问题
昨天做的时候发现初始化代码不太懂。
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,选通了Y相应的模块,即继电器和蜂鸣器模块。 对端口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; 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 1111 temp=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 1110 case 2: P3=0X3D;P42=1;P44=1; break; //0011 1101 case 3: P3=0X3B;P42=1;P44=1; break; //0011 1011 case 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=tep&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; //关闭蜂鸣器关闭led
timer0_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; //关闭蜂鸣器与led
Timer0Init();
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; //clk
void 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); //add
IIC_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 P30
void 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