M3位带地址映射和汇编实现对比

news/2024/10/15 6:18:07

01. 位带概述
位带操作简单的说,就是把每个比特膨胀为一个 32 位的字,当访问这些字的时候就达到了访问比特的目的,比如说 GPIO 的 ODR 寄存器有 32 个位,那么可以映射到 32 个地址上,我们去访问这 32 个地址就达到访问 32 个比特的目的。这样我们往某个地址写 1 就达到往对应比特位写 1 的目的,同样往某个地址写 0 就达到往对应的比特位写 0 的目的。

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

SRAM 区中的位带地址映射

对于片上外设,映射关系如下表所示

03. 位带C语言实现

//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
#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)) 
//IO口地址映射
#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 //IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

那么初始化相应的GPIO之后就可以直接使用了。

led.h

#ifndef __LED_H__
#define __LED_H__#include "sys.h"//LED初始化
void LED_Init(void);//位带操作
#define LED1 PFout(9)
#define LED2 PFout(10)#endif /*__LED_H__*/

led.c

#include "led.h"//LED初始化
void LED_Init(void)
{GPIO_InitTypeDef gpio_InitTypeDef;gpio_InitTypeDef.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;gpio_InitTypeDef.GPIO_Mode = GPIO_Mode_OUT;gpio_InitTypeDef.GPIO_Speed = GPIO_Speed_100MHz;gpio_InitTypeDef.GPIO_OType = GPIO_OType_PP;gpio_InitTypeDef.GPIO_PuPd = GPIO_PuPd_UP;//使能时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//GPIO初始化GPIO_Init(GPIOF, &gpio_InitTypeDef);//设置高电平 LED灭GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
}

main.c

#include "stm32f4xx.h"#include "delay.h"
#include "led.h"int main(void)
{		delay_init(168);LED_Init();//3. LED闪烁while(1){//灭LED1 = 1;LED2 = 1;delay_ms(1000);//亮LED1 = 0;LED2 = 0;delay_ms(1000);}	
}

写入对比

读取对比

//把“位带地址+位序号”转换成别名地址的宏

#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x20000000 + ((addr & 0xFFFFF) << 5) + (bit<<2));

//把该地址转换成一个指针

#define MEM_ADDR(addr) *((volatile unsigned long *) (adr));

 

在此基础上,我们就可以如下改写代码:

MEM_ADDR(DEVICE REG0) = 0xAB; //使用正常地址访问寄存器,即把0xAB作为DEVICE REG0地址上的值

MEM_ADDR(DEVICE_REG0) = MEM_ADDR(DEVICE_REG0) | 0x2; //传统做法

MEM_ADDR(BITBAND(DEVICE_REG0, 1)) = 0x1; //使用位带别名地址

 

请注意:当使用位带功能时,要访问的变量必须用 volatile来定义。因为 C编译器并不知道同一个比特可以有两个地址。所以就要通过 volatile,使得编译器每次都如实地把新数值写入存储器,而不再会出于优化的考虑,在中途使用寄存器来操作数据的复本,直到最后才把复本写回——这会导致按不同的方式访问同一个位会得到不一致的结果(可能被优化到不同的寄存器来保存中间结果——译注

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

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

相关文章

Spring配置相关

SpringSpring技术是JavaEE开发必备技能,企业开发技术选型命中率>90% 专业角度简化开发:降低企业级开发的复杂性 框架整合:高效整合其他技术,提高企业级应用开发与运行效率初识Spring 了解Spring家族Spring官网:https://spring.io/ Spring发展到今天已经形成了一种开发的…

M3位带地址映射

01. 位带概述位带操作简单的说,就是把每个比特膨胀为一个 32 位的字,当访问这些字的时候就达到了访问比特的目的,比如说 GPIO 的 ODR 寄存器有 32 个位,那么可以映射到 32 个地址上,我们去访问这 32 个地址就达到访问 32 个比特的目的。这样我们往某个地址写 1 就达到往对…

aardio封装库) 微软开源的js引擎(ChakraCore)

前言 做爬虫肯定少不了JavaScript引擎的使用,比如在Python中现在一般用pyexecjs2来执行JavaScript代码,另外还有一些其他执行JavaScript的库:https://github.com/eight04/node_vm2: rpc调用nodejs,需要安装node https://github.com/eight04/deno_vm: rpc调用deno,需要安装…

shell编程

!/bin/bash set -u -e 安全 export 环境变量 cat /porc/$PID/export 位置变量 $0: 表示脚本或命令本身的名称。 $1, $2, $3, ...: 表示第一个、第二个、第三个等参数的值。 $* 或 $@: 表示所有位置参数的列表。 $#: 表示传递给脚本或命令的位置参数的个数。 echo $[12^4] =8 …

python雨滴谱删除不需要的粒径列值

粒径的取值范围为:0.31~8mm 因此excel中标记红色的都需要删除: txt文件为(红框为留下来的数据),一共五组数,也就是五个时间的数: 那么我只留下我需要的d的n的数据,删除不需要的列:# -*- coding:utf-8 -*- """ @author: su @file: deletlie.py @time: 2…

[题解]P4597 序列 sequence

P4597 序列 sequence 是CF13C Sequence的加强版,\(N\leq 5*10^5\)。 如果想了解\(O(N^2)\)的DP解法请看此文。给定\(N\)个数,每次操作可以选其中一个数\(+1\)或\(-1\)。请问要让这个数列不降,最少需要多少次操作? 看到数据范围发现不能用\(O(N^2)\)的dp了,需要换一种思路。…

关于雨滴谱数据的处理

粒径的取值范围为:0.31~8mm 因此excel中标记红色的都需要删除: txt文件为(红框为留下来的数据),一共五组数,也就是五个时间的数: 那么我只留下我需要的d的n的数据,删除不需要的列:# -*- coding:utf-8 -*- """ @author: su @file: deletlie.py @time: 2…

1.验整码的发送与检验

通过restTemplate.exchage()来发送验证码,需要4个参数,url,请求方式,请求内容,需要相应类型) 响应的结果为map结合,我们需要取出key值,用俩次map取值可以取出key 检验验证 需要输入验证码和key restTeMPLATE.exhcange(url,....);//发送请求获得验证码 请求内容为空 判断…