01 蓝桥杯单片机设计与开发_标准模板

蓝桥杯单片机设计与开发_标准模板

一、前言

首先,这篇文章是笔者第一次在 CSDN 上写博文,较为生疏,读者若有任何意见,欢迎大家在评论区交流!
笔者目前为一名大二学生,参加了2021年蓝桥杯单片机设计与开发组省赛,获省赛一等奖成功入围决赛。笔者将在接下来一个月的时间内不断更新关于蓝桥杯单片机设计与开发国赛的博文,初衷是与各位同学共勉,一起准备国赛,希望和大家共同取得佳绩。
同时,笔者也希望将这篇文章作为51单片机的一个基础部分,为本校自动化专业的学弟学妹们答疑解惑,祝学弟学妹们在即将到来的科技创新思维训练的结课测试中取得优异的成绩。

二、标准模板

话不多说,我们直接进入正题。文章中所说的标准模板,其实是指包含了初始化函数锁存器驱动数码管驱动函数矩阵键盘/独立键盘以及定时器中断等模块的可编译.c文件。
笔者的标准模板并不是采用模块化编程,笔者认为定义包含多个LED、数码管等多个头文件看起来较为繁琐,对于新手不太友善。而应付蓝桥杯比赛、51单片机考试等,笔者以为通过采用“函数模块化”编程即可,故本模板在一个.c文件中分模块定义多个驱动函数,也是十分简洁、容易上手的。

1、创建工程

创建工程:依次点击菜单栏中 Project → New uvision Project… → 输入工程名称并保存 → 选择STC15F2K60S2(注:需通过 stc-isp 软件 Keil 仿真添加头文件)→ 点击 File 下方空白页新建空文件并命名为.c文件 → 双击 Source Group 将.c文件加入工程,就可以开始在文件中编写程序。
创建工程1
创建工程2

创建工程3
创建工程4

2、编程准备

(1)首先,包含头文件。对于蓝桥杯单片机设计与开发大赛采用的单片机为 IAP15 系列单片机,型号为:IAP15F2K61S2,头文件使用“STC15F2K60S2.h”即可(本文章将以此为例)。若采用“reg52.h”需在头文件中添加AUXR、SCON等特殊寄存器,以免影响串口及一些芯片的正常编程。
而对于刚入门的新手,使用 STC89S52 单片机编写简单的 LED、数码管、键盘等程序,使用“reg52.h”头文件足矣。

#include "STC15F2K60S2.h"   //头文件

#define u8 unsigned char    //宏定义常用类型
#define u16 unsinged int 
	
u8 dspbuf[8] = {10,10,10,10,10,10,10,10};    //定义缓冲区
u8 code tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};   //定义数组存放数码管数字1~9和熄灭状态

3、锁存器驱动函数

笔者主函数前编写锁存器驱动函数,将为接下来的数码管驱动、主函数功能等程序的编写带来极大的益处,十分便捷。

void door(u8 choose,input)    //数码管驱动函数
{
	P2 = (P2&0x1f) | choose;    //相与空出高3位,choose输入打通锁存器
	P0 = input;                 //送入数据
	P2 &= 0x1f;                 //关闭锁存器
}

4、数码管驱动函数

数码管显示数字 0~9 采用了编程准备步骤中的 dspbuf 缓冲区及数组 tab ,我们通过改变 dspbuf 数组中对应的八个数字即可改变八个数码管显示的数字。例:dspbuf [0] = 8;将在第一个数码管上显示数字“8”。
语句 door(0xe0,tab[dspbuf[dspcom++]]) 原理其实很简单:通过初始化语句 dspbuf [0] = 8,使得 dspbuf 数组选中第一个缓冲区(即显示第一个数码管),而 tab 数组将选中第八个数,第八个数“0x80”对应的将在数码管中显示数字“8”(对于数码管显示数字的二进制代码计算这里不再赘述)。

u8 dspcom = 0;                       //定义数码管点亮位数
void display()                       //数码管驱动函数
{
	door(0xe0,0xff);                   //消隐
	
	door(0xc0,0x01<<dspcom);           //位选
	door(0xe0,tab[dspbuf[dspcom++]]);  //段选
	
	if(dspcom >= 8)                    //初始化位数,动态循环
		dspcom = 0;
}

5、初始化函数

(1)LED、蜂鸣器等初始化

笔者在初始化函数 all_init 前定义数码管熄灭函数,主要目的是便于在设计比赛试题各种功能时熄灭数码管,避免各个功能相互干涉,使用较为方便,程序更加简洁。

void num_close()    //熄灭数码管
{
	dspbuf[0] = 10;
	dspbuf[1] = 10;
	dspbuf[2] = 10;
	dspbuf[3] = 10;
	dspbuf[4] = 10;
	dspbuf[5] = 10;
	dspbuf[6] = 10;
	dspbuf[7] = 10;
}
	
void all_init()    //初始化函数
{
	door(0x80,0xff);  //熄灭LED
	door(0xa0,0xaf);  //关闭蜂鸣器、继电器
	num_close();      //熄灭数码管
}

(2)定时器初始化

定时器初始化函数,这里主要对本校自动化专业的学弟学妹们有一些寄语,在你们初步接触51单片机时可能会认为定时中断很难、看不懂,但其实并不是这样的。对于定时器中断,只要肯花点时间去看,其实是很容易掌握的(建议学弟学妹们多花点功夫把这篇文章阅读完)。掌握之后通过中断动态显示数码管,以及用中断设置计时、LED定时闪烁,在科创的结课测试是非常好用的。

//定时器0初始化函数
void Timer0Init(void)		//2毫秒@12.000MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x40;		//设置定时初值
	TH0 = 0xA2;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	EA = 1;    //打开总中断
	ET0 = 1;   //打开定时器0中断
}

定时器初始化函数这里需要说明一下,函数内容看上去非常繁琐,对于新手可能觉得一脸懵。但其实定时器初始化函数可通过 STC-ISP 软件中的“定时器计算器”进行代码生成,再加上对应的定时器开关即可。具体操作步骤如下:点击“定时器计算机”按钮,设置系统频率为对应频率(这里以12MHZ为例),设置定时长度为2ms,其余选项保持默认,复制代码即可(注:版本较低的 STC-ISP 软件需点击生成c代码,再点击复制按钮)。
定时器初始化

6、矩阵键盘

在书写矩阵键盘的程序前,我们先看一下原理图(2.0版本)。对于蓝桥杯单片机2.0版本,即IAP15系列单片机的需要注意的是图中WR、RD引脚替换成P42、P44引脚,因此书写程序时可采取变量代换。同时,对于STC89C52系列单片机的则省去变量代换。
在这里插入图片描述
程序中都注有详细的注释,因此这里不再解释,如若有不懂的同学欢迎评论区提问。同时,笔者仅以矩阵键盘为例,对于独立键盘则同理且更加简单,这里也不再赘述。

u8 keypress = 0,keyread = 0,keyvalue = 0xff;  
u8 Read_key()
{
	u8 key_m,cal;
	
	//检测按键列数
	P3 = 0xf0;P42 = 1;P44 = 1;
	P36 = P42;P44 = P37;   //变量代换,区别于STC89C52系列单片机
	key_m = P3&0xf0;
	
	if(key_m != 0xf0)
		keypress++;       //识别到按键
	else
		keypress = 0;     //按键抖动
	
	if(keypress == 3)   //多次检测,避免误判
	{
		keypress = 0;     //清空按键计数
		keyread = 1;      //读取按键标志位
		
		//列数判断
		switch(key_m)
		{
			case 0x70:cal = 0;break;   //检测到第一列
			case 0xb0:cal = 1;break;   //检测到第二列
			case 0xd0:cal = 2;break;   //检测到第三列
			case 0xe0:cal = 3;break;   //检测到第四列
		}
		
		//检测按键行数
		P3 = 0x0f;P42 = 0;P44 = 0;
		key_m = P3&0x0f;
		
		//行数判断
		switch(key_m)
		{
			case 0x0e:keyvalue = 4*cal+7;break;   //检测到第一行
			case 0x0d:keyvalue = 4*cal+6;break;   //检测到第二行
			case 0x0b:keyvalue = 4*cal+5;break;   //检测到第三行
			case 0x07:keyvalue = 4*cal+4;break;   //检测到第四行
		}
	}
	
	//再次赋值检测松手
	P3 = 0x0f;P42 = 0;P44 = 0;
	key_m = P3&0x0f;
	
	if((keyread) && (key_m == 0x0f))  //松手检测
	{
		keyread = 0;                    //清空读取标志位
		return keyvalue;                //返回读到的按键
	}
	return 0xff;     //未识别到按键
}

7、中断函数

首先,由前文可知定时器初始化函数中定义了 2ms 产生一次中断,因此笔者将数码管显示函数 display 放在中断里,并定义了静态变量计时每 20ms 将按键标志位置一,在主函数中识别按键,可避免按键抖动带来的影响。

bit key_flag = 0;            //定义按键标志位
void timer0() interrupt 1    //定义中断模式1
{
	static u8 t_20ms = 0;      //定义静态变量计时20ms
	
	display();                 //数码管显示函数
	
	t_20ms++;                  //计数自增
	
	if(t_20ms >= 10)           //判断计时时间
	{
		t_20ms = 0;              //计数清零
		key_flag = 1;            //按键标志位置1
	}
}

三、应用示例

1、锁存器驱动函数

LED:由原理图可知,选中控制 LED 的芯片 M74HC573M1R 需要打通锁存器 Y4C。对应的二进制代码为:100 0 0000(100转换为十进制即为4,Y4C),即0x80。
Y4C
再通过 P0 口输入需要点亮 LED 对应的十六进制代码。注意:原理图中采用共阳极接法,因此对应的端口输入低电平即可点亮相应的 LED。
在这里插入图片描述
例:

door(0x80,0x00);  //0000 0000:点亮所有LED
door(0x80,0xff);  //1111 1111:熄灭所有LED
door(0x80,0xaa);  //1010 1010:点亮第1、3、5、7个LED

蜂鸣器、继电器:由原理图可知,选中控制蜂鸣器、继电器的芯片 M74HC573M1R 需要打通锁存器Y5C。对应的二进制代码为:101 0 0000(101转换为十进制即为5,Y5C),即0xa0。
Y5C
再通过 P0 口输入相应的十六进制代码。由图可见,控制蜂鸣器的 BUZZ 引脚由 P06 引脚控制,给予 P06 引脚一个高电平即可打开蜂鸣器;同理,控制继电器的 RELAY 引脚由 P04 引脚控制,给予 P04 引脚一个高电平即可打开继电器;其余 P0 口置1即可。
在这里插入图片描述
例:

door(0xa0,0xaf);  //1010 1111 关闭蜂鸣器、继电器
door(0xa0,0xef);  //1110 1111 打开蜂鸣器,关闭继电器
door(0xa0,0xbf);  //1011 1111 关闭你蜂鸣器,打开继电器

2、数码管驱动函数

数码管:前文提到的数码管驱动函数,同样是对于 M74HC573M1R 芯片。笔者通过打通锁存器Y6C,控制数码管位选,对应的二进制代码为:110 0 0000(110转换为十进制即为6,Y6C),即0xc0;通过打通锁存器Y7C,控制数码管段选,对应的二进制代码为:111 0 0000(111转换为十进制即为7,Y7C),即0xe0;
在这里插入图片描述在这里插入图片描述
再通过 P0 口输入相应的十六进制代码即可点亮响应的数码管段。注意:原理图中各数码管段采用共阳极接法,因此对应的输入端输入低电平即可点亮相应的数码管段。
在这里插入图片描述
接下来分析一下上文的数码管驱动函数中的剩余语句。语句 door(0xe0,0xff) 通过打通锁存器 Y7C 对数码管段选,输入 0xff(1111 1111)熄灭数码管,屏蔽前一时刻数码管状态实现消隐;再通过语句 door(0xc0,0x01<<dspcom) 打通锁存器 Y6C ,输入不同值进行位选;最后调用语句 door(0xe0,tab[dspbuf[dspcom++]]) 进行段选输入点亮不同状态的数码管。
例:

door(0xe0,0xff);                   //消隐
	
door(0xc0,0x01<<dspcom);           //位选
door(0xe0,tab[dspbuf[dspcom++]]);  //段选

3、主函数

主函数:即为调用前文编写的各种驱动函数。笔者通过在中断中定义按键识别标志位 key_flag,每 20 秒扫描读取一次按键,将按键返回值存取在变量 key_re 中,再通过 case 语句实现不同按键控制不同功能(通过按键调参,可节约不少时间)。

void main()      //主函数
{
	u8 key_re;     //定义变量读取按键返回值
	
	all_init();    //初始化函数
	Timer0Init();  //定时器0初始化
	
	while(1)
	{
		if(key_flag)            //每20ms扫描一次按键
		{
			key_flag = 0;         //标志位置0
			key_re = Read_key();  //读取按键
			 
			if(key_re != 0xff)    //判断按键是否按下
			{
				//判断按键
				switch(key_re)
				{
					//放入需要执行的功能
					case 4: break;
					case 5: break;
					case 6: break;
					case 7: break;
					case 8: break;
					case 9: break;
					case 10:break;
					case 11:break;
					case 12:break;
					case 13:break;
					case 14:break;
					case 15:break;
					case 16:break;
					case 17:break;
					case 18:break;
					case 19:break;
				}
			}
		}
	}
}

例:

case 4:door(0x80,0x00);break;  //按下按键4,点亮所有LED
case 5:door(0xa0,0xff);break;  //按下按键5,打开蜂鸣器、继电器
case 6:dspbuf[0] = 1;break;    //按下按键6,第一位数码管显示数字1

到此,蓝桥杯单片机设计与开发_标准模板新手入门STC89C52系列单片机基础模板讲解到此结束啦,读者若有不懂的欢迎在提问区讨论!!!
最后,在文末附上18年科创结课考试试题,供本校自动化专业的学弟学妹们参考(图片略微模糊),祝各位取得佳绩。
在这里插入图片描述

  • 45
    点赞
  • 199
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_小猪沉塘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值