0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于STC8A8K64S4A12开发板的GPIO点灯实验

CHANBAEK 来源:百家号电子友人号 作者:百家号电子友人号 2023-11-15 16:52 次阅读

前言

最近空闲时间比较多,准备说说STC8A8K64S4A12开发板。 实验做起来——先从GPIO点灯开始。

一、硬件电路原理

1.开发板指示灯硬件电路

LED(Light Emitting Diode)是发光二极管的简称,在很多设备上常用它来做为一种简单的人机接口,如网卡、路由器等通过LED向用户指示设备的不同工作状态。所以,我们习惯把这种用于指示状态的LED称为LED指示灯
STC8A8K64S4A12开发板上设计了4个LED指示灯,我们可以通过编程驱动LED指示灯点亮、熄灭、闪烁,从而达到状态指示的目的,LED指示灯驱动电路如下图所示。

图1:LED指示灯驱动电路

LED指示灯驱动电路是一个很常见、简单的电路,同时它也是一个典型的单元电路,对于初学者来说,类似常用的典型电路必须要掌握,不但要知其然、还要知其所以然。
接下来,我们来分析一下这个简单的LED指示灯驱动电路。
LED驱动电路设计的时候,要考虑两个方面:控制方式和限流电阻的选取。

2.控制方式

LED指示灯控制方式分为高电平有效和低电平有效两种,高电平有效是单片机GPIO输出高电平时点亮LED,低电平有效是单片机GPIO输出低电平时点亮LED。

2.1.低电平有效的控制方式

LED控制-低电平有效原理

低电平有效控制方式中,当单片机的GPIO输出低电平(逻辑0)的时候,LED和电阻R上的压降等于(VCC-VCCIO = 3.3V),这时候,因为存在压降,同时,这个电路是闭合回路,这就达到了电流产生的两个要素,LED上会有电流流过,LED被点亮。
当单片机的GPIO输出高电平(逻辑1)的时候,LED和电阻R上的压降等于(VCC-VCCIO = 0V),这时候,因为LED上没有压降,当然不会有电流流过,所以LED熄灭。

2.2.高电平有效的控制方式

图3:LED控制-高电平有效原理

高电平有效控制方式中,由单片机的GPIO输出电流驱动LED,当单片机的GPIO输出高电平(逻辑1)的时候,LED上存在压降,因为电路是闭合回路,所以会有电流流过,这时LED被点亮,但要注意,单片机的GPIO要能提供足够的输出电流,否则,电流过小,会导致LED亮度很弱。
当单片机的GPIO输出低电平(逻辑0)的时候,LED和电阻R上的压降等于0V,这时候,LED上没有电流流过,LED熄灭。

2.3.选择哪种方式来控制LED

绝大多数情况下,我们会选择使用低电平有效的控制方式,如艾克姆科技STC8A8K64S4A12开发板中的LED指示灯就是低电平有效。这样设计指示灯驱动电路的好处是:

① 单片机GPIO口低电平时的灌入电流一般比高电平时的拉电流要大,能提供足够的电流驱动LED。
② 单片机上电或复位启动时,GPIO口一般都是高阻输入,用低电平有效的控制方式可以确保LED在上电或复位启动时处于熄灭状态。

3.LED限流电阻的选取

3.1.限流电阻的计算

图4:LED限流电阻计算

由上图可以看出,LED限流电阻的计算公式如下:

其中,VCC=3.3V,VF是LED的正向压降,LED的数据手册都会给出正向电流为2mA时测试的VF的范围,下图是一款0805 LED的实物图和参数。在参数表中可以看到正向电流为2mA时VF最小值是2.5V,典型值是2.7V,最大值是3.6V。

图5:封装0805的LED

图6:0805 LED参数图

计算时VF的值可以用典型值来进行估算,对于电流,需要根据经验值和对LED亮度的要求相结合来确定,一般经验值是(1~5)mA,不过要注意,只要亮度符合自己的要求,电流低于1mA也没有任何问题。
电流为1mA时限流电阻值计算如下:

3.2.限流电阻的选择

根据上一节对限流电阻计算公式的描述及对选择的封装0805的指示灯参数的了解,计算出供电3.3V电流1mA时的限流电阻理论值是600Ω,供电5V电流1mA时的限流电阻理论值是2.3KΩ,为保证供电为3.3V时指示灯够亮但同时5V供电指示灯又不宜过亮,选择一个比较常见的阻值2K作为限流电阻。
还有一点需要强调的是,不同颜色的指示灯在即使同一亮度时所需的限流电阻不一定是相同的。这也是为什么有些产品的面板上有不同颜色的指示灯,各指示灯所使用的限流电阻不一样的原因。

4.STC8A8K64S4A12系列单片机GPIO口

STC8A8K64S4A12系列单片机GPIO口数量取决于芯片引脚的个数,芯片引脚个数和芯片封装密切相关。正常情况下,GPIO口数量是所选择单片机引脚个数减去5,因为单片机需要2个引脚作为供电引脚(电源正VCC、电源负GND),ADC外设会占用3个引脚(电源正ADC_Avcc、电源负ADC_Agnd、参考电压AVref)。

