单片机(MCU)如何才能不死机之对齐访问(Aligned Access)

从一个结构体说起。如下,在 STM32F0 的程序中,我们定义了一个结构体My_Struct ,那么这个结构体占用多少内存呢?

struct Struct_Def {
uint8_t Var_B;
uint16_t Var_W0;
uint16_t Var_W1;
uint32_t Var_DW;
};
struct Struct_Def My_Struct;
int main(void)
{  My_Struct.Var_B = 0x01;  My_Struct.Var_W0 = 0x0203;  My_Struct.Var_W1 = 0x0405;  My_Struct.Var_DW = 0x06070809;    while(1);
}

我们粗略一算,1 + 2 + 2 + 4 = 9 Bytes 。

下载到芯片,观察一下变量,似乎没错。

图片

如果有更进一步的好奇心,我们来到内存中实际看一下,可能会有出乎意料的发现:

图片

编译器在 Var_B 之后插入了一个字节,在 Var_W1 之后插入了两个字节。这个结构体在内存中实际占用了 1 + 1 + 2 + 2 + 2 + 4 = 12 Bytes 。

为什么会这样呢?这是 ARM Cortex M0 体系决定的,它只支持对齐访问 ( Aligned Access )。比如我们访问一个 4 字节 (Double Word) 型的变量时,如果这个变量的起始地址是能被 4 整除的话,我们说这种访问是双字节对齐的。如果访问一个 2 字节 ( Word ) 变量,当起始地址能被 2 整除时是对齐的。访问字节 ( Byte ) 型变量,总是对齐的。

那么如果进行了非对齐访问呢?那就会产生一个严重错误 ( HardFault ) !!!

大家看一下例子中的这一个赋值语句:

My_Struct.Var_DW = 0x06070809;

它是一个 4 字节 ( Double Word ) 型的变量赋值。Var_DW 这个成员,如果按照在结构体中的顺序,应该紧随 Var_W1 之后,分配在 0x20000012,但是这个地址是不能被 4 整除的,所以编译器在填充了 2 个字节 0 之后,把 Var_DW 的起始地址分配在了 0x20000014 。

图片

到这里大家肯定会有一个疑问,这样岂不是很浪费 RAM 吗?RAM 又是相对来说价格比较高的。特别是在结构体比较多的情况下,大量的 RAM 白白浪费了!

还好,在这里我们可以用到伪指令 #pragma pack 了。

如下例所示,#pragma pack(1) 将会使结构体中的变量一个字节紧挨着一个字节在内存中分配,而不再考虑是否对齐的问题。可以看到结构体占用从 0x2000000C 到 0x20000014 的9个字节 RAM空间。

#pragma pack(1)
struct Struct_Def 
{uint8_t Var_B;uint16_t Var_W0;uint16_t Var_W1;uint32_t Var_DW;
};
struct Struct_Def My_Struct;
#pragma pack()

那么问题来了,当我们读写地址非对齐的变量时,不就会产生 HardFault 吗?

在这里,编译器采取了曲线救国的方针。大家看下面赋值语句对应的汇编部分就会看到,它用 4 个STRB 指令(单字节操作,无论任何地址都是对齐操作), 代替了 1 个 STR 指令 ( 4 字节操作 )。如此,牺牲了一些效率,但是节省了内存空间。

图片

这种用法节省了 RAM,但是带来了一种比较隐蔽的错误。尤其是当我们用指针方式访问这些变量时,编译器无法发现错误,而且只有当语句实际执行时才会引起问题。所以在使用指针式要特别注意,指针所指向的地址,是否和指针类型所需要的地址对齐方式吻合。

以上面的 RAM 分配方式为例,非对齐访问时会导致 MCU 进入 HardFault 。

volatile uint32_t Test_Var;
Test_Var = *(uint8_t *)(&My_Struct.Var_B); // 这句是可以正确执行的Test_Var = *(uint16_t *)(&My_Struct.Var_W0); // 非对齐访问,进入 HardFaultTest_Var = *(uint32_t *)(&My_Struct.Var_DW); // 非对齐访问,进入 HardFault

对于变量的定义,我们还可以用下面的伪指令把变量以 n 字节对齐:

__align(n)

大家在实际工程中可以根据实际情况灵活的选择和使用这些伪指令。


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

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

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

相关文章

小米的隔空充电,看起来好酷

昨天是1月29号,昨天小米发布了一个隔空充电技术,很火爆,大胆想,如果有一条无线充电的高速公路,那电动汽车还担心没有电吗?—— 雷总的微博原文隔空充电技术:如科幻电影一般,拿着手机…

同事用void把我给秀翻了!

1、聊一聊今天跟大家推荐的这首歌最近应该挺火的,不过没办法插入AGA的原版歌曲,大家觉得不错可以去找找原版歌曲收录一下。昨天建立了"最后一个bug"技术交流群,由于群成员超过200就无法直接通过群二维码加入,如果大家想加入扫描下面…

Spring Boot之自定义属性

选择Spring Boot主要是考虑到它既能兼顾Spring的强大功能,还能实现快速开发的便捷。我想大多数人也是出于这个原因选择了Spring Boot,如果不是特殊应用场景,就只需要在application.properties中完成一些属性配置就能开启各模块的应用。而不像传统的XML配…

一个老工程师的工作经历和思考

