STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结

2023-05-16

摘自:STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结
作者:自信且爱笑‘
发布时间: 2021-04-28 21:17:40
网址:https://blog.csdn.net/Curnane0_0/article/details/116158776

学习板:STM32F103ZET6

跑马灯实验

  • 前言
  • 一、GPIO
    • 1、GPIO种类
    • 2、GPIO输入输出的8种模式:
    • 3、模式用途
  • 二、点亮LED(库函数版)
    • 1、库函数的由来
    • 2、点亮LED库函数版编写顺序
      • 1、GPIO_Init()
      • 2、RCC_APB2PeriphClockCmd()
      • 3、将配置函数放在led.c和led.h文件
  • 三、点亮LED(寄存器版本)
    • 1、新建工程、添加led文件
    • 2、配置时钟
    • 3、GPIO配置
    • 4、主函数代码编写
  • 附1 PXout()与PXin()

前言

本来准备先总结一下GPIO、几种输入输出模式以及配置方法、寄存器种类、时钟及分频等,但是想了想,还是算了,一步步的来,到时候用到再总结吧 ,不然前面总结了,后面用到还得回顾。

至于前面的安装库、安装软件、Keil中添加文件等,就不在总结了,因为这些根本并不需要记的,新手可以去跟着视频走一遍。学习过程中,不用每次都自己去新建工程,直接将官方给的模板拷过来,修改一下文件夹名称即可。
在这里插入图片描述

一、GPIO

1、GPIO种类

本博板子STM32F103ZET6共有7组IO口,每组16个,共16×7=112个,分别为:
GPIOA——>PA0、PA1、PA2…PA15
GPIOB——>PB0、PB1、PB2…PB15
.
.
.
GPIOG——>PG0、PG1、PG2、…PG15

2、GPIO输入输出的8种模式:

程序中标识模式
GPIO_Mode_AIN模拟输入
GPIO_Mode_IN_FLOATING浮空输入
GPIO_Mode_IPD下拉输入
GPIO_Mode_IPU上拉输入
GPIO_Mode_Out_OD开漏输出
GPIO_Mode_Out_PP推挽输出
GPIO_Mode_AF_OD复用开漏输出
GPIO_Mode_AF_PP复用推挽输出

3、模式用途

1、 GPIO_Mode_AIN :模拟输入
一般用于ADC模拟输入

2、GPIO_Mode_IN_FLOATING :浮空输入
可用于按键KEY实验、发送接收信号RX、TX等,不过这些实验可以不用浮空输入,如KEY用到上拉和下拉

3、GPIO_Mode_IPD:下拉输入
4、GPIO_Mode_IPU:上拉输入
IO内部上拉电阻、下拉电组输入,使情况而定,比如刚刚说的key按键实验,原理图如下:

在这里插入图片描述

可以看到KEY_UP按下后,IO口应该是3V3电平输入,未按下时为悬空状态,而悬空状态IO输入是未知的,所以为了防止程序跑飞,采用下拉输入,在悬空状态下,使IO输入下拉到低电平。这样,在悬空状态下,IO检测到的是低电平,不会去执行key_up按下后的程序。

KEY0~2按下后,IO口是低电平输入。按下时为悬空状态,而悬空状态IO输入是未知的,所以为了防止程序跑飞,采用上拉输入,在悬空状态下,使IO输入上拉到高电平。这样,在悬空状态下,IO检测到的是高电平,不会去执行KEY0 ~2按下后的程序。

5、GPIO_Mode_Out_OD:开漏输出
IO 输出 0 接 GND,IO 输出 1,悬空,需要外接上拉电阻,才能实现输出 高电平。当输出为 1 时,IO 口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样 IO 口也就可以 由外部电路改变为低电平或不变。该模式适合做电流型的驱动,吸收电流能力比较强。

6、GPIO_Mode_Out_PP:推挽输出
可以输出高、低电平。导通损耗小、效率高。既提高电路的负载能力,又提高开关速度。广泛各种实验,比如接下来要总结的LED。

7、GPIO_Mode_AF_OD:复用开漏输出
当GPIO为复用IO时的开漏输出模式,一般用于外设功能,如TX1

8、GPIO_Mode_AF_PP:复用推挽输出
当GPIO为复用IO时的推挽输出模式,一般用于外设功能,如I2C