4.1.芯片封装

封装,Package,是把集成电路装配为芯片最终产品的过程,简单地说,就是把Foundry生产出来的集成电路裸片(Die)放在一块起到承载作用的基板上,把管脚引出来,然后固定包装成为一个整体。
STC8A8K64S4A12系列有多种封装,厂家批量常见的芯片封装是:LQFP44、LQFP48和LQFP64S。

4.2.GPIO口工作模式

STC8A8K64S4A12系列单片机所有GPIO口均有4种工作模式:准双向口/弱上拉(标准8051输出口模式)、推挽输出/强上拉、高阻输入(电流既不能流入也不能流出)、开漏输出。下面针对内部结构图进行分析。
■ 准双向口/弱上拉模式:

图7:GPIO准双向口/弱上拉模式内部框图

1)准双向口(弱上拉) 输出类型可用作输出和输入功能而不需要重新配置端口输出状态。这是因为准双向口有3个上拉晶体管可适应输入输出不同的需要。
2)手册中有这样一句话:准双向口(弱上拉)在读外部状态前,要先锁存为‘1’,才可读到外部正确的状态。下图分别就锁存数据为‘1’和‘0’时进行了分析。

图8:GPIO准双向口/弱上拉模式内部框图分析

3)由上图分析可知,准双向口(弱上拉)在读外部状态前,如果锁存为‘0’,则GPIO引脚状态被固定,无法读到外部正确的状态。

■ 推挽输出/强上拉模式:

图9:GPIO推挽输出/强上拉模式内部框图

1)强推挽输出配置的下拉结构与开漏输出以及准双向口的下拉结构相同,但当锁存器为1时可提供持续的强上拉。所以,推挽输出一般用于需要更大驱动电流的情况。
2)在控制LED时,如果采用的是高电平有效的控制方式,则控制LED的单片机GPIO口必须配置成推挽输出/强上拉模式方可。
■ 高阻输入模式:

图10:GPIO高阻输入模式内部框图

1)因带有一个施密特触发输入以及一个干扰抑制电路,GPIO配置为高阻输入时,电流既不能流入也不能流出GPIO口。
2)在很多STC相关的文档中有说的高阻态即是GPIO口被设置了高阻输入模式。
■ 开漏输出模式:

图11:GPIO开漏输出模式内部框图

1)开漏模式既可以读外部状态也可以对外输出高电平或低电平。
2)如果要正确读外部状态或需要对外输出高电平时,需外加上拉电阻。

二、软件编写

1.GPIO寄存器汇集

STC8A8K64S4A12系列单片机提供了40个用于操作GPIO的寄存器,如下表所示:

表2:

2.寄存器解析

首先普及一个常用知识点:为什么说STC8A8K64S4A12系列单片机是8位单片机呢?这个8位指的是什么?
一般来说某个单片机或微处理器是几位,指的是“机器字长”。每个单片机或微处理器最基本的功能是算术逻辑运算,而算术逻辑运算的主要部件是“算术逻辑单元(ALU)”。机器字长即是指ALU的数据位宽,也就是指令能直接处理的二进制位数。
通常单片机或微处理器的寄存器的位宽等于ALU的位宽,所以一般可通过识别单片机或微处理器寄存器的位宽来确定该单片机或微处理器是多少位的。我们所接触的STC的单片机,其寄存器都是8位的,所以STC的单片机都是8位的单片机。以后学习STM32F103系列的微处理器,其寄存器是32位的,所以会说STM32F103系列微处理器是32位的。

2.1.端口数据寄存器

下图是对端口数据寄存器P0、P1、P2、P3、P4、P5、P6、P7的描述,端口数据寄存器各位代表对应端口的IO口,比如,P7端口数据寄存器B0位代表P7.0口,B7位代表P7.7口。

图12:端口数据寄存器

2.2.端口配置寄存器

端口配置寄存器PnM1和PnM0都是8位的寄存器,PnM1和PnM0寄存器必须组合使用才能正确地配置IO口工作模式。
STC8A8K64S4A12系列单片机所有GPIO口均有4种工作模式:准双向口/弱上拉(标准8051输出口模式)、推挽输出/强上拉、高阻输入(电流既不能流入也不能流出)、开漏输出。每个GPIO口工作模式由PnM1和PnM0寄存器中的相应位控制。如下图。

图13:GPIO端口配置

3.GPIO驱动LED实验(寄存器版本)

3.1.头文件引用和路径设置

■ 需要宏定义部分及引用的头文件
因为在“main.c”文件中使用了STC8的头文件“STC8.H”,所以需要引用下面的头文件。在头文件“STC8.H”中需要确定主时钟取值,所以宏定义主时钟值。

1.#define  uint16   unsigned int    
2.#define  uint8    unsigned char    

在程序设计中会用到定义变量的类型,为了定义变量方便,将较为复杂的“unsigned int”和“unsigned char ”进行了宏定义。

#define  uint16   unsigned int    
#define  uint8    unsigned char   

这样,再定义变量时可直接使用“uint16”和“uint8”来取代“unsigned int”和“unsigned char ”即可。

■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

图14:添加头文件包含路径

3.2.编写代码

