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 时钟。

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

三、中断

在图 EXTI功能框图 可以看到很多在信号线上打一个斜杠并标注“20”字样,这个表示在控制器内部类似的信号线路有20个, 这与EXTI总共有20个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他19个线路原理也就知道了。

红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到NVIC控制器内。这也是本篇中 GPIO 下降沿中断的线路。值得注意的是,图中的中断屏蔽/软件中断/下降沿等...是外设的寄存器,其最后需要传给 Cortex-M3 内核的 NVIC 寄存器进行中断优先级裁决,之后他会帮我们进行回调函数的操作。

绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。定时器等电机控制等使用的很多。在此不做描述。

3.1 中断线

EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0至EXTI15, 还有另外七根用于特定的外设事件。

 

EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。 

 

3.2 配置外设的寄存器

3.2.1 外部中断配置寄存器 1(AFIO_EXTICR1)

我们首先需要配置 AFIO_EXTICR 外部中断配置寄存器。使能 GPIOA_0 按钮引脚这条中断线的外部中断复用。因为我们是 GPIOA_0 所以自然配置 EXTIO[3:0] 这个寄存器就可以了。

如果我们还想让 GPIOB_0 也有中断能力呢?因为我们已经给GPIOA 用了,所以就不能做到了。

//片上外设基地址+APB2总线基地址+AFIO基地址+外部中断配置寄存器1(AFIO_EXTICR1) 
  *(unsigned int*)(0x40000000+0x10000+0x0000+0x08) = 0x00;

3.2.2 中断屏蔽寄存器(EXTI_IMR)

中断屏蔽器复位值是 0x0000 0000,也就是屏蔽所有中断,我们需要打开 MR0 的中断,使能 0 线的开启。

  //片上外设基地址+APB2总线基地址+EXTI基地址+中断屏蔽寄存器(EXTI_IMR) 
  *(unsigned int*)(0x40000000+0x10000+0x0400+0x00) = 0x01;

3.2.3 上升沿触发选择寄存器(EXTI_RTSR)

  //片上外设基地址+APB2总线基地址+EXTI基地址+上升沿触发选择寄存器(EXTI_RTSR) 
  *(unsigned int*)(0x40000000+0x10000+0x0400+0x08) = 0x01;

3.3 NVIC 配置

根据我们中断框图来看,在经过中断屏蔽等寄存器处理后,中断信号会传入到 NVIC 内。

在配置 Cortex-M3 内核的 NVIC 我们不再使用寄存器,为了方便而是使用 core_cm3.h 的库函数配置。

// 使用NVIC_EncodePriority函数编码中断优先级,并将结果存储在变量pri中  
uint32_t pri = NVIC_EncodePriority(5, 0, 2);  
  
// 设置EXTI0_IRQn(外部中断0)的中断优先级为之前编码的pri值  
NVIC_SetPriority(EXTI0_IRQn, pri);  
  
// 使能(启用)EXTI0_IRQn(外部中断0),使其能够在发生时被微控制器响应  
NVIC_EnableIRQ(EXTI0_IRQn);

上述代码中我们使用 2 位抢占,2 位相应的配置方式。最后我们将 EXTI0 中短线配置为 0 级抢占 2级优先级方案。

四、全部代码

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_exti.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) );
    
    

  
    //片上外设基地址+APB2总线基地址+AFIO基地址+外部中断配置寄存器1(AFIO_EXTICR1) 
    *(unsigned int*)(0x40000000+0x10000+0x0000+0x08) = 0x00;

    //片上外设基地址+APB2总线基地址+EXTI基地址+中断屏蔽寄存器(EXTI_IMR) 
    *(unsigned int*)(0x40000000+0x10000+0x0400+0x00) = 0x01;

    //片上外设基地址+APB2总线基地址+EXTI基地址+上升沿触发选择寄存器(EXTI_RTSR) 
    *(unsigned int*)(0x40000000+0x10000+0x0400+0x08) = 0x01;


    uint32_t pri=NVIC_EncodePriority(5,0,2);
    NVIC_SetPriority(EXTI0_IRQn,pri);
    NVIC_EnableIRQ(EXTI0_IRQn);
	
	/* 等待中断,由于使用中断方式,CPU不用轮询按键 */
	while(1)                            
	{
        
	}
}