二、点亮LED(库函数版)

1、库函数的由来

在学习库函数之前,应该明白,STM32F1用的是Cortex-M3芯片,是由ARM公司设计的。所以芯片的标准是由ARM公司制定的,芯片内核架构有ARM公司提供。而我们现在用的STM32由ST公司生产,所以关系是:ARM制定内核架构,ST等芯片公司根据ARM公司的标准设计了芯片。ST等公司设计的芯片,不同的是存储容量、外设、串口数量等等。

以本博的学习板STM32F103ZET6为例,固件库(库函数的集合)是由官方提供的,这个官方是ST公司,而不是正点原子官方。也就是说不仅仅这一型号单片机,ST系列其他型号的单片机库函数依旧可以适用。所以不必担心更换板子后不知如何去编程。

ST公司推出官方固件库,将底层寄存器操作都封装起来,形成一套接口(API)供我们使用,大多数情况下我们不必去考虑底层寄存器。比如本博的LED,只需调用GPIO配置函数、时钟配置函数,然后主函数初始化后,直接给引脚赋值就可以实现LED的亮灭,而不用去考虑寄存器如何工作的。当然本博会把寄存器版的LED也总结一下。毕竟想要真正理解单片机,还得去真正理解寄存器,库函数版只是让我们停留在“会使用”。当然,对应大多数人来讲,“会使用”已经完全足够了。

2、点亮LED库函数版编写顺序

1、设置时钟
2、设置GPIO

只要这两步的配置,再在主函数中给对应引脚传输高低电平即可。

