大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“【15系列】SYK-0806-A2S1 工业自动化控制之【02-硬件分析&外设接线图】”,
相信大家对我们的这款板子已不再陌生,那么今天开始,笔者就要来给大家介绍如何使用STC15系列库函数来开发这款工控板,
这一篇文章,笔者以点灯为例,来教大家如何对SYK-0806-A2S1这款控制板进行输出控制,那么我们首先来看下板子的输出口的IO引脚分布:
从板子的硬件原理图上我们可知,SYK-0806-A2S1这款工控板的6个输出口Y00-Y05所使用的GPIO口分别是P32-P27口。
另外,在上一篇文章中笔者也跟大家介绍了,SYK-0806-A2S1这款工控板的输出口需要配置成“推挽输出”才能正确驱动MOS管,
因此我们在配置GPIO口模式的时候,需要注意这点。由于我们这款板子并不是用来教单片机基础的,
所以,有关单片机相关的操作笔者也就不作详细介绍了,笔者主要是针对这款开发板的应用来介绍!
有关单片机基础的相关知识,还请读者自行找资料学习!
首先,我们来看下这个点灯工程的构成:
笔者将这个点灯工程的软件部分分成了是4个Group,他们分别是:SYSTEM、USER、BSP和APP,其中:
SYSTEM 组中主要是存放了STC15系列库文件,这个项目中只用到了两个库,即:delay库和gpio库;
USER 里面存放了一个main.c文件;
BSP 组中存放的就是板级配置文件,此处只用到了GPIO输入输出的控制,所以这个组中也存放了一个bsp_gpio.c文件;
APP 组里面放了一个app.c文件。
对于STC的库文件,我们一般不需要做太多修改。比如gpio.c这个库文件中,就是一些对GPIO的配置,我们只需要直接调用这个文件即可:
- #include "GPIO.h"
-
- //========================================================================
- // 函数: u8 GPIO_Inilize(u8 GPIO, GPIO_InitTypeDef *GPIOx)
- // 描述: 初始化IO口.
- // 参数: GPIOx: 结构参数,请参考timer.h里的定义.
- // 返回: 成功返回0, 空操作返回1,错误返回2.
- // 版本: V1.0, 2012-10-22
- //========================================================================
- u8 GPIO_Inilize(u8 GPIO, GPIO_InitTypeDef *GPIOx)
- {
- if(GPIO > GPIO_P7) return 1; //空操作
- if(GPIOx->Mode > GPIO_OUT_PP) return 2; //错误
- if(GPIO == GPIO_P0)
- {
- if(GPIOx->Mode == GPIO_PullUp) P0M1 &= ~GPIOx->Pin, P0M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P0M1 |= GPIOx->Pin, P0M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P0M1 |= GPIOx->Pin, P0M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P0M1 &= ~GPIOx->Pin, P0M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P1)
- {
- if(GPIOx->Mode == GPIO_PullUp) P1M1 &= ~GPIOx->Pin, P1M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P1M1 |= GPIOx->Pin, P1M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P1M1 |= GPIOx->Pin, P1M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P1M1 &= ~GPIOx->Pin, P1M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P2)
- {
- if(GPIOx->Mode == GPIO_PullUp) P2M1 &= ~GPIOx->Pin, P2M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P2M1 |= GPIOx->Pin, P2M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P2M1 |= GPIOx->Pin, P2M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P2M1 &= ~GPIOx->Pin, P2M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P3)
- {
- if(GPIOx->Mode == GPIO_PullUp) P3M1 &= ~GPIOx->Pin, P3M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P3M1 |= GPIOx->Pin, P3M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P3M1 |= GPIOx->Pin, P3M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P3M1 &= ~GPIOx->Pin, P3M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P4)
- {
- if(GPIOx->Mode == GPIO_PullUp) P4M1 &= ~GPIOx->Pin, P4M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P4M1 |= GPIOx->Pin, P4M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P4M1 |= GPIOx->Pin, P4M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P4M1 &= ~GPIOx->Pin, P4M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P5)
- {
- if(GPIOx->Mode == GPIO_PullUp) P5M1 &= ~GPIOx->Pin, P5M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P5M1 |= GPIOx->Pin, P5M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P5M1 |= GPIOx->Pin, P5M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P5M1 &= ~GPIOx->Pin, P5M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P6)
- {
- if(GPIOx->Mode == GPIO_PullUp) P6M1 &= ~GPIOx->Pin, P6M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P6M1 |= GPIOx->Pin, P6M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P6M1 |= GPIOx->Pin, P6M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P6M1 &= ~GPIOx->Pin, P6M0 |= GPIOx->Pin; //推挽输出
- }
- if(GPIO == GPIO_P7)
- {
- if(GPIOx->Mode == GPIO_PullUp) P7M1 &= ~GPIOx->Pin, P7M0 &= ~GPIOx->Pin; //上拉准双向口
- if(GPIOx->Mode == GPIO_HighZ) P7M1 |= GPIOx->Pin, P7M0 &= ~GPIOx->Pin; //浮空输入
- if(GPIOx->Mode == GPIO_OUT_OD) P7M1 |= GPIOx->Pin, P7M0 |= GPIOx->Pin; //开漏输出
- if(GPIOx->Mode == GPIO_OUT_PP) P7M1 &= ~GPIOx->Pin, P7M0 |= GPIOx->Pin; //推挽输出
- }
- return 0; //成功
- }
复制代码
delay.c文件中void delay_ms(unsigned char ms)函数,因为这个函数的参数的数据类型使用的是unsigned char类型,所以,这里最大只支持1~255ms的延时,库文件中也有说明:
- #include "delay.h"
-
- //========================================================================
- // 函数: void delay_ms(unsigned char ms)
- // 描述: 延时函数。
- // 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2013-4-1
- // 备注:
- //========================================================================
- void delay_ms(unsigned char ms)
- {
- unsigned int i;
- do{
- i = MAIN_Fosc / 13000;
- while(--i) ; //14T per loop
- }while(--ms);
- }
复制代码
其中,MAIN_Fosc是系统晶振,这个大家需要根据自己板子上实际使用的晶振选择,程序中选择的晶振跟电路板上的一致,
定时才能比较准确。要修改晶振的话,是在config.h这个文件中,如下所示,本例中使用的是11.0592Mhz的晶振频率:
- #ifndef __CONFIG_H
- #define __CONFIG_H
-
- /*********************************************************/
-
- //#define MAIN_Fosc 22118400L //定义主时钟
- //#define MAIN_Fosc 12000000L //定义主时钟
- #define MAIN_Fosc 11059200L //定义主时钟
- //#define MAIN_Fosc 5529600L //定义主时钟
- //#define MAIN_Fosc 24000000L //定义主时钟
-
-
- /*********************************************************/
-
- #include "STC15Fxxxx.H"
-
- #endif
复制代码
接下来我们一起来看bsp_gpio.c文件,这个文件里面的内容不多,就是对板级输入、输出引脚的配置,代码如下:
- #include "bsp_gpio.h"
-
- /************************ IO口配置 ****************************/
- void GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure; //结构定义
-
- //Y00
- GPIO_InitStructure.Pin = Y00_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(Y00_GPIO,&GPIO_InitStructure); //初始化
-
- //Y01
- GPIO_InitStructure.Pin = Y01_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(Y01_GPIO,&GPIO_InitStructure); //初始化
-
- //Y02
- GPIO_InitStructure.Pin = Y02_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(Y02_GPIO,&GPIO_InitStructure); //初始化
-
- //Y03
- GPIO_InitStructure.Pin = Y03_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(Y03_GPIO,&GPIO_InitStructure); //初始化
-
- //Y04
- GPIO_InitStructure.Pin = Y04_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(Y04_GPIO,&GPIO_InitStructure); //初始化
-
- //Y05
- GPIO_InitStructure.Pin = Y05_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(Y05_GPIO,&GPIO_InitStructure); //初始化
-
- //X00
- GPIO_InitStructure.Pin = X00_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X00_GPIO,&GPIO_InitStructure); //初始化
-
- //X01
- GPIO_InitStructure.Pin = X01_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X01_GPIO,&GPIO_InitStructure); //初始化
-
- //X02
- GPIO_InitStructure.Pin = X02_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X02_GPIO,&GPIO_InitStructure); //初始化
-
- //X03
- GPIO_InitStructure.Pin = X03_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X03_GPIO,&GPIO_InitStructure); //初始化
-
- //X04
- GPIO_InitStructure.Pin = X04_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X04_GPIO,&GPIO_InitStructure); //初始化
-
- //X05
- GPIO_InitStructure.Pin = X05_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X05_GPIO,&GPIO_InitStructure); //初始化
-
- //X06
- GPIO_InitStructure.Pin = X06_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X06_GPIO,&GPIO_InitStructure); //初始化
-
- //X07
- GPIO_InitStructure.Pin = X07_GPIO_PIN; //指定要初始化的IO
- GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(X07_GPIO,&GPIO_InitStructure); //初始化
-
- Y00 = OutputT_OFF;
- Y01 = OutputT_OFF;
- Y02 = OutputT_OFF;
- Y03 = OutputT_OFF;
- Y04 = OutputT_OFF;
- Y05 = OutputT_OFF;
- }
复制代码
GPIO_config()这个函数主要实现了Y00-Y05引脚输出模式的配置,根据前一节介绍到硬件部分,这里需要设置成推挽模式,即:GPIO_OUT_PP。
另外,在将所有输出引脚配置成推挽模式之后,还需要将所有输出端引脚的初始电平拉低,否则所有的MOS管在一上电就会被导通了。
所以在这个函数的后面,还添加了:
- Y00 = OutputT_OFF;
- Y01 = OutputT_OFF;
- Y02 = OutputT_OFF;
- Y03 = OutputT_OFF;
- Y04 = OutputT_OFF;
- Y05 = OutputT_OFF;
复制代码
这六条语句。
细心的小伙伴们可能发现了,笔者这里用到了很多的宏定义,如:Y00_GPIO_PIN、Y00_GPIO等,
这些都是宏定义,这些宏定义是在bsp_gpio.h这个文件中:
- #ifndef __BSP_GPIO_H__
- #define __BSP_GPIO_H__
-
- #include "GPIO.h"
-
- /****************** 输出GPIO ******************/
- //Y00 - Y05
- #define Y00 P37
- #define Y00_GPIO_PIN GPIO_Pin_7
- #define Y00_GPIO GPIO_P3
-
- #define Y01 P36
- #define Y01_GPIO_PIN GPIO_Pin_6
- #define Y01_GPIO GPIO_P3
-
- #define Y02 P35
- #define Y02_GPIO_PIN GPIO_Pin_5
- #define Y02_GPIO GPIO_P3
-
- #define Y03 P34
- #define Y03_GPIO_PIN GPIO_Pin_4
- #define Y03_GPIO GPIO_P3
-
- #define Y04 P33
- #define Y04_GPIO_PIN GPIO_Pin_3
- #define Y04_GPIO GPIO_P3
-
- #define Y05 P32
- #define Y05_GPIO_PIN GPIO_Pin_2
- #define Y05_GPIO GPIO_P3
-
- //X00 - X07
- #define X00 P12
- #define X00_GPIO_PIN GPIO_Pin_2
- #define X00_GPIO GPIO_P1
-
- #define X01 P13
- #define X01_GPIO_PIN GPIO_Pin_3
- #define X01_GPIO GPIO_P1
-
- #define X02 P14
- #define X02_GPIO_PIN GPIO_Pin_4
- #define X02_GPIO GPIO_P1
-
- #define X03 P15
- #define X03_GPIO_PIN GPIO_Pin_5
- #define X03_GPIO GPIO_P1
-
- #define X04 P16
- #define X04_GPIO_PIN GPIO_Pin_6
- #define X04_GPIO GPIO_P1
-
- #define X05 P17
- #define X05_GPIO_PIN GPIO_Pin_7
- #define X05_GPIO GPIO_P1
-
- #define X06 P54
- #define X06_GPIO_PIN GPIO_Pin_4
- #define X06_GPIO GPIO_P5
-
- #define X07 P55
- #define X07_GPIO_PIN GPIO_Pin_5
- #define X07_GPIO GPIO_P5
-
- #define Input_ON 0 //输入ON
- #define Input_OFF 1 //输入OFF
-
- #define OutputT_ON 1 //晶体管输出ON
- #define OutputT_OFF 0 //晶体管输出OFF
-
- void GPIO_Config(void);
-
- #endif
复制代码
用宏定义虽然有点浪费程序空间,但是对于笔者使用的IAP15W413AS这个单片机来说,这点空间还是微不足道,
而使用宏定义的好处,自然却是不少,不仅让程序的可读性变得更加友好,同时对于以后的不同项目之间的移植也是提供了极大的方便,
所以,大家以后也可以多多尝试一下。
当然咯,如果大家需要更长时间的延时,可以自行修改一下这个函数,不过笔者建议,
如果需要更大延时和更精准的延时的话,还是使用定时器比较好一些,后续笔者也会给大家介绍。
接下来,我们就来看下main.c里面的代码:
- #include "delay.h"
- #include "app.h"
-
- void main(void)
- {
- Y00 = OutputT_OFF;
- Y01 = OutputT_OFF;
- Y02 = OutputT_OFF;
- Y03 = OutputT_OFF;
- Y04 = OutputT_OFF;
- Y05 = OutputT_OFF;
-
- delay_ms(250); //延时一段时间,等待电源稳定
-
- app_init();
-
- EA = 1; //开启总中断
-
- while(1)
- {
- app_run();
- }
- }
复制代码
在这里,细心的小伙伴们应该发现了一个问题,在主函数开始的部分,笔者将Y00-Y05全部设置为OFF了,这是为什么呢?
而前面的GPIO_config()函数中,我们也做了同样的操作,这样岂不是多此一举?
其实一开始笔者也没有这么做,但是发现,板子上电的瞬间,所有输出口都会全部闪一下然后又灭了!
于是,笔者去找STC的技术,技术告诉我,上电瞬间单片机的IO口是处于不稳定状态的,初始化完毕后,才会进入设定的模式运行,
而且MOS管是电压驱动,上电IO口浮空的时候的电压足以让MOS管导通了,所以上电后先将输出口全部置0再来进行相关配置,可以避免输出瞬间闪烁的现象!
main.c文件中主要有两个函数需要注意一下,一个是“app_init()”函数,一个是“app_run()”函数,其中,“app_init()”函数主要用于系统初始化,内容也很简单:
- void app_init(void)
- {
- GPIO_Config(); //GPIO配置
- }
复制代码
而“app_run()”这个函数主要就是实现了这个“流水灯”的效果了:
- void app_run(void)
- {
- Y00 = OutputT_ON;delay_ms(250);Y00 = OutputT_OFF;delay_ms(250);
- Y01 = OutputT_ON;delay_ms(250);Y01 = OutputT_OFF;delay_ms(250);
- Y02 = OutputT_ON;delay_ms(250);Y02 = OutputT_OFF;delay_ms(250);
- Y03 = OutputT_ON;delay_ms(250);Y03 = OutputT_OFF;delay_ms(250);
- Y04 = OutputT_ON;delay_ms(250);Y04 = OutputT_OFF;delay_ms(250);
- Y05 = OutputT_ON;delay_ms(250);Y05 = OutputT_OFF;delay_ms(250);
- }
复制代码
我们将代码编译没问题后,我们就可以将程序下载到板上,打开STC-ISP软件,按照如下设置并加载keil软件生产的hex文件,
然后点击“下载/编程”按钮准备下载(详细下载方法如有不明白的可以参考STC官方手册):
当出现“正在检测目标单片机”时,我们给板子上电即可开始下载,当出现“操作成功”字样后,说明程序已经成功下载到我们的工控板里面了:
按理说,下载完程序后,我们便会看到输出端的流水灯效果,然而,实际上我们并未看到这个效果,这是为什么呢?
我们仔细看下输出端的接线端子丝印,会发现,原来还有两个V+和V-:
那么这两个接线端子是用来干嘛的呢?我们再来仔细看下原理图:
从原理图上我们可以看到,原来,要是输出端LED灯亮的话,这里还需要接外部电源,
那么我们可以直接用电源适配器供电,然后将DC+、DC-端子分别接到V+和V-上即可:
实物连接图如下图所示:
线路连好之后,我们就可以看到程序能正常的运行了,我们想要的流水灯效果如下:
好了,关于使用延时函数实现流水灯的效果笔者就介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,
下一节笔者将给大家介绍如何利用输入来控制输出,详见“SYK-0806-A2S1 工业自动化控制之【04-输入输出点动控制】”感谢大家的支持!
本章附件:
【STC15系列】SYK-0806-A2S1- 03-延时函数演示流水灯.rar
(57.33 KB, 下载次数: 1)
|