• 学习笔记|回顾(1-12节课)|应用模块化的编程|分时复用|STC32G单片机视频开发教程(冲哥)|阶段小结:应用模块化的编程(下)


    搜索功能

    如果想排量修改SEG_Fre,可以使用搜索功能。
    在这里插入图片描述
    双击选中SEG_Fre,选查找替换,范围选择整个project,替换3处为:SEG_LED_Show()。
    在这里插入图片描述

    寻址变量bdata

    在头文件中已经定义了一个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引脚定义
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果需要在其他文件中调用,需要在头文件中声明,利用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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在头文件中增加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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    Tips:missing declaration specifiers错误的解决

    这里第一次编译的时候出现错误,提示

    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
    • 2
    • 3
    • 4

    仔细观察发现,
    错误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;			//
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    按键控制倒数第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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果要更换按键的端口,可以直接修改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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以避开while一直等待的状态。
    既然上面定义了返回值,那么需要编写一个有返回值的函数:u8 KEY_ReadState(u8 keynum); //读取指定按键的状态
    再编写一个检查所有的按键状态的函数: void KEY_Deal(void); //检查所有的按键状态

    key的取值示例

    在这里插入图片描述

    在key.c中实现,先将声明复制入key.c:

    void KEY_Deal(void) 			//检查所有的按键状态
    {
    
    }
    
    u8  KEY_ReadState(u8 keynum) 	//读取指定按键的状态
    {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对比功能需求,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循环变量
    	{
    
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假设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<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    判断按钮的状态,我们用户要读到的个按键状态,
    在这里插入图片描述
    首先函数的模板为:

    u8  KEY_ReadState(u8 keynum) 	//读取指定按键的状态
    {
    	if(Count[8] > 0)			//判断按键是按下的
    	{
    
    	}
    	else						//按键已经松开了
    	{
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    根据之前的逻辑表格,编写响应的函数判断代码:

    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<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    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;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    编译完成,下载,上面的数码管显示正常进行,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中断
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122

    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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

三、按键处理

先在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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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;
		}
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

四、定时器

先在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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

将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进来执行一次,无需其他延时,重复赋值
{


}
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

利用time0实现delay_ms:

利用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到了
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

用以下格式进行调用:

if(TIM_10MS_Flag == 1)   //将需要延时的代码部分放入
		{
			TIM_10MS_Flag = 0;		//TIM_10MS_Flag 变量清空置位
			//待延时程序部分
		}
  • 1
  • 2
  • 3
  • 4
  • 5

时间间隔有误的分析

编译下载后,观察功能,时间好像不太对,第二个灯亮的非常快。
分析一下原因:缺少了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.将之前的电磁炉程序用今天的框架重新改写一遍,并且加入数码管定时的功能。

  • 相关阅读:
    Python&服务器的常用指令合集
    .Net/C#分库分表高性能O(1)瀑布流分页
    排序-选择类排序
    亿道丨三防平板丨如何从多方面选择合适的三防加固平板?
    Sentinel注解@SentinelResource详解
    [Typescript]基础篇之元组
    MAC MINI 2012安装Montery折腾笔记
    《互联网大厂晋升指南》读书笔记-上
    [Docker]六.Docker自动部署nodejs以及golang项目
    C语言中的宏定义和内联函数有什么区别?
  • 原文地址:https://blog.csdn.net/Medlar_CN/article/details/132763939