STM32 寄存器操作 GPIO 与下降沿中断

news2024/11/25 18:40:20

 一、如何使用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**********************/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PLC_博图系列☞FBD

PLC_博图系列☞FBD 文章目录 PLC_博图系列☞FBD背景介绍FBD优势局限性 FBD 元素 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 FBD 背景介绍 这是一篇关于PLC编程的文章&#xff0c;特别是关于西门子的博图软件。我并不是专业的PLC编程人员&#xff0c;也不懂电路…

1036 跟奥巴马一起编程 (15)

美国总统奥巴马不仅呼吁所有人都学习编程&#xff0c;甚至以身作则编写代码&#xff0c;成为美国历史上首位编写计算机代码的总统。2014 年底&#xff0c;为庆祝“计算机科学教育周”正式启动&#xff0c;奥巴马编写了很简单的计算机代码&#xff1a;在屏幕上画一个正方形。现在…

【JavaEE】_文件与IO

目录 1.文件概述 1.1 文件的概念 1.2 文件的存储 1.3 文件的分类 1.4 目录结构 1.5 文件操作 1.5.1 文件系统操作 1.5.2 文件内容操作 2. Java文件系统操作 2.1 File类所处的包 2.2 构造方法 2.3 方法 2.3.1 与文件路径、文件名有关的方法 2.3.2 文件是否存在与普…

c++之function和bind详解-SurfaceFlinger学习必备语法基础

背景 C中的function和bind是为了更方便地进行函数对象的封装和调用而设计的&#xff0c;在SurfaceFlinger源码中也是有很多使用部分。 比如分析Vsync相关源码时候有相关回调时候 可以看到这里的mRegistration就有个参数是 std::bind,怎么这里就可以进行回调呢&#xff1f; 所…

代码随想录 Leetcode860. 柠檬水找零

题目&#xff1a; 代码(首刷自解 2024年2月15日&#xff09;&#xff1a; class Solution { public:bool lemonadeChange(vector<int>& bills) {vector<int> leftchange(2,0);//leftchange[0]代表5元数量&#xff0c;1代表10for (int i 0; i < bills.size…

最小生成树(Kruskal算法及相关例题)

1.Kruskal算法概念以及基本思路 &#xff08;1&#xff09;概念&#xff1a; 克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。它的时间复杂度为O&#xff08;ElogE&#xff09;(E是图G的边的总数)&#xff0c;适合于求边稀疏的网的最小生成树 。 其基本思想是&#xff…

OS文件管理

文件管理 文件的属性 文件所包含的属性&#xff1a; 文件名&#xff1a;由创建文件的用户决定文件名&#xff0c;主要为了方便用户找到文件&#xff0c;同一目录下不允许有重名文件。标识符&#xff1a;一个系统内的各文件标识符唯一&#xff0c;对用户来说毫无可读性&#…

2.12:C语言测试题

1.段错误&#xff1a;申请堆区内存未返回&#xff0c;str指向NULL 2.段错误&#xff1a;局部变量&#xff0c;本函数结束&#xff0c;p也释放 3.越界访问&#xff0c;可能正常输出hello&#xff0c;可能报错 4.可能段错误&#xff0c;释放后&#xff0c;str未指向NULL&#x…

CentOS7.9+Kubernetes1.29.2+Docker25.0.3高可用集群二进制部署

CentOS7.9Kubernetes1.29.2Docker25.0.3高可用集群二进制部署 Kubernetes高可用集群&#xff08;Kubernetes1.29.2Docker25.0.3&#xff09;二进制部署二进制软件部署flannel v0.22.3网络&#xff0c;使用的etcd是版本3&#xff0c;与之前使用版本2不同。查看官方文档进行了解…

The method toList() is undefined for the type Stream

The method toList() is undefined for the type Stream &#xff08;JDK16&#xff09; default List<T> toList() { return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray()))); }

C语言strstr函数

简介 strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是&#xff0c;则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串&#xff1b;否则&#xff0c;返回NULL。 实验 #include "stdio.h" #include "string.h"c…

2.14:二维数组、非函数实现strcat、strcmp、strcpy、strlen

1.编程实现二维数组的杨辉三角 程序代码&#xff1a; 1 #include<stdio.h>2 #include<string.h>3 #include<stdlib.h>4 int main(int argc, const char *argv[])5 {6 int n;7 printf("please enter n:");8 scanf("%d",&…

2024/02/13

21 、C 22 、D 23、B 如果5先出栈那么1&#xff0c;2&#xff0c;3&#xff0c;4就已经入栈了&#xff0c;5出后4出&#xff0c;1要出栈必须先让3&#xff0c;2出栈&#xff0c;所以 不可能输出B 24、10&#xff0c;12&#xff0c;120 25、2&#xff0c;5 26、段错…

2024.02.12作业

1. 段错误 2. 段错误 3. hello 4. world 5. int a; int* a; int **a; int a[10]; int* a[10]; int(* a)[10]; int* a(int); int (*a[10])(int); 6. 6&#xff1b; 2&#xff1b; 2 7. 2 8. 2 9. b 10. a 11. a 12. c 13. b 14. c 15. a 16. c 17. b 18. a 19…

消息中间件特点

1.  消息中间件概念 消息中间件是消息传递的过程中保存消息的容器。 主要目的&#xff1a;提供路由并保证消息的传递&#xff1b;如果发送消息时接受者不可用&#xff0c;消息队列会保留信息&#xff0c;直到可以成功传递为止。 消息中间件保存消息也是有期限的。 2.  消息…

JVM工作原理与实战(三十八):JIT即时编译器原理

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、JIT即时编译器 二、HotSpot中的JIT编译器 三、JIT优化技术 1.方法内联 2.逃逸分析 四、JIT优化建议 总结 前言 JVM作为Java程序的运行环境&#xff0c;其负责解释和执行字节…

[计算机网络]---序列化和反序列化

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、再谈协议…

《VulnHub》GoldenEye:1

title: 《VulnHub》GoldenEye&#xff1a;1 date: 2024-02-16 14:53:49 updated: 2024-02-16 15:08:49 categories: WriteUp&#xff1a;Cyber-Range excerpt: 主机发现、目标信息扫描、源码 js 文件泄露敏感信息、hydra 爆破邮件服务&#xff08;pop3&#xff09;、邮件泄露敏…

成考怎么搜题答案?9个受欢迎的搜题分享了 #微信#职场发展

大学生应该养成良好的时间管理习惯&#xff0c;合理分配学习、休息和娱乐的时间&#xff0c;避免压力过大或时间浪费。 1.Forest专注森林 Forest是一款专注与时间管理应用。当你需要专注于学习或工作时&#xff0c;你可以在Forest应用中种植一棵虚拟树&#xff0c;设定一段时…

幻兽帕鲁——游戏优化【腾讯云服务器联机版本】

幻兽帕鲁8人以内联机&#xff0c;闭眼参加【腾讯云幻兽帕鲁专属游戏活动】4核16G12兆 购买腾讯云服务器后&#xff0c;游戏一键部署&#xff0c;联机流程参照这个博文 【10秒开服】雾锁王国全自动部署教程-CSDN博客 幻兽帕鲁——游戏优化 1.设置虚拟内存 第一步&#xff1a…