ReRain
认证:普通会员
所在专题目录 查看专题
【蓝桥杯嵌入式】一、STM32之LED配置
【蓝桥杯嵌入式】二、STM32之按键+蜂鸣器
【蓝桥杯嵌入式】三、STM32之定时器配置
【蓝桥杯嵌入式】四、关于STM32的定时器配置中时钟分割的问题
【蓝桥杯嵌入式】五、STM32之通用定时器输出PWM波
【蓝桥杯嵌入式】六、关于STM32的定时器配置中时钟分割的问题
作者动态 更多
【蓝桥杯单片机】十二、底层练习
2021-04-20 17:46
【蓝桥杯单片机】十一、第十届蓝桥杯省赛失败总结加试题分析
2021-04-11 20:50
【蓝桥杯单片机】十、第九届蓝桥杯国赛之“多功能测量仪表”
2021-04-01 16:36
【蓝桥杯单片机】九、第九届省赛之彩灯控制器
2021-03-30 18:19
【蓝桥杯单片机】八、第八届省赛之电子钟
2021-03-29 18:41

【蓝桥杯嵌入式】一、STM32之LED配置

导读:开启新的专栏啦~希望大家可以多多支持呀!!!《蓝桥杯嵌入式》专栏文章是博主在自学stm32期间所做的学习笔记,同时为了备战蓝桥杯比赛,博主在学习期间所用的开发板为蓝桥杯嵌入式专用开发板。菜鸟思维,写的可能会有一点繁琐,但是我觉得学习是要一步一步来的,也希望自己可以把每一点都弄懂。希望可以对初学者有些帮助,如有错误,欢迎评论区留言指正哦~


开始学习之前当然要先搭建好学习环境,关于软件的下载以及工程搭建,网上有很多博主写的都很详细,下面给出两篇参考博客,也是我在学习过程中借鉴的博客(博主写的可以说超级详细了):


stm32编程方式有两种:一种是直接操作寄存器,另一种是配置库函数, emmm,感觉大多数情况下还是库函数比较好用,配置寄存器的话要记的东西比较多,感觉我也记不住,哈哈。但是关于寄存器的知识我们还是要了解的,具体知识可以参照《STM32中文参考手册》来学习,下面我们来说说怎么实现LED的配置的。

开始写配置LED的函数之前,我们还是和学习51时一样,需要先看一下原理图,明确一下LED的电路结构:

从原理图上我们可以看到:单片机的各个引脚是通过一个573锁存器和LED相连的,这个573我们在51里也很常见,需要注意的就是只有在使能573的情况下才能实现端口数据的传输,也就是说我们需要把N-LE对应的引脚拉高。 接下来我们找一下红线框部分对应的引脚(emmm,这里和51就不太一样,可以看一下图)

上面分别是J1和J2两个排针,上面的引脚是一一对应的,从开发板上我们也可以看到,这两个排针的对应引脚是用跳线帽连接在一起的(感觉很方便呀) N-LE对应的是PD2,而D0到D7这八个LED分别对应PC8-PC15

那么LED的配置是配置什么呢? 引脚模式,也可以理解成一种初始化函数,具体步骤是:配置端口时钟(时钟使能),设置引脚号,设置引脚速率,配置端口模式,配置输出数据。

下面我们就按照步骤来一步一步完成LED的配置:

一、配置端口时钟 

明确 STM32的GPIO外设是挂接在APB2总线上的,所以要完成使能我们就需要设置APB2外设时钟使能寄存器,这时候可以查阅手册看一下APB2外设时钟使能寄存器的相关知识:

图上红色线框部分是GPIO外设的相关时钟,而我们从原理图上可以得知,LED配置需要使能的时钟是:GPIOC(IO端口C)和GPIOD(IO端口D)

置1表示使能 下面我们用两种方法来实现一下时钟的使能,也顺便理解比较一下stm32编程的两种方法:

1、配置寄存器法: 在stm32f10x.h文件(库函数内)中我们可以找到外设时钟使能寄存器的相关定义:

RCC的声明,使用RCC_TypeDef类型指针对RCC端口时钟地址进行强制类型转换,也就是说把RCC_BASE强制转换成一个结构体指针,然后通过宏定义,替换成用RCC表示的,这样我们在结构体中定义的各个变量也就相应的移植到了RCC内部的地址空间。

