GPIO应用——跑马灯+蜂鸣器实验
跑马灯和蜂鸣器的实验都是使用的 GPIO 的通用推挽输出模式,这是 GPIO 最简单的一个基本功能。
1跑马灯实验
1.1硬件设计
如图所示跑马灯使用的 LED 只有 DS0(红灯)和 DS1(绿灯)两个,DS0 接 PB5,DS1 接 PE5
LED 是发光二极管,既然是二极管,就具备二极管的正向导通,反向截止的特性。DS0 的正极通过电阻R909 连接电源正极(即 VCC_3V3),负极连接到 PB5,注意 LED0 是网络标号,网络标号相同的,在原理图上表示是相连接的。这个 R909 我们俗称为限流电阻,因为 LED 灯点亮有一个导通电流,这个电流不能太大,太大容易烧毁 LED 灯,因此我们通过串联一个来减少导通时的电流,电阻的阻值大小根据 LED 种类的不同可以灵活选取。
根据二极管的单向导通特性,如果我们给 PB5 设置输出高电平(3.3V),那么 DS0 应该不点亮,而如果给 PB5 设置输出低电平,那么由于存在正向压降,DS0 导通,因此点亮发光。
总结一下,针对本例的电路原理图,LED 灯低电平点亮,高电平熄灭。
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)
#define LED0_GPIO_PIN GPIO_Pin_5
#define LED0_PIN_ID 5
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED0_FUN_OUT PBout
#define LED1_GPIO_PIN GPIO_Pin_5
#define LED1_PIN_ID 5
#define LED1_GPIO_PORT GPIOE
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOE
#define LED1_FUN_OUT PEout
#define LED0 LED0_FUN_OUT(LED0_PIN_ID)
#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
时钟,引脚和端口全部采用宏定义的方式,主要是方便以后程序修改,后面的例子都采用这种方式来实现。在讲解这个文件构成前,我们先看一下该头文件包含的 sys.h 头文件的构成,这对我们了解 bsp_led.h 和 bsp_led.c 至关重要,==sys.h 程序==构成如下所示。
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
#define WSNEP_V01
#if !defined (WSNEP_V01) && !defined (TDL_02)
#error "Please define the board model : TDL_02 or WSNEP_V01"
#endif
#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))
#define GPIOA_ODR_Addr (GPIOA_BASE+12)
#define GPIOB_ODR_Addr (GPIOB_BASE+12)
#define GPIOC_ODR_Addr (GPIOC_BASE+12)
#define GPIOD_ODR_Addr (GPIOD_BASE+12)
#define GPIOE_ODR_Addr (GPIOE_BASE+12)
#define GPIOF_ODR_Addr (GPIOF_BASE+12)
#define GPIOG_ODR_Addr (GPIOG_BASE+12)
#define GPIOA_IDR_Addr (GPIOA_BASE+8)
#define GPIOB_IDR_Addr (GPIOB_BASE+8)
#define GPIOC_IDR_Addr (GPIOC_BASE+8)
#define GPIOD_IDR_Addr (GPIOD_BASE+8)
#define GPIOE_IDR_Addr (GPIOE_BASE+8)
#define GPIOF_IDR_Addr (GPIOF_BASE+8)
#define GPIOG_IDR_Addr (GPIOG_BASE+8)
#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,这里定义的是开发板的型号,我们可以通过定义不同的开发板型号,通过#ifdef 条件编译来适配不同的开发板。这是一个提高程序可移植性的思想。
有了 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 初始化的步骤==
- 首先我们通过 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);
bsp_LedOff(1);
bsp_LedOff(2);
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);
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;
}
else if(_on==2)
{
LED1 = 0;
}
}
bsp_LedOff 函数实现熄灭 LED 灯,和点亮 LED 灯函数类似,
void bsp_LedOff(uint8_t _off)
{
if(_off==1)
{
LED0 = 1;
}
else if(_off==2)
{
LED1 = 0;
}
}
main.c 主函数
int main(void)
{
bsp_Init();
while(1)
{
bsp_BeepOn();
bsp_LedToggle(1);
delay_ms(1000);
bsp_BeepOff();
bsp_LedToggle(2);
delay_ms(1000);
}
}
在 main 函数里有两个函数没见过,它们是 bsp_Init()和 delay_ms(),这两个函数在 bsp.c文件里
先了解一下 bsp_Init 函数
void bsp_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
bsp_InitLed();
bsp_InitBeep();
bsp_InitTimer();
}
bsp_Init 函数是按需更改的,其中下面两条语句是固定。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组
bsp_InitTimer();//初始化系统滴答定时器。函数实体在 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)
#define BEEP_GPIO_PIN GPIO_Pin_11
#define BEEP_PIN_ID 11
#define BEEP_GPIO_PORT GPIOG
#define BEEP_GPIO_CLK RCC_APB2Periph_GPIOG
#define BEEP_FUN_OUT PGout
#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
bsp_beep.c 文件
#include "bsp.h"
void bsp_InitBeep(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
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);
}
void bsp_BeepOn()
{
BEEP=1;
}
void bsp_BeepOff()
{
BEEP=0;
}
void bsp_BeepToggle()
{
BEEP_GPIO_PORT->ODR^=BEEP_GPIO_PIN;
}
uint8_t bsp_IsBeepOn()
{
if((BEEP_GPIO_PORT->ODR&BEEP_GPIO_PIN)==BEEP_GPIO_PIN)
{
return 1;
}
return 0;
}
主函数
int main(void)
{
bsp_Init();
while(1)
{
bsp_BeepOn();
bsp_LedToggle(1);
delay_ms(1000);
bsp_BeepOff();
bsp_LedToggle(2);
delay_ms(1000);
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)