弄清楚闪存访问AVR ATmega2560

问题描述 投票:0回答:1

我正在尝试使用 C 和汇编命令来理解闪存访问。我一直在阅读几个引导加载程序(如 optiboot)的代码,以了解其工作原理。我正在尝试编写一个闪存加载程序,该程序将放置在引导段中,但不会被硬件熔丝指向。当收到信号时可以从主应用程序调用的东西。

#define SPM_REG         SPMCSR  //full register
#define SPM_ENABLE      SPMEN   //bit 0
#define SPM_BUSY        RWWSB   //bit 6
#define SPM_RWWENABLE   RWWSRE  //bit 4
#define SPM_INTENABLE   SPMIE   //bit 7
//execute to the page as Z-pointer specifies
#define CMD_ENABLEINTRUPT 0b10000000
#define CMD_SIGNATUREREAD 0b00100001
#define CMD_REENABLEFLASH 0b00010001 //re-enable readable after a write or erase
#define CMD_BOOTLOCKSET   0b00001001 
#define CMD_PAGEWRITE     0b00000101 
#define CMD_PAGEERASE     0b00000011 
#define CMD_ENABLEBIT     0b00000001
address_t   Z_ADDRESS = 0; //define global address Z 32 bit
#define boot_spm_busy_wait()          do{}while(boot_is_spm_busy())
#define boot_is_spm_interrupt()       (SPM_REG & (uint8_t)_BV(SPM_INTENABLE))
#define boot_is_rww_busy()            (SPM_REG & (uint8_t)_BV(SPM_BUSY))
#define boot_is_spm_busy()            (SPM_REG & (uint8_t)_BV(SPM_ENABLE)) //autoclears when complete
//make change to SPMCSR register
#define boot_cmd_spm_interrupt_enable()   (SPM_REG |= (uint8_t)_BV(SPM_INTENABLE))
#define boot_cmd_spm_interrupt_disable()  (SPM_REG &= (uint8_t)~_BV(SPM_INTENABLE))
#define boot_cmd_spm_erase()              (SPM_REG |= CMD_PAGEERASE)      //sets 2 bits
#define boot_cmd_spm_write()              (SPM_REG |= CMD_PAGEWRITE)      //sets 2 bits
#define boot_cmd_spm_reenableread()       (SPM_REG |= CMD_REENABLEFLASH)  //sets 2 bits
#define boot_cmd_spm_setspmen()           (SPM_REG |= CMD_ENABLEBIT)
#define boot_cmd_spm_clearspmcsr()        (SPM_REG &= 0b10000000) //clears all but keeps high bit if set

然后我就这样编写了函数

//Z_ADDRESS is a global defined 32bit unsinged variable
void loadZ_ADDRESS(address_t Addr)
{
  Z_ADDRESS = Addr << 1;  //shift and leave lowest bit 0 to get low first
  "LDI RAMPZ, ((Z_ADDRESS >> 16)&0xFF)\r\n"
  "LDI ZH,    ((Z_ADDRESS >> 8)&0xFF)\r\n"
  "LDI ZL,    (Z_ADDRESS & 0xFF)\r\n"
  ;
}
//--------------------------------------------------------------------------
void readFlash1Word(uint16_t* buffer, address_t Addr)
{
  loadZ_ADDRESS(Addr);  
  "ELPM %r0, Z+\r\n"; //read flash increment to high byte
  "ELPM %r1, Z \r\n";
  "MOVW HIGH(buffer):LOW(buffer), %r1:r0\r\n";
}
//--------------------------------------------------------------------------
void readFlash2Bytes(uint8_t* buffer, address_t beginAddr, int buffPos)
{
  loadZ_ADDRESS(beginAddr);
  "ELPM %r0, Z+\r\n"; //read flash increment to high byte
  "ELPM %r1, Z \r\n";
  "MOVW buffer[bufPos+1]:buffer[bufPos], %r1:r0\r\n";
}
//-----------------------------------------------------------------------------
void boot_page_erase(address_t eraseaddress)
{
  loadZ_ADDRESS(eraseaddress);  //set Z-pointer address on page
  boot_cmd_spm_erase();         //set register to erase next
  "spm\r\n";                                   
}
//-----------------------------------------------------------------------
void eraseFlash() // erase only main section (bootloader protection)
{
  address_t eraseAddress = 0;
  if (eraseAddress < APP_END )
  {
    boot_cmd_spm_clearspmcsr();     //makes sure all other bits are not flagged
    boot_page_erase(eraseAddress);  // Perform page erase
    boot_spm_busy_wait();   // Wait until the memory is erased.
    eraseAddress += SPM_PAGESIZE; // point to next page to be erase, page size in bytes
  }
  boot_spm_busy_wait();
  boot_cmd_spm_clearspmcsr(); //makes sure all others are not flagged
  boot_cmd_spm_reenableread();
  "spm";
}//end eraseflash
//----------------------------------------------------------------------------------------

我的这些工作是否走在正确的轨道上?编译器没有给出错误,但我还没有弄清楚写入指令,所以我还不能测试读取函数。

希望有人能告诉我这段代码的方向是否正确,以及是否有错误需要纠正。为简单起见,我在 Arduino IDE 上编写此代码,以便我可以在 Arduino mega 上尝试。

c assembly arduino avr avr-gcc
1个回答
0
投票

[[评论太长]]

您不能只在一个内联汇编中加载一些 GPR,然后说明该寄存器在其他一些内联汇编中仍包含相同的值。这是因为您不能排除编译器将 GPR 用于其他用途。

如果你可以读取一些值,说一个词,然后像这样传递它:

#include <avr/io.h>

static inline __attribute__((__always_inline__))
uint16_t load_word (const __uint24 addr)
{
    uint16_t result, reg_z;
    __asm ("movw %[z], %[addr]"       "\n\t" // ATmega2560 has MOVW.
           "out  %i[rampz], %C[addr]" "\n\t" // RAMPZ is in range of OUT.
           "elpm %A[res], Z+"         "\n\t"
           "elpm %B[res], Z+"
           // avr-gcc ABI for ATmega2560 does not require to reset RAMPZ,
           // so we don't.
           : [res] "=r" (result), [z] "=&z" (reg_z)
           : [addr] "r" (addr), [rampz] "n" (& RAMPZ));
    return result;
}

uint16_t call_load_word (void)
{
    return load_word (0x12345);
}

或者,如果您愿意,也可以将该值写入同一缓冲区中

请注意,上面的代码假定为 ATmega2560。对于其他设备,您可能需要一些

#ifdef

© www.soinside.com 2019 - 2024. All rights reserved.