STM32学习——GPIO应用之跑马灯+蜂鸣器实验

2023-05-16

GPIO应用——跑马灯+蜂鸣器实验

跑马灯和蜂鸣器的实验都是使用的 GPIO 的通用推挽输出模式,这是 GPIO 最简单的一个基本功能。

1跑马灯实验

1.1硬件设计

如图所示跑马灯使用的 LED 只有 DS0(红灯)和 DS1(绿灯)两个,DS0 接 PB5,DS1 接 PE5image-20210619093448900

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)	//定义两个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 至关重要,==sys.h 程序==构成如下所示。

#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,这里定义的是开发板的型号,我们可以通过定义不同的开发板型号,通过#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 初始化的步骤==

  1. 首先我们通过 GPIO_InitTypeDef 定义一个结构体 GPIO_InitStructure。
  2. 通过 RCC_APB2PeriphClock 打开 GPIO 端口时钟。
  3. 给 GPIO_ InitStructure 里的 GPIO_Speed、GPIO_Mode、GPIO_Pin 成员赋值
  4. 通过调用 GPIO_Init 对结构体进行初始化。
  5. 设置端口初始电平,这里使用的函数是 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 函数是按需更改的,其中下面两条语句是固定。

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)

image-20210722133312175
蜂鸣器也是使用的 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

    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32学习——GPIO应用之跑马灯+蜂鸣器实验 的相关文章

  • 【Keil5】*** Target ‘xxx‘ uses ARM-Compiler ‘Default Compiler Version 5‘ which is not available.解决方法

    出现这个报错的原因在Keil 5 37以后安装compiler version 6 xff0c 如果要使用compiler version 5 xff0c 需要自己安装 下载链接 官网 https developer arm com dow
  • ubuntu 18.04.6 使用内核源码安装内核

    文章目录 前言一 编译内核以及安装二 编译内核模块总结参考资料 前言 上一篇我在ubuntu 18 04 更换内核版本后 xff0c 这篇我们在ubuntu 18 04上用内核源码编译其它版本的内核 xff0c 并进行安装 ubuntu 1
  • 关于CMMI和敏捷过程改进

    问题 xff1a 如果按照CMMI从1到5的思路 xff0c 建设企业的信息化制度 xff08 不是为了评定等级 xff0c 是为了实现项目规范管理 xff09 xff0c 可行吗 xff1f 需要关注哪些问题点呢 xff1f 公司如果是个
  • 【PX4_BUG】You should uninstall ModemManager as it conflicts with any non-modem serial device

    将编译好的固件下载到无人机 xff0c 需要输入命令 make px4 fmu v2 default upload 这里运行时可能会有报错 WARNING You should uninstall ModemManager as it co
  • 【PX4-AutoPilot教程-2】搭建并运行第一个应用程序

    搭建并运行第一个应用程序 本文主要说明如何搭建并运行你的第一个板载应用程序 Firmware src examples px4 simple app文件夹下默认已经有一个完整的例程 xff0c 如果遇到了问题可以作为参考 如果需要自己重新编
  • 【PX4-AutoPilot教程-1】PX4源码文件目录架构分析

    PX4源码文件目录架构分析 PX4源代码的结构复杂 xff0c 这是源代码的总目录结构 xff08 以v1 13 0为例 xff09 xff1a Firmware boards build cmake Documentation integ
  • 【PX4-AutoPilot教程-3】uORB主题订阅发布机制理解、应用和代码阅读

    uORB主题订阅发布机制 1 PX4 Pixhawk的软件体系结构 PX4 Pixhawk的软件体系结构主要被分为四个层次 xff0c 这可以让我们更好的理解PX4 Pixhawk的软件架构和运作 xff1a 应用程序的API xff1a
  • 2020-11-23

    https blog csdn net guofei fly article details 104136008 utm medium 61 distribute pc relevant none task blog BlogCommend
  • MapReduce原理及简单实现

    MapReduce将数据的处理分成了两个步骤 xff0c Map和Reduce Map将输入的数据集拆分成一批KV对并输出 xff0c 对于每一个 lt k1 v1 gt xff0c Map将输出一批 lt k2 v2 gt xff1b R
  • 深度理解Python迭代器

    我们手动的实现一个for循环 xff1a li1 61 list range 10 iteratorObject 61 iter li1 while 1 try print next iteratorObject except StopIt
  • 关于mysql版本差异导致FIND_IN_SET()查询不到数据的问题

    这次发现的问题 xff0c 是在接手项目的时候 xff0c 和安卓端小伙伴测试时候发现的 xff0c 插入数据之后却查不出来 xff0c 通过排查定位到FIND IN SET 函数 xff0c 也是第一次接触FIND IN SET xff0
  • YOLOv4代码学习笔记一

    YOLOV4代码学习笔记一 YOLOV4简介CSPdarknet py学习 本文是对另一个博主的 睿智的目标检测30 Pytorch搭建YoloV4目标检测平台代码的学习 xff0c 由于我是cv新手 xff0c 很多东西不懂 xff0c
  • 无人机光流模块使用技巧

    无人机光流模块使用技巧 光流模块在无 GPS 环境下 xff0c 课实时检测飞机水平移动距离 xff0c 实现对四轴无人机长时间的稳定悬停 图1显示的是湖南优象LC 302光流模块的功能框图 xff0c 光流摄像头拍摄无人机垂直向下的画面
  • CMMI 2.0 和 1.3

    CMMI2 0与1 3在组织形式区别很大 xff0c 很多PA和之前的不太一样了 xff0c 而且PA在2 0中叫实践域 xff0c 1 3中叫过程域 不过其实核心内容没有大的变化 xff0c 只是相关内容的位置进行了调整 xff0c 部分
  • ROS2的RVIZ2无法启动

    在新安装的 xff32 xff2f xff33 2中启动rviz2 xff0c 启动错误 xff0c 显示 Failed to create an OpenGL context BadValue integer parameter out
  • 【TCP 重传、滑动窗口、流量控制、拥塞控制】

    文章目录 重传机制超时重传快速重传SACK方法Duplicate SACK 滑动窗口流量控制那操作系统的缓冲区 xff0c 是如何影响发送窗口和接收窗口的呢 xff1f 窗口关闭 拥塞控制慢启动拥塞避免拥塞发生快速恢复 重传机制 TCP 实
  • 【TCP四次挥手】

    文章目录 TCP 四次挥手过程是怎样的 xff1f 为什么挥手需要四次 xff1f 第一次挥手丢失了 xff0c 会发生什么 xff1f 第二次挥手丢失了 xff0c 会发生什么 xff1f 第三次挥手丢失了 xff0c 会发生什么 xff
  • FreeRTOS事件组----任务同步

    有时 xff0c 应用程序的设计需要两个或多个任务才能彼此同步 例如 xff0c 考虑一个设计 xff0c 其中任务A接收一个事件 xff0c 然后将事件所需的一些处理提供给其他三个任务 xff1a 任务B xff0c 任务C和任务D 如果
  • Qt中给按钮设置颜色的方法

    Qt中给按钮设置颜色的方法 第一种 CSS风格第二种 使用QPalette类第三种 使用QColor的另一种方法 对于界面编程来说 xff0c 色彩无疑是一个重点关注的对象 xff0c 界面好不好看 xff0c 色彩占据一大半 这里我在学习
  • codeblocks下载安装教程(完整详细)

    最近又将codeblocks下载了一下 xff0c 将完整的过程记录一下 一 下载教程 进入codeblocks官网 xff0c http www codeblocks org 点击downloads 一般都会选择第一个Download t

随机推荐