我正在为基于STM32F103的ARM微控制器开发一些软件(使用GCC进行编译)。我的一些用户希望能够在相同芯片的不同版本(具有更多RAM)中使用相同的二进制文件,因此我需要一种方法来查找运行时有多少RAM。
有一个包含闪存大小的地址(0x1FFFF7E0),但似乎没有一个包含RAM大小的地址!
显而易见的解决方案是运行内存地址检查哪些是可读/写的,但我从RAM地址读取太高时尝试了这个和芯片HardFaults(我不知道如何恢复)。
有关最佳解决方法的想法吗?理想情况下我会通过实验来实现,因为一些芯片(如我现在使用的STM32F103RCT6)实际上似乎有64kB的RAM,即使数据表显示它们有48个。例如,0x1FFFF7E0寄存器报告256kB的可用闪存,即使512kB可用
看起来我可能能够在CCR寄存器中设置BFHFNMIGN位,然后尝试从软件中断中访问存储器 - 但是我不知道如何在GCC + STM32中调用或创建软件中断
答案 0 :(得分:1)
是的,我终于在ST论坛的用户帮助下想出了这个。
首先,您需要启用BusFault IRQ:
SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA;
然后,您需要定义一个BusFault处理程序,它将程序计数器递增2,以便跳过违规指令(以赌博为例,它实际上是一个2字节指令):
__attribute__ ((naked)) void BusFault_Handler(void) {
/* NAKED function so we can be sure that SP is correct when we
* run our asm code below */
// DO NOT clear the busfault active flag - it causes a hard fault!
/* Instead, we must increase the value of the PC, so that when we
* return, we don't return to the same instruction.
*
* Registers are stacked as follows: r0,r1,r2,r3,r12,lr,pc,xPSR
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/Babedgea.html
*
* So we want PC - the 6th down * 4 bytes = 24
*
* Then we add 2 - which IS DANGEROUS because we're assuming that the op
* is 2 bytes, but it COULD be 4.
*/
__asm__(
"ldr r0, [sp, #24]\n" // load the PC
"add r0, #2\n" // increase by 2 - dangerous, see above
"str r0, [sp, #24]\n" // save the PC back
"bx lr\n" // Return (function is naked so we must do this explicitly)
);
}
现在 - 最后 - 我们可以尝试从任意内存位置读取。如果它错了,则调用BusFault处理程序,但是我们跳过读或写指令,好像它不存在一样。
这意味着写入内存位置然后回读相对容易 - 如果你得到相同的东西,你知道它是有效的(你只需要确保你的代码不会被两个str都愚弄)和ldr作为no-ops)。
答案 1 :(得分:1)
我发现通过设置FAULTMASK位(禁用所有中断和故障处理程序禁用),同时设置BFHFNMIGN位,可以探测访问地址是否会产生总线故障而不会引发异常。不提高异常的好处意味着您不必编写异常处理程序。以下示例是执行探测的C函数:
/**
* @brief Probe an address to see if can be read without generating a bus fault
* @details This function must be called with the processor in privileged mode.
* It:
* - Clear any previous indication of a bus fault in the BFARV bit
* - Temporarily sets the processor to Ignore Bus Faults with all interrupts and fault handlers disabled
* - Attempt to read from read_address, ignoring the result
* - Checks to see if the read caused a bus fault, by checking the BFARV bit is set
* - Re-enables Bus Faults and all interrupts and fault handlers
* @param[in] read_address The address to try reading a byte from
* @return Returns true if no bus fault occurred reading from read_address, or false if a bus fault occurred.
*/
bool read_probe (volatile const char *read_address)
{
bool address_readable = true;
/* Clear any existing indication of a bus fault - BFARV is write one to clear */
HWREG (NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
HWREG (NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
asm volatile (" CPSID f;");
*read_address;
if ((HWREG (NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
{
address_readable = false;
}
asm volatile (" CPSIE f;");
HWREG (NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
return address_readable;
}
该代码是为Texas Instruments ARM编译器编写的,使用Texas Instruments TivaWare软件进行寄存器定义,并在基于Cortex-M4F的TM4C器件上进行了测试。从理论上讲,它应该适用于其他Cortex-M3或Cortex-M4器件。
答案 2 :(得分:1)
在具有不同SRAM大小的MCU上运行相同二进制文件的一个相关问题-堆栈指针(SP)在复位时使用位置0的值进行初始化。通常,在构建程序时,编译器/启动文件/链接器脚本将SRAM的TOP值(适当对齐)放入位置0。因此,当将二进制文件放在具有更多SRAM的计算机上时,堆栈现在不再位于SRAM的顶部。如果要利用较大的SRAM大小,则一旦找到堆栈,程序必须将堆栈重定位到SRAM的真正顶部。
答案 3 :(得分:0)
应该有一个除闪光灯尺寸以外的寄存器,一个零件ID应该告诉你正在使用哪个部件,并从那里查找一个ram尺寸表。或者,如果闪光灯尺寸没有别的,你可以获得芯片和闪光灯尺寸附带的一系列可能的冲头尺寸?选择最小的。不确定这样的平台的代码类型是否需要额外的ram或应用程序的可变大小的ram。很快你会进入一个外围变化足以成为问题的部分......
答案 4 :(得分:-1)
我的一些用户希望能够在相同芯片的不同版本(具有更多RAM)中使用相同的二进制文件,因此我需要一种方法来查找运行时有多少RAM。
不,你不需要。只需使用最小 RAM量编译芯片代码即可。二进制文件可以在更大的版本上使用而无需任何更改 - 多余的RAM只是未使用。