GPIO应用——跑马灯 蜂鸣器实验
使用跑马灯和蜂鸣器的实验 GPIO 通用推拉输出模式 GPIO 最简单的基本功能。
1
1.1
如图所示,跑马灯使用 LED 只有 DS0(红灯)和 DS两个(绿灯),DS0 接 PB5,DS1 接 PE5
LED 它是一种发光二极管,由于是二极管,具有二极管正向导通、反向截止的特点。DS0 正极通过电阻R909 连接电源正极(即 VCC_3V三、负极连接 PB5,注意 LED0 是网络标号,网络标号相同,原理图表示相连。这个 R909 我们通常称之为限流电阻,因为 LED 灯亮时有导电流,不能太大,太大容易烧坏 LED 因此,我们通过串联一个来减少导通时的电流,根据电阻的电阻值 LED 可灵活选择不同类型。
根据二极管的单向导通特性,如果我们给它 PB5 设置高输出电平(3).3V),那么 DS0 不要点亮,但是如果给的话 PB5 由于正向压降,设置输出低电平,DS0 导通,所以点亮发光。
综上所述,本例的电路原理图, 。
1.2
首先,我们需要重建bsp_led.c和bsp_led.h文件
接下来我们来看看 bsp_led.h 的程序构成
#ifndef _BSP_LED_H #define _BSP_LED_H #include "sys.h" #define RCC_LED_ALL (LED0_GPIO_CLK | LED1_GPIO_CLK) //定义两个LED灯的端口时钟 #define LED0_GPIO_PIN GPIO_Pin_5 //LED0引脚号 #define LED0_PIN_ID 5 //LED0引脚序号 #define LED0_GPIO_PORT GPIOB //LED0端口号 #define LED0_GPIO_CLK RCC_APB2Periph_GPIOB //LED0时钟 #define LED0_FUN_OUT PBout //LED0端口输出配置 #define LED1_GPIO_PIN GPIO_Pin_5 //LED1引脚号 #define LED1_PIN_ID 5 //LED1引脚序号 #define LED1_GPIO_PORT GPIOE //LED1端口号 #define LED1_GPIO_CLK RCC_APB2Periph_GPIOE //LED1时钟 #define LED1_FUN_OUT PEout //LED1端口输出配置 #define LED0 LED0_FUN_OUT(LED0_PIN_ID) //定义LED的输出 #define LED1 LED1_FUN_OUT(LED0_PIN_ID) //函数声明 void bsp_InitLed(void); void bsp_LedOn(uint8_t _on); void bsp_LedOff(uint8_t _off); void bsp_LedToggle(uint8_t _no); uint8_t bsp_IsLedOn(uint8_t _no); #
endif
/***************************** (END OF FILE) *********************************/
时钟,引脚和端口全部采用宏定义的方式,主要是方便以后程序修改,后面的例子都采用这种方式来实现。在讲解这个文件构成前,我们先看一下该头文件包含的 sys.h 头文件的构成,这对我们了解 bsp_led.h 和 bsp_led.c 至关重要,====构成如下所示。
#ifndef __SYS_H #define __SYS_H #include "stm32f10x.h" /* ********************************************************************************************************* * * 模块名称 : sys模块 * 文件名称 : sys.h * 说 明 : 这是所有驱动h文件需要包含的h文件,需要在头文件中包含,实现位带操作 * ********************************************************************************************************* */ #define WSNEP_V01 //#define TDL_02 /* 检查是否定义了
开发板型号 */ #if !defined (WSNEP_V01) && !defined (TDL_02) #error "Please define the board model : TDL_02 or WSNEP_V01" #endif //位带操作,实现51类似的GPIO控制功能 //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页). //IO口操作宏定义 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) //IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 //IO口操作,只对单一的IO口! //确保n的值小于16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入 #endif
该头文件包含里 stm32f10x.h 的头文件,这个头文件是 ST 官方提供的,里面主要STM32F10x 系列的寄存器定义,这和 51 单片机里的 reg52.h 类似。文件中定义了 WSNEP_V01,这里定义的是开发板的型号,我们可以通过定义不同的开发板型号,。这是一个提高程序可移植性的思想。
有了 sys.h 里的代码,我们就可以像 51 单片机一样操作 STM32 的 IO 口了,比如我们调用 PBout(5)=1 是设置了 PB5 输出为高电平。
在简要的分析了 sys.h 的代码后,我们回到 bsp_led.h 文件里。注意这里将GPIO 端口,GPIO 引脚号,以及 GPIO 端口时钟进行了封装。其中 GPIO_CLK 宏是“RCC_APB2Periph_GPIOB”是 STM32 标准库里用来定义 GPIO 端口时钟相关的宏,它的作用与“GPIO_Pin_x”这类宏类似。LED0_FUN_OUT 定义的是 sys 里面的 PBout,以后我们还会用到输入函数,那么将使用 XXX_FUN_IN,配置对应的端口 Pxin,x 对应的是 GPIO 端 口中的 A~G。注意这里的 LED0_PIN_ID 是表示的端口序号。另外将 LED 操作函数也做了一个封装,#define LED0 LED0_FUN_OUT(LED0_PIN_ID),经过这些宏定义操作后,我们就可以进行进一步的操作了。
注意在 bsp_led.h 头文件最后是将 c 文件的函数做一个函数申明。
接下来我们看一下在 bsp_led.c 文件里怎么使用这些宏定义。先看一下 bsp_InitLed 函数
在这个函数中我们讲解一下对====
- 首先我们通过 GPIO_InitTypeDef 定义一个结构体 GPIO_InitStructure。
- 通过 RCC_APB2PeriphClock 打开 GPIO 端口时钟。
- 给 GPIO_ InitStructure 里的 GPIO_Speed、GPIO_Mode、GPIO_Pin 成员赋值
- 通过调用 GPIO_Init 对结构体进行初始化。
- 设置端口初始电平,这里使用的函数是 bsp_LedOff
void bsp_InitLed(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_LED_ALL, ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
bsp_LedOff(1); //初始化led为关闭状态
bsp_LedOff(2);
//LED0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置速度
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置输出方式(复用开漏输出 )
GPIO_InitStructure.GPIO_Pin = LED0_GPIO_PIN; //设置端口号
GPIO_Init(LED0_GPIO_PORT,&GPIO_InitStructure); //初始化配置
//LED1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
GPIO_Init(LED1_GPIO_PORT,&GPIO_InitStructure);
}
接下来看一下 bsp_LedOn 函数,此函数实现点亮 LED 灯,这里提供了两种实现方法,一种是寄存器,一种是位带的方式。
void bsp_LedOn(uint8_t _on)
{
if(_on==1)
{
LED0 = 0;
// GPIO_ResetBits(LED0_GPIO_PORT,LED0_GPIO_PIN);
}
else if(_on==2)
{
LED1 = 0;
// GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);
}
}
bsp_LedOff 函数实现熄灭 LED 灯,和点亮 LED 灯函数类似,
void bsp_LedOff(uint8_t _off)
{
if(_off==1)
{
LED0 = 1;
// GPIO_SetBits(LED0_GPIO_PORT,LED0_GPIO_PIN);
}
else if(_off==2)
{
LED1 = 0;
// GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);
}
}
main.c 主函数
int main(void)
{
/* ST固件库中的启动文件已经执行了 SystemInit() 函数,该函数在 system_stm32f10x.c 文件,主要功能是 配置CPU系统的时钟 */
bsp_Init(); /* 硬件初始化 */
while(1)
{
bsp_BeepOn(); //打开蜂鸣器
bsp_LedToggle(1); //LED0翻转
delay_ms(1000); //延时1000ms
bsp_BeepOff(); //关闭蜂鸣器
bsp_LedToggle(2); //LED1翻转
delay_ms(1000); //延时1000ms
}
}
在 main 函数里有两个函数没见过,它们是 bsp_Init()和 delay_ms(),这两个函数在 bsp.c文件里
先了解一下 bsp_Init 函数
void bsp_Init(void)
{
/* 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。 系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件 */
/* 优先级分组设置为2 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
bsp_InitLed(); /* 配置LED的GPIO端口 */
bsp_InitBeep(); /* 配置Beep的GPIO端口 */
bsp_InitTimer(); /* 初始化系统滴答定时器 (此函数会开中断) */
}
bsp_Init 函数是按需更改的,其中下面两条语句是固定。
//优先级分组
//初始化系统滴答定时器。函数实体在 bsp_timer.c 里
bsp_InitLed 函数之前在 bsp_led.c 部分已经讲解过了。这里直接调用进行 LED 灯端口和模式的初始化。
delay_ms 函数从函数名可以看出这是延时毫秒的函数,可以直接调用
2.蜂鸣器实验
1.1
如图所示跑马灯使用的 LED 只有 DS0(红灯)和 DS1(绿灯)两个,DS0 接 PB5,DS1 接 PE5)
蜂鸣器也是使用的 GPIO 的输出功能,这里相当于进一步熟悉一下 STM32 的 IO 端口的使用。注意一下,这里使用了 NPN 三极管,实际上使用的是三极管导通和截止的功能,对 于图中的接法,。
2.2软件设计
bsp_beep.h 文件
#ifndef _BSP_BEEP_H
#define _BSP_BEEP_H
#include "sys.h"
#define RCC_ALL_BEEP (BEEP_GPIO_CLK) //beep时钟
#define BEEP_GPIO_PIN GPIO_Pin_11 //beep引脚号
#define BEEP_PIN_ID 11 //beep引脚序号
#define BEEP_GPIO_PORT GPIOG //beep端口号
#define BEEP_GPIO_CLK RCC_APB2Periph_GPIOG //beep时钟
#define BEEP_FUN_OUT PGout //beep输出端口配置函数
//#define LED0_FUN_IN PBin //beep输入端口配置函数
//
//IO操作函数
#define BEEP BEEP_FUN_OUT( BEEP_PIN_ID )
/*函数声明*/
void bsp_InitBeep(void);
void bsp_BeepOn(void);
void bsp_BeepOff(void);
void bsp_BeepToggle(void);
uint8_t bsp_IsBeepOn(void);
#endif
/***************************** (END OF FILE) *********************************/
bsp_beep.c 文件
#include "bsp.h"
/******************************************************************************* * 函数名 :bsp_InitBeep * 函数功能 :配置有源蜂鸣器相关的 GPIO, 该函数被 bsp_Init()调用 * 输入 : 无 * 输出 : 无 *******************************************************************************/
void bsp_InitBeep(void)
{
//定义一个结构体变量
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIO时钟
RCC_APB2PeriphClockCmd(RCC_ALL_BEEP,ENABLE );
//设置输出速度
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
//设置输出模式为开漏输出
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
//
GPIO_InitStructure.GPIO_Pin = BEEP_GPIO_PIN;
//
GPIO_Init( BEEP_GPIO_PORT , &GPIO_InitStructure);
}
/******************************************************************************* * 函数名 :bsp_BeepOn() * 函数功能 :开启蜂鸣器 * 输入 : 无 * 输出 : 无 *******************************************************************************/
void bsp_BeepOn()
{
//beep引脚给高电平,蜂鸣器工作
//方法一
//BEEP_GPIO_PORT->BSRR=BEEP_GPIO_PIN
//方法二
BEEP=1;
}
/******************************************************************************* * 函数名 :bsp_BeepOff() * 函数功能 :开启蜂鸣器 * 输入 : 无 * 输出 : 无 *******************************************************************************/
void bsp_BeepOff()
{
//BEEP_GPIO_PORT->BSRR=BEEP_GPIO_PIN
BEEP=0;
}
/******************************************************************************* * 函数名 :bsp_BeepToggle() * 函数功能 :蜂鸣器翻转函数 * 输入 : 无 * 输出 : 无 *******************************************************************************/
void bsp_BeepToggle()
{
BEEP_GPIO_PORT->ODR^=BEEP_GPIO_PIN;
}
/******************************************************************************* * 函数名 :bsp_IsBeepOn() * 函数功能 :判断蜂鸣器是否鸣叫 * 输入 : 无 * 输出 : 1表示鸣叫,0表示静音 *******************************************************************************/
uint8_t bsp_IsBeepOn()
{
if((BEEP_GPIO_PORT->ODR&BEEP_GPIO_PIN)==BEEP_GPIO_PIN)
{
return 1;
}
return 0;
}
/***************************** (END OF FILE) *********************************/
主函数
int main(void)
{
/* ST固件库中的启动文件已经执行了 SystemInit() 函数,该函数在 system_stm32f10x.c 文件,主要功能是 配置CPU系统的时钟 */
bsp_Init(); /* 硬件初始化 */
while(1)
{
bsp_BeepOn(); //打开蜂鸣器
bsp_LedToggle(1); //LED0翻转
delay_ms(1000); //延时1000ms
bsp_BeepOff(); //关闭蜂鸣器
bsp_LedToggle(2); //LED1翻转
delay_ms(1000); //延时1000ms
}
}