首先介绍下毫秒级的延时函数。控制指示灯亮和灭需要中间有足够的间隔时间,这个间隔一般通过延时函数实现。微秒级的延时时间很难控制,这和主频大小及其精度有密切关系。但毫秒级的延时还是可以控制的,下面给出在11.0592MHZ下的毫秒延时函数,仅供参考。

代码清单:毫秒延时函数

1./************************************** 
2.功能描述:延时函数 
3.入口参数:uint16 x ,该值为1时,延时1ms 
4.返回值:无 
5.***************************************/  
6.void delay_ms(uint16 x)   
7.{    
8.    uint16 j,i;     
9.    for(j=0;j< x;j++)     
10.    {      
11.        for(i=0;i< 1580;i++);     
12.    }    
}  

在对端口数据寄存器介绍时我们简单介绍过控制单片机GPIO的过程。需要再说明的地方是关于两个关键字“sfr”和“sbit”。
sfr是Keil C51为能直接访问51内核单片机中的SFR而提供了一个关键词,其用法是:
1)sfrt 变量名=地址值。
sbit是定义特殊功能寄存器的位变量。其用法有三种:
1)sbit 位变量名=地址值。
2)sbit 位变量名=SFR名称^变量位地址值。
3)sbit 位变量名=SFR地址值^变量位地址值。

程序清单:头文件“STC8.H”定义P0端口部分

1.sfr P7          =   0Xf8;    
2.sbit P70        =   P7^0;  
3.sbit P71        =   P7^1;  
4.sbit P72        =   P7^2;  
5.sbit P73        =   P7^3;  
6.sbit P74        =   P7^4;  
7.sbit P75        =   P7^5;  
8.sbit P76        =   P7^6;  
9.sbit P77        =   P7^7; 
}  

然后,在主函数中先对P7.2口进行模式配置,针对P7.2口是被配置了准双向口,后主循环中将用户指示灯D3点亮,延时200ms,再熄灭,再延时200ms的过程,这样可观察到指示灯D3不停闪烁的现象。

代码清单:主函数

1.int main()  
2.{  
3.    P7M1 &= 0xFB;    P7M0 &= 0xFB;    //设置P7.2为准双向口  
4.    // P7M1 &= 0xFB;  P7M0 |= 0x04;      //设置P7.2为推挽输出
5.    // P7M1 |= 0x04;  P7M0 &= 0xFB;      //设置P7.2为高阻输入  
6.    //  P7M1 |= 0x04;  P7M0 |= 0x04;    //设置P7.2为开漏输出  
7.      
8.  while(1)  
9.  {  
10.        P72=0;        //控制P7.2端口输出低电平,点亮用户指示灯D3  
11.        delay_ms(200);  
12.        P72=1;        //控制P7.2端口输出高电平,熄灭用户指示灯D3  
13.        delay_ms(200);  
14.    }  
15.} 

4.GPIO驱动LED实验(库函数版本)

4.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

该GPIO.c是STC官方提供的有关GPIO配置的函数库。

4.2.头文件引用和路径设置

■ 需要引用的头文件
因为在“main.c”文件中使用了GPIO相关的库,所以需要引用下面的头文件。

1.#include    "GPIO.h"  

■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

图15:添加头文件包含路径

4.3.编写代码

首先在GPIO口初始化函数中调用库函数GPIO_Inilize完成P7.2口的工作模式配置,即配置P7.2为准双向口。

代码清单:GPIO口初始化函数

1./************************************** 
2.功能描述:GPIO口初始化 
3.入口参数:无 
4.返回值:无 
5.***************************************/  
6.void    GPIO_config(void)  
7.{  
8.    GPIO_InitTypeDef  GPIO_InitStructure;  
9.   
10.    //设置P7.2口工作模式  
11.    GPIO_InitStructure.Mode=GPIO_PullUp;      //配置P7.2口为准双向口  
12.      //GPIO_InitStructure.Mode=GPIO_OUT_PP;    //配置P7.2口为推挽输出(强上拉)  
13.      //GPIO_InitStructure.GPIO_HighZ;          //配置P7.2口为高阻输入  
14.      //GPIO_InitStructure.Mode=GPIO_OUT_OD;    //配置P7.2口为开漏输出  
15.    GPIO_InitStructure.Pin=GPIO_Pin_2;  
16.    GPIO_Inilize(GPIO_P7,&GPIO_InitStructure);  
17.   
18.}  

打开库函数GPIO_Inilize后会发现,配置P7.2口实际最终操作还是P0M0和P0M1寄存器。代码如下。

程序清单:头文件“GPIO.c”定义GPIO初始化库函数