好了,明确了上面的那些内容,我们就可以进行相应时钟(GPIOD和GPIOC)的使能了,GPIOC和GPIOD分别对应位4和位5,所以我们下面要做的就是让APB2外设时钟使能寄存器的位4和位5置1,看代码:

RCC->APB2ENR |= (1 << 4);//使能GPIOC时钟
RCC->APB2ENR |= (1 << 5);//使能GPIOD时钟

我们上面解释过了RCC是一个结构体指针,APB2ENG是这个结构体内部声明的一个变量,所以在调用时就需要用 "->" 这个符号,下面以使能GPIOC为例解释一下为什么可以实现位4置1(就是将APB2外设时钟使能寄存器的第五位置成1): 

先来看1 << 41用16进制表示出来是:00000001,左移四位变成了00010000

再看RCC->APB2ENR |= (1 << 4)这段代码:APB2外设时钟使能寄存器的复位值是00000000,和00010000进行运算,结果是:00010000,实现了位4置1,并且不改变其它位

注意:位是从0 开始表示的,所以位4实际上就是寄存器的第5位

2、调用库函数法: STM32有着非常好用的库函数,功能很全也很多,其中就有初始化时钟的函数,在stm32f10x_rcc.h文件中可以找到相关定义,然后可以点击函数名按快捷键F12直接定位到函数体,然后我们就可以明白各个变量的含义了:

第一个变量表示使能哪一个时钟,NewState表示使能(ENABLE)还是不使能(DISABLE)

看代码:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE);//使能GPIOD和GPIOC

