assembly AVR ATmega2560闪存访问

e5nszbig  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(85)

编辑:更新学习程序集:好了,这个函数不会引起编译器警告,现在它们都应该被打开了。我的约束值对于这些输入和输出是正确的吗?

void boot_page_erase(address_t Addr)
{
  Addr = Addr << 1;
  uint16_t reg_z;
  uint8_t  cmderase = CMD_PAGEERASE;
  
  __asm volatile
  (
    "MOVW %[z],      %[addr]  \n\t"   //copies into ZH and ZL
    "OUT  %i[rampz], %C[addr] \n\t"  //%i means SRAM, %C means get 3rd byte of [addr]
    "IN   %[spmcsr], %[eras] \n\t"
    "spm \r\n"
      //output operends
    :[z]  "=&z" (reg_z)  //  
      //input operends
    :[addr] "r" (Addr),   [rampz]  "n" (RAMPZ),
     [spmcsr] "n" (SPMCSR), [eras] "M" (cmderase)  
  );                               
}

// FILL TEMP PAGE BUFFER
//set address in Z-pointer
//data in R1:R0 __tmp_reg__
//Write 0b00000001 to SPMCSR
//call spm
void loadTempPage(uint8_t* buffer, int noBytes, address_t Addr)
{
  Addr = Addr << 1;
  uint16_t reg_z, word1;
  for(int n=0; n<noBytes; n+=2)
  {
    word1 = buffer[n] | (buffer[n+1] << 8));    
    __asm volatile 
    ( 
      "MOVW %[z],         %[addr]\n\t"  //copies into ZH and ZL
      "OUT  %i[rampz],    %C[addr]\n\t" //%i ram, %C means get 3rd byte of [addr]
      "MOVW __tmp_reg__,  %[data]\n\t"  //load r0 and r1
      "IN   %[spmcsr],    %[cmd] \n\t"  //set SPMCSR
      "spm\n\t"
        //output operends
      :[z]  "=&z" (reg_z)
        //input operends 
      :[addr] "r" (Addr), [rampz] "n" (RAMPZ), 
       [data] "r" (word1),
       [spmcsr] "n" (SPMCSR), [cmd] "M" (CMD_ENABLEBIT)    
    );
    boot_spm_busy_wait();
    Addr +=2;
  }
}//page fill done

字符串
编辑:更新,@emacs让我抓狂我从你的例子中理解了更多,我找到了关于内联访问汇编指令的AVR-libc部分的参考。https://www.nongnu.org/avr-libc/user-manual/inline_asm.html
我仍然找不到“OUT”语句中“%i”的含义。
还有,当有人只是有一个诚实的问题,并正在学习,为什么有人标记的东西消极?认真的人。

uint16_t readFlash1Word(const address_t Addr)
{
  uint16_t result, reg_z; 
  __asm
  ( 
    "MOVW %[z],      %[addr]\n\t"   //copies into ZH and ZL
    "OUT  %i[rampz], %C[addr]\n\t"  //%i means??, %C means get 3rd byte of [addr]
    "ELPM %A[res], Z+\n\t"          //read flash, %A= put in low byte of res, increment 
    "ELPM %B[res], Z+\n\t"          //read flash, %A= put in low byte of res, increment 
    :[z]  "=&z" (reg_z), [res]  "=r" (result) //output operends
    :[addr] "r" (Addr), [rampz] "n" (RAMPZ)     //input operends
  );
  return result;
}


我正在尝试理解使用C和汇编命令访问闪存。我已经阅读了几个 Bootstrap 的代码,如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大型。

bxpogfeg

bxpogfeg1#

Too长的评论
你不能只在一个inline asm中加载一些GPR,然后期望寄存器在另一个inline asm中仍然包含相同的值。这是因为你不能排除编译器在中间使用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  __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));
    return result;
}

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

字符串
或者,如果你愿意,也可以将该值写入同一个asm* 中的某个缓冲区 *。方便的是,AVR-LibC提供了memcpy_PF,它可以从远端闪存复制到RAM:

#include <avr/pgmspace.h>

void load_n_bytes (uint8_t *dst, __uint24 src, size_t n_bytes)
{
    memcpy_PF (dst, src, n_bytes);
}


1该代码假定为ATmega 2560。对于其他设备,您可能需要一些#ifdef

相关问题