首页 > 其他分享 >STM32学习笔记(二)流水灯

STM32学习笔记(二)流水灯

时间:2024-06-01 19:00:39浏览次数:22  
标签:LED1 LED0 LED PIN Pin 笔记 STM32 流水 GPIO

STM32学习笔记(二)流水灯

这次我们来实现LED流水灯成为点灯大师

使用的核心板的MCU型号为STM32F103ZET6,使用标准库函数来实现。

一、 原理部分

1.1 LED原理


其中 PWR 是系统电源指示灯,为蓝色。 LED0和 LED1分别接在 PB5 和 PE5 上,LED0为红色,LED1为绿色。
它们采用共阳极接法,所以端口置为低电平,LED灯就会亮起。

所以思路很明确,我们点亮一个LED灯后,每隔一段时间翻转它对应的IO口,就可以看到它在不断地闪烁了。

1.2 GPIO原理

在配置 STM32 外设的时候,任何时候都要先使能该外设的时钟。GPIO 是挂载在 APB2 总线上的外设,
在固件库中对挂载在 APB2 总线上的外设时钟使能是通过函数 RCC_APB2PeriphClockCmd()来实现的。
在这里插入图片描述)
使能APB2外设时钟后,我们需要初始化IO参数,使用GPIO_Init()函数来实现。

完成以上的操作,我们才可以操作对应的IO口来控制LED灯的亮灭。

二、工程部分

我们先把之前建的工程模板复制,放到一个新的文件夹,文件夹可以取名为"LED流水灯",然后打开工程。

首先我们要先初始化 LED灯的IO端口,然后才能对它们进行操作。

之前谈到的操作IO口的基本三个步骤,可以总结为:
1.RCC开启对应的GPIO时钟
2.使用GPIO_Init函数初始化对应GPIO
3.使用GPIO输出/输入函数来控制对应GPIO口

所以第一步,我们要先使用RCC_APB2PeriphClockCmd()开启外设时钟。

//使能外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//对应在PB口的LED0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//对应在PE口的LED1

第二步就是使用GPIO_Init函数初始化对应GPIO参数了。
该函数的定义:

void GPIO_Init (GPIO_TypeDef* GPIOx, GPIO_InitTypeDef*GPIO_InitStruct);

这个函数有两个参数,第一个参数是用来指定 GPIO,取值范围为 GPIOA~GPIOG。
第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。
该结构体的定义为:

typedef struct
{
  uint16_t GPIO_Pin;  //对应要初始化的IO口          

  GPIOSpeed_TypeDef GPIO_Speed;  //IO口输出速度设置

  GPIOMode_TypeDef GPIO_Mode;   //IO口输入或输出模式
  
}GPIO_InitTypeDef;

对于简单的IO口操作,速度没有太大的要求,一般取50MHz。然后GPIO的输出模式设置为推挽输出,在这种模式下,
IO口的驱动能力很强。

接下来就可以使用 GPIO_Init()函数来初始化GPIO了。

//配置外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
//结构体变量
GPIO_InitTypeDef GPIO_InitStruct;
//结构体对应参数
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //设置为推挽输出模式
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
//GPIO初始化,两个LED灯对应的都是Pin_5,所以可以用同一个结构体来初始化
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_Init(GPIOE,&GPIO_InitStruct);

通过以上代码,我们就是实现了LED的IO口初始化的操作。这些跟硬件相关的内容我们可以放在"HardWare"文件下。

事实上,每次我们初始化LED灯的GPIO都是相同的操作,我们可以在"HardWare"文件下,创建并加入"LED.h"以及"LED.c"两个文件,并且把以上的代码封装在函数"LED_Init()"中。
先在头文件"LED.h"中声明函数:

#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"                  // Device header
//函数声明
void LED_Init(void);
#endif

然后是"LED.c"中定义函数:
在这里插入图片描述
上面的函数就是我们初始化LED灯的GPIO的函数,你可能会注意到末尾我添加了两句新的代码:

//两个LED灯端口置为高电平,让它们在初始化后保持熄灭状态
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
	GPIO_SetBits(GPIOE,GPIO_Pin_5);

这就是我们初始化IO口后,对IO口操作的函数了,它们的意思也很好理解,GPIO_SetBits(GPIOB,GPIO_Pin_5)即对GPIOB的5号端口置高电平(位操作),我们之前说过,这块板子的LED灯是共阳极接法,所以如果它们对应IO口输出被置为高电平,它们就会保持熄灭状态。

如果你想点亮LED灯的话,可以使用GPIO_ResetBits(GPIOx,GPIO_Pin),它们把指定的GPIO口输出置为低电平,对应的LED灯就会亮起。

现在我们就可以在主函数中编写流水灯的程序了,我们先让LED0亮起(红色),LED1熄灭;然后延时一段时间,让LED0熄灭,LED1亮起(绿色)。然后不断地循环这个过程,这就实现了LED流水灯。

先在主函数中插入必要的头文件:

#include “stm32f10x.h”
#include “LED.h” //这里面有我们需要的初始化LED灯的GPIO的函数

然后我们需要一个延时函数,这里采用最简单的延时方法,通过让代码跑空循环来实现延时,这样的延时精度不会很高。

void Delay(uint32_t time)
{
  uint32_t i,j;

  for(i=0;i<time;++i)
  {
    for(j=0;j<10000;++j)
    {       
   
    }
  }
}

接下来就是main函数里面的部分了,写一个while循环,然后轮流让两个灯亮起即可。

int main(void)
{
	//初始化LED
	LED_Init();
	
	while(1)
	{
		
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);//PB5号口置为低电平,对应的LED0亮起
		GPIO_SetBits(GPIOE,GPIO_Pin_5);//PE5号口置为高电平,LED1熄灭
	    Delay(500); //延时一段时间
		
		GPIO_SetBits(GPIOB,GPIO_Pin_5);//PB5号口置为高电平,LED0熄灭
		GPIO_ResetBits(GPIOE,GPIO_Pin_5);//PE5号口置为低电平,LED1亮起
		Delay(500); //延时一段时间
		
	}
}

将代码烧录进单片机,开始点灯!

<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="XD9rPM2y-1717239332299" src="https://live.csdn.net/v/embed/395176"></iframe>

LED流水灯

三、加入宏定义

你在看别人的代码的时候,有没有发现同样是点灯,但是别人的程序好像更简洁、直观一点?这有很大的原因是因为类似"LED1_ON"、"LED2_OFF"这样的宏定义的使用。

还是之前的main函数,但是可以是下面这个形式:

int main(void)
{
	//初始化LED
	LED_Init();
	
	while(1)
	{
		
		LED0_ON;
		LED1_OFF;
	    Delay(500); //延时一段时间
		
		LED0_OFF;
		LED1_ON;
		Delay(500); //延时一段时间
		
	}
}

其实操作很简单,只需要在"LED.h"的头文件加入一些宏定义即可。

/* 调用标准库函数方法 */
#define LED0_OFF                       GPIO_SetBits(LED0_GPIO,LED0_GPIO_PIN)
#define LED0_ON                      GPIO_ResetBits(LED0_GPIO,LED0_GPIO_PIN)

#define LED1_OFF                       GPIO_SetBits(LED1_GPIO,LED1_GPIO_PIN)
#define LED1_ON                     GPIO_ResetBits(LED1_GPIO,LED1_GPIO_PIN)

这就相当于
LED0_ON=GPIO_ResetBits(LED0_GPIO,LED0_GPIO_PIN); //LED0对应的GPIOB_Pin_5置0,点灯
LED0_OFF=GPIO_SetBits(LED0_GPIO,LED0_GPIO_PIN);//LED0对应的GPIOB_Pin_5置1,灭灯
LED1_ON =GPIO_ResetBits(LED1_GPIO,LED1_GPIO_PIN);//LED1对应的GPIOE_Pin_5置0,点灯
LED1_OFF =GPIO_SetBits(LED1_GPIO,LED1_GPIO_PIN);//LED1对应的GPIOE_Pin_5置1,灭灯

其中

LED0_GPIO=GPIOB,LED0_GPIO_PIN=GPIO_Pin_5
LED1_GPIO=GPIOE,LED1_GPIO_PIN=GPIO_Pin_5

它们也是宏定义的变量,这样程序就会更直观易懂,并且移植工程非常方便,甚至有时候只需要修改对应的几个宏定义就可以完美运行。
"LED.h"中涉及的宏定义如下,

#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"                  // Device header

/* 宏定义 */
#define LED0_RCC_CLOCKGPIO            RCC_APB2Periph_GPIOB
#define LED0_GPIO_PIN                 GPIO_Pin_5
#define LED0_GPIO                     GPIOB


#define LED1_RCC_CLOCKGPIO            RCC_APB2Periph_GPIOE
#define LED1_GPIO_PIN                 GPIO_Pin_5
#define LED1_GPIO                     GPIOE

			
/* 调用标准库函数方法 */
#define LED0_OFF                       GPIO_SetBits(LED0_GPIO,LED0_GPIO_PIN)
#define LED0_ON                      GPIO_ResetBits(LED0_GPIO,LED0_GPIO_PIN)

#define LED1_OFF                       GPIO_SetBits(LED1_GPIO,LED1_GPIO_PIN)
#define LED1_ON                     GPIO_ResetBits(LED1_GPIO,LED1_GPIO_PIN)


/*函数声明*/
void LED_Init(void);

#endif

此外,再对"LED_Init"函数中的输入的变量用宏定义的变量替换,这样修改宏定义的时候,对应的初始化函数也会随之修改,这样就能十分方便地移植了。

标签:LED1,LED0,LED,PIN,Pin,笔记,STM32,流水,GPIO
From: https://blog.csdn.net/savage_hurricane/article/details/137005840

相关文章

  • 《C++primer》读书笔记---第九章:顺序容器
    9.1顺序容器概述下表列出了标准库的顺序容器,所有容器都提供了快速顺序访问元素的能力:多种容器中,通常使用vector是最好的选择,除非你有很好的理由选则其他容器。以下是一些选择容器的基本原则:除非你有很好的理由选择其他容器,否则选择vector如果你的程序有很多小的元素,且空......
  • 【Python爬虫--scrapy+selenium框架】超详细的Python爬虫scrapy+selenium框架学习笔记
    六,selenium想要下载PDF或者md格式的笔记请点击以下链接获取python爬虫学习笔记点击我获取Scrapy+selenium详细学习笔记点我获取Python超详细的学习笔记共21万字点我获取1,下载配置##安装:pipinstallselenium##它与其他库不同的地方是他要启动你电脑上的浏览器......
  • 【Python--openCV图像处理】Python学习-OpenCV图像处理基础超详细的学习笔记(黑马程序
    一,openCV基础说明:笔记是跟着B站黑马程序员的openCV课程时做的课程资料可以在黑马程序员评论区获取1,图像基本操作1-1图像基础操作1-1-1安装相关库pipinstallopencv-pythonpipinstallopencv-contrib-python##尽量保持两个库安装的版本,比如我都是4.9.0.80ope......
  • CSS(4)(学习笔记)
    一、定位1.1为什么需要定位有些效果,标准流或浮动都无法快速实现,此时需要定位来实现。(浮动不会压住文字和图片,浮动指挥影响后面的盒子不会影响前面的盒子)比如:所以:1.浮动可以让多个块级盒子一行没有缝隙排列显示,经常用于横向排列盒子。2.定位则是可以让盒子自由的在某个盒......
  • 吴恩达 机械学习笔记1
    机械学习的两个主要模型:笔记右下角有页码1.监督学习......
  • 期末复习笔记
    常规1.基本输入输出:一般用cin型输入输出即可,主要包括一下几种类型:inta;doublea;floata;//一般需要用浮点数的变量直接用double就行cin>>a;cout<<a;:(特殊的题目要求我们保留几位小数)printf("%.3lf",a);//注:里面的.3是根据需要保留几位小数而写的,这里是保留3位小数......
  • 杂项——STM32ZET6要注意的一些问题——高级定时器问题和PB3,PB4引脚问题
    ZET6可能会用到定时器,高级定时器要输出PWM要加上这样一行代码,否则无法正常输出PWM波TIM_CtrlPWMOutputs(TIM8,ENABLE); //主输出使能,当使用的是通用定时器时,这句不需要ZET6中PB3,PB4引脚默认功能是JTDO和NJTRST,如果想将其当作正常IO口使用需要加上两行代码 RCC_APB2Pe......
  • 数据结构复习笔记4:队列,双端队列,循环队列
    1.队列概念和特点        队列是⼀种特殊的线性表,特殊之处就在于它只允许在表的前端进⾏删除操作,在表的后端进⾏插⼊操作。和栈⼀样,队列也是⼀种操作受到限制的线性表。进⾏插⼊操作的端称之为队尾,进⾏删除操作的端称之为队头。队列中没有队列的时候,称之为空队列。......
  • Caliburn.Micro框架学习笔记——Action的参数传递机制
    据此篇文章,我们继续来谈谈Caliburn.Mirco的Action参数传递机制。因此程序结构都是默认MVVM的形式。基本机制它的机制是——Caliburn.Micro的智能对象参数绑定机制通过约定和反射使得视图和视图模型之间的交互变得更加直观和简洁。通过cal:Message.Attach语法(附加属性的......
  • FFmpeg开发笔记(二十五)Linux环境给FFmpeg集成libwebp
    ​《FFmpeg开发实战:从零基础到短视频上线》一书介绍了JPEG、PNG、GIF等图片格式,以及如何通过FFmpeg把视频画面转存为这些格式。除了上述这些常见的图片格式,还有较新的WebP格式,它由VP8视频标准派生而来,VP8演进的视频格式叫做WebM,图片格式则叫WebP。若想让FFmpeg支持WebP图片的编......