Cortex-M架构MCU位带操作最详细解析(主要以STM32为例,包括判断哪些MCU可用)

news2025/3/1 16:16:08

Cortex-M架构MCU位带操作最详细解析(主要以STM32为例,包括判断哪些MCU可用)

代码实践部分直接跳转:
https://blog.csdn.net/weixin_53403301/article/details/125543844

一、位带操作理论及实践

位带操作的概念其实30年前就有了,那还是 CM3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版

位带区: 支持位带操作的地址区

位带别名: 对别名地址的访问最终作 用到位带区的访问上(注意:这中途有一个 地址映射过程)

位带操作对于硬件 I/O 密集型的底层程序最有用处

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在CM4中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个区中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

位操作就是可以单独的对一个比特位读和写,类似与51中sbit定义的变量,stm32中通过访问位带别名区来实现位操作的功能
STM32中有两个地方实现了位带,一个是SRAM,一个是片上外设。
在这里插入图片描述
(1)位带本质上是一块地址区(例如每一位地址位对应一个寄存器)映射到另一片地址区(实现每一位地址位对应一个寄存器中的一位),该区域就叫做位带别名区,将每一位膨胀成一个32位的字。
(2)位带区的4个字节对应实际寄存器或内存区的一个位,虽然变大到4个字节,但实际上只有最低位有效(代表0或1)

在这里插入图片描述

要设置0x2000 0000这个字节的第二个位bit2为1,使用位带操作的步骤有:
1、将1写入位 带别名区对应的映射地址(即0x22000008,因为1bit对应4个byte);
2、将0x2000 0000的值 读取到内部的缓冲区(这一步骤是内核完成的,属于原子操作,不需要用户操作);
3、将bit2置1,再把值写 回到0x2000 0000(属于原子操作,不需要用户操作)。

关于GPIO引脚对应的访问地址,可以参考以下公式
寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

如:端口F访问的起始地址GPIOF_BASE

#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)

在这里插入图片描述

但好在官方库里面都帮我们定义好了 只需要在BASE地址加上便宜即可

例如:

GPIOF的ODR寄存器的地址 = GPIOF_BASE + 0x14

寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

设置PF9引脚的话:

uint32_t *PF9_BitBand =
*(uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR– 0x40000000) *32 + 9*4)

封装一下:

#define PFout(x) *(volatile uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR – 0x40000000) *32 + x*4)

现在 可以把通用部分封装成一个小定义:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

那么 设置PF引脚的函数可以定义:

#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414   
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 

#define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

若使PF9输入输出则:

PF_O(9)=1;  //输出高电平
uint8_t dat = PF_I(9);  //获取PF9引脚的值

总线输入输出:

void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PF_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PF_I(i)<<i)&0xFFFF;
	}
	return num;
}

适用于所有STM32的位带操作和其他CM架构的位带请看:
https://blog.csdn.net/weixin_53403301/article/details/125543844
【最新】基于Cortex-M0、M3、M4的GPIO口位带操作宏定义(可总线输入输出,可用于STM32、ADuCM4050等)

STM32的可用下面的函数:

#ifndef __GPIO_H__
#define __GPIO_H__
#include "stm32l496xx.h"

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
#define PA_O(n)   	BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PA_I(n)    	BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PB_O(n)   	BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PB_I(n)    	BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PC_O(n)   	BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PC_I(n)    	BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PD_O(n)   	BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PD_I(n)    	BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PE_O(n)   	BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PE_I(n)    	BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PG_O(n)   	BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PG_I(n)    	BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PH_O(n)   	BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PH_I(n)    	BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PI_O(n)			BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PI_I(n)   	BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

void PA_BUS_O(unsigned int num);
unsigned int PA_BUS_I(void);

void PB_BUS_O(unsigned int num);
unsigned int PB_BUS_I(void);

void PC_BUS_O(unsigned int num);
unsigned int PC_BUS_I(void);

void PD_BUS_O(unsigned int num);
unsigned int PD_BUS_I(void);

void PE_BUS_O(unsigned int num);
unsigned int PE_BUS_I(void);

void PF_BUS_O(unsigned int num);
unsigned int PF_BUS_I(void);

void PG_BUS_O(unsigned int num);
unsigned int PG_BUS_I(void);

void PH_BUS_O(unsigned int num);
unsigned int PH_BUS_I(void);

void PI_BUS_O(unsigned int num);
unsigned int PI_BUS_I(void);

#endif

#include "GPIO.h"

