前言
书接上文,上期我们基本完成了十三届省赛题,但还是存在一些问题,本期我将对上期存在的一些问题,提出一些解决方案,并加以实践验证可行性,废话少说,让我们往下看。
上一期遗留的问题
上期我们提到,数码管和LED在使用的时候会存在外设之间相互干扰的问题,在我们不断的探索之下,我发现其实不仅仅是LED和数码管,继电器和蜂鸣器之间也会存在相互干扰的问题…………
导致我们在控制其中一个外设时,其他的外设也会被干扰,出现一些奇怪的问题,对于这种干扰,我们老师教了我一种很神奇的方法,下面让我来介绍一些这种方法神奇在哪。
解决的方法
- 通过创建数组来实现对数码管/LED的控制
什么意思呢?我们知道,我们蓝桥杯单片机是通过74HC573锁存器实现P0口对大量外设的控制的,这样的方法毋庸置疑是节省了大量的IO口,但也由于这样的设定,导致P0口的电平一直处于跳变之中,上一秒控制LED的亮起与熄灭,下一秒就去控制数码管的显示了,导致每一个LED(外设)的状态没有被记录下来,而下一次再来控制LED的时候,此时的P0口电平早已不是之前控制LED的电平状态了,也就是因为这个原因,导致由74HC573锁存器控制的各个外设之间相互干扰,互相打架。
因此我们可以通过创建一个数组,来控制LED/数码管,甚至所有通过锁存器控制的外设。具体实现代码如下:
1. LED
数组LED[8] 记录8个LED的状态,为1亮起,为0熄灭,
unsigned char LED[8] = {0,0,0,0,0,0,0,0}; //LED显示数组
//LED写入函数,写入内容为LED数组的内容,1表示开启LED,反之则为关闭LED
void LED_Control(unsigned char* p )
{
unsigned char temp = 0;
unsigned char i = 0;
for(i=0;i<8;i++)
{
if(*(p+i) != 0)
{
temp |= 0x01<< i ;
}
}
LS_Set(4);
P0 = ~temp;
LS_Set(0);
}
2. 数码管
通过对SEG[8] 写入数据,将SEG_Index[SEG[pos]] 写入P0口,实现对数码管的完美控制
unsigned char SEG[8] = {20,20,20,20,20,20,20,20}; //数码管显示数组
//数码管段码
//0 1 9
unsigned char code SEG_Index[30] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
/*0. 1. 9. */
0x40, 0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x10,0x20,
/*消隐 。 - P A C*/
0xFF,0x7F,0xBF,0x8C,0x88,0xC6};
//数码管显示函数,显示内容为SEG_Index【SEG】数组的内容,
void SEG_Control(unsigned char* p)
{
unsigned char i = 0;
for(i=0;i<8;i++)
{
LS_Set(0);
LS_Set(6);
P0 = 0x01<<i;
LS_Set(0);
LS_Set(7);
P0 = SEG_Index[*(p+i)];
LS_Set(0);
P0 = 0xFF;
DelayXms(1);
}
}
其他的外设也可以用类似的控制方法实现,后面遇到了我再去实现
这样写的好处
好处是什么呢?那可太多了。
下次我们需要修改数码管/LED的状态时,只需要改变数组中的内容,然后通过定时器每个一段时间(我用的是10ms)刷新一次LED/数码管的状态,就可以实现对LED/数码管的完美控制了。而且还减少了CPU的占用。
你就说好不好吧😎😎
解决了这个问题之后,我们继续回到今天的正题,十二届省赛题实操。
任务要求
1.基本要求
2.竞赛板配置要求
3.硬件框图
4.功能描述
实现思路
关于实现思路,我做出一个优化,具体结构是这样的:
我将单片机代码进行了一个区分,分别为数码管显示函数、按键扫描函数、逻辑处理函数、数据处理函数,将不同功能的代码放置到对应的函数,然后每隔一段时间对数码管和按键扫描函数调用,这样看起来更加简介,在便于调试的同时,节省了单片机的资源。
代码实现
1.main.C
#include <STC15F2K60S2.H>
#include "LS138.h"
#include "Key.h"
#include "onewire.h"
#include "iic.h"
unsigned char LED[8] = {0,0,0,0,0,0,0,0}; //LED显示数组
unsigned char SEG[8] = {20,20,20,20,20,20,20,20}; //数码管显示数组
unsigned int Tempreature = 2425; //温度
unsigned int Tempreature_Set = 2500; //温度参数
unsigned int V_DAC= 325; //DAC输出电压
unsigned int Key_Num = 0; //键码值
unsigned int Page = 1; //显示的页面
unsigned int Mode = 1; //模式
unsigned char SEG_Flag = 0; //数码管显示标志
unsigned char Tempreature_Flag = 0; //温度读取标志位
//定时器1初始化,十六位自动重装载,1ms触发一次
void Timer1_Init(void)
{
TMOD = 0x00;
ET1 = 1;
EA = 1;
TH1 = 0xFC;
TL1 = 0x18;
TR1 = 1;
}
//初始化函数
void Init()
{
LS_Init(); //关闭蜂鸣器和继电器
Timer1_Init(); //定时器1初始化
LED_Control(LED); //关闭所有LED
SEG_Control(SEG); //关闭所有数码管
Tempreature = DS_Read_Tempreatur(); //读取温度
}
//温度界面显示函数
void Wendu_Page(unsigned int Tempreature)
{
SEG[0] = 25;
SEG[1] = 20;
SEG[2] = 20;
SEG[3] = 20;
SEG[4] = Tempreature/1000%10;
SEG[5] = Tempreature/100%10 +10;
SEG[6] = Tempreature/10%10;
SEG[7] = Tempreature%10;
}
//参数设置显示界面
void Canshu_Page(unsigned int Tempreature_Set )
{
SEG[0] = 23;
SEG[1] = 20;
SEG[2] = 20;
SEG[3] = 20;
SEG[4] = 20;
SEG[5] = 20;
SEG[6] = Tempreature_Set/1000%10;
SEG[7] = Tempreature_Set/100%10;
}
//DAC输出电压显示界面
void DAC_Page(unsigned int V_DAC)
{
SEG[0] = 24;
SEG[1] = 20;
SEG[2] = 20;
SEG[3] = 20;
SEG[4] = 20;
SEG[5] = V_DAC/100%10 + 10 ;
SEG[6] = V_DAC/10%10;
SEG[7] = V_DAC%10;
}
//数码管任务,通过Page来决定显示的界面,
void SEG_Task(void)
{
switch(Page)
{ //控制对应的LED亮起
case 1: Wendu_Page(Tempreature); LED[1] = 1;LED[2] = 0; LED[3] = 0;
break;
case 2: Canshu_Page(Tempreature_Set); LED[1] = 0;LED[2] = 1; LED[3] = 0;
break;
case 3: DAC_Page(V_DAC); LED[1] = 0;LED[2] = 0; LED[3] = 1;
break;
}
if(SEG_Flag)
{
SEG_Control(SEG); //10ms更新一次数码管和LED显示状态
LED_Control(LED);
SEG_Flag = 0;
}
}
//按键扫描函数,有按键按下则返回键码值,否则键码值为0
void Key_Task(void)
{
unsigned int temp = 0;
temp = KeyScan();
if(temp != 0)
{
Key_Num = temp;
}
}
//处理逻辑相关的任务
void Logic_Task(void)
{
unsigned int temp = 0;
temp = Tempreature_Set;
if(Key_Num == 4) //按键4按下,改变显示界面
{
Page++;
if(Page >= 4)
{
Page = 1;
}
}
if(Key_Num == 5) //按键5按下,改变模式
{
Mode++;
if(Mode >= 3)
{
Mode = 1;
}
}
if(Page == 2 && Key_Num == 8) //参数界面下,按键8按下,参数减一
{
temp -= 100;
}
if(Page == 2 && Key_Num == 9) //参数界面下,按键8按下,参数加一
{
temp += 100;
}
Tempreature_Set = temp;
}
//处理数据相关的任务函数
void Data_Task(void)
{
if(Tempreature_Flag) //每100ms测量一次温度
{
Tempreature = DS_Read_Tempreatur();
Tempreature_Flag = 0;
}
if(Mode == 1) //模式1,DAC输出电压与温度相关
{
LED[0] = 1;
if(Tempreature < Tempreature_Set )
{
IIC_DAC(0x00); //温度小于温度参数,输出0V电压
}
else
{
IIC_DAC(0xFF); //温度大于温度参数,暑促5V电压
}
}
if(Mode == 2) //模式2,DAC输出电压如图7所给关系
{
LED[0] = 0;
if(Tempreature <= 2000) //小于20度,输出1V
{
IIC_DAC(0x33);
}
if(Tempreature >= 4000) //大于40度,输出4V
{
IIC_DAC(0xCC);
}
else //呈线性关系
{
IIC_DAC((1 + (Tempreature /100 - 20)*3/20) *51) ;
}
}
}
void main()
{
Init(); //初始化函数调用
while(1)
{
SEG_Task(); //数码管,LED显示函数
Key_Task(); //按键扫描函数
Logic_Task(); //逻辑相关处理
Data_Task(); //数据相关处理
}
}
//定时器中断服务函数
void Timer1_Handler() interrupt 3
{
static unsigned int Timer1_Count = 0;
Timer1_Count++;
if(Timer1_Count % 10 == 0) //每10ms刷新一次数码管和LED显示状态
{
SEG_Flag = 1;
}
if(Timer1_Count % 100 == 0) //每100ms读取一次温度
{
Tempreature_Flag = 1;
}
}
2.LS138.h
#ifndef __LS138_H_
#define __LS138_H_
#include <STC15F2K60S2.H>
//延时函数,延时Xms
void DelayXms(unsigned int x);
//初始化函数
void LS_Init(void);
//LS138位选函数,输入0为清除位选
void LS_Set(unsigned int dat);
//LED写入函数,写入内容为LED数组的内容,1表示开启LED,反之则为关闭LED
void LED_Control(unsigned char* p );
//数码管显示函数,显示内容为SEG_Index【SEG】数组的内容,
void SEG_Control(unsigned char* p);
#endif
3.LS138.C
#include "LS138.h"
//数码管段码
//0 1 9
unsigned char code SEG_Index[30] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
/*0. 1. 9. */
0x40, 0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x10,0x20,
/*消隐 。 - P A C*/
0xFF,0x7F,0xBF,0x8C,0x88,0xC6};
//延时函数,延时Xms
void DelayXms(unsigned int x) //@12.000MHz
{
unsigned char i, j,k;
for(k=0;k<x;k++)
{
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
//初始化函数
void LS_Init(void)
{
LS_Set(5);
P0 = 0x00; //关闭蜂鸣器和继电器
LS_Set(0);
}
//LS138位选函数,输入0为清除位选
void LS_Set(unsigned int dat)
{
switch(dat)
{
case 0: P2 = P2 & 0x1F;
break;
case 4: P2 = (P2 & 0x1F) | 0x80;
break;
case 5: P2 = (P2 & 0x1F) | 0xA0;
break;
case 6: P2 = (P2 & 0x1F) | 0xC0;
break;
case 7: P2 = (P2 & 0x1F) | 0xE0;
break;
}
}
//LED写入函数,写入内容为LED数组的内容,1表示开启LED,反之则为关闭LED
void LED_Control(unsigned char* p )
{
unsigned char temp = 0;
unsigned char i = 0;
for(i=0;i<8;i++)
{
if(*(p+i) != 0)
{
temp |= 0x01<< i ;
}
}
LS_Set(4);
P0 = ~temp;
LS_Set(0);
}
//数码管显示函数,显示内容为SEG_Index【SEG】数组的内容,
void SEG_Control(unsigned char* p)
{
unsigned char i = 0;
for(i=0;i<8;i++)
{
LS_Set(0);
LS_Set(6);
P0 = 0x01<<i;
LS_Set(0);
LS_Set(7);
P0 = SEG_Index[*(p+i)];
LS_Set(0);
P0 = 0xFF;
DelayXms(1);
}
}
4.按键扫描函数
#include "Key.h"
//按键扫描函数,返回值为键码值,键码值为0表示按键按下为送手或无按键按下
unsigned int KeyScan(void)
{
static unsigned char cnt = 0, //continue value
last_trg = 0; //last trigger value
unsigned char trg = 0, //trigger value
cur = 0, //current value
value = 3, //必须初始化为3
key_x = 0,
key_y = 0;
P3 = 0x0f;
P4 = 0x00;
if(!P30) key_x = 3; //获取X轴坐标
else if(!P31) key_x = 2;
else if(!P32) key_x = 1;
else if(!P33) key_x = 0;
P3 = 0xf0;
P4 = 0xff;
if(!P34) key_y = 4; //获取Y轴坐标
else if(!P35) key_y = 3;
else if(!P42) key_y = 2;
else if(!P44) key_y = 1;
cur = key_y^0;
trg = cur^cnt & cur;
cnt = cur;
if((last_trg ^ trg & last_trg) && cur)
{
//计算矩阵按键的键值
value = key_x + key_y * 4;
}
last_trg = trg;
return value; //返回独立按键的键值
}
5.温度读取函数
需要注意的是:这里的单总线延时函数,需要加上一个:for(i=0;i<12;i++);
为什么要这样呢?这是因为官方提供的延时函数时12T工作模式下的,而我们使用的单片机工作在1T模式下,因此需要作此改动。
//单总线延时函数
void Delay_OneWire(unsigned int t) //STC89C52RC
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//DS18B20读取温度函数
unsigned int DS_Read_Tempreatur(void)
{
unsigned int tempreature = 0;
unsigned char HSB,LSB;
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
LSB = Read_DS18B20();
HSB = Read_DS18B20();
tempreature = (HSB << 8 )| LSB ;
tempreature = tempreature /16 *100;
tempreature = tempreature + (LSB & 0x0F) * 6.25 ;
return tempreature; //返回值放大了100倍,可实现保留两位小数
}
6.DA转换函数
//DA转换函数,转换范围为0~5V
void IIC_DAC(unsigned char dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x40);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
以上就是赛题用到的所有代码,不包含官方提供的驱动代码,如果那里有问题的话,欢迎评论区留言探讨。👀👀👀文章来源:https://www.toymoban.com/news/detail-824456.html
总结
十二届的上省赛题总体上来说难度会比十三届的低,也许是我学会了新的方法的原因,也许时我变强了,哈哈哈哈。不过我觉得我上面提供的思路确实不错,对于还没有自己代码风格的小伙伴可以尝试一下,强烈推荐。🫡🫡🫡文章来源地址https://www.toymoban.com/news/detail-824456.html
到了这里,关于蓝桥杯单片机学习15——第十二届省赛题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!