如何在STm32核板上写入SRAM(mbed)

时间:2017-06-06 20:42:07

标签: c stm32 mbed

我想在芯片上的SRAM中存储一个简单的整数。 (Cortex M4) 我使用的程序是在线进行的。 我知道SRAM的地址从0x2000 0000开始,芯片有4KB的内存。

我已阅读数据表和bitbanding部分,但它对我来说没有意义。

有人可以向我解释如何存储例如SRAM中的数字5并再次阅读吗?

当前代码是这样的(c是用户通过按钮更改的整数):
    if(c==100){ temp=c; MBX_B0 = 1; // Word write temp = MBX_B7; // Word read
TIMER_B0 = temp; // Byte write return TIMER_B7; // Byte read } pc.printf("%d",temp);

一旦c == 100,它就会停止运行 电源关闭后应该保存值。

2 个答案:

答案 0 :(得分:3)

编辑,你的问题完全改变了答案,因为除了flash / eeprom之外你根本不对SRAM写作感兴趣......

因此,在此答案中添加一个主要部分,您的评论至关重要:

  

但即使断电后该值是否仍然存储?那不是什么SRAM   会做而不是普通的RAM吗? RAM =无电时损失值,   SRAM =没电时保持值?

SRAM表示静态RAM,RAM表示随机存取存储器。现在RAM的定义可以安全地用于像ROM(只读存储器)这样的东西,因为随机部分与寻址有关,我可以寻址任何我想要的随机地址,或者我只能使用线性一个地址来读取这个东西另一条规则。

惯例是ROM是非易失性的,RAM是易失性的,这是相关术语。 ROM实现在技术上不是只读的PROM是可编程的ROM,这意味着可写,因此有点打破了EPROM电可编程术语,EEPROM是电可擦除和可编程的。闪存是一种新型技术,可电擦除和可编程ROM或非易失性存储。

这种意义上的易失性意味着它能够或不能在电源循环中存活。 volaitle意味着它不能非易失性意味着它可以。

SRAM中的S用于静态确定哪个术语暗示它可能在esp中存活,当你学习DRAM时D表示动态,并且完全可以假设一个在电源循环中存活而另一个不幸但不幸的是不是什么他们指的是。相反,在这两种情况下,它们都与仍然供电的内存有关,它们都是易失性的记忆。去维基百科看看这些。静态使用四个晶体管,比如在经典的触发器实现中有两个带反馈的门,你把这个位写入高或低,只要电源没有关闭它就保持它不会忘记的值(只要电源保持打开) 。虽然DRAM使用一个晶体管并且在某种程度上严重依赖于晶体管中的电容,有点像跛脚的可充电电池,你想要它记住1你需要充电它并且它快速放电(以毫秒为单位)所以你必须经常提醒它是一个或零(刷新)。

所以静态ram是静态的,我们只需要告诉它一次并记住,动态ram是动态的,因为我们告诉dram系统那个位是什么,作为一个系统,我们必须不断提醒它这是通过读取该位然后以特定频率重新编程/充电该位来完成的。

DRAM是便宜的,可以将相同数量的晶体管的数量打包四倍,SRAM很快就没有复杂的开销,也没有刷新周期阻碍,它只是门,因此可以像其他门一样快速运行和门做其他事情一样快(处理指令)。

微控制器将在其中具有某种形式的非易失性存储器,其中包括ROM,PROM,EEPROM或闪存(其中现在有各种风格)。有时你可以在这里看到你可能要求的那种东西的闪存和eeprom,有时出于反向兼容性的原因,他们有一个eeprom传统接口,但它确实使用主闪存存储。无论如何,您必须查看芯片和/或芯片系列的文档。目前,应用程序能够写入片上非易失性存储器(eeprom / flash)是很常见的(尽管有很多例外)。文档告诉您如何执行此操作。

这一切都很棒,但是一些免费的建议是,如果你做错了,你可以在几小时或几天内磨损你的闪光灯...字面意思......这个部分可以被摧毁。理想情况下,您希望电路板上的支持能够检测到电源下降,同时具有足够的大容量电容或电池或两者,以使电路板/设备保持足够长的时间,以便在最坏的情况下保存非需要的时间。易失性信息(理想情况下首先确认值已经改变,否则不会烧毁擦除周期)。实施起来相当简单,但仍然比磨损闪光灯要好。

关于如何不磨损你的闪存的很多解决方案和意见,遗憾的是一些闪存硬件具有写入均衡的逻辑,如果软件和硬件都试图分散以减少闪存的磨损他们可以相互对抗,弊大于利。

您的部件支持的写入周期数应记录在数据表中,超过使用此设备构建的产品的生命周期,它可能会忘记您所写的内容。这是它可能说10000次写入的最低支持,但在测试中,您可能会达到100,000并且有一个仍然有效的设备。并不意味着它们的所有重置都将超过数据表中的评级,因此您可以从中反向工作,如果我每隔这么多单位获得一个新值,并且产品的生命周期是我希望这么多单位的时间,然后我不能保存超过一些简单的数学时间单位(每个存储位置/擦除边界等)。

所以首先要了解如何在应用程序中擦除你没有使用的块,然后向它写一些内容,然后在重新启动时查看它是否存在,如果没有闪存,请尝试使用eeprom。这些STM32设备通常记录良好且非常容易。然后,一旦你知道该怎么做,那就开始担心你觉得你需要这么做的频率。

有些汽车曾经注意到,当你转过它们时,#34;关闭"并且时钟仍然可以工作,收音机会记住您喜爱的电台,或者空气状况会记住您使用的最后温度和风扇速度。但如果断开电池,部分或全部电池将丢失。他们没有使用非易失性存储,他们正在使用RAM(SRAM),电源只是熄火,他们依赖备用电池。主板确实可能仍然为你的" CMOS"或者" BIOS"设置。电池支持ram基本上是因为ram没有断电,主电源可能会关闭,但电池正在保持ram供电。这是你可以使用的另一种设计解决方案,一个电池或超级电容(acitor),可能假设你永远不需要存储到闪存,如果像汽车音响,电池就好了。

当然,所有这些都需要我的回答,为了获得控制eeprom / flash的寄存器,你需要知道如何从程序中访问它们:

此处不需要首先进行位绑定(向ram存储/加载某些值),您是在询问如何在ram中写入和读取特定地址,还是在询问如何使用位带?通常你不会使用带有ram的位带,例如,该功能可以改变寄存器中的位子集,设计者由于某种原因将单独的项目打包到同一个寄存器中(gpio引脚配置之类的东西是有意义的,而你可能想要在没有软件读取 - 修改 - 写入的情况下更改单个引脚的配置(硬件可能仍然需要执行读取 - 修改 - 写入))

当然你可以在ram上使用bitbanding功能,如果cortex-m允许它我需要重新读取它,不一定有意义,除非你因为你需要将单独的东西装入ram而非常缺乏ram一个单词(比如bitfields,但甚至不开始)......

#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;

所以这些都不是特别的,或者与皮质或手臂相关,只是基本的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(位带非常特定于这些臂皮质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;
}

需要编译并链接它以查看整个故事:

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中的某个位置(由链接器选择)。

在比特带方面,如果你阅读你的手臂文档,你会发现它并不总是受支持,在某些内核中它是一个可选功能,这意味着当他们编译芯片时,他们可以选择不包含它。如果对于examaple这是一个特定的芯片或系列,您可能会发现他们忘记记录一个或两个位带地址(0x22000000,0x42000000)但是将它放在库中。

Personaly我不是挥发性指针技巧的粉丝,我看到编译器无法生成正确的指令,因此我编写了一个小的两行汇编函数,我可以抽象所有这样的访问,通过这些访问具有很大的强制副作用无论如何,你可以在linux或其他驱动程序中使用抽象。允许代码更有用,可以抽象访问软件模拟,可以抽象访问逻辑模拟,可以通过mmap抽象,可以在内核驱动程序中使用,可以添加printf层进行调试,单一如果您喜欢这种类型的调试,可以设置一个断点,可以使用几行asm实现裸机,或者如果您愿意,可以使用通用宏/定义来执行易失性指针。 YMMV。

注意局部变量

void fun ( void )
{
    unsigned int some_value;
    some_value = 5;
}

不一定最终处于ram状态,理想情况下它们会进入堆栈,但如果进行优化则可以优化(建议用于资源不足的设备,如微控制器,除非MISRA或其他一些要求阻止您使用优化器)。上面的代码当然是完全死的代码,导致简单的返回:

00000000 <fun>:
   0:   e12fff1e    bx  lr

答案 1 :(得分:0)

在阅读bitbanding时,我在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
}
  

有人可以向我解释如何存储例如SRAM中的数字5并再次阅读吗?

在上面的示例代码中,temp位于RAM中。

如果您现在不关心使用比特带,只需声明变量int x = 5将数字5存储在RAM中。