一、题目:【基础技能10】定时器的进阶综合案例解析_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Bt41187hw?p=10&vd_source=7bbed4bd56787012aaa2a6203c34509e
二、代码思路:
首先把之前独立按键的代码移植,点亮L1,L2,L3代替三个功能做测试,一个按键控制清零,一个按键控制暂停与启动;然后移植数码管代码,直接显示图中数字做测试;最后移植定时器代码,整合上述代码,编写三个函数实现三个功能。
1、实现过程中遇到的问题:
(1)无法同时实现按键电灯和数码管显示:
因为有松手检测会程序卡在松手检测,导致数码管无法正常显示
~~~》将数码管显示放在中断函数中,让他每隔一段时间显示一下
~~~》一次性显示八个数使得在中断函数的时间太长,导致无法通过按键消抖的两次显示
~~~》可以每次中断函数只显示一个数字,但要将发生中断周期缩小
——》尽管数码管显示正常了,但是由于led显示函数放在主函数中,而通过P0口传数据与通过P2口选择锁存器的步骤又是分开的,导致最后可能出现打开led显示锁存器但是传的是数码管显示的数据
~~~~》我觉得LED显示的函数也要放在中断中
——》出现了全部显示的情况,按下按键对应的led灯会变暗一点
~~~~》我觉得可能以是因为我只改变一个只改变了一个位,而其他位保持原来状态;二是我只是使led灯翻转,导致led灯状态一直翻转
~~~~》每次必须整个P0进行改变,不然会收到其他操作的影响,所以定义位的操作应该少做;消除标志,使每次改变标志位只能执行一次程序;保存好每次P0点灯的数据,将之前数据和现在数据进行整合就能实现之前点亮的灯本次还能保持
——》结果变成全亮了。。。。就连之前按下按键的反应都没有了
~~~~》位操作失误了,这也暴露了位操作虽然方便但还是可读性差的缺点
三、参考代码:
1、宏定义:
#ifndef _PUBLIC_H
#define _PUBLIC_H
#include "STC15F2K60S2.H"
#define u8 unsigned char
#define u16 unsigned int
void Delay_1ms(u16 num);
void Close_All(void);
void Delay_10us(u16 num);
#endif
#include "Public.h"
// 延时函数(最小约1ms@12MHz)
void Delay_1ms(u16 num)
{
unsigned int i;
while(num--)
for(i=0; i<628; i++);
}
/*
输入变量:无
输出变量:无
功能:关闭蜂鸣器和继电器
*/
void Close_All(void)
{
//关闭蜂鸣器和继电器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xA0;
P2 &= 0x1f;
//关闭LED灯
P0 = 0xff;
P2 = (P2 & 0x1F) | 0x80;
P2 &= 0x1f;
}
void Delay_10us(u16 num)
{
u16 i;
while(num--)
for(i=0; i<3; i++);
}
2、主函数:
// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "Public.h"
#include "stdio.h"
//定时器
u8 hour,minute,sec,ms_count,time_on = 1;
//数码管
u8 SEG_COT[9],SEG_Code[8];
u8 SEF_POS = 0,seg_delay;
//LED灯
u8 led_sign,last_led = 0xff;
//按键
u8 key_old,key_delay;
void SWIT_Led(u8 l_x);
void Timer_0_Init(u16 time);
void Key_Proc();
void SEG_Show(u8 num,u16 PIS);
void SEG_TSL(u8 *input,u8 *output);
void SEG_Proc();
// 主函数
void main(void)
{
Close_All();
Timer_0_Init(1000);//1ms
while(1)
{
Key_Proc();
SEG_Proc();
}
}
/*****************按键*************************/
/*
输入变量:无
输出变量:7-4代表第几个按键被按下;0代表没有按键按下
功能:检测哪个独立按键被按下
注意:只有消抖,没有松手检测
*/
u8 Key_read()
{
u8 i=0;
for(i=0;i<4;i++)
{
if(!(P3 & (0x01<<i)))
{
return 7-i;
}
}
return 0;
}
void Key_Proc()
{
u8 key_now=0,key_down=0,key_up =0;
static u8 D_sign = 0;
//消抖,同时节省CPU资源
if(key_delay) return;
key_delay = 1;
//读取按键按下的编号
key_now = Key_read();
//按键按下:通过判断按键状态是否发生变化,并且变化后状态是否为按下
key_down = key_now & (key_old ^ key_now);
//记录当前状态为下一次检测做准备
key_old = key_now;
if(key_down == 5 )
{
led_sign = 1;
hour = 0;
minute = 0;
sec = 0;
}
else if(key_down == 4)
{
D_sign ++;
if (D_sign == 1)
{
led_sign = 2 ;
time_on = 0;
}
else if (D_sign == 2)
{
led_sign = 3 ;
D_sign = 0;
time_on = 1;
}
}
}
/*****************定时器*************************/
/*
输入变量:定时时长___us
输出变量:无
功能:配置并开启定时器0
*/
void Timer_0_Init(u16 time)
{
//12T模式
AUXR &= 0x7f;
//定时器0 模式0
TMOD &= 0xf0;
//设置初值
TH0 = (65536-time)/256;
TL0 = (65536-time)%256;
//打开中断
ET0 = 1;
EA = 1;
//开始计数
TR0 = 1;
}
void Timer_0_IT(void) interrupt 1
{
if(++key_delay == 10) key_delay = 0;
if(++seg_delay == 40) seg_delay = 0;
if(time_on)
{
ms_count++;
if(ms_count == 50)//50ms
{
sec++;
ms_count = 0;
}
if(sec == 20)
{
minute++;
sec = 0;
}
if(minute == 60)
{
hour++;
minute = 0;
}
if(hour == 60)
{
hour = 0;
}
}
SEG_Show(SEG_Code[SEF_POS],SEF_POS);
if(++SEF_POS == 8)SEF_POS = 0;
SWIT_Led(led_sign);
}
/*****************LED*************************/
/*
输入变量:Lx灯翻转
输出变量:无
功能:选择相应的锁存器后,通过P0口控制某个LED灯翻转,但不影响其他LED灯
注意:使用前要对last_led进行宏定义
*/
void LED_ON_Lx(u8 L_X)
{
//由于P0口是公用的口,我们要对每次点灯的数据进行记录,不然会受其他操作的影响
P0 = last_led ^ (0x01<<(L_X-1));
last_led = P0;
P2 = (P2 & 0x1f) | 0x80;
P2 &= 0x1f;
}
void SWIT_Led(u8 l_x)
{
switch(l_x)
{
case 1:LED_ON_Lx(1);led_sign = 0;break;
case 2:LED_ON_Lx(2);led_sign = 0;break;
case 3:LED_ON_Lx(3);led_sign = 0;break;
}
}
/***************************数码管*****************************/
/*
输入变量:input,输入字符数组;output:输出16进制数数组
输出变量:无
功能:将字符串转化为对应数码管显示的16进制数
注意:记得定义数组——u8 SEG_COT[9];u8 SEG_Code[8];
*/
void SEG_TSL(u8 *input,u8 *output)
{
u8 i=0,temp=0,j=0;
for(i=0;i<8;i++,j++)
{
switch(input[j])
{
case '0': temp = 0xc0; break;
case '1': temp = 0xf9; break;
case '2': temp = 0xa4; break;
case '3': temp = 0xb0; break;
case '4': temp = 0x99; break;
case '5': temp = 0x92; break;
case '6': temp = 0x82; break;
case '7': temp = 0xf8; break;
case '8': temp = 0x80; break;
case '9': temp = 0x90; break;
case 'A': temp = 0x88; break;
case 'B': temp = 0x83; break;
case 'C': temp = 0xc6; break;
case 'D': temp = 0xA1; break;
case 'E': temp = 0x86; break;
case 'F': temp = 0x8E; break;
case 'H': temp = 0x89; break;
case 'L': temp = 0xC7; break;
case 'N': temp = 0xC8; break;
case 'P': temp = 0x8c; break;
case 'U': temp = 0xC1; break;
case '-': temp = 0xbf; break;
case ' ': temp = 0xff; break;
default: temp = 0xff;
}
if(input[j+1] == ".")
{
temp &= 0x7f;
j++;
}
output[i] = temp;
}
}
/*
输入变量:num,要显示数据;PIS,显示位置,从左到右分别为0-7
输出变量:无
功能:操作138译码器,4-7分别对应Y4-Y7,其余都会使译码器不起作用
注意:需要把存放从1-f对应的16进制数数组也移植
*/
void SEG_Show(u8 num,u16 PIS)
{
//消影
P0 = 0xff;
P2 = (P2 & 0x1f) | 0xE0;
P2 &= 0x1f;
//改变显示位置
P0 = 0x01<<PIS;
P2 = (P2 & 0x1f) | 0xC0;
P2 &= 0x1f;
//改变数据
P0 = num;
P2 = (P2 & 0x1f) | 0xE0;
P2 &= 0x1f;
}
void SEG_Proc()
{
if(seg_delay) return;
seg_delay = 1;
sprintf(SEG_COT, "%02u-%02u-%02u",(u16)hour,(u16)minute,(u16)sec);
SEG_TSL(SEG_COT,SEG_Code);
}