打开原理图文件(下图我打开了6个文件,都是需要的,而且大部分情况下,有这6个文件足以。都在板子附带资料的文件夹里
在这里插入图片描述
在这里插入图片描述

从原理图中得到以下信息:

①DS0 LED0阳极接+,阴极接PB5;DS1 LED1阳极接+,阴极接PE5。

②SYS LED由名称“PWR”顾名思义,为电源指示灯,所以单片机接通电源后,电源指示灯常亮。

③芯片的PB5引脚软件置0后,LED0亮;PE5引脚软件置0后,LED1亮。

所以要配置GPIOB(因为PB5)和GPIOE(因为PE5)。

然后是时钟设置,只要是对GPIO操作,就必须进行时钟配置(而且时钟配置在前)。GPIO是挂载在APB2总线上的外设,所以在对GPIO的时钟进行设置时,通过函数RCC_APB2PeriphClockCmd()来实现。

下面进入实战:

打开模板文件:(时间久远了,不知道模板文件原来放哪个文件夹下,找不到的话可以把LED官方例程打开,关于LED的.c和.h文件删掉,主函数清空,就可以当以后的模板来用了,不用每次都创建工程)
在这里插入图片描述在这里插入图片描述

首先查看GPIO配置函数,既然是GPIO,那么先找一下头文件,在main.c下找GPIO头文件,并点击进入。

在这里插入图片描述

在这里插入图片描述

找到对应函数:(下一博客总结所有GPIO函数的用法、以及延时函数)

在这里插入图片描述

上图标注,GPIO_Init()函数初始化,进行设置GPIO,GPIO_SetBits()函数给对应引脚置1,GPIO_ReSetBits()函数给对应引脚置0。

1、GPIO_Init()

右键选中函数,点击【Go to Definition of …】,进入函数详细说明
在这里插入图片描述在这里插入图片描述

可以看到,函数的形参有两个,而且都是指针。进入第一个形参“类型”

在这里插入图片描述
在这里插入图片描述

看到GPIOx指针是指向上图这个结构体的,也就是每组GPIO都包含的7个寄存器。

比如LED实验,传递GPIOB(PB5)过来后,*GPIOB就指向这七个寄存器,初始化函数就是对七个寄存器的操作,不过被库函数封装起来了,emmmm…说太多了,只要知道GPIO_Init()传过来的第一个参数表示对该组GPIO配置就行了。

察看第二组形参“类型”

在这里插入图片描述
在这里插入图片描述
看到第二个形参也是结构体指针,指向的结构体含有三个参数GPIO_Pin、GPIO_Speed、GPIO_Mode

到这里就可以用C++语法来说明了。比如第二个形参是a(注意是指针),那么:
a.GPIO_Pin=…
a.GPIO_Speed=…
a.GPIO_Mode=…
就完成了对参数GPIO参数的设置。

接下来我们再看看上面三个赋值语句的右边究竟是什么东西:
转回到初始化函数:

在这里插入图片描述
在这里插入图片描述1表示第二个形参
2表示对 GPIO_Init()的第一个形参的处理(就是那个结构体里有7个寄存器的东西)
3表示mode的配置
4表示pin的配置
5表示速speed的配置。

点开GPIO_Mode设置函数:

在这里插入图片描述

可以看到就是我们第一大部分总结的8中输入输出模式

在这里插入图片描述

点开pin设置函数

在这里插入图片描述

可以看到pin是我们第一大部分总结的一组GPIO的15个IO口

在这里插入图片描述

点开速度设置函数

在这里插入图片描述

可以看到速度可设置的值:

在这里插入图片描述
到这里,GPIO的设置函数应该会写了:

第一步:设置形参1和形参2
第二步:上面那三个赋值语句的设置
第三步:运行GPIO_Init()函数

程序:(先在主函数中书写,.c文件中书写接下来会总结

	GPIO_TypeDef GPIO_B;//形参1
	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(&GPIO_B,&GPIO_InitStruct);

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注意GPIO_Init()传递的是指针,所以应该用取地址符“&”。

上面的程序还是有问题的,因为定义了GPIO_B为第一个参数,但是程序并不知道GPIO_B是对GPIOB的操作,所以在 GPIO_Init(&GPIO_B,&GPIO_InitStruct);语句中,“&GPIO_B”应该是真正的、物理上的地址,而不能像参数2一样,只是程序定义参数时分配的地址。

输入“GPIOB”,并进入

在这里插入图片描述

发现官方真的定义了GPIOB,而且还是真正的、物理层的地址

在这里插入图片描述在这里插入图片描述在这里插入图片描述

所以之前程序中的GPIO_B可以删掉了,不是物理层的地址,定义了、传递给GPIO_Init()函数也没用。

正确完整程序:

//GPIO_TypeDef GPIO_B;//形参1
	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	 //注意GPIOB是地址!GPIO_InitStruct是指针,传递过去后的俩个实参数都是指针。

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

以上配置了GPIOB是为了点亮LED0,现配置LED1(PE5)的GPIO,参考上面的程序:

	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

因为之前设置了mode和speed,而实参2是没有指向的,即并不能知道实参2属于实参1,所以哪怕再重新定义一个GPIOE的实参2,重新定义mode和speed也没有意义,所以就可以省略了。

接下来可以将LED引脚置高电平,熄灭LED。使初始状态下LED是灭的。

int main(void)
 {	
	
	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	 
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
	
 }

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2、RCC_APB2PeriphClockCmd()

之前说过,GPIO是挂载在APB2总线上的外设,所以在对GPIO的时钟进行设置时,通过函数RCC_APB2PeriphClockCmd()来实现。打开RCC.h头文件,找到时钟函数

在这里插入图片描述

同样的方法确定形参类型

在这里插入图片描述

在这里插入图片描述

形参1:

在这里插入图片描述

形参2:

在这里插入图片描述

所以程序:(注意时钟配置函数应该放在最前面

	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE , ENABLE);
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	 
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

加入延时函数,形成流水灯:(只在Main.c文件编程)
其中GPIO_SetBits(GPIOB, GPIO_Pin_5)是将PB5引脚置1;GPIO_ReSetBits(GPIOB, GPIO_Pin_5)是将PB5引脚置0
(是通过库函数对BSRR和BRR寄存器操作完成置0置1,下一博客会涉及到)

#include "stm32f10x.h"
#include "delay.h"
 int main(void)
 {	
	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE , ENABLE);
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	 
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
	
	 delay_init();	    //延时函数初始化	  
	while(1)
	{
		GPIO_SetBits(GPIOB, GPIO_Pin_5);
		GPIO_SetBits(GPIOE, GPIO_Pin_5);
		delay_ms(1000);                       //注意包含头文件delay.h,这个好像是正点原子官方写的
		GPIO_ResetBits(GPIOB, GPIO_Pin_5);
		GPIO_ResetBits(GPIOE, GPIO_Pin_5);
		delay_ms(1000); 
	}
 }

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

3、将配置函数放在led.c和led.h文件

在LED使用的工程文件夹新建一个LED文件夹

在这里插入图片描述

进行下一步骤:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

创建一个text文件,命名为led.h,保存在LED group中

在这里插入图片描述

将头文件添加进来

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

同理。新建一个led.c文件,将.c文件也添加进来

在这里插入图片描述

上述步骤是创建一个LED Group,现在将头文件添加进来:

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

找到刚刚创建的文件夹并添加

在这里插入图片描述
上述步骤是为了之后调用里面生成的led.c和led.h

编辑头文件:
固定格式:

#ifndef  一个未定义字符串
#define 一个未定义字符串

#include ...
#include ...  //各种需要在本.h文件中用到的头文件
...
...//一些函数声明、甚至定义	 				    
#endif

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

本实验led.h文件可这样写:

#ifndef __LED_H //led.h文件
#define __LED_H	 
void LED_Init(void);//初始化 				    
#endif

 
 
 
 
  • 1
  • 2
  • 3
  • 4

接下来编辑led.c文件
需要有本.c文件用到的头文件,如果要用到别的文件中定义的变量,可以采用外部声明重新声明该变量。在.c文件实现.h文件声明的函数

将我们之前main函数中关于GPIO配置和时钟声明的函数移植过来得到完整的LED程序:

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

/**led.h**/
#ifndef __LED_H //led.h文件
#define __LED_H	 
void LED_Init(void);//初始化 		
#endif

/**led.c**/
#include "led.h"
#include "stm32f10x.h"
void LED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;//形参2
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE , ENABLE);
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//形参2.mode=推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//形参2.pin=5
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//形参2.speed=50MHZ
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
}

