STM32 寄存器操作 GPIO 与中断

news/2024/4/17 17:03:02

 一、如何使用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 时钟。

int main(void)
{//片上外设基地址+AHB总线基地址+RCC外设基地址+RCC的AHB1时钟使能寄存器地址*(unsigned int*)(0x40000000+0x20000+0x1000+0x18) |= ((1)<<3);while(1);
}// 函数为空,目的是为了骗过编译器不报错
void SystemInit(void)
{}

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

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

1.3 配置 GPIOB_0 模式

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

//片上外设基地址+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 使其输出低电平

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

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

 全部代码如下:

#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 配置成浮空输入,根据上表来配置寄存器。

/* 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 是浮空输入。

全部代码如下:

#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 拉低的程序。

 

 

 

 


https://www.xjx100.cn/news/3271421.html

相关文章

搜索二维矩阵[中等]

一、题目 给你一个满足下述两条属性的m x n整数矩阵&#xff1a; 【1】每行中的整数从左到右按非严格递增顺序排列。 【2】每行的第一个整数大于前一行的最后一个整数。 给你一个整数target&#xff0c;如果target在矩阵中&#xff0c;返回true&#xff1b;否则&#xff0c;返…

Linux第50步_移植ST公司的linux内核第2步_编译ST公司的linux源码和修改网络驱动

1、修改“linux-5.4.31”目录下的“Makefile” 1)、使用VSCode打开“linux-5.4.31.code-workspace” 2)、点击“linux-5.4.31”目录下的“Makefile” 3)、点击“编辑”&#xff0c;点击“查找”&#xff0c;输入“CROSS_COMPILE回车”&#xff0c;找到“ARCH ? $(SUBARCH)”…

Transformer实战-系列教程16:DETR 源码解读3(DETR类)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 DETR 算法解读 DETR 源码解读1&#xff08;项目配置/CocoDetection类&#xff09; …

单页404源码

<!doctype html> <html> <head> <meta charset"utf-8"> <title>简约 404错误页</title><link rel"shortcut icon" href"./favicon.png"><style> import url("https://fonts.googleapis.co…

c++恶魔轮盘制造第1期输赢

小常识&#xff0c;恶魔叫DEALER&#xff0c;上帝叫God. 赢了很简单 void sheng() { cout<<"你获胜了&#xff01;";MessageBox(NULL,TEXT("你的钱~~~~~~给你"),TEXT("DEALER"),MB_OK);system("pause");system("cls"…

powershell 雅地关闭UDP监听器

在PowerShell中优雅地关闭UDP监听器意味着你需要一种机制来安全地停止正在运行的UdpClient实例。由于UdpClient类本身没有提供直接的停止或关闭方法&#xff0c;你需要通过其他方式来实现这一点。通常&#xff0c;这涉及到在监听循环中添加一个检查点&#xff0c;以便在接收到停…

使用 C++23 从零实现 RISC-V 模拟器(1):最简CPU

&#x1f449;&#x1f3fb; 文章汇总「从零实现模拟器、操作系统、数据库、编译器…」&#xff1a;https://okaitserrj.feishu.cn/docx/R4tCdkEbsoFGnuxbho4cgW2Yntc 本节实现一个最简的 CPU &#xff0c;最终能够解析 add 和 addi 两个指令。如果对计算机组成原理已经有所了…

MySQL篇----第十四篇

系列文章目录 文章目录 系列文章目录前言一、MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?二、锁的优化策略三、索引的底层实现原理和优化四、什么情况下设置了索引但无法使用前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽…