STM32 寄存器操作 GPIO 与中断

一、如何使用stm32寄存器点灯?

1.1 寄存器映射表

寄存器本质就是一个开关,当我们把芯片寄存器配置指定的状态时即可使用芯片的硬件能力。

寄存器映射表则是开关的地址说明。对于我们希望点亮 GPIO_B 的一个灯来说,需要关注以下的两个寄存器:

1.2 配置时钟

对于我们实现希望点亮一个灯的需求来说,不仅需要配置配置 GPIO_B 的时钟,首先需要配置 GPIO_B 的时钟。

为什么需要先配置时钟呢?

STM32 外设通常都是给了时钟后才能设置它的寄存器(即才能使用这个外设)。STM32、LPC1XXX 等等都是这样,这么做的目的是为了省电,使用了所谓时钟门控的技术。寄存器是基于触发器的,触发器的赋值是一定需要时钟的,而寄存器的时钟是由总线时钟提供的,就是说没有总线时钟的话,你给寄存器值它是不会读入的。

对于下图中的系统框图来看,GPIO_B 挂载在 AHB 总线下 APB2 时钟。所以我们需要开启 APB2 的总线时钟。

STM32F10xxx 参考手册 6.3.7 告诉我们怎么使能 GPIO_B 的时钟。

由 1.1 的寄存器映射表图片来看,寄存器映射表可知

0x40000000(片上外设基地址) + 0x20000(AHB总线基地址) + 0x1000 (RCC外设基地址)

最后我们再加上RCC_APBENR 的地址 0x18 即可成功访问这个寄存器。

对这个寄存器使用左移三位进行置位 IOPB 操作,这样就成功开启了 GPIO_B 时钟。

cpp 复制代码
int main(void)
{
    //片上外设基地址+AHB总线基地址+RCC外设基地址+RCC的AHB1时钟使能寄存器地址
    *(unsigned int*)(0x40000000+0x20000+0x1000+0x18) |= ((1)<<3);
	while(1);
}

// 函数为空,目的是为了骗过编译器不报错
void SystemInit(void)
{
    
}

值得注意的是这种写法,相当于向地址 0XFF 写入 0XFE。

cpp 复制代码
*(unsigned int*)0xFE = 0XFF;
//等价于
unsigned int *p = 0xFE;    //无符号 uint32_t 指针类型指向 0XFE 这个地址
*p = 0xFF;                 //解地址符 向这个地址写入0XFF

1.3 配置 GPIOB_0 模式

根据芯片手册提示 如果我们需要把 GPIOB_0 配置为输出高电平,只需要将 GPIO_CRL 第四位寄存器配置成 0001 即可,这代表了通用开漏输出模式,最大输出10MHz。

cpp 复制代码
//片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOB_CRL地址
*(unsigned int*)(0x40000000+0x10000+0x0C00+0x00) &= ~( (0xFF)<<(4*0) );    //低四位清零
*(unsigned int*)(0x40000000+0x10000+0x0C00+0x00) |=  ( (1) << (4*0) );    //第四位搞成0001

1.4 配置 GPIOB_0 使其输出低电平

cpp 复制代码
//片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOx_ODR地址
*(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) &= ~(1<<0);

经过以上我们配置了 RCC_APB2ENR、GPIOB_CRL、GPIOB_ODR寄存器后。**GPIOB_0 被配置成了开漏输出低电平。**这样就可以点亮我们的灯泡了。

全部代码如下:

cpp 复制代码
#include "stm32f10x.h"

int main(void)
{
    //片上外设基地址+AHB总线基地址+RCC外设基地址+RCC的AHB1时钟使能寄存器地址
    *(unsigned int*)(0x40000000+0x20000+0x1000+0x18) |= ((1)<<3);
    
    //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOB_CRL地址
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x00) &= ~( (0xFF)<<(4*0) );
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x00) |=  ( (1) << (4*0) );
    
    //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOx_ODR地址
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) &= ~(1<<0);
    
	while(1);
}

// 函数为空,目的是为了骗过编译器不报错
void SystemInit(void)
{
    
}

二、配置输入模式

我们希望将 GPIOA_0 配置成浮空输入,根据上表来配置寄存器。

