STM32裸机学习笔记(三)—寄存器映射之BSRR与延时的爱恨情仇


STM32裸机学习笔记(三)—寄存器映射之BSRR与延时的爱恨情仇

GPIO与引脚区别?

  • GPIO包含在引脚内(引脚内还有电源、晶振等特殊功能引脚),除GPIO拓展内容即为单片机最小系统

GPIO功能如何检索?

  • 通过芯片datasheet中的pin definitions去检索GPIO功能

输出控制

  • 推挽输出(ODR寄存器输出1时推出3.3V/25mA,输出0时拉入GND)image-20200610184810766

  • 开漏输出(只能输出低电平,开漏时P-MOS不工作,N-MOS工作;开漏输出就是不输出电压,控制输出低电平时引脚接地,控制输出高电平时引脚既不输出高电平,也不输出低电平,为高阻态。如果外接上拉电阻,则在输出高电平时电压会拉到上拉电阻的电源电压。这种方式适合在连接的外设电压比单片机电压低的时候。)

    补充说明:


  • GPIO_BSRR与GPIO_BRR寄存器


    GPIOx_BSRR的高16位称作清除寄存器,而GPIOx_BSRR的低16位称作设置寄存器。BRR低16位即为BSRR高16位。

    • ODR寄存器可读可写:既能控制管脚为高电平,也能控制管脚为低电平。管脚对于位写1 GPIO管脚为高电平,写 0 为低电平(有被中断打断的风险)

    • BSRR 只写寄存器:既能控制管脚为高电平,也能控制管脚为低电平。对寄存器高16位 写1 对应管脚为低电平,写0无动作;对寄存器的第16位写1对应管脚为高电平,写 0 无动作。

    • BRR 只写寄存器:只能改变管脚状态为低电平,对寄存器 管脚对于位写 1 相应管脚会为低电平。写 0 无动作。ODR 能控制管脚高低电平为什么还需要BSRR和SRR寄存器的原因是:用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险。也就不需要关闭中断,关闭中断明显会延迟或丢失一事件的捕获,所以控制GPIO的状态最好还是用SBRR和BRR。

  • GPIO输入模式配置上拉下拉步骤

    配置步骤:

    ? 1.通过GPIOX_CRL/H设置对应IO口CNF位为10:上拉/下拉输入(下图为CRL为例)

    2.通过BSRR对对应上拉/下拉IO口执行置位操作即可。(附:图中TTL肖特基触发器即为施密特触发器:满足TTL电平规则置电平为0/1)

  • GPIO初始化顺序

    (一般都是CRL/H->ODR,稳健起见还是CRL/H->BSRR~)

GPIO其他寄存器的映射、BSRR进行LED的控制:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//main.c
#include "stm32f10x.h"

void delay(unsigned int i);

int main(void)
{
#if 0
//  //置1操作:|= ; 清零操作: &=~ ;
//  //GPIOB的RCC时钟开启
//  *(unsigned int*) 0x40021018 |= (1<<3);
//  //GPIOB的配置(推挽输出,10MHZ)
//  *(unsigned int*) 0x40010C00 |= (1<<0);
//  *(unsigned int*) 0x40010C00 |= (1<<4);
//  *(unsigned int*) 0x40010C00 |= (1<<20);
//  //while循环闪烁
//  while(1)
//  {
//      //GPIOB的输出
//      *(unsigned int*) 0x40010C0C &= ~(1<<0);
//      *(unsigned int*) 0x40010C0C |= (1<<1);
//      *(unsigned int*) 0x40010C0C |= (1<<5);
//      delay(2000);
//      *(unsigned int*) 0x40010C0C &= ~(1<<1);
//      *(unsigned int*) 0x40010C0C |= (1<<0);
//      *(unsigned int*) 0x40010C0C |= (1<<5);
//      delay(2000);
//      *(unsigned int*) 0x40010C0C &= ~(1<<5);
//      *(unsigned int*) 0x40010C0C |= (1<<0);
//      *(unsigned int*) 0x40010C0C |= (1<<1);
//      delay(2000);
//      *(unsigned int*) 0x40010C0C &= ~(1<<0);
//      *(unsigned int*) 0x40010C0C &= ~(1<<1);
//      *(unsigned int*) 0x40010C0C &= ~(1<<5);
//      delay(2000);
//  }
#else
    //RCC_APB2ENR开启GPIOB的使能
    RCC_APB2ENR|= (1<<(3));
    //GPIOB_CRL配置为推挽输出(先进行清0操作)
    GPIOB_CRL &= ~(1<<(4*0));
    GPIOB_CRL &= ~(1<<(4*1));
    GPIOB_CRL &= ~(1<<(4*5));
    //GPIOB_CRL配置为推挽输出(再进行置1操作)
    GPIOB_CRL |= (1<<(4*0));
    GPIOB_CRL |= (1<<(4*1));
    GPIOB_CRL |= (1<<(4*5));
    //GPIOB_BSRR设置反复替换闪烁
    while(1)
    {
        //白色
        GPIOB_BRR |= (1<<5)|(1<<1)|(1<<0);
        delay(2000);
        GPIOB_BRR |= (1<<0);
        GPIOB_BSRR_S |= (1<<5)|(1<<1);
        delay(2000);
        GPIOB_BRR |= (1<<1);
        GPIOB_BSRR_S |= (1<<0);
        delay(2000);
        GPIOB_BRR |= (1<<5);
        GPIOB_BSRR_S |= (1<<1);
        delay(2000);
    }
#endif
}
void SystemInit(void)
{
    //
}

void delay(unsigned int i)
{
    unsigned char j;
    for(i;i>0;i--)
        for(j = 255; j>0; j--);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//stm32f10x.h
//定义寄存器外设基础

#define  PERIPH_BASE    ((unsigned int)0x40000000)
#define  APB1PERIPH_BASE PERIPH_BASE
#define  APB2PERIPH_BASE (APB1PERIPH_BASE + 0x10000)
#define  AHBPERIPH_BASE (APB2PERIPH_BASE + 0x10000)

//定义GPIOB与RCC外设基础寄存器映射

#define  GPIOB_BASE         (APB2PERIPH_BASE + 0x0C00)
#define  RCC_BASE           (AHBPERIPH_BASE + 0x1000)

//定义GPIOB_CRL、GPIOB_BSRR与RCC_APB2ENR寄存器映射

#define  GPIOB_CRL          *(unsigned int*)(GPIOB_BASE + 0x00)
#define  GPIOB_BSRR         (GPIOB_BASE + 0X10)
#define  GPIOB_BSRR_S       *(unsigned int*)(GPIOB_BSRR + 0X00)
#define  GPIOB_BSRR_R       *(unsigned int*)(GPIOB_BSRR + 0x01)
#define  GPIOB_BRR          *(unsigned int*)(GPIOB_BASE + 0x14)
#define  RCC_APB2ENR        *(unsigned int*)(RCC_BASE + 0x18)
  • 掉坑日常之为什么BRy不起作用

    在我还在纳闷为什么延时后进行BSRR寄存器上的BRy位置1不能把该位Reset回0的时候,看见了BSRR寄存器说明上有这么一段话:

    (好家伙,因为BSRR、BRR均不受中断影响其变化,而延时也算是一种中断,在延时工程中BSRR的BSy位一直处于置1状态,结束延时后也无法通过BSRR的BRy位引起改变—因为此时相当于同时设置了BSy和BRy的对应位,只有BSy起作用;遇见延时还想回到最开始那美好的0,那就让BRR出场吧~)