在这里不敢以”资深”工程师自居,因为学历和技术水平确实一般。为什么说“老”呢?因为工作时间确实够长,已经接近20年。下面把自身工作和学习经历和大家分享一下,使初学者能够得到一些有用的东西。2000年毕业,机械电子…

电子工程师都在看什么?送你一份“修炼宝典”

现如今,形形色色的公众号如繁星一般让人眼花缭乱。近几年科技的飞速发展,让更多人开始关注科技,甚至成为极客。然而学习是永无止境的,如何才能追赶如此高速的发展?曾经,我也是不知道去看哪些,便…

DataCleaner(4.5)第一章

Part1. Introduction to DataCleaner  介绍DataCleaner |--What is data quality(DQ)  数据质量?|--What is data profiling?   数据分析?|--What is datastore?     数据存储?   Composite datastore   综合性数据存储 |…

约瑟夫斯问题-java版数组解法和链表解法

10个人围成一圈,从1到10编号,从1开始数,数到3或3的倍数的位置,则该位置的人出局,求最后剩下哪一个号? 数组解法: 数组存放数组:a[10]存在1到10编号人 数组遍历到尾部又从头遍历&…

少写点if-else吧,它的效率有多低你知道吗?

# 干了这碗鸡汤我要再和生活死磕几年。要么我就毁灭,要么我就注定铸就辉煌。如果有一天,你发现我在平庸面前低了头,请向我开炮。--杰克凯鲁亚克if-else涉及到分支预测的概念,关于分支预测上篇文章《虚函数真的就那么慢吗&#xff…

为什么不能在中断上半部休眠?

这是一个老生常谈的问题。我们先简单说下什么是中断「因为最近在群里看到有人竟然不懂什么是中断」。中断是计算机里面非常核心的东西,我们可以跑OS,可以多任务运行都因为中断的存在。假设你是一个CPU,你正在睡觉。你突然觉得肚子疼&#xff…

j.u.c系列(08)---之并发工具类:CountDownLatch

写在前面 CountDownLatch所描述的是”在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待“:用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受…

巧用1个GPIO控制2个LED显示4种状态

很多电子产品有状态指示灯,比如电视机:待机状态亮红灯开机状态亮绿灯实现起来很简单,微控制器MCU的两个GPIO分别控制就行:不过资源总是紧张的,有时候会碰到GPIO不够用的情况。如果只用1个GPIO,可不可以实现…

大大大大数怎么求余?C语言

问题:一个特别大的数除以23求余数用C语言应该怎么算啊?比如23232323232323232323232323232323232323232323232323232323233除以23,怎么算余数?数据类型在计算机的存储是有大小限制的,所以才出现了大数求余这种问题&…

程序员因拒绝带电脑回家工作被开除!获赔19.4万元

近日,男子拒绝春节带电脑回家工作被开除的消息,成为了不少网友关注的焦点,引发网友共鸣。因为春节拒绝带工作电脑回家被开除,上海一位软件工程师起诉公司获赔19.4万元。2月2日,据上海浦东法院公众号消息,该…

随便写写(5)

也许是今年发生的事情太多了,所以比以前要更关注时事,虽然面对一些既成的事实,难免要进行痛心的思考。 昨天晚上关注了一下东方卫视播出的9.8特大尾矿库溃坝事故的后续报道,这起特大人为事故已经得到了认定,相关的责任…

利用C语言中的setjmp和longjmp,来实现异常捕获和协程

一、前言二、函数语法介绍与 goto 语句比较与 fork 函数比较与 Python 语言中的 yield/resume 比较三、利用 setjmp/longjmp 实现异常捕获四、利用 setjmp/longjmp 实现协程五、总结一、前言 在 C 标准库中,有两个威力很猛的函数:setjmp 和 longjmp&…

centos6.9系列LNMP环境的安装

一、Nginx 1.先解决Nginx的依赖关系: yum install -y pcre-devel openssl-devel 2.安装wget:sudo yum -y install wget 3.下载nginx的安装包:wget http://nginx.org/download/nginx-1.10.3.tar.gz 4.解压nginx文件包:tar xf nginx…

Linux 修改 ELF 解决 glibc 兼容性问题

转自:Soul Of Free Loophttps://zohead.com/archives/mod-elf-glibc/Linux glibc 问题相信有不少 Linux 用户都碰到过运行第三方(非系统自带软件源)发布的程序时的 glibc 兼容性问题,这一般是由于当前 Linux 系统上的 GNU C 库&am…

VS2010创建ATL工程及使用C++测试COM组件

VS2010创建ATL工程及使用C测试COM组件 1.创建ATL项目,取名MyCom 2. ATL 项目向导,勾选 【支持COM 1.0】和【支持部件注册器】,其他默认,点击完成。 3.在该项目中添加类 4.添加一个ATL简单对象 5. ATL 简单对象向导&#xff0c…

芯片IC附近为啥要放0.1uF的电容?看完秒懂~

数字电路要运行稳定可靠,电源一定要”干净“,并且能量补充一定要及时,也就是滤波去耦一定要好。什么是滤波去耦,简单的说就是在芯片不需要电流的时候存储能量,在需要电流的时候又能及时地补充能量。有读者看到这里会说…

无线中继蹭网(转)

随着无线技术的逐渐成熟,无线设备的价格也越来越低,已经有不少的家庭开始在自己的家中建立无线网络,利用笔记本,具备WiFi功能的手机连接无线网络享受冲浪乐趣,很多时候为了节约网费可能几家人一起共用一个ADSL上网帐号…