STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置

摘自:STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置
作者:自信且爱笑‘
发布时间: 2021-05-01 12:08:32
网址:https://blog.csdn.net/Curnane0_0/article/details/116276876?spm=1001.2014.3001.5501

学习板:STM32F103ZET6

GPIO的七大寄存器+GPIOx_LCKR作用和配置+编程小总结

  • 一、GPIO的寄存器
    • 1、端口配置低寄存器(GPIOx_CRL) (x=A..E)
      • 1、详述
      • 2、举例
    • 2、端口配置高寄存器(GPIOx_CRH) (x=A..E)
    • 3、端口输入数据寄存器(GPIOx_IDR)(x=A...E)
      • 1、详述
      • 2、举例
    • 4、端口输出数据寄存器(GPIOx_ODR)(x=A...E)
      • 1、详述
      • 2、举例
    • 5、端口位设置/清除寄存器(GPIOx_BSRR)(x=A...E)
      • 1、详述
      • 2、举例1
      • 3、举例2
    • 6、端口位清除寄存器(GPIOx_BRR)(x=A...E)
      • 1、详述
      • 2、举例
    • 7、端口配置锁定寄存器(GPIOx_LCKR) (x=A..E)
      • 1、详述
      • 2、举例
  • 二、总结
    • 1、几种IO口输出类型(以PB5和PB10为例)
      • 1、使用GPIOB_ODR寄存器
      • 2、使用GPIOB_BSRR寄存器
      • 3、使用GPIOB_BRR寄存器
      • 4、小总结(比较重要)

一、GPIO的寄存器

参考文件:《STM32中文参考手册》

每个GPIO端口有两个32位配置寄存器(GPIOx_CRL, GPIOx_CRH),两个32位数据寄存器
(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存
器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)

1、端口配置低寄存器(GPIOx_CRL) (x=A…E)

1、详述

该寄存器是用来配置低位寄存器(PX0~PX7),为32位寄存器。对于GPIOX,从PX0 ~PX7共8 个IO口,32位寄存器的每四位配置一个IO口。用来配置GPIO的输入输出模式和输出时的speed。
在这里插入图片描述

对于每个IO口配置的四位,由两位的MODE和两位的CNF,其中MODE配置Speed,CNF配置是哪种输出模式

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

所以配置GPIO的步骤:
①判断是低8位IO还是高8位IO
②判断该IO对应的CNF和MODE值为多少
③编写配置函数:

GPIOA(B、C、D、E)——>CRL(或CRH)=0x....
  • 1

2、举例

以按键为例

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

由原理图可知,KEY_UP按下后,PA0变为高电平,没有按下时,PA0处于悬空状态;KEY0、KEY1、KEY2按下后PE4、PE3、PE2分别变为低电平,未按下时,对应IO口处于悬空状态。所以按键的GPIO配置的模式可以是浮空输入。不过KEY_UP按下后为了更好的检测到高电平,可以采用上拉输入;KEY0、KEY1、KEY2按下后为了更好的检测到低电平,可以采用下拉输入。

KEY_UP的GPIO配置:
①PA0为低位,采用GPIOA_CRL
②上拉输入,所以CNF为10表示上拉/下拉输入,MODE为00,表示输入模式
在这里插入图片描述

代码:

		GPIOA->CRL&=0xfffffff0;GPIOA->CRL|=0x00000008;
  • 1
  • 2

第一行代码是为了让第0、1、2、3这四位置0,其它位不变;第二行代码是为了让第0、1、2、3这4位变为1000,其它位不变。

KEY0的GPIO配置:
①PE4为低4位,所以使用GPIOE_CRL寄存器
②采用下拉输入,所以CNF为10,输入模式,MODE为00

在这里插入图片描述

代码:

		GPIOE->CRL&=0xfff0ffff;GPIOE->CRL|=0x00080000;
  • 1
  • 2

第一行代码是为了将第16、17、18、19位置0,其它位保持不变;第二行代码是为了将第16、17、18、19位置1000,其它位保持不变。

2、端口配置高寄存器(GPIOx_CRH) (x=A…E)

