STM32裸机学习笔记(三)—寄存器映射之BSRR与延时的爱恨情仇
GPIO与引脚区别?
-
GPIO包含在引脚内(引脚内还有电源、晶振等特殊功能引脚),除GPIO拓展内容即为单片机最小系统
GPIO功能如何检索?
-
通过芯片datasheet中的pin definitions去检索GPIO功能
输出控制
-
推挽输出(ODR寄存器输出1时推出3.3V/25mA,输出0时拉入GND)
-
开漏输出(只能输出低电平,开漏时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出场吧~)