1.//========================================================================  
2.// 函数: uint8    GPIO_Inilize(uint8 GPIO, GPIO_InitTypeDef *GPIOx)  
3.// 描述: 初始化IO口.  
4.// 参数: GPIOx: 结构参数,请参考gpio.h里的定义.  
5.// 返回: 成功返回0, 空操作返回1,错误返回2.  
6.//========================================================================  
7.uint8   GPIO_Inilize(uint8 GPIO, GPIO_InitTypeDef *GPIOx)  
8.{  
9.    if(GPIO > GPIO_P7)               return 1;   //空操作  
10.    if(GPIOx->Mode > GPIO_OUT_PP) return 2;   //错误  
11.  
12.    if(GPIO == GPIO_P0)  
13.    {  
14.        if(GPIOx->Mode == GPIO_PullUp)       P0M1 &= ~GPIOx->Pin, P0M0 &= ~GPIOx->Pin;  //上拉准双向口  
15.        if(GPIOx->Mode == GPIO_HighZ)          P0M1 |=  GPIOx->Pin,   P0M0 &= ~GPIOx->Pin;  //浮空输入  
16.        if(GPIOx->Mode == GPIO_OUT_OD)       P0M1 |=  GPIOx->Pin, P0M0 |=  GPIOx->Pin;  //开漏输出  
17.        if(GPIOx->Mode == GPIO_OUT_PP)       P0M1 &= ~GPIOx->Pin, P0M0 |=  GPIOx->Pin;  //推挽输出  
18.    }  
19.    if(GPIO == GPIO_P1)  
20.    {  
21.        if(GPIOx->Mode == GPIO_PullUp)       P1M1 &= ~GPIOx->Pin, P1M0 &= ~GPIOx->Pin;  //上拉准双向口  
22.        if(GPIOx->Mode == GPIO_HighZ)          P1M1 |=  GPIOx->Pin,   P1M0 &= ~GPIOx->Pin;  //浮空输入  
23.        if(GPIOx->Mode == GPIO_OUT_OD)       P1M1 |=  GPIOx->Pin, P1M0 |=  GPIOx->Pin;  //开漏输出  
24.        if(GPIOx->Mode == GPIO_OUT_PP)       P1M1 &= ~GPIOx->Pin, P1M0 |=  GPIOx->Pin;  //推挽输出  
25.    }  
26.    if(GPIO == GPIO_P2)  
27.    {  
28.        if(GPIOx->Mode == GPIO_PullUp)       P2M1 &= ~GPIOx->Pin, P2M0 &= ~GPIOx->Pin;  //上拉准双向口  
29.        if(GPIOx->Mode == GPIO_HighZ)          P2M1 |=  GPIOx->Pin,   P2M0 &= ~GPIOx->Pin;  //浮空输入  
30.        if(GPIOx->Mode == GPIO_OUT_OD)       P2M1 |=  GPIOx->Pin, P2M0 |=  GPIOx->Pin;  //开漏输出  
31.        if(GPIOx->Mode == GPIO_OUT_PP)       P2M1 &= ~GPIOx->Pin, P2M0 |=  GPIOx->Pin;  //推挽输出  
32.    }  
33.    if(GPIO == GPIO_P3)  
34.    {  
35.        if(GPIOx->Mode == GPIO_PullUp)       P3M1 &= ~GPIOx->Pin, P3M0 &= ~GPIOx->Pin;  //上拉准双向口  
36.        if(GPIOx->Mode == GPIO_HighZ)          P3M1 |=  GPIOx->Pin,   P3M0 &= ~GPIOx->Pin;  //浮空输入  
37.        if(GPIOx->Mode == GPIO_OUT_OD)       P3M1 |=  GPIOx->Pin, P3M0 |=  GPIOx->Pin;  //开漏输出  
38.        if(GPIOx->Mode == GPIO_OUT_PP)       P3M1 &= ~GPIOx->Pin, P3M0 |=  GPIOx->Pin;  //推挽输出  
39.    }  
40.    if(GPIO == GPIO_P4)  
41.    {  
42.        if(GPIOx->Mode == GPIO_PullUp)       P4M1 &= ~GPIOx->Pin, P4M0 &= ~GPIOx->Pin;  //上拉准双向口  
43.        if(GPIOx->Mode == GPIO_HighZ)          P4M1 |=  GPIOx->Pin,   P4M0 &= ~GPIOx->Pin;  //浮空输入  
44.        if(GPIOx->Mode == GPIO_OUT_OD)       P4M1 |=  GPIOx->Pin, P4M0 |=  GPIOx->Pin;  //开漏输出  
45.        if(GPIOx->Mode == GPIO_OUT_PP)       P4M1 &= ~GPIOx->Pin, P4M0 |=  GPIOx->Pin;  //推挽输出  
46.    }  
47.    if(GPIO == GPIO_P5)  
48.    {  
49.        if(GPIOx->Mode == GPIO_PullUp)       P5M1 &= ~GPIOx->Pin, P5M0 &= ~GPIOx->Pin;  //上拉准双向口  
50.        if(GPIOx->Mode == GPIO_HighZ)          P5M1 |=  GPIOx->Pin,   P5M0 &= ~GPIOx->Pin;  //浮空输入  
51.        if(GPIOx->Mode == GPIO_OUT_OD)       P5M1 |=  GPIOx->Pin, P5M0 |=  GPIOx->Pin;  //开漏输出  
52.        if(GPIOx->Mode == GPIO_OUT_PP)       P5M1 &= ~GPIOx->Pin, P5M0 |=  GPIOx->Pin;  //推挽输出  
53.    }  
54.    if(GPIO == GPIO_P6)  
55.    {  
56.        if(GPIOx->Mode == GPIO_PullUp)       P6M1 &= ~GPIOx->Pin, P6M0 &= ~GPIOx->Pin;  //上拉准双向口  
57.        if(GPIOx->Mode == GPIO_HighZ)          P6M1 |=  GPIOx->Pin,   P6M0 &= ~GPIOx->Pin;  //浮空输入  
58.        if(GPIOx->Mode == GPIO_OUT_OD)       P6M1 |=  GPIOx->Pin, P6M0 |=  GPIOx->Pin;  //开漏输出  
59.        if(GPIOx->Mode == GPIO_OUT_PP)       P6M1 &= ~GPIOx->Pin, P6M0 |=  GPIOx->Pin;  //推挽输出  
60.    }  
61.    if(GPIO == GPIO_P7)  
62.    {  
63.        if(GPIOx->Mode == GPIO_PullUp)       P7M1 &= ~GPIOx->Pin, P7M0 &= ~GPIOx->Pin;  //上拉准双向口  
64.        if(GPIOx->Mode == GPIO_HighZ)          P7M1 |=  GPIOx->Pin,   P7M0 &= ~GPIOx->Pin;  //浮空输入  
65.        if(GPIOx->Mode == GPIO_OUT_OD)       P7M1 |=  GPIOx->Pin, P7M0 |=  GPIOx->Pin;  //开漏输出  
66.        if(GPIOx->Mode == GPIO_OUT_PP)       P7M1 &= ~GPIOx->Pin, P7M0 |=  GPIOx->Pin;  //推挽输出  
67.    }  
68.    return 0;   //成功  
}  

