编辑:更新学习程序集:好了,这个函数不会引起编译器警告,现在它们都应该被打开了。我的约束值对于这些输入和输出是正确的吗?
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大型。
1条答案
按热度按时间bxpogfeg1#
Too长的评论
你不能只在一个inline asm中加载一些GPR,然后期望寄存器在另一个inline asm中仍然包含相同的值。这是因为你不能排除编译器在中间使用GPR做其他事情。
如果你可以读取一些值,说出一个单词,然后像这样传递:
字符串
或者,如果你愿意,也可以将该值写入同一个asm* 中的某个缓冲区 *。方便的是,AVR-LibC提供了
memcpy_PF
,它可以从远端闪存复制到RAM:型
1该代码假定为ATmega 2560。对于其他设备,您可能需要一些
#ifdef
。