GPIO应用实例(流水灯和蜂鸣器控制实现)
前言
前面一篇文章学习了GPIO功能以及配置过程,对GPIO有了大致的了解。本次是我对GPIO的应用实例学习,包括:流水灯控制、蜂鸣器控制、直流电机控制、步进电机控制。
我使用的是普中科技的DSP F28335开发板。如有芯片等不一样的,请参照自己芯片的原理图来编写代码。
一、流水灯控制
1、原理图
这个就是LED模块及每个LED灯对应的引脚。
在该模块中,由于LED发光二极管的电流单向流动特性,只有当每个LED灯对应的引脚模块为低电平时,LED灯才能发光。
下面看具体流水灯的实现步骤。
2、LED初始化函数
对于第一次编程来说,GPIO等一些简单的外设最好自己写.c和.h文件,为后面编程配置一些更复杂的外设打好基础。
(1)leds.c文件
结合上一篇文章学习的GPIO配置步骤,就很容易理解下面每个语句什么意思。
#include "leds.h"
void LED_Init(void)
{
EALLOW;
//LED1配置
GpioCtrlRegs.GPCMUX1.bit.GPIO68 = 0 ; //选择为通用数字I/O口
GpioCtrlRegs.GPCDIR.bit.GPIO68 = 1 ; //引脚为输出
GpioCtrlRegs.GPCPUD.bit.GPIO68 = 0; //禁止上拉
//LED2配置
GpioCtrlRegs.GPCMUX1.bit.GPIO67 = 0 ;
GpioCtrlRegs.GPCDIR.bit.GPIO67 = 1 ;
GpioCtrlRegs.GPCPUD.bit.GPIO67 = 0;
//LED3配置
GpioCtrlRegs.GPCMUX1.bit.GPIO66 = 0 ;
GpioCtrlRegs.GPCDIR.bit.GPIO66 = 1 ;
GpioCtrlRegs.GPCPUD.bit.GPIO66 = 0;
//LED4配置
GpioCtrlRegs.GPCMUX1.bit.GPIO65 = 0 ;
GpioCtrlRegs.GPCDIR.bit.GPIO65 = 1 ;
GpioCtrlRegs.GPCPUD.bit.GPIO65 = 0;
//LED5配置
GpioCtrlRegs.GPCMUX1.bit.GPIO64 = 0 ;
GpioCtrlRegs.GPCDIR.bit.GPIO64 = 1 ;
GpioCtrlRegs.GPCPUD.bit.GPIO64 = 0;
//LED6配置
GpioCtrlRegs.GPAMUX1.bit.GPIO10 = 0 ;
GpioCtrlRegs.GPADIR.bit.GPIO10 = 1 ;
GpioCtrlRegs.GPAPUD.bit.GPIO10 = 0;
//LED7配置
GpioCtrlRegs.GPAMUX1.bit.GPIO11 = 0 ;
GpioCtrlRegs.GPADIR.bit.GPIO11 = 1 ;
GpioCtrlRegs.GPAPUD.bit.GPIO11 = 0;
EDIS;
GpioDataRegs.GPCSET.bit.GPIO68 = 1 ; //设置初始电平为高电平
GpioDataRegs.GPCSET.bit.GPIO67 = 1 ;
GpioDataRegs.GPCSET.bit.GPIO66 = 1 ;
GpioDataRegs.GPCSET.bit.GPIO65 = 1 ;
GpioDataRegs.GPCSET.bit.GPIO64 = 1 ;
GpioDataRegs.GPASET.bit.GPIO10 = 1 ;
GpioDataRegs.GPASET.bit.GPIO11 = 1 ;
}
对于上拉电阻选项,作为输出时,感觉上拉电阻对电路影响不大,但一般配置为禁止上拉电阻。但是,如果GPIO作为输入时,就要根据外部电路来选择禁止/使能上拉电阻,影响比较大。在这里,因为我把所有控制LED的引脚都设为了输出,所以上拉电阻全部配置成了禁止上拉。(对于什么时候配置上拉电阻,我个人也很少遇到,理解不是很透彻。大家可以看这两篇文章https://blog.csdn.net/uiojhi/article/details/107678488以及https://blog.csdn.net/Ronnie_Hu/article/details/81514844,我觉得讲的还是比较好的)
(2)leds.h文件
leds.h文件用于存放leds.c的F28335头文件、 管脚定义、 全局变量声明、 函数声明等内容。
#ifndef _LEDS_H_
#define _LEDS_H_
#include "DSP2833x_Device.h" //F28335头文件
#include "DSP2833x_Examples.h" //F28335头文件
#define LED1_ON (GpioDataRegs.GPCCLEAR.bit.GPIO68 = 1) //点亮LED1宏定义
#define LED1_OFF (GpioDataRegs.GPCSET.bit.GPIO68 = 1) //熄灭LED1宏定义
#define LED1_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO68 = 1)//LED1电平翻转宏定义
#define LED2_ON (GpioDataRegs.GPCCLEAR.bit.GPIO67 = 1)
#define LED2_OFF (GpioDataRegs.GPCSET.bit.GPIO67 = 1)
#define LED2_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO67 = 1)
#define LED3_ON (GpioDataRegs.GPCCLEAR.bit.GPIO66 = 1)
#define LED3_OFF (GpioDataRegs.GPCSET.bit.GPIO66 = 1)
#define LED3_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO66 = 1)
#define LED4_ON (GpioDataRegs.GPCCLEAR.bit.GPIO65 = 1)
#define LED4_OFF (GpioDataRegs.GPCSET.bit.GPIO65 = 1)
#define LED4_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO65 = 1)
#define LED5_ON (GpioDataRegs.GPCCLEAR.bit.GPIO64 = 1)
#define LED5_OFF (GpioDataRegs.GPCSET.bit.GPIO64 = 1)
#define LED5_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO64 = 1)
#define LED6_ON (GpioDataRegs.GPACLEAR.bit.GPIO10 = 1)
#define LED6_OFF (GpioDataRegs.GPASET.bit.GPIO10 = 1)
#define LED6_TOGGLE (GpioDataRegs.GPATOGGLE.bit.GPIO10 = 1)
#define LED7_ON (GpioDataRegs.GPACLEAR.bit.GPIO11 = 1)
#define LED7_OFF (GpioDataRegs.GPASET.bit.GPIO11 = 1)
#define LED7_TOGGLE (GpioDataRegs.GPATOGGLE.bit.GPIO11 = 1)
void LED_Init(void);
#endif /* _LEDS_H_ */
对于xxx.h的编写和作用,大家可以重新温习一下c语言。
#ifndef_LEDS_H
#define_LEDS_H
//此处省略了头文件定义的内容
#endif/*LED_H_*/
此处语句的作用是防止头文件被重复包含,出现编译错误。通过#ifndef判断标号_LEDS_H是否被其他头文件定义使用,若没有被其他头文件定义使用则#define 标号 到#endif之间的内容全部有效;否则,全部无效。
因为在xxx.c和xxx.h文件中使用了F28335的相关寄存器,所以要包含#include “DSP2833x_Device.h” 和#include “DSP2833x_Examples.h” 头文件。在之后的文件中,建议把F28335的两个头文件全部包含进来,防止头文件未定义报错。
使用宏定义可以让编码更易懂。
最后,要注意把leds.c文件中定义的函数写进leds.h文件。
这样,LED的初始化函数就写完了。之后的文章的.c和.h文件基本都是这样定义配置。
3、流水灯主函数
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP2833x Examples Include File
#include "leds.h"
void main()
{
InitSysCtrl(); //系统初始化函数,完成系统时钟配置以及使能各外设时钟(包括GPIO时钟)
LED_Init(); //LED初始化
while(1)
{
//初始时所有灯都是熄灭状态,通过让每个灯亮的时侯,上一个亮的灯熄灭并通过一定的延时来达到流水灯效果;发现让LED灯电平翻转容易实现这种效果
LED7_OFF; //因为初始时LED7是熄灭的,但是在最后时LED7是处于亮的状态,因此使用LED7_OFF,而不使用LED7_TOGGLE,否则LED7会是一直亮的;
LED1_TOGGLE; //LED1电平翻转
DELAY_US(1000000);//延时1s,使用该延时函数要保证系统时钟为150MHz
LED1_TOGGLE;
LED2_TOGGLE;
DELAY_US(1000000);
LED2_TOGGLE;
LED3_TOGGLE;
DELAY_US(1000000);
LED3_TOGGLE;
LED4_TOGGLE;
DELAY_US(1000000);
LED4_TOGGLE;
LED5_TOGGLE;
DELAY_US(1000000);
LED5_TOGGLE;
LED6_TOGGLE;
DELAY_US(1000000);
LED6_TOGGLE;
LED7_TOGGLE;
DELAY_US(1000000);
}
}
这样流水灯就实现了。
二、蜂鸣器控制
1.原理图
蜂鸣器控制相对简单,只用一个GPIO6就能控制。原理图如下:
我采用的蜂鸣器是无源蜂鸣器,需提供一定频率的脉冲信号才能发声,频率大小通常在1.5-5KHz 之间。有源蜂鸣器只需要接上电源就能发声,其内部自带振荡电路。
由于控制引脚是通过驱动芯片ULN2003D连接到蜂鸣器,该驱动芯片的输入和输出电平是相反的,即:输入高、输出低;输入低、输出高。
我仿照流水灯配置,配置出了蜂鸣器初始化函数。
2.蜂鸣器初始化函数
(1)beep.c
代码如下:
#include "beep.h"
void BEEP_Init(void)
{
EALLOW;
GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 0; //配置为通用数字I/O口
GpioCtrlRegs.GPADIR.bit.GPIO6 = 1; //引脚配置为输出
GpioCtrlRegs.GPAPUD.bit.GPIO6 = 0; //禁止上拉
EDIS;
GpioDataRegs.GPACLEAR.bit.GPIO6 = 1; //初始电平配置为低电平
}
(2)beep.h
#ifndef BEEP_H_
#define BEEP_H_
#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"
#define BEEP_ON (GpioDataRegs.GPASET.bit.GPIO6 = 1)
#define BEEP_OFF (GpioDataRegs.GPACLEAR.bit.GPIO6 = 1)
#define BEEP_TOGGLE (GpioDataRegs.GPATOGGLE.bit.GPIO6 = 1)
void BEEP_Init(void);
#endif /* BEEP_H_ */
3、蜂鸣器控制主函数
#include"DSP2833x_Device.h" //DSP2833x Headerfile Include File
#include"DSP2833x_Examples.h" //DSP2833x Examples Include File
#include"leds.h"
#include"beep.h"
void main()
{
InitSysCtrl();
BEEP_Init();
while(1)
{
//通过让电平每隔100us翻转(频率为5kHz)输出脉冲,从而让蜂鸣器发声
//可以改变音调即修改管脚输出频率,改变声音大小即修改占空比
BEEP_TOGGLE;
DELAY_US(100);//延时100us,使用该延时函数要保证系统时钟为150MHz
}
}
这样就可以使蜂鸣器发声,并且可以通过修改管脚输出频率和修改占空比来改变音调和声音大小
对于如何查看自己的系统时钟配置。
首先Ctrl+鼠标左键点击主函数中的InitSysCtrl()函数,找到下面函数:
接着Ctrl+鼠标左键点击DSP28_PLLCR,找到这两个宏定义:
系统时钟的计算公式就是:振荡时钟(30MHz)10倍频后再2分频,所以等于150MHZ (系统时钟 = 30 * DSP28_PLLCR / DSP28_DIVSEL)。
总结
通过对GPIO的配置写出了实现流水灯和蜂鸣器发声效果的程序。学会了通过查看原理图查找相关引脚来控制相应外设。并且学会了查找、修改和计算系统时钟。
大家可以相互讨论,共同进步。