然后,在主函数中先调用GPIO口初始化函数,后主循环中将用户指示灯D3点亮,延时500ms,再熄灭,再延时500ms的过程,这样可观察到指示灯D3不停闪烁的现象。

代码清单:主函数

1.int main()  
2.{  
3.  GPIO_config();   //设置P7.2口为准双向口  
4.      
5.  while(1)  
6.  {  
7.        P72=0;        //控制P7.2端口输出低电平,点亮用户指示灯D3  
8.        delay_ms(500);  
9.        P72=1;        //控制P7.2端口输出高电平,熄灭用户指示灯D3  
10.        delay_ms(500);  
11.    }  
12.} 

5.流水灯实验(单个c文件)

5.1.头文件引用和路径设置

本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-1-1:GPIO驱动LED(寄存器版本)”部分。
在程序设计中重新定义了P2寄存器的位变量P26和P27及P7寄存器的位变量P71和P72,这是为了控制GPIO口比较鲜明地知道其用途。

1./********************************************* 
2.引脚别名定义 
3.**********************************************/           
4.sbit LED_D1=P2^6;     //用户指示灯D1用IO口P26    
5.sbit LED_D2=P2^7;     //用户指示灯D2用IO口P27    
6.sbit LED_D3=P7^2;     //用户指示灯D3用IO口P72    
sbit LED_D4=P7^1;     //用户指示灯D4用IO口P71   

须知,语句“LED_D1=0; ”和语句“P26=0;” 效果是完全一样的;语句“LED_D1=1; ”和语句“P26=1;” 效果也是完全一样的。

5.2.编写代码

首先编写一个函数,该函数会控制4个用户LED分别依次点亮,代码如下。

程序清单:流水灯点亮函数

1./************************************************************************* 
2.功能描述:流水灯 
3.入口参数:无 
4.返回值:无 
5. ************************************************************************/  
6.void LED_Blink(void)  
7.{  
8.      LED_D1=0;        //点亮用户指示灯D1  
9.      LED_D2=1;        //熄灭用户指示灯D2  
10.      LED_D3=1;        //熄灭用户指示灯D3  
11.      LED_D4=1;        //熄灭用户指示灯D4  
12.      delay_ms(300);  
13.      LED_D1=1;        //熄灭用户指示灯D1  
14.      LED_D2=0;        //点亮用户指示灯D2  
15.      LED_D3=1;        //熄灭用户指示灯D3  
16.      LED_D4=1;        //熄灭用户指示灯D4  
17.      delay_ms(300);  
18.      LED_D1=1;        //熄灭用户指示灯D1  
19.      LED_D2=1;        //熄灭用户指示灯D2  
20.      LED_D3=0;        //点亮用户指示灯D3  
21.      LED_D4=1;        //熄灭用户指示灯D4  
22.      delay_ms(300);  
23.      LED_D1=1;        //熄灭用户指示灯D1  
24.      LED_D2=1;        //熄灭用户指示灯D2  
25.      LED_D3=1;        //熄灭用户指示灯D3  
26.      LED_D4=0;        //点亮用户指示灯D4  
27.      delay_ms(300);  
28.      LED_D1=1;        //熄灭用户指示灯D1  
29.      LED_D2=1;        //熄灭用户指示灯D2  
30.      LED_D3=1;        //熄灭用户指示灯D3  
31.      LED_D4=1;        //熄灭用户指示灯D4  
32.      delay_ms(300);  
33.}  

然后,在主函数中先对P2.6、P2.7、P7.1、P7.2口进行模式配置,后主循环中调用流水灯函数,这样可观察到指示灯D1、D2、D3、D4被流水点亮。