void KEY1_IRQHandler(void)
{
    //uint32_t a = *(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) ~= 1;
    
    *(unsigned int*)(0x40000000+0x10000+0x0C00+0x0C) ^= 1;
    EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
}
/*********************************************END OF FILE**********************/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/389903.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode hot100不同路径

本题可以采用动态规划来解决。还是按照五部曲来做 确定dp数组&#xff1a;dp[i][j]表示走到&#xff08;i&#xff0c;j&#xff09;有多少种路径 确定递推公式&#xff1a;我们这里&#xff0c;只有两个移动方向&#xff0c;比如说我移动到&#xff08;i&#xff0c;j&#x…

【机器学习】数据清洗之识别重复点

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步…

React入门到精通:掌握前端开发的必备技能!

介绍&#xff1a;React是一个由Facebook开发和维护的JavaScript库&#xff0c;用于构建用户界面&#xff0c;特别是用于构建单页应用程序和移动应用程序的用户界面。以下是对React的详细介绍&#xff1a; 虚拟DOM&#xff1a;React通过使用虚拟DOM&#xff08;Document Object …

Rust 数据结构与算法:3栈:用栈实现符号匹配

1、符号匹配 如&#xff1a; (56)(78)/(43)、{ { ( [ ] [ ])}}、(ab)(c*d)func() 等各类语句的符号匹配。 这里我们关注的不是数字而是括号&#xff0c;因为括号更改了操作优先级&#xff0c;限定了语言的语义&#xff0c;这是非常重要的。如果括号不完整&#xff0c;那么整个…

C语言指针(初阶)

文章目录 1:内存与地址1.1内存1.2:如何理解编址 2:指针变量与地址2.1:指针变量与解引用操作符2.1.1:指针变量2.1.2:如何拆解指针类型2.1.3:解引用操作符 2.2:指针变量的大小 3:指针变量类型的意义代码1解引用修改前解引用修改后 代码2解引用修改前解引用修改后 4:const修饰指针…

RSIC-V“一芯”学习笔记(三)——读后感以及部分PA0工作

文章目录 一、别像弱智一样提问二、提问的智慧三、安装linux以及配置问题3.1 关于问题配置 一、别像弱智一样提问 提问前&#xff0c;应该清晰问自己几个问题&#xff0c;1. 是否尝试了在搜索引擎进行搜索过2. 相关的手册和文档是否看了3. 找找有没有常见的问题文档&#xff0…

Android Jetpack:提高开发效率的终极工具集

Android Jetpack&#xff1a;提高开发效率的终极工具集 1. 引言 Android Jetpack是一套为Android应用程序开发提供帮助的工具集。它旨在简化开发流程&#xff0c;提高开发效率&#xff0c;并提供一致的用户体验。无论您是新手还是经验丰富的开发者&#xff0c;Jetpack都可以为…

命令行参数和环境变量

命令行参数 命令行参数是在用户在命令行中输入命令时&#xff0c;跟随命令一起输入的一些附加信息。这些参数可以用来配置命令的行为或传递一些数据给命令。 让同样的程序在不同的命令行参数下运行出不同的结果&#xff01; 将这些命令和参数可以传给 main 函数生&#xff0…

Junit5基础教程

文章目录 一&#xff0c;导入依赖二&#xff0c;基本功能一、常用断言二、执行顺序和常用注解1、通过BeforeAll类的注解来保证顺序2、通过order注解来保证执行顺序 三、依赖测试四、参数化测试五、测试套件SelectPackages、IncludePackages、SelectClasses、IncludeTags等注解的…

苹果手机充电充不进去怎么办?这里有你想要的一些故障排除技巧