/**main.c**/
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
 int main(void)
 {	
	LED_Init();
	delay_init();	    //延时函数初始化	  
	while(1)
	{
		GPIO_SetBits(GPIOB, GPIO_Pin_5);
		GPIO_SetBits(GPIOE, GPIO_Pin_5);
		delay_ms(1000);                       //注意包含头文件delay.h,这个好像是正点原子官方写的
		GPIO_ResetBits(GPIOB, GPIO_Pin_5);
		GPIO_ResetBits(GPIOE, GPIO_Pin_5);
		delay_ms(1000); 
	}
 }

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

三、点亮LED(寄存器版本)

1、新建工程、添加led文件

毕竟是32系列第一个博客,还是希望详细一点,之后的实验就会慢慢省略一部分东西,寄存器版本完整来一遍

打开模板文件,如果没有,就把LED库函数例程打开,删掉led.c和led.h,当做以后所有实验的模板就行了。

一般情况下,应该【HARDWARE】中创建.c文件。也可以在文件【HARDWARE】之外建,看个人习惯。

如果懒得去搞模板,就用我这个吧

先将模板文件拷过来,在HARDWARE文件夹下创建LED文件夹

在这里插入图片描述

打开模板文件

在这里插入图片描述

新建两个text文件,保存在刚刚创建的LED文件夹下,并改名为led.c、led.h

在这里插入图片描述

将led.c文件添加进工程

在这里插入图片描述在这里插入图片描述

将LED文件目录添加进来

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述
现在进入实战

led.h编辑,之前库函数版本讲过了,直接附代码:

#ifndef LED_H  //led.h文件
#define LED_H
int LED_Init(void);
#endif

 
 
 
 
  • 1
  • 2
  • 3
  • 4

led.c文件编辑
开始还是老规矩:

#include "led.h"
#include "stm32F10x.h"

int LED_Init(void)
{

}

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、配置时钟

打开《STM32中文参考手册》7.3.7 APB2外设时钟使能寄存器(RCC_APB2ENR)

在这里插入图片描述在这里插入图片描述

寄存器下一博客总结,现在只需知道,APB2外设时钟使能寄存器的第3位和第6位分别对应GPIOB(LED0、DS0)和GPIOE(LED1、DS1)

时钟使能代码如下:

RCC->APB2ENR|=1<<3;
RCC->APB2ENR|=1<<6;

 
 
 
 
  • 1
  • 2

解释一下:

首先RCC->APB2ENR是对外设时钟使能寄存器的访问
在这里插入图片描述

