文章目录
1.回顾(1-12节课)
一、认识单片机
二、了解单片机硬件(介绍开发板)
三、开发环境搭建和下载,新工程建立资料下载
四、点亮点一个LED(CDC和HID下载) --GPIO
五、C语言运算符和进制数入门
六、LED闪烁和花式点灯 --GPIO
七、按键点亮灯 --GPIO
八、蜂鸣器 --GPIO
九、数码管的静态使用 --GPIO
十、数码管动态点亮 --GPIO
十一、定时器 --TIM
十二、计数器的使用 --TIM
重点理清程序的逻辑思路。重点理清程序的逻辑思路。重点理清程序的逻辑思路。什么时候打开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中设置:
添加后,重启软件就可以使用了。
创建程序文件三步
新建文件并保存
添加到工程
添加引用路径
引脚定义都在.h文件
sbit 名称 = P10;
#define 名称 P10
函数定义三步
定义
声明
调用
修饰符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
可以将此预定义存为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};
静态变量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
删除主函数中冗余代码,留定时器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;
在.h文件中增加引脚定义:
//------------------------引脚定义------------------------//
#define SEG_SEG P6
#define SEG_COM P7
#define LED_POW P40 //P40是led的电源开关
编译,选目标文件,下载完成,数码管显示0-7,但led还是不亮,需要加入相应点亮刷新代码。
seg_led.h中定义LED的显示变量和电源开关:
extern u8 LED_DATA; //LED的显示变量
#define LED_POW P40 //P40是led的电源开关
增加引脚定义:
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清零
}
demo.c中对LED赋初值:
LED_DATA = 0x0f; //赋初值,亮一半灭一半
完整程序为:
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取反
//}
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清零
}
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