cpp 复制代码
/* PA0 key1引脚 */
//片上外设基地址+AHB总线基地址+RCC外设基地址+RCC的AHB1时钟使能PA寄存器地址
*(unsigned int*)(0x40000000+0x20000+0x1000+0x18) |= ((1)<<2);

//片上外设基地址+APB2总线基地址+GPIOA外设基地址+GPIOA_CRL地址
*(unsigned int*)(0x40000000+0x10000+0x0800+0x00) &= ~( (0xFF) << (4*0) );
*(unsigned int*)(0x40000000+0x10000+0x0800+0x00) |=  ( (0X04) << (4*0) );

在最后一行,我们将GPIOA_CRL 寄存器低四位配置成 0100 是浮空输入。

全部代码如下:

cpp 复制代码
#include "stm32f10x.h"

int main(void)
{
    /* PB1 点灯引脚  */
    //片上外设基地址+AHB总线基地址+RCC外设基地址+RCC的AHB1时钟使能PB寄存器地址
    //打开PB时钟
    *(unsigned int*)(0x40000000+0x20000+0x1000+0x18) |= ((1)<<3);
    
    //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOB_CRL地址
    //配置成PB1开漏输出
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x00) &= ~( (0xFF)<<(4*0) );
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x00) |=  ( (1) << (4*0) );
    
    //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOx_ODR地址
    //默认给一个PB1高电平 灯灭
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) = 1;
    
    /* PA0 key1引脚 */
    //片上外设基地址+AHB总线基地址+RCC外设基地址+RCC的AHB1时钟使能PA寄存器地址
    //打开PA时钟
    *(unsigned int*)(0x40000000+0x20000+0x1000+0x18) |= ((1)<<2);
    
    //片上外设基地址+APB2总线基地址+GPIOA外设基地址+GPIOA_CRL地址
    //配置成PA0浮空输入
    *(unsigned int*)(0x40000000+0x10000+0x0800+0x00) &= ~( (0xFF) << (4*0) );
    *(unsigned int*)(0x40000000+0x10000+0x0800+0x00) |=  ( (0X04) << (4*0) );
        
    
	while(1){
        //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOB_IDR地址
        //读取是否按下按钮
        if(*(unsigned int*)(0x40000000+0x10000+0x0800+0x08) & 1 != 0)
            //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOx_ODR地址
            //低电平 灯灭
            *(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) &= ~(1<<0);
        else
            //片上外设基地址+APB2总线基地址+GPIOB外设基地址+GPIOx_ODR地址
            //高电平 灯灭
            *(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) = 1;
    }
}

// 函数为空,目的是为了骗过编译器不报错
void SystemInit(void)
{
    
}

这样我们就实现了按下按钮点灯的操作。

本质上是等待 PA0 低电平后就把 PB1 拉低的程序。

相关推荐
落雨封海1 小时前
【1】GD32 系统架构、内核、中断系统、存储器系统
单片机·gd32
weixin_462901972 小时前
STM32F103C8T6裸机多任务编程的问题
stm32·单片机·嵌入式硬件
Jumbuck_104 小时前
基于OpenMV+STM32+OLED与YOLOv11+PaddleOCR的嵌入式车牌识别系统开发笔记
笔记·stm32·嵌入式硬件
小智学长 | 嵌入式8 小时前
单片机-89C51部分:4、固件烧录
c语言·单片机·嵌入式硬件
时之彼岸Φ8 小时前
Adruino:传感器及步进电机
单片机·嵌入式硬件
网易独家音乐人Mike Zhou8 小时前
【Linux应用】交叉编译环境配置,以及最简单粗暴的环境移植(直接从目标板上复制)
linux·stm32·mcu·物联网·嵌入式·iot
少年、潜行9 小时前
【开源】基于51单片机的简易智能楼道照明设计
单片机·嵌入式硬件·51单片机
子朔不言9 小时前
MH2103 MH22D3系列的JTAG/SWD复用功能和引脚映射,IO初始化的关键点
单片机·mcu·mh2103·mh22d3·新龙微·兆讯
国科安芯9 小时前
基于先进MCU的机器人运动控制系统设计:理论、实践与前沿技术
人工智能·单片机·机器人
honey ball9 小时前
为啥低速MCU单板辐射测试会有200M-1Ghz的辐射信号
单片机·嵌入式硬件