0、从GPIO_TypeDef结构体的定义中STM32F103C8T6的GPIO有7个寄存器:
typedef struct
{
_IO uint32_t CRL; //低8位端口配置寄存器(配置工作模式,输出速度)
_IO uint32_t CRH; //高8位端口配置寄存器(配置工作模式,输出速度)
_IO uint32_t IDR; //端口输入数据寄存器(用于判断IO引脚的电平)
_IO uint32_t ODR; //端口输出数据寄存器(用于设置IO引脚输出的电平)
_IO uint32_t BSRR;//端口位设置,清除寄存器(设置ODR寄存器的值)
_IO uint32_t BRR; //F4之后没有这个寄存器,考虑代码兼容性的话不建议使用
_IO uint32_t LCKR;//配置锁定,用的不多
}GPIO_TypeDef;
1、CRL(Configuration Register Low)
用于配置GPIO端口的低8位(即引脚0到7)的模式和特性,每个引脚占用4个位,配置8个引脚。
CRL寄存器的结构
- 示例:用配置寄存器的方法配置PA0为输出模式(通用推挽输出)
#include"stm32f10x.h"
int main(void)
{
//开启GPIOA的时钟
RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;
//直接修改CRL寄存器来配置PA0
//每个引脚占4位,所以PA0的配置位于CRL的低4位
//设置为输出模式(01:10MHz General Purpose Output Push-Pull
GPIOA->CRL&=~(0x0F<<(0*4));/*清除PA0原来的配置,~相当于把00001111倒写为11110000。
<<为左移操作符,0*4=0,表示左移0位,如果是配置的是PA1,则要改为1*4=4,左移4位*/
GPIOA->CRL|=(0x03<<(0*4));//设置PA0为输出模式(0x03表示10MHz General Purpose Output push-pull)
/*详细说明1
结合CRL寄存器结构表来看,每4位分2组2位,分别为CNF和MODE。
其中CNF取值:
1、当MODE=00时:
00 模拟输入模式
01 浮空输入模式
10 上拉/下拉输入模式
11 保留
2、当MODE>00时:
00 通用推挽输出模式
01 通用开漏输出模式
10 复用功能推挽输出模式
11 复用功能开漏输出模式
其中MODE取值:
00 输入模式(复位后的状态)
01 输出模式,最大速度10MHz
10 输出模式,最大速度2MHz
11 输出模式,最大速度50MHz
*/
/*详细说明2
与操作&=:只有两个相应的二进制位都为1时,结果才为1
11111111(reg)
& 11110000(mask)
----------11110000(result)
或操作|=:只有两个相应的二进制位中有一个为1,结果就为1
11110000(reg)
| 00001111(mask)
----------11111111(result)
*/
//设置PA0为高电平
GPIO->BSRR=(1<<0);//BSRR寄存器的低16位用于设置引脚为高电平
while(1)
{
//主循环
}
2、CRH(Configuration Register High)
用于配置GPIO端口的高8位(即引脚8到14)的模式和特性,每个引脚占用4个位,配置8个引脚,和CRL差不多。
CRH寄存器的结构
- 示例:用配置寄存器的方法配置PA8为输出模式(通用开漏输出)
#include "stm32f10x.h"
int main(void)
{
//开启GPIOA的时钟
RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;
GPIO->CRH&=~(0x0F<<((8-8)*4));/*清除PA8原来的配置,~相当于把00001111倒写为11110000。
<<为左移操作符,(8-8)*4=0,表示左移0位,如果配置的是PA9,则要改为(9-8)*4=4,左移4位*/
GPIOA->CRH|=(0x05<<((8-8)*4));//设置PA8为通用开漏输出模式,最大速度10MHz,即00000101
//设置PA8为高电平
GPIOA->BSRR=(1<<0);//BSRR寄存器的低16位用于设置引脚为高电平
while(1)
{
//主循环
}
}
3、IDR(Input Data Register)
用于读取对应I/O口的状态,前16位的各1位对应1个I/O口;后16位保留,始终为0。注意,一般用来读取输入模式下的状态,如果是输出模式,也可以读取,但建议直接读取或检查ODR的值。
IDR寄存器的结构
- 示例:在PA0为浮空输入模式的高电平状态下,读取PA0的值,然后执行对应的操作。
#include "stm32f10x.h"//根据你的STM32系列选择合适的头文件
int main(void){
RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
GPIOA->CRL&=~(0x0F<<(0*4));//对PA0清零
GPIOA->CRL|=(0x04<<(0*4));//配置PA0为浮空输入模式
GPIOA->BSRR=(1<<0);//设置PA0为高电平
while(1){
//读取PA0的值
if(GPIOA->IDR&GPIO_PIN_0){
/*如果PA0为高电平,在"stm32f1xx_hal_gpio.h"里,GPIO_PIN_0定义为 (uint16_t)0x0001,也就是被读取的值与1进行与运算,全为1才是真,不全为假*/
//执行相关操作
}else{
//如果PA0为低电平
//执行相关操作
}
}
}
4、ODR(Output Data Register)
用于设置或读取某个IO口输出低电平还是高电平,只有在输出模式下有效,前16位的各1位对应1个I/O口;后16位保留,始终为0。
ODR寄存器的结构
- 示例:在PA0为通用推挽输出模式、最大速度10MHz下,设置PA0为高电平,然后检查PA0的状态,执行对应的操作
#include "stm32f10x.h"
int main(void){
RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
GPIOA->CRL&=~(0x0F<<(0*4));//对PA0清零
GPIOA->CRL|=(0x01<<(0*4));//配置PA0为通用推挽输出模式、最大速度10MHz
GPIOA->ODR|=0x01;//设置PA0为高电平,也可以写成GPIOA->ODR|=GPIO_ODR_ODR0,因为在"stm32f10xb.h"里对GPIO_ODR_ODR0的宏定义为0x1UL
uint32_t odrValue=GPIOA->ODR;//读取GPIOA的ODR寄存器的值
//检查PA0的电平状态
if (ordValue&GPIO_ODR_ODR0){
//PA0是高电平
}else{
//PA0是低电平
}
while(1){
}
}
}
5、BSRR(Bit Set/Reset Register)
用于同时设置或复位多个引脚的电平状态,前16位为BS,后16位为BR,都是对应0~15的I/O口,都是在设置为1时起作用,BS的优先级比BR高,在对应BS和BR都设置为1时,只起BS的作用。注意,一般情况下,BSRR对输出模式才起作用。
BSRR寄存器的结构
- 示例:将PA0~PA15设置为推挽输出模式,然后设置为高电平,再把PA8-PA15改为低电平
#include "stm32f10x.h"//根据你的STM32系列选择合适的头文件
int main(void){
RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;//开启GPIOA的时钟
for(uint32_t k=0;k<8;k++)
{
GPIOA->CRL&=~(0x0F<<(k*4));//对PA0~PA7清零
GPIOA->CRL|=(0x01<<(k*4));//配置PA0~PA7为通用推挽输出模式、最大速度10MHz
GPIOA->ODR|=(0x01<<k);//用ODR对PA0~PA7设置为高电平
}
for(uint32_t i=0;i<8;i++)
{
GPIOA->CRH&=~(0x0F<<(i*4));//对PA8~PA15清零
GPIOA->CRL|=(0x01<<(i*4));//配置PA8~PA15为通用推挽输出模式、最大速度10MHz
GPIOA->ODR|=(0x01<<(i+8));//用ODR对PA8~PA15设置为高电平
}
GPIOA->BSRR|=(0x0F<<(3*8));//对PA8~PA15清零
GPIOA->BSRR|=(0x00<<(1*8));//确保没有影响
uint32_t odrValue=GPIOA->ODR;//读取GPIOA的ODR寄存器的值
//检查PA0的电平状态
if(odrValue&0xFF){
//PA0~PA7是高电平,PA8~PA15是低电平
}else{
//不符合"PA0~PA7是高电平,PA8~PA15是低电平"的条件
}
while(1){
}
}