void PA_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PA_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PA_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PA_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PB_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PB_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PB_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PB_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PC_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PC_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PC_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PC_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PD_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PD_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PD_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PD_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PE_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PE_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PE_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PE_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PF_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PF_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PG_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PG_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PG_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PG_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PH_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PH_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PH_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PH_I(i)<<i)&0xFFFF;
	}
	return num;
}

void PI_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{
	int i;
	for(i=0;i<16;i++)
	{
		PI_O(i)=(num>>i)&0x0001;
	}
}
unsigned int PI_BUS_I(void)  //输出值num最大为0xFFFF
{
	unsigned int num;
	int i;
	for(i=0;i<16;i++)
	{
		num=num+(PI_I(i)<<i)&0xFFFF;
	}
	return num;
}

二、如何判断MCU的外设是否支持位带

根据《ARM Cortex-M3与Cortex-M4权威指南(第3版)》中第6章第7节描述
在这里插入图片描述
也就是说 要实现对GPIO的位带操作 必须保证GPIO位于外设区域的第一个1MB中
第一个1MB应该是0x4010 0000之前 位带不是直接操作地址 而是操作地址映射 地址映射被操作以后 MCU自动会修改对应寄存器的值

位带区只有1MB 所以只能改0x4000 0000 - 0x400F FFFF的寄存器
像F4系列 GPIO的首地址为0x4002 0000 就可以用位带来更改

STM32L476的GPIO就不行:
在这里插入图片描述
AHB2的都不能用位带
ABP 还有AHB1都可以用
在这里插入图片描述
但是L476的寄存器里面 GPIO和ADC都是AHB2

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

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

相关文章

FlutterWeb性能优化探索与实践

美团外卖商家端基于 FlutterWeb 的技术探索已久,目前在多个业务中落地了App、PC、H5的多端复用,有效提升了产研的整体效率。在这过程中,性能问题是我们面临的最大挑战,本文结合实际业务场景进行思考,介绍美团外卖商家端在 FlutterWeb 性能优化上所进行的探索和实践,希望对…

【原生Ajax】全面了解xhr的概念与使用。

✍️ 作者简介: 前端新手学习中。 &#x1f482; 作者主页: 作者主页查看更多前端教学 &#x1f393; 专栏分享&#xff1a;css重难点教学 Node.js教学 从头开始学习 ajax学习 文章目录XHR的基本使用  什么是XHR  使用xhr发起GET请求  了解xhr对象的readyState属性  使…

婴儿肠绞痛怎么办?

婴儿肠绞痛的原因婴儿绞痛是婴儿生活中最难解释的现象之一。没有人知道为什么有些婴儿比其他婴儿绞痛更严重&#xff0c;但有很多关于绞痛的理论。其中一个原因可能是一些婴儿的消化系统更不成熟或更敏感。婴儿消化道中用于分解食物的消化酶或消化液很少&#xff0c;尤其是淀粉…

图像锐化一:几个常见的滤波核

文章目录1.滤波核2.代码3. 效果分析示例1.示例2.图像锐化和图像平滑相对应&#xff0c;前者用于增强细节表现&#xff0c;后者一般用于降噪在图像锐化时&#xff0c;往往会 1. 放大 噪声&#xff0c;2. 引入aritfact, 3. 振铃效应 等负面效果 因此需要分析相关锐化方法的效果和…

计算机里一半的部件是什么

解码器 在下图中&#xff0c;“a”和“b”是来自左侧的输入。它们都连接到NOT门。NOT门产生与其输入相反的输入。页面上有四条垂直线&#xff0c;分别来自“a”和“b”以及“a”与“b”的反数&#xff0c;因此&#xff0c;对于每个“a”和“b”&#xff0c;页面上都有两条导线&…

Jira—使用 JMX 接口进行实时监控

使用 JMX 接口进行实时监控 什么是 JMX? JMX(Java Management Extensions) 是一种用于监视和管理 Java 应用程序的技术。JMX 使用称为 MBean(托管 Bean)的对象来公开应用程序中的数据和资源。 对于 Jira Server 或 Jira Data Center 的大型实例,启用 JMX 可以让您更轻松…

知识蒸馏算法汇总

知识蒸馏有两大类&#xff1a;一类是logits蒸馏&#xff0c;另一类是特征蒸馏。logits蒸馏指的是在softmax时使用较高的温度系数&#xff0c;提升负标签的信息&#xff0c;然后使用Student和Teacher在高温softmax下logits的KL散度作为loss。中间特征蒸馏就是强迫Student去学习T…

文件上传漏洞实验-通过截取http请求绕过前端javascript验证进行文件上传

1、什么是文件上传漏洞 文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器并执行。这里上传的文件可以是木马&#xff0c;病毒&#xff0c;恶意脚本或者WebShell等。这种攻击方式是最为直接和有效的&#xff0c;部分文件上传漏洞的利用技术门槛非常的低&#xff0c;对…