RCC->APB2ENR|=1的意思是:RCC->APB2ENR=RCC->APB2ENR|0x00000001(32位寄存器),也就是说将该寄存器的第0位软件置1,其它位保持不变。"<<3"是将刚刚设置的那个第0位的1左移3位,也就是此时第3位为1.同理“<<6”是将第6位设置为1;此时就使能了GPIOB和GPIOE的时钟

在这里插入图片描述

3、GPIO配置

用到端口配置寄存器,由于是对PB5、PE5的配置,是低位IO口(Px0~Px7是低位、Px8 ~Px15是高位),所以用到端口配置低寄存器GPIOx_CRL

打开《STM332中文参考手册》
在这里插入图片描述

看到第21、20位控制模式和速度,为50M输出,所以这两位是11;23 、22控制哪种输出,为推挽输出,所以这两位为00,所以GPIOx_CRL的状态值为:0x00300000;程序如下:

    GPIOB->CRL&=0xff0fffff; //PB5
	GPIOB->CRL|=0x00300000;
	
	GPIOE->CRL&=0xff0fffff; //PE5
	GPIOE->CRL|=0x00300000;

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

解释一下:

GPIOB->CRL&=0xff0fffff 是将GPIOB的20、21、22、23这四位置0,其它位保持不变;

GPIOB->CRL|=0x00300000是将GPIOB的20、21、22、23这四位置1,其它位保持不变;

此时配置好了,然后可以给IO口赋初值,如开始时让LED处于熄灭状态,则PB5、PE5均置1。用到的寄存器:端口输出数据寄存器GPIOx_ODR

在这里插入图片描述

代码:

	GPIOB->ODR|=1<<5;
	GPIOE->ODR|=1<<5;

 
 
 
 
  • 1
  • 2

led.c文件完整代码:

#include "led.h" //led.c文件
#include "stm32F10x.h"
int LED_Init(void)
{
	RCC->APB2ENR|=1<<3;
	RCC->APB2ENR|=1<<6;
	
	GPIOB->CRL&=0xff0fffff; //PB5
	GPIOB->CRL|=0x00300000;
	
	GPIOE->CRL&=0xff0fffff; //PE5
	GPIOE->CRL|=0x00300000;
	
	GPIOB->ODR|=1<<5;
	GPIOE->ODR|=1<<5;
}

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4、主函数代码编写

进入主函数后,首先应调用刚刚写的LED初始化函数,完成GPIO配置;程序会用到延时函数,将延时函数也初始化,代码:(头文件包含led.h)

	#include "sys.h"
	#include "delay.h"
	#include "led.h"
	
	 int main(void)
	 {	
		LED_Init();
		delay_init();
	 }

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后在死循环中,对PB5和PE5 IO口赋值就行了,还是用到端口输出数据寄存器GPIOx_ODR

不过给IO口置0时,需要注意,应该和0xffffffdf进行与运算
在这里插入图片描述

	GPIOB->ODR&=0xffffffdf;//置0
	GPIOE->ODR&=0xffffffdf;//置0

 
 
 
 
  • 1
  • 2

或者移位运算,将第0位置0再向左移5位

	GPIOB->ODR&=0xfffffffe<<5;
	GPIOE->ODR&=0xfffffffe<<5;

 
 
 
 
  • 1
  • 2

给IO口置1就和0x00000020进行或运算

在这里插入图片描述

 	GPIOB->ODR|=0x00000020;
	GPIOE->ODR|=0x00000020;

 
 
 
 
  • 1
  • 2

或者直接位移运算,先和0x00000001进行或运算,使第0位置1,再将第0位向左移动5,代码:

	GPIOB->ODR|=1<<5;
	GPIOE->ODR|=1<<5;

 
 
 
 
  • 1
  • 2

main.c文件完整程序:

#include "sys.h" //main.c文件
#include "delay.h"
#include "led.h"

 int main(void)
 {	
	LED_Init();
	delay_init();
	 while(1)
	 {
		GPIOB->ODR&=0xffffffdf;//置0
		GPIOE->ODR&=0xffffffdf;//置0
		//GPIOB->ODR&=0xfffffffe<<5;
		//GPIOE->ODR&=0xfffffffe<<5;
		 
		 delay_ms(1000);
		
		 GPIOB->ODR|=0x00000020;
		 GPIOE->ODR|=0x00000020;
		//GPIOB->ODR|=1<<5;
		//GPIOE->ODR|=1<<5;
		delay_ms(1000);
	 }
 }



 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

附1 PXout()与PXin()

打开sys.h头文件

在这里插入图片描述
定义了PXout()与PXin()函数,用法为:

点亮熄灭DS0、DS1,只需:

PBout(5)=0;//点亮
PEout(5)=0;//点亮
delay_ms(1000);
PBout(5)=1;//熄灭
PEout(5)=1;//熄灭
delay_ms(1000);

 
 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

或者定义:

#define LED0 PBout(5)
#define LED1 PEout(5)

LED0=0;//点亮
LED1=0;//点亮
delay_ms(1000);
LED0=1;//熄灭
LED1=1;//熄灭
delay_ms(1000);

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

STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结 的相关文章

  • (九)Java算法:快速排序(详细图解)

    目录 一 前言1 1 概念1 2 算法过程 二 maven依赖三 流程解析3 1 全部数据分区3 2 左边数据分区3 3 右边数据分区 四 编码实现结语 一 前言 1 1 概念 快速排序 xff1a 用数组的第一个数作为基准数据 xff0c
  • 【Linux】树莓派控制光强传感器(C、python手把手教学)

    本文分为三个部分 xff1a 1 光强传感器说明 2 程序解读 3 前期准备 xff08 放在最后一部分 xff0c 供小白查阅借鉴 xff0c 包括本文需要用到的wiringPi库函数 xff09 一 光强传感器说明 1 TSL256x
  • Ubuntu安装VNC,配置多用户vnc连接Ubuntu,开机自启vnc命令

    Ubuntu安装VNC span class token function sudo span span class token function apt span update span class token function sudo
  • 解决登陆github慢的问题

    解决方法 首先本文解决的问题是Github网站可以访问 xff0c 但是由于网络代理商的原因 xff0c 造成访问速度很慢 Ping www github com 时 xff0c 速度只有200多ms 解决思路 xff1a 1 可以花钱购买
  • 什么是反卷积(快速理解)

    什么是反卷积 参考博客 我们知道输入图像通过卷积神经网络 xff08 CNN xff09 提取特征后 xff0c 输出的尺寸往往会变小 xff0c 而又是我们需要将图像恢复到原来的尺寸以便进行进一步的计算 xff0c 整个扩大图像尺寸 xf
  • 李雅普诺夫稳定(内部稳定)与BIBO稳定(外部稳定)的关系

  • 情绪识别论文阅读

    情绪识别论文阅读 情感脑机接口研究综述一种基于情感脑电信号时 频 空特征的3D密集连接网络 1 吕宝粮 张亚倩 郑伟龙 情感脑机接口研究综述 J 智能科学与技术学报 2021 3 01 36 48 情感脑机接口研究综述 情感脑机接口研究面临
  • 一文详细介绍情绪识别常用的数据集

    一文详细介绍情绪识别常用的数据集 SEED采集情况文件介绍 SEED IV采集情况文件介绍 CIAIC多模态情感识别数据采集情况文件介绍 DEAP采集情况文件情况 SEED V采集情况文件情况 本文详细介绍了脑机接口情绪识别常用的数据集 x
  • 父子进程虚拟地址空间情况

    父子进程虚拟地址空间情况 笔记来源于牛客网 Linux多进程开发 The child process and the parent process run in separate memory spaces At the time of f
  • Pytorch中nn.Module中的self.register_buffer解释

    self register buffer作用解释 今天遇到了这样一种用法 xff0c self register buffer name Tensor xff0c 该方法的作用在于定义一组参数 该组参数在模型训练时不会更新 xff08 即调
  • leetcode_回溯算法

    回溯算法刷题总结 回溯法理论基础回溯算法的模板组合问题77 组合优化版本 216 组合总和III17 电话号码的字母组合组合总和组合总和II 分割131 分割回文串93 复原IP地址 子集78 子集90 子集II491 递增子序列 xff0
  • React styled-components(二)—— props、attrs属性

    styled components props attrs属性 96 props 96 96 props 96 穿透添加 96 attrs 96 属性获取 96 state 96 中的样式 变量控制样式通过 96 props 96 控制样式
  • leetcode_贪心算法

    贪心算法相关题 简单题目455 分发饼干1005 K次取反后最大化的数组和860 柠檬水找零 序列问题376 摆动序列法一 xff1a 贪心法法二 xff1a 动态规划 单调递增的数字简化版本 有点难度53 最大子序和贪心算法动态规划 13
  • leetcode_动态规划

    leetcode 动态规划 基础题目509 斐波那契数70 爬楼梯62 不同路径63 不同路径II343 整数拆分96 不同的二叉搜索树 01背包分割等和子集1049 最后一块石头的重量II494 目标和474 一和零 完全背包518 零钱
  • 北东地/东北天两种导航坐标系与姿态转换

    一 坐标系 1 导航坐标系 常用的导航坐标系有北东地和东北天两种 两种坐标系的指向分别定义如下 xff1a 1 1 北东地坐标系 X轴 xff1a 指北 Y轴 xff1a 指东 Z轴 xff1a 指地 1 2 东北天坐标系 X轴 xff1a
  • 如何消除CACHE对DMA的影响?

    各位看官 xff0c 阅读之前 xff0c 请帮忙点击这里一下 xff0c 多谢 xff01 嵌入式系统中的CACHE 问题 郑州解放军信息工程大学 xff08 450002 xff09 陈曦 李汉宁 随着社会的发展 人们生活水平的提高 x
  • 12种性格导致贫穷

    每个人都有不同的性格和个性 有的性格对于挣钱是有帮助的 有的性格却容易导致贫困 大量的研究表明 至少有十二种性格容易导致贫困和失败 第一 知足 只要有吃有穿 腹饱体暖 就感到满足 对于财富没有追求 第二 自满 自己的总是最好的 这种人不愿与
  • 上海房地产市场救市14条政策出台

    值得纪念的一天 http www sina com cn 2008年10月23日05 53 东方网 东方网10月22日消息 xff1a 为促进本市房地产市场健康发展 xff0c 根据财政部 国家税务总局 中国人民银行等部门有关文件精神 xf
  • 天涯上各大公司裁员应对金融危机的强帖,不断更新中……

    http www tianya cn publicforum content no20 1 171416 shtml 俺的东家赫然在列 xff0c 俺作证 xff0c 裁员是真的 活过冬天就会在春天复苏 xff0c 如果死在冬天 xff0c
  • 学习笔记-Semihosting

    如何理解Semihosting xff08 老帖重发 xff0c 写得比较久了 xff09 Semihosting xff0c 顾名思义 xff0c 就是 34 半主控 34 xff0c 意思就是说 xff0c 运行在target boar

