• 学习笔记|回顾(1-12节课)|应用模块化的编程|添加函数头|静态变量static|STC32G单片机视频开发教程(冲哥)|阶段小结:应用模块化的编程(上)


    1.回顾(1-12节课)

    一、认识单片机
    二、了解单片机硬件(介绍开发板)
    三、开发环境搭建和下载,新工程建立资料下载
    四、点亮点一个LED(CDC和HID下载)		--GPIO
    五、C语言运算符和进制数入门
    六、LED闪烁和花式点灯       			--GPIO
    七、按键点亮灯					--GPIO
    八、蜂鸣器					--GPIO
    九、数码管的静态使用				--GPIO
    十、数码管动态点亮 				--GPIO
    十一、定时器					--TIM
    十二、计数器的使用				--TIM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    重点理清程序的逻辑思路。重点理清程序的逻辑思路。重点理清程序的逻辑思路。什么时候打开LED?LED打开多久?什么时候切换数码管显示?什么时候按键按下触发什么功能?
    之前的课主要是带大家熟悉写程序的方法,分析逻辑,实现我们要的功能。
    那么这一节课开始,我们要规范程序,符合我们工程师级别的代码规范

    2.应用模块化的编程(.c + .h)

    一、LED & 数码管 --led_seg.c, led_seg.h
    二、按键 --key.c, key.h
    三、蜂鸣器 --beep.c, beep.h
    四、定时器 --tim.c, tim.h
    一个功能对应一个.c和.h

    Tips:添加函数头

    //========================================================================
    // 函数名称:
    // 函数功能:
    // 入口参数: @
    // 函数返回:
    // 当前版本: VER1.0
    // 修改日期: 2023
    // 当前作者:
    // 其他备注:
    //========================================================================
    在keil中设置:
    在这里插入图片描述

    在这里插入图片描述
    添加后,重启软件就可以使用了。

    创建程序文件三步

    新建文件并保存
    添加到工程
    添加引用路径
    
    • 1
    • 2
    • 3

    引脚定义都在.h文件

    sbit 名称 = P10;
    #define 名称 P10
    
    • 1
    • 2

    函数定义三步

    定义
    声明
    调用
    
    • 1
    • 2
    • 3

    修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。
    举例1:如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。
    举例2:如果文件a.c需要引用b.c中变量int v,就可以在b.h中声明extern int v,然后a.c调用b.h就可以引用变量v。
    注意extern修饰的变量不能赋初值。
    详细介绍可参考:extern关键字,C语言extern关键字用法详解

    bdata位寻址变量的使用

    a.c a.h
    u8 bdata LED = 0x00; extern u8 bdata LED;
    sbit LED0 = LED^0; extern bit LED0;
    sbit LED1 = LED^1; extern bit LED1;

    3.工程文件编写

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    因为他们公用P6端口,所以给他们放在了一个文件。

    之前的程序是在定时器中通过一个函数刷新数码管,现在就要给他增加刷新LED的功能。这里刷新显示,在别的地方赋值。
    利用上节的文件,复制成工程9.TIM多任务,进入,新建目录(路径)HARDWARE,所有子程序都放入,本次要写的4个功能分别建立4个文件夹,留着备用:
    在这里插入图片描述
    打开project,选择Add New ltem to Group ‘source Group 1’…
    在这里插入图片描述

    就可以在LED目录中新建seg_led.c和seg_led.h:
    在这里插入图片描述

    添加引用路径:
    在这里插入图片描述

    至此,工程文件模板就建立了。
    seg_led.h修改,添加infdef预定义:

    #ifndef __SEG_LED_H
    #define __SEG_LED_H
    
    #include "COMM/stc.h"		//调用头文件
    #include "COMM/usb.h"
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以将此预定义存为template,方便使用。
    在seg_led.c和demo.c中都增加对seg_led.h的调用。
    将demo.c中数码管的功能移植至seg_led.c中:

    u8 SEG_Tab[21] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0xff};	//共21个字节:0-9段码共10个,0-9带小数点共10个,全部不显示共1个,
    u8 COM_Tab[8] = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };																	//0-7的位码数组
    u8 Show_Tab[8] = {20,20,20,20,20,20,20,20};
    
    • 1
    • 2
    • 3

    静态变量static的使用

    如果要在其他文件中控制变量Show_Tab用于显示,则需要将其放在.h文件中作为外部变量:extern u8 Show_Tab[8];,之后就可以在主函数里调用。
    移植void SEG_Fre(void)函数至seg_led.c,并在函数体中增加static 变量,静态变量,只有第一次执行的时候再赋初始值,定义:static int num = 0;,后面不再赋值。
    如果不加static,每次进入函数后都会重新赋为0 ,不会刷新第2位以后的值,故需要使用静态变量,只赋值一次。
    需要在seg_led.h中声明一下:void SEG_Fre(void);
    删除或注释掉demo.c中原程序代码的计数器初始化部分:

    //	TMOD = 0x50;			//设置计数器模式   
    //	TL1 = 0x00;				//设置计数初始值
    //	TH1 = 0x00;				//设置计数初始值
    //	TF1 = 0;				//清除TF1标志
    //	TR1 = 1;				//定时器1开始计时
    //	ET1 = 1;				//使能定时器1中断
    //	    
    //	P3PU = 0x20; 			//打开内部上拉4.1K
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    删除主函数中冗余代码,留定时器0初始化。
    在主程序demo.c文件中加入数码管赋初值。

    	//数码管初始化,显示0-7:
    	Show_Tab[0] = 0;
    	Show_Tab[1] = 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在.h文件中增加引脚定义:

    //------------------------引脚定义------------------------//
    #define SEG_SEG P6
    #define SEG_COM P7
    #define LED_POW P40 	//P40是led的电源开关
    
    • 1
    • 2
    • 3
    • 4

    编译,选目标文件,下载完成,数码管显示0-7,但led还是不亮,需要加入相应点亮刷新代码。
    seg_led.h中定义LED的显示变量和电源开关:

    extern u8 LED_DATA;		//LED的显示变量
    
    #define LED_POW P40 	//P40是led的电源开关
    
    • 1
    • 2
    • 3

    增加引脚定义:
    seg_led.c中更新刷新函数SEG_Fre:

    void SEG_Fre( void )
    {
    	static int num = 0;
    
    	if(num <=7 )						//num==0-7的执行
    	{
    		LED_POW = 0;					//关闭LED电源
    		SEG_COM = COM_Tab[num];			//相应数码管位码的选择
    		SEG_SEG = SEG_Tab[Show_Tab[num]];//需要显示的数字的内码
    	}
    
    	else if ( num <= 8 )				//num==8的执行
    	{
    		LED_POW = 0;					//LED刷新,打开电源
    		SEG_COM = 0xff;					//关闭数码管
    		SEG_SEG = LED_DATA;				//输出LED状态
    	}
    	else								//num==9的执行
    	{
    		LED_POW = 1;					//LED关闭电源
    		SEG_COM = 0xff;					//关闭数码管
    		SEG_SEG = 0xff;					//关闭所有段码信号
    	}
    
    	num++;
    	if( num >=10 )
    		num = 0;						//num清零
    }
    
    • 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

    demo.c中对LED赋初值:

    LED_DATA = 0x0f;	//赋初值,亮一半灭一半
    
    • 1

    完整程序为:

    demo.c:

    #include "COMM/stc.h"		//调用头文件
    #include "COMM/usb.h"
    #include "seg_led.h"
    
    #define KEY1 P32		//定义一个按键 引脚选择P32
    #define KEY2 P33		//定义一个按键 引脚选择P33
    
    #define BEEP P54		//定义一个按键 引脚选择P54
    
    #define SEG_Delay  1	//延时多少ms
    
    #define MAIN_Fosc 24000000UL	//定义主时钟
    
    char *USER_DEVICEDESC = NULL;
    char *USER_PRODUCTDESC = NULL;
    char *USER_STCISPCMD = "@STCISP#";
    
    
    u32 TimCount = 0;		//计数单位1ms
    bit RUN_State = 0;		//开始运行/结束运行
    u8 num = 0;
    u16 Count_T1 = 0;
    
    void sys_init();	//函数声明
    //void delay_ms(u16 ms);	//unsigned int
    //void INT1_Isr(void);
    void Timer0_Isr(void);
    
    
    
    
    void main()					//程序开始运行的入口
    {
    
    	sys_init();				//USB功能+IO口初始化
    	usb_init();				//usb库初始化
    
    	EA = 1;					//CPU开放中断,打开总中断。
    
    	//数码管初始化,显示0-7:
    	Show_Tab[0] = 0;
    	Show_Tab[1] = 1;
    	Show_Tab[2] = 2;
    	Show_Tab[3] = 3;
    	Show_Tab[4] = 4;
    	Show_Tab[5] = 5;
    	Show_Tab[6] = 6;
    	Show_Tab[0] = 7;
    
    	LED_DATA = 0x0f;	//赋初值,亮一半灭一半
    
    	while(1)		//死循环
    	{
    		if( DeviceState != DEVSTATE_CONFIGURED ) 	//
    			continue;
    		if( bUsbOutReady )
    		{
    			usb_OUT_done();
    
    		}
    
    	}
    }
    
    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_Isr(void) interrupt 1 //1ms进来执行一次,无需其他延时,重复赋值
    {
    	SEG_Fre();		//数码管刷新1ms执行一次
    
    }
    
    //void INT1_Isr(void) interrupt 3 //计时器中断
    //{
    //	//P60 = !P60; //led取反
    //}
    
    • 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

    seg_led.c:

    #include "seg_led.h"
    
    u8 SEG_Tab[21] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0xff};	//共21个字节:0-9段码共10个,0-9带小数点共10个,全部不显示共1个,
    u8 COM_Tab[8]  = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };																	//0-7的位码数组
    u8 Show_Tab[8] = {20,20,20,20,20,20,20,20}; 																					//数码管显示数组,上电后,全部关闭显示
    //u8 LED_DATA  =  0xff;																											//LED初始全部熄灭
    
    void SEG_Fre( void )
    {
    	static int num = 0;
    
    	if(num <=7 )						//num==0-7的执行
    	{
    		LED_POW = 0;					//关闭LED电源
    		SEG_COM = COM_Tab[num];			//相应数码管位码的选择
    		SEG_SEG = SEG_Tab[Show_Tab[num]];//需要显示的数字的内码
    	}
    
    	else if ( num <= 8 )				//num==8的执行
    	{
    		LED_POW = 0;					//LED刷新,打开电源
    		SEG_COM = 0xff;					//关闭数码管
    		SEG_SEG = LED_DATA;				//输出LED状态
    	}
    	else								//num==9的执行
    	{
    		LED_POW = 1;					//LED关闭电源
    		SEG_COM = 0xff;					//关闭数码管
    		SEG_SEG = 0xff;					//关闭所有段码信号
    	}
    
    	num++;
    	if( num >=10 )
    		num = 0;						//num清零
    }
    
    • 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

    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 LED_DATA;		//LED的显示变量
    
    
    //------------------------引脚定义------------------------//
    #define SEG_SEG P6
    #define SEG_COM P7
    #define LED_POW P40 	//P40是led的电源开关
    
    void SEG_Fre(void);  		//仅声明
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    R中的min()函数 和max()函数
    第一个Shader程序
    英语语音篇 - 自然拼读
    【高速数字化仪应用案例系列】虹科数字化仪在大型物理实验领域的应用
    第三章 神经网络——什么是神经网路&激活函数&3层神经网络的简单实现&手写数字识别
    Android学习之路(18) 数据存储与访问
    全栈性能测试详解
    程序开机自启动(基于linux)
    Python工程化管理:package包层次结构组织以及module模块详解
    周星驰亲自下场招人 Web3.0究竟是什么?
  • 原文地址:https://blog.csdn.net/Medlar_CN/article/details/132733915