/*还可以分开写*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//使能GPIOC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);//使能GPIOD

三、设置引脚相关

引脚号,引脚速率,引脚模式,也就是初始化引脚配置,我们这里就直接用库函数配置的方法来做:

STM32的IO模式(引脚模式)有8种:模拟输入、浮空输入、上拉输入、下拉输入、通用推挽输出、通用开漏输出、复用推挽输出、复用开漏输出这8种模式比较重要,想要详细了解的话,可以参照下面的博客:https://blog.csdn.net/techexchangeischeap/article/details/72569999此外输出模式还包括3种输出速率,emmm,可以想见我们的库函数里对于这一部分肯定也是用结构体封装的。

stm32f10x_gpio.h文件里我们可以找到相关定义:

stm32f10x_gpio.h文件里我们可以找到对应的端口引脚定义:

ok,现在我们来写引脚模式配置的部分,以GPIOD(PD2)为例说明

思想就是:先定义一个GPIO_InitTypeDef类型的结构体变量,然后为这个结构体的各个变量赋值,然后再调用初始化函数

GPIO_InitTypeDef GPIO_InitStructure;//定义一个结构体变量

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//设置引脚号
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;//设置引脚速率10MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//设置为通用推挽输出模式
GPIO_Init(GPIOD, &GPIO_InitStructure);//调用初始化函数,初始化引脚配置

关于初始化函数,在stm32f10x_gpio.h文件里可见定义,还是同样的操作,按快捷键F12,找到对应的函数体:

关于GPIOC相关引脚的配置,在设置引脚号时有技巧: 我们如果一个一个引脚定义的话,需要从PC8定义到PC15,太麻烦了,我们可以把对应的地址相加,得到结果是0xFF00,就实现了这8个引脚号的配置了:

四、配置输出数据

说白了就是给我们上面定义的引脚赋一个初值(我们默认上电LED的状态是关闭的,所以这里要把相关引脚拉高),但是要注意LED相关引脚的赋值要在使能573的情况下进行,这里我们可以操作寄存器,也可以操作相应的库函数,emmm,前面对两种方法已经作了比较,这里就不过多说明,我觉得这里操作寄存器比较方便,就说下操作寄存器的方法吧: 这里就又涉及到了一个知识,就是GPIO端口寄存器的结构,读者可以在手册上查找到相关内容,这里我只说一下端口输出数据寄存器(要用到的):

这个部分和RCC时钟配置差不多,思想也是一样的,相关结构体的定义在stm32f10x,h文件里可以找到,下面贴出:

一样的思想,还是强制类型转换,通过宏定义将GPIOA等定义为和GPIO_TypeDef同类型的结构体指针

看代码:

GPIOC->ODR |= 0xFF00;//将PC8-PC15引脚拉高
GPIOD->ODR |= (1 << 2);//拉高PD2,使能573
GPIOD->ODR &= ~(1 << 2);//关闭使能

下面是完整代码:

#include "stm32f10x.h"

void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE);//使能GPIOD和GPIOC
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//设置引脚号
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;//设置引脚速率10MHZ
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//设置为通用推挽输出模式
    GPIO_Init(GPIOD, &GPIO_InitStructure);//调用初始化函数,初始化引脚配置
    
    GPIO_InitStructure.GPIO_Pin = 0xFF00;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    GPIOC->ODR |= 0xFF00;//将PC8-PC15引脚拉高
    GPIOD->ODR |= (1 << 2);//拉高PD2,使能573
    GPIOD->ODR &= ~(1 << 2);//关闭使能
}

完毕~~


emmm,再给一个测试例程吧,实现L1每隔1s闪烁:

#include "stm32f10x.h"
#include "led.h"

u32 TimingDelay = 0;

void Delay_Ms(u32 nTime);

//Main Body
int main(void)
{
    SysTick_Config(SystemCoreClock/1000);
	LED_Init();
    
	while(1)
    {
        GPIOC->ODR ^= (1 << 8);
        GPIOD->ODR |= (1 << 2);
        GPIOD->ODR &= ~(1 << 2);
        Delay_Ms(1000);
    }
}

void Delay_Ms(u32 nTime)
{
	TimingDelay = nTime;
	while(TimingDelay != 0);	
}

解释一下 “GPIOC->ODR ^= (1 << 8);” 介个东东 ^异或符学过51的应该不陌生,很好用,这个异或是按位异或,对每一位都执行操作的,我们每隔1000ms让GPIOC->ODR中的数据和(1 <<8)相异或,也就是实现第8位每隔1s取反,视觉上就是L1每隔1s闪烁了:

  •  GPIOC->ODR初始化数据:11111111 
  • (1 << 8):10000000 
  • 按位异或 GPIOC->ODR ^= (1 << 8):01111111 
  • 实现了第8位每隔1s取反的操作~~~
声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 195
收藏 194
关注 302
成为作者 赚取收益
全部留言
0/200
  • dy-SgCRSAWd 2021-04-29 21:25
    感谢分享
    回复
  • dy-lZg1ikQS 2021-04-29 21:05
    大开眼界,真是好文
    回复
  • dy-6jaMNnKj 2021-04-29 11:06
    什么时候更新
    回复
  • dy-uP6R9bIG 2021-04-28 22:22
    精彩,很多东西还没接触到
    回复
  • dy-faUxdNVf 2021-04-28 22:17
    讲的真好!
    回复
  • dy-ARsdJtu2 2021-04-28 16:43
    围观学习
    回复
  • dy-PfBg9fHc 2021-04-28 15:18
    讲的真好!
    回复
  • dy-BntE74dS 2021-04-27 11:38
    感谢分享
    回复
  • dy-AstN3YsZ 2021-04-27 11:26
    佩服楼主
    回复
  • dy-kWQSvfcY 2021-04-26 11:29
    大开眼界,真是好文
    回复
  • dy-IRhxrrTG 2021-04-26 11:07
    精彩,很多东西还没接触到
    回复
  • dy-EWZRbIzj 2021-04-22 11:28
    佩服楼主
    回复
  • dy-HlVFyepq 2021-04-21 20:53
    佩服楼主
    回复
  • dy-fEyVNAbF 2021-04-21 15:54
    感谢博主!
    回复
  • dy-cfgdwamL 2021-04-21 13:00
    对我很有帮助
    回复
  • dy-ayNTwT3L 2021-04-21 12:30
    佩服楼主
    回复
  • dy-LiDZV1Qr 2021-04-20 18:11
    精彩,很多东西还没接触到
    回复
  • dy-rfrUF2fp 2021-04-20 17:04
    对我很有帮助
    回复
  • dy-bHww98js 2021-04-19 23:04
    不亚于看了一篇高质量论文
    回复
  • dy-FPtpScGU 2021-04-19 09:08
    不亚于看了一篇高质量论文
    回复