与端口配置低寄存器(GPIOx_CRL)(x=A…E)类似,唯一不同的是对应PX8~PX15 IO口。

3、端口输入数据寄存器(GPIOx_IDR)(x=A…E)

1、详述

在这里插入图片描述

该寄存器为32位寄存器, 其中高16位保持不变,低16位依次对应PX0~PX15,该寄存器只能以16位的形式读出

那怎么获取某一位的值呢?可以用与运算,如想要知道第6位是不是输入了高电平,即检测第6位是否为1,只需与1111111110111111与运算,即与0xffbf进行与运算
代码:

uint16_t x;//定义一个16位的数
x=GPIOE->IDR&0xffbf;
if(x==0xffff)//高电平
....
if(x==0xffbf)//低电平
....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

或者:

if((GPIOE->IDR&0xffbf)==GPIOE->IDR)//低电平
....
if((GPIOE->IDR&0xffbf)!=GPIOE->IDR)//高电平
....
  • 1
  • 2
  • 3
  • 4

总之,端口输入数据寄存器(GPIOx_IDR) 就是来判读各位的IO口是什么状态。

2、举例

例:按下开关KEY0后LED1亮,取消按下后LED1灭

为了方便代码粘贴,全部程序在主函数中编写
代码:

#include "sys.h"
#include"stm32f10x.h"int main(void){	uint16_t x;//定义一个16位的数//KEY0 PE4  CFN+MODE 1000//LED0 PE5  CFN+MODE 0011RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE , ENABLE);//时钟使能// RCC->APB2ENR|=0x0040;GPIOE->CRL&=0xff00ffff;GPIOE->CRL|=0x00380000;//PE5与PE4一起配置while(1){x=GPIOE->IDR&0x0010;if(x==0)//KEY0按下{GPIOE->ODR&=0xffdf;//PE5置0,点亮LED0}GPIOE->ODR|=0xFFFF;//恢复PE都为高电平}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

解释一下例子:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE , ENABLE);//时钟使能
  • 1

上述代码是配置时钟,GPIO时钟配置都用RCC_APB2PeriphClockCmd()
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

	GPIOE->CRL&=0xff00ffff;GPIOE->CRL|=0x00380000;//PE5与PE4一起配置
  • 1
  • 2

上面代码是配置PE5(LED0)和PE4(KEY0)

PE4是KEY0,按下后PE4引脚变为低电平,未按下时PE4引脚为悬空,采用下拉输入,所以CFN+MODE为1000;PE5为LED0,低电平时点亮,高电平时熄灭,所以采用推挽输出,速度为50M,CFN+MODE为0011,两者都为低位IO,采用GPIOE_CRL寄存器配置。

在这里插入图片描述
第一行代码先将16~23位置0,其它位不变,第二行代码将16 ~23位置00111000,其它位不变,完成PE4和PE5的配置。

	x=GPIOE->IDR&0x0010;
  • 1

上述代码是为了检测KEY0是否按下,如果按下,则GPIOE_IDR的第4位变为0,此时与0x0010与运算后,值为0
在这里插入图片描述
检测到按下后,执行:

	GPIOE->ODR&=0xffdf;//PE5置0,点亮LED0
  • 1

上述代码的寄存器是接下来要总结的寄存器,就是将IO的某位软件置0或1输出,而GPIOx_IDR是外界原因置0或1来输入到芯片。
现在要点亮LED0,则GPIOE的第5位变为0,所以和0xffdf进行与运算就行了

在这里插入图片描述
需要注意的是,进行与运算和或运算时,改变的只能是相关的IO引脚,其它无关的IO引脚的电平值一定要保持不变

GPIOE->ODR|=0xFFFF;//恢复PE都为高电平
  • 1

上述代码是为了将GPIOE全部引脚恢复原状,因为死循环中执行一次后,PE4和PE5都变为低电平,若不恢复原状,LED的引脚PE5一直处于低电平状态,灯会常亮,不再受KEY控制。

其实恢复PE5为高电平就行了,这行代码可改为:

	GPIOE->ODR&=0xfef;
  • 1

或者利用移位运算:

GPIOE->ODR=1<<5;
//GPIOE->ODR|=1<<5;//都可以
  • 1
  • 2

4、端口输出数据寄存器(GPIOx_ODR)(x=A…E)

1、详述

该寄存器与GPIOx_IDR类似,高16位也是保留位,就当做啥也没有,进行与运算和或运算时,只需和16位的数进行运算就行,从某种意义上讲,该寄存器与前面的GPIOx_IOR就是16位寄存器。
在这里插入图片描述
只要设置了某IO口的为输出模式(GPIOx_CRL、GPIOx_CRL)就可以利用该寄存器对该位进行置0或1。

前面第三部分的例子中也用到了该寄存器,现再举例说明

2、举例

例:控制蜂鸣器发声

打开原理图,找到蜂鸣器和芯片的连接图
在这里插入图片描述
在这里插入图片描述
由原理图可得:
①芯片PB8接蜂鸣器,所以配置GPIO时用到端口配置高位寄存器(GPIOB_CRH)
②当引脚输出高电平时,三极管基极电流变大,集电极电流也变大,蜂鸣器发声。

代码:(为了代码说明方便,将代码都写入到主函数)

#include "sys.h"
#include "stm32f10x.h"
#include "delay.h"int main(void){	uint16_t x;//定义一个16位的数//BEEP PB8 CFN+MODE 0011RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);//时钟使能// RCC->APB2ENR|=0x0008;GPIOB->CRH&=0xfffffff0;GPIOB->CRH|=0x00000003;//配置PB8while(1){GPIOB->ODR|=0x0100;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

解释一下:

	GPIOB->CRH&=0xfffffff0;GPIOB->CRH|=0x00000003;//配置PB8
  • 1
  • 2

上述代码是配置GPIOB的第8引脚,用的端口配置高位寄存器,为通用推挽输出,速度为50M,所以CNF+MODE为0011

在这里插入图片描述
先将0~3位通过与运算置0,再通过或运算置0011。

	GPIOB->ODR|=0x0100;
  • 1

上述代码是使PB8输出高电平
在这里插入图片描述

当然可以将蜂鸣器关闭,只需:(还可以用GPIOB_BSRR寄存器、GPIOB_BRR寄存器)

 	GPIOB->ODR&=0xfeff;
  • 1

或者:

GPIOB->ODR&=0xfffe<<8;
  • 1

在这里插入图片描述

移位运算在ODR中不推荐使用,虽然操作简单,但是移位运算是将16或32位的数整体左移,高位会溢出,低位会补零,若GPIOx只有一个IO被用到,可以采用移位方法,但是多个IO被使用,整体左移后会对其它IO状态产生影响。

正因为移位运算在IO口复杂情况下会对IO口造成紊乱,所以引入BSRR和BRR寄存器,可以在这两个寄存器中去移位来操作ODR寄存器,从而操作对应IO。这俩个寄存器中移位时,补0和溢出0都不会对ODR相应位产生影响,从而避免紊乱!

5、端口位设置/清除寄存器(GPIOx_BSRR)(x=A…E)

1、详述

该寄存器是对GPIOx_ODR寄存器的操作,我们之前举例时,都是用GPIOx_ODR去和一个16位数进行与运算和或运算,在进行运算时,需要求这个16位数,比较麻烦。不过可以移位法,将第一位置1,然后左移一定的位数(<<)。GPIOx_BSRR可以直接对GPIOx_ODR寄存器的某位进行设置。唯一不同的用GPIOx_BSRR操作GPIOx_ODR寄存器时,不用考虑GPIOx_ODR寄存器的不相关位。

在这里插入图片描述

GPIOx_BSRR也是32位寄存器,其中低16位是对GPIOx_ODR寄存器16个IO位置1,高16位是对GPIOx_ODR寄存器16个IO位置0
注意的是,如果GPIOx_BSRR的高16位和低16位都对某一IO口进行了配置,则以GPIOx_BSRR寄存器的低16位的配置为优先级。(后面例子中会说明)

2、举例1

1、以(3、GPIOxIDR寄存器的例子说明):按下开关KEY0后LED1亮,取消按下后LED1灭
因为GPIOx_BSRR寄存器高16位进行了清零操作,低16位进行了置1操作,所以不应该把它和一个32位的数进行与运算和或运算,如:

#include "sys.h"
#include"stm32f10x.h"int main(void){	uint16_t x;//定义一个16位的数//KEY0 PE4  CFN+MODE 1000//LED0 PE5  CFN+MODE 0011RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE , ENABLE);//时钟使能// RCC->APB2ENR|=0x0040;GPIOE->CRL&=0xff00ffff;GPIOE->CRL|=0x00380000;//PE5与PE4一起配置while(1){x=GPIOE->IDR&0x0010;if(x==0)//KEY0按下{//GPIOE->ODR&=0xffdf;//PE5置0,点亮LED0GPIOE->BSRR|=0x00200000;}// GPIOE->ODR|=0xFFFF;//恢复PE都为高电平GPIOE->BSRR|=0x00000030;//设置PE5(00000020)与设置PE4(00000010)合并(7、6、5、4位:0011)}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

上面程序的代码:

 	GPIOE->BSRR|=0x00200000;GPIOE->BSRR|=0x00000030;
  • 1
  • 2

是对GPIOE_BSRR和32位数进行位或运算,所以在设置低位BS4、BS5时,高位BR4、BR5也同时进行了设置,但是以低位设置为优先级
好好理解下图标注的地方!!!英文原话:Note: If both BSx and BRx are set, BSx has priority

在这里插入图片描述
还可以用位移方法:

完整准确代码:

#include "sys.h"
#include "stm32f10x.h"int main(void){	uint16_t x;//定义一个16位的数//KEY0 PE4  CFN+MODE 1000//LED0 PE5  CFN+MODE 0011RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE , ENABLE);//时钟使能 // RCC->APB2ENR|=0x0040;GPIOE->CRL&=0xff00ffff;GPIOE->CRL|=0x00380000;//PE5与PE4一起配置while(1){x=GPIOE->IDR&0x0010;if(x==0)//KEY0按下{//GPIOE->ODR&=0xffdf;//PE5置0,点亮LED0GPIOE->BSRR=1<<21;//亮灯}//GPIOE->ODR|=0xFFFF;//恢复PE都为高电平GPIOE->BSRR|=1<<5;//灭灯GPIOE->BSRR|=1<<4;//按键恢复悬空(没办法,只能设置为高电平)/*也可以如下:(去掉或)*///GPIOE->BSRR=1<<5;// GPIOE->BSRR=1<<4;}}
  • 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

还可以搭配GPIOx_BRR寄存器实现,第6部分总结。

3、举例2

再把上面的控制蜂鸣器发声的程序用GPIOB_BSRR寄存器写一下:
代码:

#include "sys.h"
#include "stm32f10x.h"
#include "delay.h"int main(void){	//BEEP PB8 CFN+MODE 0011//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);//时钟使能RCC->APB2ENR|=0x0008;GPIOB->CRH&=0xfffffff0;GPIOB->CRH|=0x00000003;//配置PB8while(1){GPIOB->BSRR=1<<8;//或者:GPIOB->BSRR|=1<<8;//或者:GPIOB->BSRR|=0x00000100;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

6、端口位清除寄存器(GPIOx_BRR)(x=A…E)

1、详述

GPIOx_BRR寄存器也是32位寄存器,但是高16位被保留,所以可以把它当做是16位寄存器。它的作用是将对应的0~15 IO口清零。即当对应位为1时,对应IO口置0,当对应位为0时,对应IO口保持原来的状态。
在这里插入图片描述在这里插入图片描述
编程时,只需:GPIO(A~E)=1<<m,即可将PXm置0。

2、举例

点亮LED

#include "sys.h"
#include "stm32f10x.h"
#include "delay.h"int main(void){	//LED0 PB5     推挽50M 0011//LED1  PE5		推挽50M 0011//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE, ENABLE);//时钟使能GPIOA和GPIOERCC->APB2ENR|=0x0048;//RCC->APB2ENR=1<<3 ;//使能GPIOB//RCC->APB2ENR=1<<6 ;//使能GPIOEGPIOB->CRL&=0xff0fffff;GPIOB->CRL|=0x00300000;//配置PB5GPIOE->CRL&=0xff0fffff;GPIOE->CRL|=0x00300000;//配置PE5while(1){GPIOB->BRR=1<<5;//其实本来初始状态就是亮的,没必要再次点亮,,只是为了说明这个寄存器GPIOE->BRR=1<<5;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