代码清单:主函数

1.int main(void)  
2.{  
3.    P2M1 &= 0x3F;   P2M0 &= 0x3F;     //设置P2.6~P2.7为准双向口  
4.    //P2M1 &= 0x3F; P2M0 |= 0xC0;   //设置P2.6~P2.7为推挽输出  
5.  //P2M1 |= 0xC0;   P2M0 &= 0x3F;   //设置P2.6~P2.7为高阻输入  
6.  //P2M1 |= 0xC0;   P2M0 |= 0xC0;   //设置P2.6~P2.7为开漏输出  
7.    P7M1 &= 0xF9;   P7M0 &= 0xF9;     //设置P7.1~P7.2为准双向口  
8.    //P7M1 &= 0xF9; P7M0 |= 0x06;   //设置P7.1~P7.2为推挽输出  
9.  //P7M1 |= 0x06;   P7M0 &= 0xF9;   //设置P7.1~P7.2为高阻输入  
10.  //P7M1 |= 0x06;   P7M0 |= 0x06;   //设置P7.1~P7.2为开漏输出  
11.      
12.  while(1)  
13.  {  
14.        LED_Blink();      //指示灯流水点亮  
15.    }  
16.}  

6.流水灯实验(多个c文件)

6.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

6.2.头文件引用和路径设置

■ 需要引用的头文件
因为在“main.c”文件中使用了控制led的函数和延时函数(延时函数没有在main.c中定义),所以需要引用下面的头文件。

1.#include    "led.h"  
2.#include "delay.h" 

■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:

MDK中点击魔术棒,打开工程配置窗口,添加头文件包含路径。

6.3.编写代码

首先在delay.c文件中编写两个延时函数delay_ms和Delay10us,delay_ms函数是毫秒延时,Delay10us函数是10微秒延时,代码如下。

程序清单:延时函数

1./************************************** 
2.功能描述:延时函数 
3.入口参数:uint16 x ,该值为1时,延时1ms 
4.返回值:无 
5.***************************************/  
6.void delay_ms(uint16 x)   
7.{    
8.    uint16 j,i;     
9.    for(j=0;j< x;j++)     
10.    {      
11.        for(i=0;i< 1580;i++);     
12.    }    
13.}

程序清单:延时函数

1./******************************************************************* 
2.功能描述:延时函数,延时约10us,在11.0592MHZ下 
3.入口参数:无 
4.返回值:无 
5.********************************************************************/  
6.void Delay10us(void)          
7.{  
8.    uint8 i;  
9.    _nop_();  
10.    i = 33;  
11.    while (--i);  
12.}  

该delay_ms函数会在delay.h头文件中被声明,这样可以被外部调用。如下。

1.extern void delay_ms(uint16 x);  
extern void Delay10us(void);  

然后在led.c文件中封装和4个LED有关的所有基本操作函数。如下表所示的5个函数。

LED基本操作函数程序清单如下:

程序清单:点亮一个指定的LED

1. /************************************************************************** 
2.功能描述:点亮一个指定的指示灯(D1、D2、D3、D4) 
3.入口参数:uint8 led_idx  (可取值LED_1、LED_2、LED_3、LED_4) 
4.返回值:无 
5. *************************************************************************/  
6.void led_on(uint8 led_idx)  
7.{  
8.  switch(led_idx)  
9.    {  
10.        case LED_1:  
11.      LED_D1=0;        //控制P2.6端口输出低电平,点亮用户指示灯D1  
12.          break;          
13.        case LED_2:  
14.      LED_D2=0;        //控制P2.7端口输出低电平,点亮用户指示灯D2  
15.          break;  
16.        case LED_3:  
17.      LED_D3=0;        //控制P7.2端口输出低电平,点亮用户指示灯D3  
18.          break;          
19.        case LED_4:  
20.      LED_D4=0;        //控制P7.1端口输出低电平,点亮用户指示灯D4  
21.          break;  
22.        default:  
23.          break;  
24.  }  
25.}  

程序清单:熄灭一个指定的LED

1./************************************************************************** 
2.功能描述:熄灭一个指定的指示灯(D1、D2、D3、D4) 
3.入口参数:uint8 led_idx  (可取值LED_1、LED_2、LED_3、LED_4) 
4.返回值:无 
5. *************************************************************************/  
6.void led_off(uint8 led_idx)  
7.{  
8.  switch(led_idx)  
9.    {  
10.        case LED_1:  
11.      LED_D1=1;        //控制P2.6端口输出高电平,熄灭用户指示灯D1  
12.          break;          
13.        case LED_2:  
14.      LED_D2=1;        //控制P2.7端口输出高电平,熄灭用户指示灯D2  
15.          break;  
16.        case LED_3:  
17.      LED_D3=1;        //控制P7.2端口输出高电平,熄灭用户指示灯D3  
18.          break;          
19.        case LED_4:  
20.      LED_D4=1;        //控制P7.1端口输出高电平,熄灭用户指示灯D4  
21.          break;  
22.        default:  
23.          break;  
24.  }  
25.}  

程序清单:翻转一个指定的LED的状态

1./************************************************************************** 
2.功能描述:翻转一个指定的指示灯(D1、D2、D3、D4) 
3.入口参数:uint8 led_idx  (可取值LED_1、LED_2、LED_3、LED_4) 
4.返回值:无 
5.*************************************************************************/  
6.void led_toggle(uint8 led_idx)  
7.{  
8.  switch(led_idx)  
9.    {  
10.        case LED_1:  
11.      LED_D1=~LED_D1;      //控制P2.6端口输出不同于上一次的电平,翻转用户指示灯D1  
12.          break;          
13.        case LED_2:  
14.      LED_D2=~LED_D2;    //控制P2.7端口输出不同于上一次的电平,翻转用户指示灯D2  
15.          break;  
16.        case LED_3:  
17.      LED_D3=~LED_D3;      //控制P7.2端口输出不同于上一次的电平,翻转用户指示灯D3  
18.          break;          
19.        case LED_4:  
20.      LED_D4=~LED_D4;    //控制P7.1端口输出不同于上一次的电平,翻转用户指示灯D4  
21.          break;  
22.        default:  
23.          break;  
24.  }  
25.}  

程序清单:同时点亮开发板上的4个指示灯

1./************************************************************************** 
2.功能描述:点亮开发板上的4个指示灯(D1、D2、D3、D4) 
3.入口参数:无 
4.返回值:无 
5. *************************************************************************/  
6.void leds_on(void)  
7.{  
8.      LED_D1=0;        //控制P2.6端口输出低电平,点亮用户指示灯D1  
9.      LED_D2=0;        //控制P2.7端口输出低电平,点亮用户指示灯D2  
10.      LED_D3=0;        //控制P7.2端口输出低电平,点亮用户指示灯D3  
11.      LED_D4=0;        //控制P7.1端口输出低电平,点亮用户指示灯D4  
12.}  

程序清单:同时熄灭开发板上的4个LED

1./************************************************************************** 
2.功能描述:熄灭开发板上的4个指示灯(D1、D2、D3、D4) 
3.入口参数:无 
4.返回值:无 
5. *************************************************************************/  
6.void leds_off(void)  
7.{  
8.      LED_D1=1;        //控制P2.6端口输出高电平,熄灭用户指示灯D1  
9.      LED_D2=1;        //控制P2.7端口输出高电平,熄灭用户指示灯D2  
10.      LED_D3=1;        //控制P7.2端口输出高电平,熄灭用户指示灯D3  
11.      LED_D4=1;        //控制P7.1端口输出高电平,熄灭用户指示灯D4  
12.} 

在led.c文件中还编写了一个流水灯点亮的函数LED_Blink,该函数调用LED的基本函数实现流水点亮4个用户指示灯的目的。代码如下。

程序清单:流水灯点亮函数

1./************************************************************************** 
2.功能描述:流水灯 
3.入口参数:无 
4.返回值:无 
5. *************************************************************************/  
6.void LED_Blink(void)  
7.{  
8.        leds_off();           //熄灭所有用户指示灯  
9.        led_on(LED_1);        //点亮用户指示灯D1  
10.        delay_ms(300);  
11.        leds_off();           //熄灭所有用户指示灯  
12.        led_on(LED_2);        //点亮用户指示灯D2  
13.        delay_ms(300);  
14.        leds_off();           //熄灭所有用户指示灯  
15.        led_on(LED_3);        //点亮用户指示灯D3  
16.        delay_ms(300);  
17.        leds_off();           //熄灭所有用户指示灯  
18.        led_on(LED_4);        //点亮用户指示灯D4  
19.        delay_ms(300);  
20.        leds_off();           //熄灭所有用户指示灯  
21.        delay_ms(300);  
}  

在led.h头文件中会声明可供外部调用的函数,LED_Blink函数便是其中之一。如下。

1.extern void led_on(uint8 led_idx);  
2.extern void led_off(uint8 led_idx);  
3.extern void led_toggle(uint8 led_idx);  
4.extern void leds_on(void);  
5.extern void leds_off(void);  
6.extern void LED_Blink(void);  

最后,在主函数中先对P2.6、P2.7、P7.1、P7.2口进行模式配置,后主循环中调用流水灯函数,这样可观察到指示灯D1、D2、D3、D4被流水点亮。

代码清单:主函数

1.int main(void)  
2.{  
3.    P2M1 &= 0x3F;   P2M0 &= 0x3F;     //设置P2.6~P2.7为准双向口  
4.    //P2M1 &= 0x3F; P2M0 |= 0xC0;   //设置P2.6~P2.7为推挽输出  
5.  //P2M1 |= 0xC0;   P2M0 &= 0x3F;   //设置P2.6~P2.7为高阻输入  
6.  //P2M1 |= 0xC0;   P2M0 |= 0xC0;   //设置P2.6~P2.7为开漏输出  
7.    P7M1 &= 0xF9;   P7M0 &= 0xF9;     //设置P7.1~P7.2为准双向口  
8.    //P7M1 &= 0xF9; P7M0 |= 0x06;   //设置P7.1~P7.2为推挽输出  
9.  //P7M1 |= 0x06;   P7M0 &= 0xF9;   //设置P7.1~P7.2为高阻输入  
10.  //P7M1 |= 0x06;   P7M0 |= 0x06;   //设置P7.1~P7.2为开漏输出  
11.  
12.  while(1)  
13.  {  
14.      LED_Blink();    //指示灯流水点亮  
15.    }  
16.} 