当你的iPhone插上充电器或将其放在无线充电器上充电时,稍后再检查发现它没有充电,怎么办呢?可能的原因不少。让我们来看看一些最常见的iPhone充电问题,以及你能做些什么。 常规故障排除提示 故障排除中最基本的技术之一是用已知的好的相同组件代替不起作用的组件,如把你的…

【复现】cellinx摄像设备 未授权漏洞_50

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 cellinx是一家韩国的摄像设备 二 .漏洞影响 通过未授权访问可以创建用户进入后台&#xff0c;可能造成系统功能破坏。 三.漏洞复…

python工具方法 45 基于ffmpeg以面向对象多线程的方式实现实时推流

1、视频推流 参考基于ffmpeg模拟监控摄像头输出rtsp视频流并opencv播放 实现视频流的推流。 其基本操作就是,安装视频流推流服务器,ffmpeg,准备好要推流的视频。 命令如下所示:ffmpeg -re -stream_loop -1 -i 风景视频素材分享.flv -c copy -f rtsp rtsp://127.0.0.1:554/…

挑战杯 python的搜索引擎系统设计与实现

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python的搜索引擎系统设计与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;5分创新点&#xff1a;3分 该项目较为新颖&#xff…

租赁香港服务器多少钱一个月?24元

阿里云香港服务器2核1G、30M带宽、40GB ESSD系统盘优惠价格24元/月&#xff0c;288元一年&#xff0c;每月流量1024GB&#xff0c;多配置可选&#xff0c;官方优惠活动入口 https://t.aliyun.com/U/bLynLC 阿里云服务器网aliyunfuwuqi.com分享阿里云香港服务器优惠活动、详细配…

图像识别基础之模板匹配

principle 图像匹配 本质&#xff1a;图像的相似度很高(矩阵的相似度很高) code /*\brief 我的图像匹配函数&#xff0c;获取差方和均值最小的矩阵作为结果\param srcPicFile:用以匹配的图像文件\param templatePicFile:模板图像文件\param destPicFile:输出的检测结果文件…

【方法】如何打开带密码的RAR分卷压缩文件?

RAR分卷文件是一种特殊的RAR压缩文件格式&#xff0c;也就是将文件压缩成多个相同大小的压缩包&#xff0c;可以更方便传输。那如果收到了带有密码的RAR分卷压缩文件&#xff0c;要如何打开呢&#xff1f; 无论RAR分卷压缩文件是否设置了密码保护&#xff0c;在打开或者解压分…

数据结构与算法:双向链表

朋友们大家好啊&#xff0c;在上节完成单链表的讲解后&#xff0c;我们本篇文章来对带头循环双向链表进行讲解 双向链表 双向链表、头节点和循环的介绍构建双向链表节点的构建初始化双向循环链表&#xff08;空链表&#xff09;销毁双向链表 链表的打印双向链表头尾的插与删尾插…

基于Java (spring-boot)的房屋租赁管理系统

一、项目介绍 基于Java (spring-boot)的房屋租赁管理系统功能&#xff1a;登录、管理员、租客、公告信息管理、房屋信息管理、用户信息管理、租金信息管理、故障信息管理、房屋出租信息详情、个人信息、修改密码、等等等。 适用人群&#xff1a;适合小白、大学生、毕业设计、课…

【C语言】指针的进阶篇,深入理解指针和数组,函数之间的关系

欢迎来CILMY23的博客喔&#xff0c;本期系列为【C语言】指针的进阶篇&#xff0c;深入理解指针和数组&#xff0c;函数之间的关系&#xff0c;图文讲解其他指针类型以及指针和数组&#xff0c;函数之间的关系&#xff0c;带大家更深刻理解指针&#xff0c;以及数组指针&#xf…

谁拿了最多奖学金——NOIP 2005 提高组

输入样例&#xff1a; 4 YaoLin 87 82 Y N 0 ChenRuiyi 88 78 N Y 1 LiXin 92 88 N N 0 ZhangQin 83 87 Y N 1 输出样例&#xff1a; ChenRuiyi 9000 28700 这道题用结构体做对吧 #include <bits/stdc.h> using namespace std; class student{public:string name;int FG…