随机推荐

  • 开发笔记-NAND Flash Bad Block Management

    1 xff09 为什么会出现坏块 由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠 xff0c 因此 xff0c 在NAND的生产中及使用过程中会产生坏块 坏块的特性是 xff1a 当编程
  • ThreadX 实时操作系统

    ThreadX 实时操作系统 xff08 RTOS xff09 Express Logic日前發佈了支援Altera Nios II軟式核心嵌入處理器的ThreadX即時作業系統 RTOS xff0c 全新的ThreadX具備快速軟體計時器
  • React 条件渲染组件

    组件通常需要根据不同的条件显示不同的内容 xff0c 以及根据应用的状态变化只渲染其中的一部分 在 React 中 xff0c 可以使用 JavaScript 语法有条件地呈现 JSX xff0c 比如 if 语句 amp amp 和 操作
  • BDM的禅义

    2006 4 13 13 16 00 BDM的禅义 这片文章译自 the zen of bdm xff0c 翻译的不好 xff0c 还请大家多提意见 介绍 你可能感到奇怪 xff0c 为什么说BDM的禅义 BDM xff08 背景调试模式
  • RTOS基本原理:多任务和调度-FreeRTOS是如何工作的

    FreeRTOS是如何工作的 RTOS基本原理 多任务和调度 作者 xff1a Gavin Lee 来源 xff1a upsdn net 更新日期 xff1a 2006 04 12 RTOS基本原理 多任务 调度 上下文切换 实时应用 实时
  • 上下文切换-FreeRTOS是如何工作的

    FreeRTOS是如何工作的 上下文切换 作者 xff1a Gavin Lee 来源 xff1a upsdn net 更新日期 xff1a 2006 04 13 上下文切换 跟任何其他程序一样 xff0c 一个任务执行时 xff0c 它使用
  • 国产手机操作系统艰难探索

    4大阵营纷争劲起 国产手机操作系统艰难探索 作者 xff1a 连晓东 出处 xff1a 中国电子报 2005 7 13 13 07 52 阅读 lt script src 61 34 http service donews com numb
  • 嵌入式系统和嵌入式操作系统

    嵌入式系统和嵌入式操作系统 西南交通大学电气学院 张湘 肖建 2004 10 2 文章从概念 特点 种类等不同方面就嵌入式系统和嵌入式操作系统做了介绍 一 什么是嵌入式系统 嵌入式系统一般指非PC系统 xff0c 有计算机功能但又不称之为计
  • FPGA与SRAM相结合完成大容量数据存储

    FPGA与SRAM相结合完成大容量数据存储 作者 xff1a 安莎莎 赖伟林 张辉 阅读 xff1a 248 引用 xff1a 0 发布时间 xff1a 2006 05 25 20 01 出处 xff1a 电子产品世界 西安电子科技大学 通
  • Git客户端(Windows系统)的使用

    分类 xff1a Ubuntu 2013 09 03 02 15 212人阅读 评论 0 收藏 举报 本文环境 xff1a 操作系统 xff1a Windows XP SP3 Git客户端 xff1a TortoiseGit 1 8 5 0
  • PX4飞控-自定义发送MavLink消息

    PX4飞控 自定义发送MavLink消息 一 自定义XML文件 如图所示 这相当于定义了一个结构体 xff0c 结构体里面就有一个类型为uint8 t的u8元素 使用的id端口为150 xff0c 这里注意不要与已经存在的id重复 二 使用
  • IPv4和IPv6何去何从

    一 缘起 某一天 xff0c 在公司技术群内运维大佬们发了这么一条消息 这条消息瞬间勾起了我的好奇心 什么是IPv6 xff1f 为什么要切IPv6 xff1f 于是在上班时多去了几次去卫生间之后 xff0c 对IPv6有了一些初步认识 x
  • 利用kalibr工具进行camera-IMU标定

    camera IMU标定 一 camera IMU标定 简述 1标定目的 xff1a 我们进行camera IMU的目的是为了得到IMU和相机坐标系的相对位姿矩阵T 2标定工具 xff1a 我们利用Kalibr标定工程对camera IMU
  • React State 状态

    React State 状态 React 把组件看成是一个状态机 xff08 State Machines xff09 通过与用户的交互 xff0c 实现不同状态 xff0c 然后渲染 UI xff0c 让用户界面和数据保持一致 React
  • C#实现窗体内容随着窗体的缩放而缩放

    private float X private float Y 获取控件的width height left top 字体大小的值 存放在控件的Tag属性中 private void setTag Control cons 遍历窗体中的控件
  • ROS基础 - dynamic_reconfigure 动态参数

    ROS基础 dynamic reconfigure 动态参数使用 实例详解 采用 ROS dynamic reconfigure 的形式 xff0c 可以在程序运行过程中实时更改参数大小 xff0c C 43 43 代码通过回调函数接收数据
  • C_C++变量命名规则

    C C 43 43 变量命名规则 变量命名规则是为了增强代码的可读性和容易维护性 以下为C 43 43 必须遵守的变量命名规则 xff1a 1 变量名只能是字母 xff08 A Z xff0c a z xff09 和数字 xff08 0 9
  • Git 初次提交代码

    Git 初次提交发布代码 首先打开 bash 出现git的命令行 可以在GitHub上新建一个仓库 这是提交了的代码的仓库 xff0c 刚创建的时候只有 README md 和 gitignore 两个文件 xff01 在进入git的命令行
  • MATLAB 快速绘制曲线图的形状,粗细,颜色

    目录 MATLAB 快速绘制曲线图的形状 xff0c 粗细 xff0c 颜色1 通过改变R G B 的值改变线条的颜色 xff1a 2 通过改变c 1 43 的值改变线条的粗细 xff1a 3 线条形状 xff0c 粗细 xff0c 颜色选
  • STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结

    摘自 xff1a STM32F103五分钟入门系列 xff08 一 xff09 跑马灯 xff08 库函数 43 寄存器 xff09 43 加编程模板 43 GPIO总结 作者 xff1a 自信且爱笑 发布时间 xff1a 2021 04