如何使用AI图片清晰度增强器软件增强和锐化图片、提高照片清晰度并去除噪点

通过使用深度学习AI算法对照片进行批量锐化、去噪和去模糊处理&#xff0c;该程序可以应用再大部分照片和图片&#xff0c;包括徽标、卡通和动漫 可能很多朋友都会遇到需要批量增强和锐化照片的情况&#xff1a;例如&#xff0c;如果拍摄过程中曝光不足、夜晚噪点多或者画面模…

基于蚁群算法的TPS问题求解策略研究(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

Java面试干货:关于数组查找的几个常用实现算法

查找算法在我们的面试和开发中&#xff0c;是很常见的一种算法&#xff0c;今天我就给大家介绍几个常用的查找算法。 一. 线性查找 1.概念 线性查找也叫顺序查找&#xff0c;这是最基本的一种查找方法。该算法是从给定的值中进行搜索&#xff0c;从一端开始逐一检查每个元素…

华为堆叠技术讲解

目录 为什么出现堆叠 什么是堆叠 堆叠的特征 堆叠的优缺点 华为堆叠技术 框式交换机堆叠技术CSS CSS堆叠涉及的相关基础概念 主交换机选举过程 堆叠系统主备倒换 CSS两种堆叠口 CSS堆叠方式 CSS以太网链路聚合 本地优先转发 CSS双主检测 CSS版本升级 CSS堆叠实…

java和vue车辆管理系统车管所系统

简介 车辆管理系统车管所系统&#xff0c;管理员添加车主信息&#xff0c;车主提交自己的车辆信息&#xff0c;管理员审核车辆&#xff0c;对车辆行进年检&#xff0c;统计&#xff0c;记录车辆违规信息。车主可以查看自己的车辆信息、投诉、查看自己的违规记录等。 演示视频…

【附源码】计算机毕业设计JAVA客户台账管理

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

2021亚太杯C题全网最全解题思路+塞罕坝林场数据数据分享

全网绝对能获奖的免费思路&#xff01;&#xff01;&#xff01; 文章目录1.写在前面&#xff0c;需要塞罕坝林场数据的这里链接获取&#xff1a;2.C题全网最全解题思路1.写在前面&#xff0c;需要塞罕坝林场数据的这里链接获取&#xff1a; https://download.csdn.net/downlo…

如何在SpringBoot项目中,实现记录用户登录的IP地址及归属地信息?

在登录模块&#xff0c;我们经常要记录登录日志&#xff0c;其中比较重要的信息有ip地址和ip归属地&#xff0c;像我们公司开发的产品会提供给用户试用&#xff0c;因为我们做的是无人机应用方向的&#xff0c;即使试用也会产生费用&#xff0c;因为我们很多功能一旦用了就会消…

GFS分布式文件系统及其部署

目录 一、GlusterFS 1 MFS 2 GlusterFS 二 、GlusterFs特点 1 扩展性和高性能 2 高可用性 3 全局统一命名空间 4 弹性卷管理 5 基于标准协议 三 GlusterFS 术语 1 Brick(存储块) 2 volume(逻辑卷) 3 FUSE 4 VFS 5 Glusterd (后台管理进程) 四 模块化堆枝式架构…

深度支持赛事宣发,DF平台助推第三届全国人工智能大赛顺利举办!

由深圳市人民政府和鹏城实验室主办、深圳市科技创新委员会与新一代人工智能产业技术创新战略联盟联合承办的人工智能领域顶尖赛事——第三届全国人工智能大赛已圆满落幕。DataFountain大数据竞赛平台&#xff08;简称DF平台&#xff09;作为本次大赛的宣传合作伙伴&#xff0c;…

EFK部署centos7.9(二)head插件部署

安装配置head监控插件 本人是在ES服务器安装head插件是Nodejs实现的&#xff0c;所以需要先安装Nodejs。 wget https://nodejs.org/dist/v14.17.6/node-v14.17.6-linux-x64.tar.xz 下载安装包 tar xf node-v14.17.6-linux-x64.tar.xz -C /usr/local/ 解压安装包 vim /e…

PostgreSQL实战之物理复制和逻辑复制(三)

目录 PostgreSQL实战之物理复制和逻辑复制&#xff08;三&#xff09; 3.1 单实例、异步流复制、同步流复制性能测试 3.1 读性能测试 3.2 写性能测试 PostgreSQL实战之物理复制和逻辑复制&#xff08;三&#xff09; 3.1 单实例、异步流复制、同步流复制性能测试 根据Post…