蓝桥杯单片机备赛笔记
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 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=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; //关闭蜂鸣器关闭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++;