如何在 STm32 核板 (mbed) 上写入 SRAM
How to write to SRAM on STm32 nucleo board (mbed)
我想在芯片的SRAM中存储一个简单的整数。 (皮质 M4)
我使用的程序是 mbed online。
我知道 SRAM 的地址从 0x2000 0000 开始,芯片有 4KB 的内存。
我已经阅读了数据表和 bitbanding 部分,但它对我来说没有意义。
有人可以向我解释一下如何将数字 5 存储在 SRAM 中并再次读取吗?
现在的代码是这样的(c是一个整数,用户用一个按钮改变):
如果(c==100){
温度=c;
MBX_B0 = 1; //写字
温度 = MBX_B7; // 单词读取<br>
TIMER_B0 = 温度; // 字节写入
return TIMER_B7; // 读取字节
}
pc.printf("%d",temp);
它只停止 运行 一旦 c==100
即使在断电后也应保存该值。
正在阅读有关 bitbanding, I found this code in an Application Note
我复制出来编译了。这应该可以帮助您入门。
#define BITBAND_SRAM_REF 0x20000000
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4))) // Convert SRAM address
#define BITBAND_PERI_REF 0x40000000
#define BITBAND_PERI_BASE 0x42000000
#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + (a-BITBAND_PERI_REF)*32 + (b*4))) // Convert PERI address
#define MAILBOX 0x20004000
#define TIMER 0x40004000 // Mailbox bit 0
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0))) // Mailbox bit 7
#define MBX_B7 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,7))) // Timer bit 0
#define TIMER_B0 *((volatile unsigned char*)(BITBAND_PERI(TIMER,0))) // Timer bit 7
#define TIMER_B7 *((volatile unsigned char*)(BITBAND_PERI(TIMER,7)))
int main(void)
{
unsigned int temp = 0;
MBX_B0 = 1; // Word write
temp = MBX_B7; // Word read
TIMER_B0 = temp; // Byte write
return TIMER_B7; // Byte read
}
Can someone please explain to me how I can store for example the number 5 in the SRAM and read it again?
在上面的示例代码中,temp
在 RAM 中。
如果您现在不关心使用位带,只需声明一个变量 int x = 5
将数字 5 存储在 RAM 中。
编辑,您的问题完全改变了答案,因为您对 SRAM 写入根本不感兴趣,但 flash/eeprom...
因此在此答案中添加一个主要部分,您的评论在这里很重要:
But Is the value stored even after power down? Isn't that what SRAM
would do instead of normal RAM? RAM=lose value when no power,
SRAM=Keep value when no power?
SRAM表示静态RAM,RAM表示随机存取存储器。现在,根据该定义,RAM 可以安全地用于 ROM(只读存储器)之类的东西,因为随机部分与寻址有关,我可以寻址我想要的任何随机地址,还是只能在之后使用线性一个地址读取这个东西另一个按照一些规则。
惯例是ROM是非易失性的,RAM是易失性的,这是这里的相关术语。 ROM 实现在技术上不是只读的 PROM 是可编程的 rom,这意味着可写,所以有点打破了 EPROM 电可编程的术语,EEPROM 是电可擦除和可编程的。而闪存是一种较新的电可擦除和可编程 ROM 或非易失性存储技术。
从这个意义上说,易失性意味着它可以或不能在电源循环中存活下来。 volaitle 意味着它不能 non-volatile 意味着它可以。
SRAM 中的 S 表示静态,确保该术语暗示它可能会存活,尤其是当您了解 DRAM 时,D 表示动态,并且完全可以假设一个在电源循环中存活而另一个则不能,但不幸的是不是他们所指的。相反,在这两种情况下,它们都与保持供电的内存有关,它们都是易失性内存。去维基百科上查找这些。静态使用四个晶体管,比方说,在经典的触发器实现中有两个带反馈的门,您将位写为高电平或低电平,只要电源不关闭,它就会保持它不会忘记的值(只要电源保持打开状态) . DRAM 虽然使用一个晶体管并且在某种程度上严重依赖于该晶体管中的电容,有点像一个蹩脚的可充电电池,你希望它记住一个 1 你必须给它充电并快速放电(以毫秒为单位)所以你必须不断提醒它是 1 还是 0(刷新)。
所以静态 ram 是静态的,因为我们只需要告诉它一次并且它会记住,动态 ram 是动态的,因为我们告诉 dram 系统那位是什么,作为一个系统我们必须保留提醒一下,这是通过读取位然后 reprogramming/charging 以特定频率读取该位来完成的。
DRAM 很便宜,可以将四倍的位数封装到相同数量的晶体管中,SRAM 速度很快,没有复杂的开销,也没有刷新周期妨碍,它只是门 运行 和其他门一样快 和做其他事情(处理指令)的门一样快。
微控制器将在 ROM、PROM、EEPROM 或闪存(现在有各种类型)中具有某种形式的非易失性存储。有时您同时拥有闪存和 eeprom eeprom 用于您可能在这里要求的那种东西,有时出于反向兼容性原因,它们具有 eeprom 遗留接口,但它实际上使用主闪存进行存储。无论如何,您都必须查看芯片和/或芯片系列的文档。如今,在应用程序中写入片上非易失性存储器 (eeprom/flash) 的能力很常见(尽管有很多例外)。并且文档会告诉您如何执行此操作。
这一切都很好,但一些免费的建议是,如果你做错了,你的闪光灯可能会在几小时或几天内磨损......从字面上看......该部件可能会被丢弃。理想情况下,您希望在您的电路板上支持检测电源下降,同时有足够的大容量电容或电池或两者兼有,以保持 board/device 在最坏情况下需要足够长的时间来保存非易失性信息(最好首先确认值已更改,否则不要烧毁擦除周期)。实现起来很简单,但仍然比磨损闪光灯要好。
很多关于如何不磨损闪存的解决方案和意见,遗憾的是,一些闪存硬件具有写入均衡的逻辑,如果软件和硬件都试图将事情分散开来减少磨损闪光他们可以互相对抗,弊大于利。
您的部件支持的写入周期数应记录在数据表中,您超过了您使用此设备构建的产品的生命周期,它可能会忘记您写入的内容。这是支持的最低限度,它可能会说 10000 次写入,但在测试中您可能会达到 100,000 次并且设备仍然可以工作。并不意味着它们的所有重置都会超过数据表中的额定值,因此您可以从中逆向计算,如果我每隔这么多时间单位获得一个新值,我希望产品的使用寿命是这么多时间单位,那么我只能保存一些简单的数学时间单位(每个存储 location/erase 边界等)。
因此,首先要学习如何在应用程序中擦除您不使用的块,然后向其中写入一些内容,然后在您重新启动时查看它是否存在,如果没有闪存,请尝试使用 eeprom .通常在这些 STM32 设备上有据可查并且很容易做到。然后一旦你知道如何去做,然后开始担心你觉得你需要多久做一次。
你是否曾注意到,在某些汽车中,当你转动它们 "off" 并重新启动时,时钟仍然有效,收音机会记住你最喜欢的电台,或者空调会记住你上次使用的温度和风扇速度。但如果你断开电池,部分或全部就会丢失。他们没有使用非易失性存储,他们使用的是 ram (SRAM),而电源只是断了,他们依靠备用电池。主板已经并且可能仍然会为您的 "CMOS" 或 "BIOS" 设置执行此操作。电池支持的 ram 基本上是因为 ram 不会断电,主电源可能会关闭,但电池会为 ram 供电。这是您可以使用的另一种设计解决方案,电池或超级电容器(acitor),可能假设您永远不需要存储到闪光灯,如果像汽车音响一样,电池没电了哦。
当然,所有这些都需要我事先回答,为了获得控制 eeprom/flash 的寄存器,您需要知道如何从您的程序访问它们:
首先这里不需要位带(对 store/load 某些值 to/from ram),你是在问如何写入和读取 ram 中的特定地址,还是在问如何使用位带?通常你不会在 ram 中使用位带,例如,该功能用于更改寄存器中的位子集,设计师出于某种原因将单独的项目打包到同一个寄存器中(gpio 引脚配置之类的东西很有意义,你可能想要更改单个引脚的配置,而无需在软件中进行读-修改-写(硬件可能仍然需要执行读-修改-写))
当然你可以在 ram 上使用 bitbanding 功能,如果 cortex-m 允许它我需要重新阅读它,不一定有意义除非你非常渴望 ram 需要单独包装事情变成一个词(像位域,但甚至不要以此开头)...
#define BITBAND_SRAM_REF 0x20000000
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4)))
#define BITBAND_PERI_REF 0x40000000
#define BITBAND_PERI_BASE 0x42000000
#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + (a-BITBAND_PERI_REF)*32 + (b*4)))
#define MAILBOX 0x20004000
#define TIMER 0x40004000
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0)))
#define MBX_B7 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,7)))
#define TIMER_B0 *((volatile unsigned char*)(BITBAND_PERI(TIMER,0)))
#define TIMER_B7 *((volatile unsigned char*)(BITBAND_PERI(TIMER,7)))
MBX_B0 = 1;
所以none这个比较特殊,或者跟cortex-m或者arm相关,只是基本的C代码。 MBX_B0 是您向后操作宏的宏
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0)))
然后
#define MAILBOX 0x20004000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4)))
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM_REF 0x20000000
所以
0x22000000+(0x20004000-0x20000000)*32 + (0*4)
= 0x22080000
volatile unsigned int 只是一种 C 语法方式,它采用诸如 0x22080009 之类的常量,并说这是我想指向的东西的地址,所以
MBX_B0 = 1;
意味着将 0x00000001 写入地址 0x22080000,但由于这是使用位带,这意味着设置地址 0x20004000 的位 0 的位 1(位带非常特定于这些 arm cortex-m 内核)
如果您只想将值 5 写入内存中的某个位置,您可以
#define SOME_ADD *((volatile unsigned int*)(0x20001234)
unsigned int x;
SOME_ADD = 5;
x = SOME_ADD;
要看到这一切都为您完成,您可以试试看:
#define BITBAND_SRAM_REF 0x20000000
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4)))
#define MAILBOX 0x20004000
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0)))
#define SOME_ADD *((volatile unsigned int*)(0x20001234))
unsigned int fun ( void )
{
unsigned int x;
MBX_B0 = 1;
SOME_ADD = 5;
x = SOME_ADD;
}
arm-none-eabi-gcc -c -O2 so.c -o so.o
arm-none-eabi-objdump -D so.o
00000000 <fun>:
0: e3a0c001 mov r12, #1
4: e3a02005 mov r2, #5
8: e59f1010 ldr r1, [pc, #16] ; 20 <fun+0x20>
c: e59f3010 ldr r3, [pc, #16] ; 24 <fun+0x24>
10: e581c000 str r12, [r1]
14: e5832234 str r2, [r3, #564] ; 0x234
18: e5933234 ldr r3, [r3, #564] ; 0x234
1c: e12fff1e bx lr
20: 22080000 andcs r0, r8, #0
24: 20001000 andcs r1, r0, r0
处理器加载地址 0x20001000,在这种情况下,汇编程序选择将立即数 0x234 添加到该地址,而不是将整个 0x20001234 放入加载的地址中,其中六个......两种方式都没有不同的成本,因为编写编译器不需要对齐加载的值。
现在,如果您不需要访问特定地址(0x20001234 或某些外围寄存器等),那么只需
unsigned int some_value;
void fun ( void )
{
some_value = 5;
}
需要编译和link才能看到全貌:
00000004 <fun>:
4: e3a02005 mov r2, #5
8: e59f3004 ldr r3, [pc, #4] ; 14 <fun+0x10>
c: e5832000 str r2, [r3]
10: e12fff1e bx lr
14: 20000000 andcs r0, r0, r0
Disassembly of section .bss:
20000000 <some_value>:
20000000: 00000000 andeq r0, r0, r0
并且代码现在已将数字 5 存储到 ram 中的某个位置(由 linker 选择)。
在 bitbanding 方面,如果你阅读你的 arm 文档,你会发现它并不总是受支持,在某些内核中它是一个可选功能,这意味着当他们编译芯片时他们可以选择不包含它。例如,如果这是一个特定的 st 芯片或系列,您可能会发现他们忘记记录一个或两个位带地址(0x22000000、0x42000000),但将其保存在库中。
就我个人而言,我不是易失性指针技巧的粉丝,我看到编译器无法生成正确的指令,所以我编写了一个很小的两行汇编函数,我可以抽象所有此类访问,通过它有很大的副作用像在 linux 或其他驱动程序中那样强制抽象。让代码更有用,可以抽象出对软件模拟的访问,可以抽象出对逻辑模拟的访问,可以通过mmap抽象,可以在内核驱动中使用,可以添加一个printf层来调试,单如果您喜欢那种类型的调试,可以设置断点的地方,可以用几行 asm 实现裸机,或者可以使用通用 macro/define 来做易失性指针的事情,如果你喜欢的话。 YMMV.
注意局部变量
void fun ( void )
{
unsigned int some_value;
some_value = 5;
}
不一定最终进入 ram,理想情况下它们会进入堆栈,但如果您优化可以优化出来(推荐用于微控制器等资源匮乏的设备,除非 MISRA 或其他一些要求阻止您使用优化器).上面的代码当然是完全死代码,结果是一个简单的 return:
00000000 <fun>:
0: e12fff1e bx lr
我想在芯片的SRAM中存储一个简单的整数。 (皮质 M4) 我使用的程序是 mbed online。 我知道 SRAM 的地址从 0x2000 0000 开始,芯片有 4KB 的内存。
我已经阅读了数据表和 bitbanding 部分,但它对我来说没有意义。
有人可以向我解释一下如何将数字 5 存储在 SRAM 中并再次读取吗?
现在的代码是这样的(c是一个整数,用户用一个按钮改变):
如果(c==100){
温度=c;
MBX_B0 = 1; //写字
温度 = MBX_B7; // 单词读取<br>
TIMER_B0 = 温度; // 字节写入
return TIMER_B7; // 读取字节
}
pc.printf("%d",temp);
它只停止 运行 一旦 c==100 即使在断电后也应保存该值。
正在阅读有关 bitbanding, I found this code in an Application Note
我复制出来编译了。这应该可以帮助您入门。
#define BITBAND_SRAM_REF 0x20000000
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4))) // Convert SRAM address
#define BITBAND_PERI_REF 0x40000000
#define BITBAND_PERI_BASE 0x42000000
#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + (a-BITBAND_PERI_REF)*32 + (b*4))) // Convert PERI address
#define MAILBOX 0x20004000
#define TIMER 0x40004000 // Mailbox bit 0
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0))) // Mailbox bit 7
#define MBX_B7 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,7))) // Timer bit 0
#define TIMER_B0 *((volatile unsigned char*)(BITBAND_PERI(TIMER,0))) // Timer bit 7
#define TIMER_B7 *((volatile unsigned char*)(BITBAND_PERI(TIMER,7)))
int main(void)
{
unsigned int temp = 0;
MBX_B0 = 1; // Word write
temp = MBX_B7; // Word read
TIMER_B0 = temp; // Byte write
return TIMER_B7; // Byte read
}
Can someone please explain to me how I can store for example the number 5 in the SRAM and read it again?
在上面的示例代码中,temp
在 RAM 中。
如果您现在不关心使用位带,只需声明一个变量 int x = 5
将数字 5 存储在 RAM 中。
编辑,您的问题完全改变了答案,因为您对 SRAM 写入根本不感兴趣,但 flash/eeprom...
因此在此答案中添加一个主要部分,您的评论在这里很重要:
But Is the value stored even after power down? Isn't that what SRAM would do instead of normal RAM? RAM=lose value when no power, SRAM=Keep value when no power?
SRAM表示静态RAM,RAM表示随机存取存储器。现在,根据该定义,RAM 可以安全地用于 ROM(只读存储器)之类的东西,因为随机部分与寻址有关,我可以寻址我想要的任何随机地址,还是只能在之后使用线性一个地址读取这个东西另一个按照一些规则。
惯例是ROM是非易失性的,RAM是易失性的,这是这里的相关术语。 ROM 实现在技术上不是只读的 PROM 是可编程的 rom,这意味着可写,所以有点打破了 EPROM 电可编程的术语,EEPROM 是电可擦除和可编程的。而闪存是一种较新的电可擦除和可编程 ROM 或非易失性存储技术。
从这个意义上说,易失性意味着它可以或不能在电源循环中存活下来。 volaitle 意味着它不能 non-volatile 意味着它可以。
SRAM 中的 S 表示静态,确保该术语暗示它可能会存活,尤其是当您了解 DRAM 时,D 表示动态,并且完全可以假设一个在电源循环中存活而另一个则不能,但不幸的是不是他们所指的。相反,在这两种情况下,它们都与保持供电的内存有关,它们都是易失性内存。去维基百科上查找这些。静态使用四个晶体管,比方说,在经典的触发器实现中有两个带反馈的门,您将位写为高电平或低电平,只要电源不关闭,它就会保持它不会忘记的值(只要电源保持打开状态) . DRAM 虽然使用一个晶体管并且在某种程度上严重依赖于该晶体管中的电容,有点像一个蹩脚的可充电电池,你希望它记住一个 1 你必须给它充电并快速放电(以毫秒为单位)所以你必须不断提醒它是 1 还是 0(刷新)。
所以静态 ram 是静态的,因为我们只需要告诉它一次并且它会记住,动态 ram 是动态的,因为我们告诉 dram 系统那位是什么,作为一个系统我们必须保留提醒一下,这是通过读取位然后 reprogramming/charging 以特定频率读取该位来完成的。
DRAM 很便宜,可以将四倍的位数封装到相同数量的晶体管中,SRAM 速度很快,没有复杂的开销,也没有刷新周期妨碍,它只是门 运行 和其他门一样快 和做其他事情(处理指令)的门一样快。
微控制器将在 ROM、PROM、EEPROM 或闪存(现在有各种类型)中具有某种形式的非易失性存储。有时您同时拥有闪存和 eeprom eeprom 用于您可能在这里要求的那种东西,有时出于反向兼容性原因,它们具有 eeprom 遗留接口,但它实际上使用主闪存进行存储。无论如何,您都必须查看芯片和/或芯片系列的文档。如今,在应用程序中写入片上非易失性存储器 (eeprom/flash) 的能力很常见(尽管有很多例外)。并且文档会告诉您如何执行此操作。
这一切都很好,但一些免费的建议是,如果你做错了,你的闪光灯可能会在几小时或几天内磨损......从字面上看......该部件可能会被丢弃。理想情况下,您希望在您的电路板上支持检测电源下降,同时有足够的大容量电容或电池或两者兼有,以保持 board/device 在最坏情况下需要足够长的时间来保存非易失性信息(最好首先确认值已更改,否则不要烧毁擦除周期)。实现起来很简单,但仍然比磨损闪光灯要好。
很多关于如何不磨损闪存的解决方案和意见,遗憾的是,一些闪存硬件具有写入均衡的逻辑,如果软件和硬件都试图将事情分散开来减少磨损闪光他们可以互相对抗,弊大于利。
您的部件支持的写入周期数应记录在数据表中,您超过了您使用此设备构建的产品的生命周期,它可能会忘记您写入的内容。这是支持的最低限度,它可能会说 10000 次写入,但在测试中您可能会达到 100,000 次并且设备仍然可以工作。并不意味着它们的所有重置都会超过数据表中的额定值,因此您可以从中逆向计算,如果我每隔这么多时间单位获得一个新值,我希望产品的使用寿命是这么多时间单位,那么我只能保存一些简单的数学时间单位(每个存储 location/erase 边界等)。
因此,首先要学习如何在应用程序中擦除您不使用的块,然后向其中写入一些内容,然后在您重新启动时查看它是否存在,如果没有闪存,请尝试使用 eeprom .通常在这些 STM32 设备上有据可查并且很容易做到。然后一旦你知道如何去做,然后开始担心你觉得你需要多久做一次。
你是否曾注意到,在某些汽车中,当你转动它们 "off" 并重新启动时,时钟仍然有效,收音机会记住你最喜欢的电台,或者空调会记住你上次使用的温度和风扇速度。但如果你断开电池,部分或全部就会丢失。他们没有使用非易失性存储,他们使用的是 ram (SRAM),而电源只是断了,他们依靠备用电池。主板已经并且可能仍然会为您的 "CMOS" 或 "BIOS" 设置执行此操作。电池支持的 ram 基本上是因为 ram 不会断电,主电源可能会关闭,但电池会为 ram 供电。这是您可以使用的另一种设计解决方案,电池或超级电容器(acitor),可能假设您永远不需要存储到闪光灯,如果像汽车音响一样,电池没电了哦。
当然,所有这些都需要我事先回答,为了获得控制 eeprom/flash 的寄存器,您需要知道如何从您的程序访问它们:
首先这里不需要位带(对 store/load 某些值 to/from ram),你是在问如何写入和读取 ram 中的特定地址,还是在问如何使用位带?通常你不会在 ram 中使用位带,例如,该功能用于更改寄存器中的位子集,设计师出于某种原因将单独的项目打包到同一个寄存器中(gpio 引脚配置之类的东西很有意义,你可能想要更改单个引脚的配置,而无需在软件中进行读-修改-写(硬件可能仍然需要执行读-修改-写))
当然你可以在 ram 上使用 bitbanding 功能,如果 cortex-m 允许它我需要重新阅读它,不一定有意义除非你非常渴望 ram 需要单独包装事情变成一个词(像位域,但甚至不要以此开头)...
#define BITBAND_SRAM_REF 0x20000000
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4)))
#define BITBAND_PERI_REF 0x40000000
#define BITBAND_PERI_BASE 0x42000000
#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + (a-BITBAND_PERI_REF)*32 + (b*4)))
#define MAILBOX 0x20004000
#define TIMER 0x40004000
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0)))
#define MBX_B7 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,7)))
#define TIMER_B0 *((volatile unsigned char*)(BITBAND_PERI(TIMER,0)))
#define TIMER_B7 *((volatile unsigned char*)(BITBAND_PERI(TIMER,7)))
MBX_B0 = 1;
所以none这个比较特殊,或者跟cortex-m或者arm相关,只是基本的C代码。 MBX_B0 是您向后操作宏的宏
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0)))
然后
#define MAILBOX 0x20004000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4)))
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM_REF 0x20000000
所以
0x22000000+(0x20004000-0x20000000)*32 + (0*4)
= 0x22080000
volatile unsigned int 只是一种 C 语法方式,它采用诸如 0x22080009 之类的常量,并说这是我想指向的东西的地址,所以
MBX_B0 = 1;
意味着将 0x00000001 写入地址 0x22080000,但由于这是使用位带,这意味着设置地址 0x20004000 的位 0 的位 1(位带非常特定于这些 arm cortex-m 内核)
如果您只想将值 5 写入内存中的某个位置,您可以
#define SOME_ADD *((volatile unsigned int*)(0x20001234)
unsigned int x;
SOME_ADD = 5;
x = SOME_ADD;
要看到这一切都为您完成,您可以试试看:
#define BITBAND_SRAM_REF 0x20000000
#define BITBAND_SRAM_BASE 0x22000000
#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4)))
#define MAILBOX 0x20004000
#define MBX_B0 *((volatile unsigned int*)(BITBAND_SRAM(MAILBOX,0)))
#define SOME_ADD *((volatile unsigned int*)(0x20001234))
unsigned int fun ( void )
{
unsigned int x;
MBX_B0 = 1;
SOME_ADD = 5;
x = SOME_ADD;
}
arm-none-eabi-gcc -c -O2 so.c -o so.o arm-none-eabi-objdump -D so.o
00000000 <fun>:
0: e3a0c001 mov r12, #1
4: e3a02005 mov r2, #5
8: e59f1010 ldr r1, [pc, #16] ; 20 <fun+0x20>
c: e59f3010 ldr r3, [pc, #16] ; 24 <fun+0x24>
10: e581c000 str r12, [r1]
14: e5832234 str r2, [r3, #564] ; 0x234
18: e5933234 ldr r3, [r3, #564] ; 0x234
1c: e12fff1e bx lr
20: 22080000 andcs r0, r8, #0
24: 20001000 andcs r1, r0, r0
处理器加载地址 0x20001000,在这种情况下,汇编程序选择将立即数 0x234 添加到该地址,而不是将整个 0x20001234 放入加载的地址中,其中六个......两种方式都没有不同的成本,因为编写编译器不需要对齐加载的值。
现在,如果您不需要访问特定地址(0x20001234 或某些外围寄存器等),那么只需
unsigned int some_value;
void fun ( void )
{
some_value = 5;
}
需要编译和link才能看到全貌:
00000004 <fun>:
4: e3a02005 mov r2, #5
8: e59f3004 ldr r3, [pc, #4] ; 14 <fun+0x10>
c: e5832000 str r2, [r3]
10: e12fff1e bx lr
14: 20000000 andcs r0, r0, r0
Disassembly of section .bss:
20000000 <some_value>:
20000000: 00000000 andeq r0, r0, r0
并且代码现在已将数字 5 存储到 ram 中的某个位置(由 linker 选择)。
在 bitbanding 方面,如果你阅读你的 arm 文档,你会发现它并不总是受支持,在某些内核中它是一个可选功能,这意味着当他们编译芯片时他们可以选择不包含它。例如,如果这是一个特定的 st 芯片或系列,您可能会发现他们忘记记录一个或两个位带地址(0x22000000、0x42000000),但将其保存在库中。
就我个人而言,我不是易失性指针技巧的粉丝,我看到编译器无法生成正确的指令,所以我编写了一个很小的两行汇编函数,我可以抽象所有此类访问,通过它有很大的副作用像在 linux 或其他驱动程序中那样强制抽象。让代码更有用,可以抽象出对软件模拟的访问,可以抽象出对逻辑模拟的访问,可以通过mmap抽象,可以在内核驱动中使用,可以添加一个printf层来调试,单如果您喜欢那种类型的调试,可以设置断点的地方,可以用几行 asm 实现裸机,或者可以使用通用 macro/define 来做易失性指针的事情,如果你喜欢的话。 YMMV.
注意局部变量
void fun ( void )
{
unsigned int some_value;
some_value = 5;
}
不一定最终进入 ram,理想情况下它们会进入堆栈,但如果您优化可以优化出来(推荐用于微控制器等资源匮乏的设备,除非 MISRA 或其他一些要求阻止您使用优化器).上面的代码当然是完全死代码,结果是一个简单的 return:
00000000 <fun>:
0: e12fff1e bx lr