上述程序中,死循环中的两行代码就是通过GPIOB_BRR和GPIOE_BRR分别操作GPIOB_ODR和GPIOE_ODR来分别控制PB5和PE5输出低电平。

7、端口配置锁定寄存器(GPIOx_LCKR) (x=A…E)

1、详述

端口配置锁定寄存器是为了锁住GPIO的配置,在下次系统复位前不让其工作(只要下次复位不执行该寄存器,就不会被锁了)。
注意:锁住的是端口配置寄存器CRL或CRH

之前总结过端口配置寄存器GPIOx_CRL和GPIOx_CRH,对于每个IO,在寄存器中对应4位,即控制输入输出模式的2位CFN,控制speed的2位MODE。当端口寄存器锁住某IO口后,对应的CRL或CRH中对应的4位就被锁住,此时不能配置该位的输入、输出模式,以及不能配置speed,此时该IO口就不能使用。

在这里插入图片描述

具体叙述一下:

首先第16位,即高16位的第1位为LCKK,要开启锁IO模式,必须先“开锁”,开锁密码:写1——>写0——>写1——>读0——>读1。最后的读1可省略,但其它“密码”顺序、内容都不能错。

开锁程序:(GPIOB为例)

花了好长时间才调试成功(狗头)

	uint32_t t;GPIOB->LCKR|=0x00010000;//LCKK写入1GPIOB->LCKR&=0x0000ffff;//LCKK写入0GPIOB->LCKR|=0x00010000;//LCKK写入1t=GPIOB->LCKR;//LCKK读0t=GPIOB->LCKR;//LCKK读出1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后就是给某IO口上锁了,需要注意的是,只有第16位——>LCKK为0时,GPIOx_LCKR寄存器才可以被写入,某位写入1,则对应的IO口被锁住。

以PB5为例:

	 //开启锁定寄存器模式GPIOB->LCKR&=0x0000ffff;//LCKK写入0GPIOB->LCKR=1<<5;//锁定PB5
  • 1
  • 2
  • 3

2、举例

例:同时配置LED0和LED1,但是LED0被GPIOB_LCKR寄存器锁住,观察两个LED能否都被点亮。

直接代码:

#include "sys.h"
#include "stm32f10x.h"
#include "delay.h"int main(void){	uint32_t t;delay_init();//LED0 PB5  //LED1  PE5//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE, ENABLE);//时钟使能GPIOA和GPIOERCC->APB2ENR|=0x0048;//RCC->APB2ENR=1<<3 ;//使能GPIOB//RCC->APB2ENR=1<<6 ;//使能GPIOE/* 开锁*/GPIOB->LCKR|=0x00010000;//LCKK写入1GPIOB->LCKR&=0x0000ffff;//LCKK写入0GPIOB->LCKR|=0x00010000;//LCKK写入1t=GPIOB->LCKR;//LCKK读0t=GPIOB->LCKR;//GPIOB_LCKR读出1//开启锁定寄存器模式GPIOB->LCKR&=0x0000ffff;//LCKK写入0GPIOB->LCKR=1<<5;//锁定PB5GPIOB->CRL&=0xff0fffff;GPIOB->CRL|=0x00300000;//配置PB5GPIOE->CRL&=0xff0fffff;GPIOE->CRL|=0x00300000;//配置PE5GPIOB->BSRR=1<<5;//熄灭LED0delay_ms(10);while(1){GPIOB->BRR=1<<5;//点亮LED0GPIOE->BRR=1<<5;}}
  • 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

调试:
下载程序后,LED0一直熄灭,LED2一直亮。
分析:
程序开始时对GPIOB和GPIOE进行了时钟配置,接下来就是“开锁”,然后就是对PB5“上锁”,这些上面都总结了。

