摘自: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寄存器。

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

  1. STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

    学习板:STM32F103ZET6 往期博客: STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结 STM32F103五分钟入门系列(二)GPIO的七大寄存器+G ...

  2. STM32F103五分钟入门系列(十六)输入捕获(精雕细琢-.-)

    学习板:STM32F103ZET6 往期博客: STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结 STM32F103五分钟入门系列(二)GPIO的七大寄存器+G ...

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

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

  4. STM32F103五分钟入门系列(十三)独立看门狗IWDG

    参考:STM32F103五分钟入门系列(十三)独立看门狗IWDG 作者:自信且爱笑' 发布时间:2021-07-31 19:50:28 网址:https://blog.csdn.net/Curnane ...

  5. Reflex WMS入门系列二十五:将叉车纳入系统进行管理

    Reflex WMS入门系列二十五:将叉车纳入系统进行管理 据笔者所知,SAP WM 模块里是不对仓库里常用的叉车等仓库管理工具进行管理的.笔者发现,Reflex WMS系统则会在很多仓库部门日常操作 ...

  6. 机器学习入门系列二(关键词:多变量(非)线性回归,批处理,特征缩放,正规方程

    机器学习入门系列二(关键词:多变量(非)线性回归,批处理,特征缩放,正规方程) 目录(?)[+] 一多变量的线性回归 二批处理 三特征缩放 四正规方程 五多变量非线性回归 一.多变量的线性回归 在#机 ...

  7. Quantopian 入门系列二 - 流水线 (下)

    本文含 8225 字,28 图表截屏 建议阅读 42 分钟 本贴接着上贴[Quantopian 入门系列二 - 流水线 (上)]的内容,讨论下面目录的 5- 8 节: 简介 因子 筛选器 分类器 掩码 ...

  8. C语言速看,C语言高速入门系列(二)

    C语言高速入门系列(二) -----转载请注明出处coder-pig 本节引言: 在前面一节中我们对C语言进行了初步的了解,学会了使用IDE进行代码的编写,编译执行! 在这一节中我们会对C语言的基本的 ...

  9. Maven五分钟入门

    Maven 五分钟入门 ---本文翻译自Maven官网的Maven in 5 Minutes,稍有删改,所有版权归maven所有.本文只作学习交流之用. 安装 Maven 是一个java工具,因此,在 ...

最新文章

  1. 百度第七期智能对话训练营来了!
  2. mysql查看表格的列信息
  3. C++_pthread read-write lock_读写锁_visual studio 2015下配置
  4. Java异常以及继承的一些问题
  5. hdu4907 水dp 或者set
  6. WCF事务编程[中篇]
  7. QT的QStorageInfo类的使用
  8. 安卓APP_ Fragment(3)—— Fragment的生命周期
  9. DELPHI之常用函数
  10. 浏览器之本地缓存存储 localStorage 和 sessionStorage的区别以及用法
  11. amap vueamap 与_vue中使用vue-amap(高德地图)
  12. git中如何提交空目录
  13. windows自带黑体_微软黑体下载-微软黑体官方下载[字体下载]-华军软件园
  14. c语言使用CodeBlocks软件,使用CodeBlocks学习C语言
  15. Opencv4Android的OpenCL的测试,使用Opencv的ocl封装库
  16. doodoo.js快速入门教程
  17. C#实现SqlServer连接查询
  18. 马里兰大计算机专业学phd博士,亚利桑那州立大学计算机CS博士PHD全奖录取
  19. 一个屌丝程序猿的人生(一百二十六)
  20. 2022.3.3总结+力扣258. 各位相加

热门文章

  1. Java常量池理解与总结
  2. redux进一步优化
  3. Dynagen0.11+Pemuwrapper入手麻烦二三事——告诉初学者直路
  4. Hibernate的generator属性
  5. SQLite数据库常用语句及MAC上的SQLite可视化工具MeasSQLlite使用
  6. HBuilder:最快的Web开发IDE
  7. Android--UI之DatePicker、TimePicker...
  8. HTML5学习笔记简明版(4):新元素之video,audio,meter,datalist,keygen,output
  9. Ubuntu 屏幕亮度调整
  10. 主流开源编解码器Xvid,x264,ffmpeg 性能对比