如果想排量修改SEG_Fre,可以使用搜索功能。
双击选中SEG_Fre,选查找替换,范围选择整个project,替换3处为:SEG_LED_Show()。
在头文件中已经定义了一个LED的一个8位的状态,实际使用时想单独控制LED的某一个位,可以使用寻址变量bdata。
在 extern u8 bdata LED_DATA; //LED的显示变量,使用寻址变量形式
u8 bdata LED_DATA = 0xff;
和原来定义的地方都加上bdata修饰,之后就可以用sbit进行定义:
比如:
sbit LED0 = LED_DATA^0; //LED0引脚定义
sbit LED1 = LED_DATA^1; //LED1引脚定义
sbit LED2 = LED_DATA^2; //LED2引脚定义
sbit LED3 = LED_DATA^3; //LED3引脚定义
sbit LED4 = LED_DATA^4; //LED4引脚定义
sbit LED5 = LED_DATA^5; //LED5引脚定义
sbit LED6 = LED_DATA^6; //LED6引脚定义
sbit LED7 = LED_DATA^7; //LED7引脚定义
如果需要在其他文件中调用,需要在头文件中声明,利用shift+alt竖向选择,复制:
extern bit LED0
extern bit LED1
extern bit LED2
extern bit LED3
extern bit LED4
extern bit LED5
extern bit LED6
extern bit LED7
在头文件中增加LED总控和数码管的单独控制变量,修改后的seg_led.h文件为:
#ifndef __SEG_LED_H
#define __SEG_LED_H
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
extern u8 Show_Tab[8]; //数码管的内码显示变量
extern u8 bdata LED_DATA; //LED的显示变量,使用寻址变量形式
//------------------------引脚定义------------------------//
#define SEG_SEG P6
#define SEG_COM P7
#define LED_POW P40 //P40是led的电源开关
#define LED LED_DATA //8个LED的控制变量,8个2进制
#define SEG0 Show_Tab[0] //数码管的单独控制变量
#define SEG1 Show_Tab[1]
#define SEG2 Show_Tab[2]
#define SEG3 Show_Tab[3]
#define SEG4 Show_Tab[4]
#define SEG5 Show_Tab[5]
#define SEG6 Show_Tab[6]
#define SEG7 Show_Tab[7]
extern bit LED0; //LED的单独状态控制
extern bit LED1;
extern bit LED2;
extern bit LED3;
extern bit LED4;
extern bit LED5;
extern bit LED6;
extern bit LED7;
void SEG_LED_Show(void); //仅声明
#endif
这里第一次编译的时候出现错误,提示
Demo.c(29): warning C138: expression with possibly no effect
Demo.c(29): error C25: syntax error near '='
Demo.c(30): warning C34: 'Show_Tab': missing declaration specifiers
Demo.c(30): error C25: syntax error near '='
仔细观察发现,
错误1:头文件的定义中,#define SEG0 Show_Tab[0]的定义后面加了“;”结尾,预编译将空格以后的“Show_Tab[0];"当成了整体,不应该加“;”
错误2:“extern bit LED0;”语句后面缺少了“;”,不完整,影响编译。extern等IDE蓝色字体显示的关键字每行是一条完整的语句,结束时必须加“;”。
尝试一下单独控制,在demo.c中对main.c进行修改:
//数码管初始化,显示0-7:
SEG0 = 0;
SEG1 = 1;
Show_Tab[2] = 2;
Show_Tab[3] = 3;
Show_Tab[4] = 4;
Show_Tab[5] = 5;
Show_Tab[6] = 6;
Show_Tab[7] = 7;
LED_DATA = 0x0f; //赋初值,亮一半灭一半,可以写8位的变量.从7开始数到0
LED0 = 0; //
LED0应该写0,写最低位(0x0f二讲制是00001111是高位也就是第七位本来就是0,所以我们应该是把最低位去改0)
(初始状态是)第0位到第3位之间我们给他写了个1,现在就给他写二个第0位。
编译下载完成,下载完成数码管显示01234567,下面这里4个灯和最后一个灯点亮,实现了我们的功能
因为是初始化的时候给它控制了一个LED的灯,可以用这几个按键来模拟一下,是不是真的可以通过给定义的这个LED0控制。
增加按键测试代码,在demo.c的main函数前增加函数声明:void delay_ms(u16 ms);,main函数中测试代码:
if(KEY1 == 0) //P32
{
delay_ms(10);
if(KEY1 == 0)
{
while(KEY1 == 0); //一直等待直到松开后再执行
LED0 =! LED0;
}
}
if(KEY2 == 0) //P33
{
delay_ms(10);
if(KEY2 == 0)
{
while(KEY2 == 0); //一直等待直到松开后再执行
LED1 =! LED1;
}
}
按键控制倒数第1,2个灯。
当单独控制P6, 如果想LED点亮,就需要写P60=0,如果再刷新数码管,那么那它是不是这个P60一写进去,数码管就乱码了。
如果要控制,单独控制变量就可以实现操作。程序里用了循环刷新的办法(专业术语叫分时复用)。每次刷新前8个毫秒是刷新数码管,
第9个毫秒是刷新LED,第10个ms全部熄灭,然后循环,就做到了LED和数码管同时显示,而且不会存在干扰。每一次控制就只控制这几个变量。
切换变量,就不会对引脚进行直接操作,比如下一次换了个板子,这个数码管的引脚可能不是P6了,P3或者P2,只需局部修改就行。
头文件中的定义,上面是引脚定义,下面是变量声明和函数声明,.c文件中进行变量定义。
seg_led.c和seg_led.h可以在后期直接复制目录调用。这个就是一个简单的一个通过工程文件去处理,下次使用的时候直接复制黏贴。
在hardware-key目录中,新建key.c和key.h,添加引用路径:
key.h插入模板:
#ifndef __KEY_H
#define __KEY_H
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
//------------------------引脚定义------------------------//
//------------------------变量声明------------------------//
//------------------------函数声明-----------------------//
#endif
key.c和demo.c中调用key.h:#include “key.h” //调用头文件
增加引脚定义:
//------------------------引脚定义------------------------//
#define KEY P3 //定义一个按键 引脚选择P32-P36
#define KEY1 2 //按键1
#define KEY2 3 //按键2
#define KEY3 4 //按键3
#define KEY4 5 //按键4
如果要更换按键的端口,可以直接修改P3。
有没有什么办法可以代替之前的按键操作?
按键要满足的功能
1.按键消抖 10ms
2.按键按下的瞬间
3.按键松开的瞬间
状态 功能
按键未按下 0
消抖 1
单击 2
单击结束 3
长按3s 4
长按结束 5
按键松开 6
变量声明:
//------------------------变量声明------------------------//
//状态 功能
#deine KEY_NOPRESS 0 //按键未按下 0
#deine KEY_FLCKER 1 //消抖 1
#deine KEY_RESS 2 //单击 2
#deine KEY_PRESSOVER 3 //单击结束 3
#deine KEY_LONGPRESS 4 //长按3s 4
#deine KEY_LONGOVER 5 //长按结束 5
#deine KEY_RELAX 6 //按键松开 6
可以避开while一直等待的状态。
既然上面定义了返回值,那么需要编写一个有返回值的函数:u8 KEY_ReadState(u8 keynum); //读取指定按键的状态
再编写一个检查所有的按键状态的函数: void KEY_Deal(void); //检查所有的按键状态
在key.c中实现,先将声明复制入key.c:
void KEY_Deal(void) //检查所有的按键状态
{
}
u8 KEY_ReadState(u8 keynum) //读取指定按键的状态
{
}
对比功能需求,8位的变量,判断时间小于30ms或者等于30ms。首先进行按键状态的计数,时间是多久,
新建变量,u16 Count[8] = {0,0,0,0,0,0,0,0}; //按键的时间状态变量初始化8位
修改KEY_Deal函数,使用for循环,格式为:
u8 i = 0;
for(i=0;i<8;i++) //for循环变量
{
}
假设P3有8个位,检查所有的按键状态代码:
void KEY_Deal(void) //检查所有的按键状态
{
u8 i = 0;
,or(i=0;i<8;i++) //for循环变量 循环8次,i取值为0-7,代表P30-P37的状态查询
{
if(^KEY & {1<
判断按钮的状态,我们用户要读到的个按键状态,
首先函数的模板为:
u8 KEY_ReadState(u8 keynum) //读取指定按键的状态
{
if(Count[8] > 0) //判断按键是按下的
{
}
else //按键已经松开了
{
}
}
根据之前的逻辑表格,编写响应的函数判断代码:
void KEY_Deal(void) //检查所有的按键状态,10ms执行一次
{
u8 i = 0;
for(i=0;i<8;i++) //for循环变量 循环8次,i取值为0-7,代表P30-P37的状态查询
{
if(~KEY & (1< 0 ) //如果这个按键是按下过的,
{
LastState |= (1< 0) //判断按键是按下的
{
if(Count[keynum] < 3) //按下小于30ms,返回消抖状态
{
return KEY_FLCKER;
}
else if(Count[keynum] == 3) //按正好等于30ms,返回单击状态
{
return KEY_RESS;
}
else if(Count[keynum] < 300 ) //按下小于3000ms,返回单击结束
{
return KEY_PRESSOVER;
}
else if(Count[keynum] == 300 ) //按下正好等于3000ms,返回长按
{
return KEY_LONGPRESS;
}
else //长按结束
{
return KEY_LONGOVER;
}
}
else //按键已经松开了,返回KEY_RELAX状态
{
if(LastState &(1<
demo.c中进行调用,先删除原有的按键控制代码和按键define,并在while(1)主循环中增加:
delay_ms(10); //延时10ms扫描一次
KEY_Deal(); //P3上所有端口都需要执行一遍
if(KEY_ReadState(KEY1)== KEY_RESS) //判断KEY1按钮是否为单击
{
LED0 = 0;
}
else if(KEY_ReadState(KEY1)== KEY_LONGPRESS) //判断KEY1按钮是否为长按
{
LED1 = 0;
}
else if(KEY_ReadState(KEY1)== KEY_RELAX) //判断KEY1按钮是否为松开
{
LED = 0XFF;
}
编译完成,下载,上面的数码管显示正常进行,KEY1按钮单击点亮第1个灯,长按点亮第2个灯,松开熄灭,功能正常。
等待松开也不需要等待。这个按键也可以看做是一个状态机,因为是实时读取key的状态,而不是死在那里。
增加函数头,编写简要介绍。
完整代码如下:
main.c
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
#include "seg_led.h"
#include "key.h" //调用头文件
#define BEEP P54 //定义一个按键 引脚选择P54
#define MAIN_Fosc 24000000UL //定义主时钟
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init(); //函数声明
void Timer0_Init(void);
void delay_ms(u16 ms);
void main() //程序开始运行的入口
{
sys_init(); //USB功能+IO口初始化
usb_init(); //usb库初始化
Timer0_Init();
EA = 1; //CPU开放中断,打开总中断。
//数码管初始化,显示0-7
SEG0 = 0;
SEG1 = 1;
Show_Tab[2] = 2;
Show_Tab[3] = 3;
Show_Tab[4] = 4;
Show_Tab[5] = 5;
Show_Tab[6] = 6;
Show_Tab[7] = 7;
LED_DATA = 0x0f; //赋初值,亮一半灭一半,可以写8位的变量.从7开始数到0
LED0 = 0; //
while(1) //死循环
{
// if( DeviceState != DEVSTATE_CONFIGURED ) //
// continue;
if( bUsbOutReady )
{
usb_OUT_done();
}
delay_ms(10);
KEY_Deal(); //P3上所有端口都需要执行一遍
if(KEY_ReadState(KEY1)== KEY_RESS) //判断KEY1按钮是否为单击
{
LED0 = 0;
}
else if(KEY_ReadState(KEY1)== KEY_LONGPRESS) //判断KEY1按钮是否为长按
{
LED1 = 0;
}
else if(KEY_ReadState(KEY1)== KEY_RELAX) //判断KEY1按钮是否为松开
{
LED = 0XFF;
}
}
}
void sys_init() //函数定义
{
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 &= ~0x03;
P3M1 |= 0x03;
//设置USB使用的时钟源
IRC48MCR = 0x80; //使能内部48M高速IRC
while (!(IRC48MCR & 0x01)); //等待时钟稳定
USBCLK = 0x00; //使用CDC功能需要使用这两行,HID功能禁用这两行。
USBCON = 0x90;
}
void delay_ms(u16 ms) //unsigned int
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
void Timer0_Init(void) //1毫秒@24.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x30; //设置定时初始值
TH0 = 0xF8; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
KEY.C:
#include "key.h" //调用头文件
u16 Count[8] = {0,0,0,0,0,0,0,0}; //按键的时间状态变量初始化8位
u8 LastState = 0; //8位变量,b0=1 则表示key0上一次按下过
//========================================================================
// 函数名称:KEY_Deal
// 函数功能:按键状态的获取
// 入口参数:无
// 函数返回:无
// 当前版本: VER1.0
// 修改日期: 2023-1-1
// 当前作者:
// 其他备注:循环读取8个端口的状态,并将按下的时间赋值给Count数组,然后按下的状态赋值给变量LastState
//========================================================================
void KEY_Deal(void) //检查所有的按键状态,10ms执行一次
{
u8 i = 0;
for(i=0;i<8;i++) //for循环变量 循环8次,i取值为0-7,代表P30-P37的状态查询
{
if(~KEY & (1< 0 ) //如果这个按键是按下过的,
{
LastState |= (1< 0) //判断按键是按下的
{
if(Count[keynum] < 3) //按下小于30ms,返回消抖状态
{
return KEY_FLCKER;
}
else if(Count[keynum] == 3) //按正好等于30ms,返回单击状态
{
return KEY_RESS;
}
else if(Count[keynum] < 300 ) //按下小于3000ms,返回单击结束
{
return KEY_PRESSOVER;
}
else if(Count[keynum] == 300 ) //按下正好等于3000ms,返回长按
{
return KEY_LONGPRESS;
}
else //长按结束
{
return KEY_LONGOVER;
}
}
else //按键已经松开了,返回KEY_RELAX状态
{
if(LastState &(1<
KEY.H:
#ifndef __KEY_H
#define __KEY_H
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
//------------------------引脚定义------------------------//
#define KEY P3 //定义一个按键 引脚选择P32-P36
#define KEY1 2 //按键1
#define KEY2 3 //按键2
#define KEY3 4 //按键3
#define KEY4 5 //按键4
//------------------------变量声明------------------------//
//状态 功能
#define KEY_NOPRESS 0 //按键未按下 0
#define KEY_FLCKER 1 //消抖 1
#define KEY_RESS 2 //单击 2
#define KEY_PRESSOVER 3 //单击结束 3
#define KEY_LONGPRESS 4 //长按3s 4
#define KEY_LONGOVER 5 //长按结束 5
#define KEY_RELAX 6 //按键松开 6
//------------------------函数声明-----------------------//
void KEY_Deal(void); //检查所有的按键状态
u8 KEY_ReadState(u8 keynum); //读取指定按键的状态
#endif
先在BEEP文件夹(\9.TIM多任务\HARDWARE\BEEP)中新建文件:beep.c和beep.h
beep.c:
#include "beep.h"
u16 Time_Beep; //如果这个变量大于0,蜂鸣器打开,等于0关闭,每10ms这个数值减1
//========================================================================
// 函数名称:BEEP_RUN
// 函数功能:蜂鸣器运行函数,可以独立运行
// 入口参数:无
// 函数返回:无
// 当前版本: VER1.0
// 修改日期: 2023-1-2
// 当前作者:
// 其他备注:10ms执行一次,保证每次蜂鸣10ms
//========================================================================
void BEEP_RUN(void)
{
if(Time_Beep > 0) //如果这个变量大于0
{
BEEP = 0; //打开蜂鸣器
Time_Beep--; //计时变量减1
}
else //如果这个变量等于0
BEEP = 1; //关闭蜂鸣器
}
//========================================================================
// 函数名称:BEEP_ON
// 函数功能:设置蜂鸣器响多少个10ms
// 入口参数: @time; 多少个10ms
// 函数返回: 无
// 当前版本: VER1.0
// 修改日期: 2023-1-2
// 当前作者:
// 其他备注:
//========================================================================
void BEEP_ON(u16 time)
{
Time_Beep = time;
}
//========================================================================
// 函数名称:BEEP_OFF
// 函数功能:关闭蜂鸣
// 入口参数: 无
// 函数返回: 无
// 当前版本: VER1.0
// 修改日期: 2023-1-2
// 当前作者:
// 其他备注:
//========================================================================
void BEEP_OFF(void)
{
Time_Beep = 0;
}
beep.h:
#ifndef __BEEP_H
#define __BEEP_H
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
//------------------------引脚定义------------------------//
#define BEEP P54 //定义一个蜂鸣器 引脚选择P54
//------------------------函数声明-----------------------//
void BEEP_RUN(void);
void BEEP_ON(u16 time);
void BEEP_OFF(void);
#endif
demo.c中增加beep执行代码:
while(1) //死循环
{
// if( DeviceState != DEVSTATE_CONFIGURED ) //
// continue;
if( bUsbOutReady )
{
usb_OUT_done();
}
delay_ms(10);
KEY_Deal(); //P3上所有端口都需要执行一遍
BEEP_RUN();
if(KEY_ReadState(KEY1)== KEY_RESS) //判断KEY1按钮是否为单击
{
BEEP_ON(2); //蜂鸣20ms
LED0 = 0;
}
else if(KEY_ReadState(KEY1)== KEY_LONGPRESS) //判断KEY1按钮是否为长按
{
BEEP_ON(2); //蜂鸣20ms
LED1 = 0;
}
else if(KEY_ReadState(KEY1)== KEY_RELAX) //判断KEY1按钮是否为松开
{
LED = 0XFF;
}
}
先在TIM文件夹(\9.TIM多任务\HARDWARE\TIM)中新建文件:tim0.c和tim0.h,增加头文件引用路径。
tim0.h:
#ifndef __TIM0_H
#define __TIM0_H
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
//------------------------函数声明-----------------------//
void Timer0_Init(void); //定时器初始化函数
#endif
将demo.c中的Timer0_Init移植至tim0.c:
#include "tim0.h"
void Timer0_Init(void) //1毫秒@24.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x30; //设置定时初始值
TH0 = 0xF8; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
/* //复制这个文件的时候,记得把这个中断函数复制到主程序
//这个是属于用户型的一个文件(用户需要在里面编写自己的功能),建议将其放在主程序main函数之后,方便更好的引用
void Timer0_Isr(void) interrupt 1 //1ms进来执行一次,无需其他延时,重复赋值
{
}
*/
利用void Timer0_Isr(void);中断实现10ms延时功能。
先设置10ms标志位:bit TIM_10MS_Flag; //10ms标志位
在Timer0_Isr中实现延时:
void Timer0_Isr(void) interrupt 1 //1ms进来执行一次,无需其他延时,重复赋值
{
static timecount = 0;
SEG_LED_Show(); //数码管刷新
timecount++; //1ms+1
if(timecount>=10) //如果这个变量大于等于10,说明10ms到达
{
TIM_10MS_Flag = 1; //10ms到了
}
}
用以下格式进行调用:
if(TIM_10MS_Flag == 1) //将需要延时的代码部分放入
{
TIM_10MS_Flag = 0; //TIM_10MS_Flag 变量清空置位
//待延时程序部分
}
编译下载后,观察功能,时间好像不太对,第二个灯亮的非常快。
分析一下原因:缺少了timecount = 0;变量的清空,方便下一次从0数到10;说明10ms已经到达。
重新编译下载,功能正常。
后续工程可以直接拷贝.c和.h文件,实现相应的功能,并且KEY的功能是可以扩展的,在main函数中,按需要进行定义。
TIM_10MS_Flag变量,代表10ms延时,假设现在这里有8个LED灯(LED0-LED7),想要每200个毫秒点亮一个灯,或者每300ms
或者每400ms每个灯取反。
1.学会模块化的编写程序
1.LED0给他200ms取反一次,LED1给他400ms取反一次,LED2给他800ms取反一次
2.将之前的电磁炉程序用今天的框架重新改写一遍,并且加入数码管定时的功能。