接下来配置PE5和PB5,然后把LED0熄灭(程序开始都清零,LED默认处于点亮状态),若LED0(PB5)的配置没有被锁住,则在死循环中LED0应该被点亮,调试时LED0应该常亮。

“开锁”时一定要注意:第0~15位的值不能被改变,所以写入0和1的时候要进行与运算和或运算。

二、总结

1、几种IO口输出类型(以PB5和PB10为例)

1、使用GPIOB_ODR寄存器

置1:

GPIOB->ODR|=0x0420;//PB10和PB5都输出高电平
  • 1

在这里插入图片描述

		GPIOB->ODR=1<<5;//只操作PB5置1//GPIOB->ODR|=1<<5;//注意同时设置PB5和PB10时不用移位
  • 1
  • 2
  • 3
		GPIOB->ODR=1<<10;//只操作PB10置1//GPIOB->ODR|=1<<510;//注意同时设置PB5和PB10时不用移位
  • 1
  • 2
  • 3

置0:

GPIOB->ODR&=0xfbdf;//同时将PB5和PB10置0
  • 1

在这里插入图片描述

	GPIOB->ODR=0<<5;//只对PB5置0// GPIOB->ODR&=0xfffe<<5;
  • 1
  • 2
GPIOB->ODR&=0xff2f;//只对PB5置0
  • 1
GPIOB->ODR&=0xf4ff;//只对PB10置0
  • 1
 GPIOB->ODR=0<<10;//只对PB10置0
  • 1
GPIOB->ODR&=0xfffe<<10;//只对PB10置0
  • 1

注意:GPIOB只有一个引脚使用时,才能通过移位运算操作ODR寄存器

2、使用GPIOB_BSRR寄存器

置1:

GPIOB->BSRR|=0x00000420;//同时设置PB5和PB10为1
//GPIOB->BSRR=0x00000420;
  • 1
  • 2

在这里插入图片描述

注意:此时低位设置覆盖了高位设置

	GPIOB->BSRR|=1<<5;//单独设置PB5为高电平//GPIOB->BSRR=1<<5;
  • 1
  • 2
	GPIOB->BSRR|=1<<10;//单独设置PB10为高电平//GPIOB->BSRR=1<<10;
  • 1
  • 2
		/*同时设置PB5和PB10为高电平*/GPIOB->BSRR=1<<5;GPIOB->BSRR&=0;//清零GPIOB->BSRR=1<<10;
  • 1
  • 2
  • 3
  • 4

注意:上面代码必须清零,否则第三行代码移位时,会把原来第5位的1左移到第15位,对PB15也产生了影响!

置0:

不能和32位数进行与运算、或运算,否则低位设置会覆盖高位,导致要不PB5、PB10置1、要不保持原来的状态不变!

同时设置PB5和PB10

			/*注意,一定要清零*/GPIOB->BSRR|=1<<21;//设置PB5GPIOB->BSRR&=0;//清零GPIOB->BSRR|=1<<26;//设置PB10
  • 1
  • 2
  • 3
  • 4

只设置一个IO的话,就把上述代码第一行、第三行单独拿出来就行了

3、使用GPIOB_BRR寄存器

该寄存器只能置0

	GPIOB->BRR|=0x0420;//同时设置PB5、PB10为0//GPIOB->BRR=0x0420;
  • 1
  • 2

在这里插入图片描述

该寄存器是32位寄存器,但是高16位保留,所以可以当做16位寄存器来使用,和16位数与、或运算就行了。不过并不是所有单片机都可以这样,应该是这款单片机与、或运算时,是低位对齐,高位没对齐就补0。并不是所有单片机都这样,所以最好写成32位的形式。

	/*同时操作PB5、PB10 为0*//*注意:一定要清零*/GPIOB->BRR|=1<<5;//操作PB5为0GPIOB->BRR&=0;//清0GPIOB->BRR|=1<<10;//操作PB10为0
  • 1
  • 2
  • 3
  • 4
  • 5

4、小总结(比较重要)

使用寄存器与、或运算比较麻烦,因为要算16、32位的那个数。采用移位法可以操作ODR来输出高低电平,但是如果IO占用复杂,移位法就会造成IO口紊乱,GPIOx只有一个IO口是,才可以用移位法控制ODR寄存器。