好啦!以上就是今天要讲的内容,当然期间也咨询了艾克姆科技的技术人员帮忙搞定的,希望对你有所帮助!

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • led
    led
    +关注

    关注

    237

    文章

    22449

    浏览量

    645878
  • 指示灯
    +关注

    关注

    3

    文章

    403

    浏览量

    27995
  • 开发板
    +关注

    关注

    25

    文章

    4429

    浏览量

    93996
  • 硬件电路
    +关注

    关注

    39

    文章

    233

    浏览量

    28988
  • GPIO
    +关注

    关注

    16

    文章

    1133

    浏览量

    50573
收藏 人收藏

    评论

    相关推荐

    请问哪位朋友有火龙电子FD51D_8STC8A8K64S4A12开发板)的资料呢?

    请问哪位朋友有火龙电子FD51D_8STC8A8K64S4A12开发板)的资料呢?如果方便的话请发邮箱252868106@qq.com非常感谢!
    发表于 12-19 13:03

    STC8A8K64S4A12串口通信

    宏晶 的51单片机STC8A8K64S4A12想用r422的串口通信方式,谁能发个原理图,r422芯片是TI的65HVD33。
    发表于 03-09 15:48

    关于STC8A8K64S4A12开发板外部中断问题

    买了一块 STC8A8K64S4A12 开发板,测试外部中断,感觉是不是芯片有些问题? 如图,图片左边是串口输出信息,按理来说 输出应该是 0 和 1 交替输出, 连续输出 1 或者 0 都是不对
    发表于 05-23 10:02

    STC8A8K64S4A12单片机VCC和GND内部短路了怎么解决?

    问题:STC8A8K64S4A12单片机VCC和GND内部短路了(也就是已经坏掉了),但我想不通为什么会出现这种问题?PCB电路是我自己画的,我只焊接了最小系统电路、LED灯电路和一个按钮,其余
    发表于 07-01 21:45

    STC8A8K64S4A12开发板学习资料

    STC8A8K64S4A12教学,板载资源丰富。具体参考下图: 此开发板将单片机STC8A8K64S4A12所有的外设资源都诠释到,关联到相应的模块电路或接口上。
    发表于 01-06 10:25

    STC8A8K64S4A12单片机实现的“基于脉冲宽度调制(PWM)技术的智能温度控制器”

    STC8A8K64S4A12单片机,这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一
    发表于 07-14 07:57

    浅析STC8A8K64S4A12开发板

    版权声明:本文为博主原创文章,转载请附上原文出处链接。文章目录前言一、STC8A8K64S4A12系列单片机介绍二、STC8A8K64S4A12开发板概述三、STC8A8K64S4A12
    发表于 07-19 09:24

    如何对基于STC8A8K64S4A12单片机的ADC进行检测

    STC8系列单片机有哪些优点?与ADC相关的寄存器有哪些?如何对基于STC8A8K64S4A12单片机的ADC进行检测?
    发表于 08-03 07:30

    如何去实现一种基于STC8A8K64S4A12开发板GPIO按键设计

    如何去实现一种基于STC8A8K64S4A12开发板GPIO按键设计?如何对基于STC8A8K64S4A12开发板
    发表于 08-19 07:24

    STC8A8K64S4A12静态数码管驱动的方法是什么

    STC8A8K64S4A12静态数码管驱动的方法是什么
    发表于 02-09 06:11

    基于STC8A8K64S4A12智能车设计资料分享

    智能车进阶版文章目录基于STC8A8K64S4A12智能车设计一、板载外设二、功能三、实物:四、代码五、PCB和元件六、焊接基于STC8A8K64S4A12智能车设计一、板载外设1.1 红外接收头
    发表于 02-16 06:43

    STC8A8K64S4A12的命名规则是什么

    STC8A8K64S4A12的命名规则STC8 -- STC8系列A -- 子系列中的A子系列8K
    发表于 02-18 07:32

    如何使用STC8A8K64S4A12单片机实现简单的LED灯闪烁

    前几日,使用STC8A8K64S4A12单片机写了一个简单的LED灯闪烁,是在库函数里直接写的,生成的HEX有100多K,下载进入板子,本来设置的1秒延时突然变的有20秒的样子,检查一天都没有结果
    发表于 02-18 07:17

    基于STC8A8K64S4A12单片机设计的数字温度计

    描述基于微控制器的数字温度计本作品是一款基于STC8A8K64S4A12单片机设计的数字温度计,按键按下时可通过数码管显示当前环境温度。1.单片机芯片STC8A8K64S4A12(C86615
    发表于 08-15 07:52

    STC8A8K64S4A12单片机怎么能掉电后保存数据?

    STC8A8K64S4A12单片机怎么才能做到掉电后把数据写到EEPROM通过哪些功能实现
    发表于 10-08 08:00