所以使用BSRR寄存器和BRR寄存器去解决移位时IO口紊乱的问题。但是BSRR寄存器高位和低位同时配置时,低位会覆盖高位的设置,所以推荐使用以下方法:

如果要控制IO输出高低电平、采用BSRR和BRR寄存器来设置ODR寄存器,进而控制对应IO口。置1时,采用BSRR进行低位操作;置0时,采用BRR寄存器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/265575.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、图像(硬件IIC)

参考&#xff1a;基于STM32-Oled&#xff08;IIC&#xff09;的使用 作者&#xff1a;奋斗的小殷 发布时间&#xff1a; 2021-05-07 13:09:26 网址&#xff1a;https://blog.csdn.net/boybs/article/details/116465668 目录IIC总线简介IIC协议简介IIC总线系统结构IIC总线物理层…

推送公司今日菜单内容到手机

此文已由作者张耕源授权网易云社区发布。欢迎访问网易云社区&#xff0c;了解更多网易技术产品运营经验。自从公司的易信公众服务号有了查询今日菜单的功能&#xff0c;自己慢慢养成了每次去吃饭前查一 下各个窗口的菜谱&#xff0c;再决定去哪吃饭的习惯。不过这个功能使用的越…

Android开发中如何创建自定义对话框

背景&#xff1a; 无论是Android&#xff0c;还是其他什么平台&#xff0c;对话框的主要形式都是弹出&#xff0c;或者我们可以说是一个在当前活动中打开的提示&#xff0c;来执行一些操作或实现一些功能。 你可以用一个对话框来问用户以确认一个行动&#xff0c;向用户通告一个…

树莓派接入公网(花生壳)

参考&#xff1a;树莓派接入公网 作者&#xff1a;图触靓 发布时间&#xff1a; 2020-12-22 17:28:19 网址&#xff1a;https://blog.csdn.net/bhbhhyg/article/details/107994829 目录为什么要接入公网接入公网的方法&#xff08;花生壳&#xff09;1、下载工具huashenke2、安…

cant find module express

安装express成功&#xff0c;测试一个应用如下&#xff1a;但是&#xff0c;运行却报错&#xff0c;如下图&#xff1a;说是express找不到&#xff0c;不应该啊&#xff01;命令都能用啊&#xff01;表面的原因是因为我install express用的是-g。参考链接如下&#xff1a;http:…

Linux虚拟机sqlite数据库安装教程、命令实现sqlite

参考&#xff1a;Linux虚拟机sqlite数据库安装教程 作者&#xff1a;图触靓 发布时间&#xff1a; 2021-04-08 19:07:56 网址&#xff1a;https://blog.csdn.net/bhbhhyg/article/details/115528254 一、在官网下载压缩包到window里面 SQLite官网下载地址 如图我们下载sqlite-a…

SecureCRT 使用 rz命令提示waiting to receive.**B0100000023be50

SecureCRT 远程连接Linux服务器&#xff0c;使用 rz命令提示waiting to receive.**B0100000023be50&#xff0c;或者使用sz命令提示&#xff1a; **B0100000023be50 解决方法设置如下&#xff1a; 转载于:https://www.cnblogs.com/pandaly/p/9898173.html

Python时间复杂度计算题答案

评论 题目链接 https://blog.csdn.net/qq_33254766/article/details/132255078 答案 时间复杂度&#xff1a;O(n)。 分析&#xff1a;这段代码遍历了n次&#xff0c;所以时间复杂度是线性的&#xff0c;即O(n)。 时间复杂度&#xff1a;O(n^2)。 分析&#xff1a;两个嵌套的…

wxpython使用方法_python图形界面开发之wxPython树控件使用方法详解

wxPython树控件介绍树(tree)是一种通过层次结构展示信息的控件&#xff0c;如下图所示是树控件示例&#xff0c;左窗口中是树控件&#xff0c;在wxPython中树控件类是wx.TreeCtrl。wx.TreeCtrl常用的方法有AddRoot(text, image-1, selImage-1, dataNone)。添加根节点&#xff0…

DSP_SCI

F2833x Serial Communication Interface 簡介 串行通信接口&#xff08;SCI&#xff09;模塊是一個串行I / O端口&#xff0c;允許F2833x與其他外圍設備之間的異步通信。 它通常被稱為UART&#xff08;通用異步接收器發送器&#xff09;&#xff0c;通常根據RS232標準使用。 SC…

如何让自己的写的程序在阿里云一直运行

参考&#xff1a;如何让自己的写的程序在阿里云一直运行 作者&#xff1a;图触靓 发布时间&#xff1a; 2020-08-02 11:53:28 网址&#xff1a;https://blog.csdn.net/bhbhhyg/article/details/107742311 目录1、下载screen&#xff1a;2、运行screen&#xff0c;创建一个scree…

C程序设计语言--第五章:指针与数组

为什么80%的码农都做不了架构师&#xff1f;>>> 指针是一种保存变量地址的变量. 5.1 指针与地址 通常的机器都有一系列连续编号或编址的存储单元,这些存储单元可以单个进行操纵,也可以连续成组的方式操纵.指针是能够存放一个地址的一组存储单元(通常是两个或四个…

java list 自定义类型转换_使用Java Stream API将List按自定义分组规则转换成Map的一个例子...

本文完整测试代码见文末。测试数据是List里的4个员工对象实例&#xff1a;根据员工所在的城市进行分组&#xff1a;结果分成了三组&#xff1a;第一组的员工在上海&#xff1a;第二组的员工在成都&#xff1a;统计每组员工个数&#xff1a;把员工进行分组&#xff0c;得分大于1…

安卓app与阿里云服务器的无线通信(非局域网)

参考&#xff1a;安卓app与阿里云服务器的无线通信&#xff08;非局域网&#xff09; 作者&#xff1a;图触靓 发布时间&#xff1a; 2020-08-01 16:13:14 网址&#xff1a;https://blog.csdn.net/bhbhhyg/article/details/107732156 目录写在阿里云里面的服务器代码&#xff1…

10.继承和派生

2019独角兽企业重金招聘Python工程师标准>>> 类成员访问权限&#xff1a; 成员访问权限&#xff1a;私有成员&#xff1a;只能被类自身的成员和友元访问&#xff1b; 公有成员&#xff1a;可以被任何普通函数和任何类的成员函数或子类访问&#xff1b; 保护成员&a…

树莓派与阿里云服务器之间的无线通信(非局域网)

参考&#xff1a;Linux虚拟机sqlite数据库安装教程 作者&#xff1a;图触靓 发布时间&#xff1a; 2020-08-01 15:45:026 网址&#xff1a;https://blog.csdn.net/bhbhhyg/article/details/107731166 购买服务器后。我们可以点击控制台&#xff0c;找到实例列表&#xff0c;点击…

业余剪辑——从拍摄到剪辑到加字幕

工作需要帮公司做一个公司的宣传片&#xff0c;要求不是很高所以就让我这个半吊子“设计”来做视频了。。。 以下设备和软件的使用只是我个人本次的使用的记录&#xff0c;大家按需使用即可 一、拍摄 设备&#xff1a;手机 软件&#xff1a;剪辑大师app&#xff08;应用商店下载…

树莓派作为客户端与WemosD1作为服务器的无线通信(局域网通信)

参考&#xff1a;树莓派作为客户端与WemosD1作为服务器的无线通信&#xff08;局域网通信&#xff09; 作者&#xff1a;图触靓 发布时间&#xff1a; 2020-07-26 21:50:39 网址&#xff1a;https://blog.csdn.net/bhbhhyg/article/details/107599915 目录wemos版作为服务端代码…

Lync Server的环境搭建(五):Lync-Server的安装部署

今天,我们接着来进行Lync-Server服务端的安装。我们分别打开“Lync-DC”和“Lync-Server”这两台虚拟机&#xff0c;切换到“Lync-Server”下&#xff0c;“开始”—“Lync Server安装部署&#xff0c;单击“安装或更新Lync Server系统”。弹出”部署向导“窗口&#xff0c;我们…