用户页面 NVRAM 使用后奇怪的 UC3 重置行为
Weird UC3 Reset behavior after user page NVRAM usage
我最近需要在 AT32UC3L0256 的构建 NVRAM/EEPROM 中使用来存储一些配置数据。我终于设法使用 MCU 的用户页面 NVRAM(经过几天的反复试验和诅咒 GCC 忽略 noinit
指令并像往常一样修复和解决 ASF 中的错误)到这样的事情:
typedef struct
{
int writes; // write cycles counter
int irc_pot; // IRC_POT_IN position state
} _cfg;
volatile static int *nvram_adr=(int*)(void*)0x80800000; // user page NVRAM
volatile static _cfg ram_cfg; // RAM copy of cfg
void cfg_load() // nvram_cfg -> ram_cfg
{
ram_cfg.writes =nvram_adr[8];
ram_cfg.irc_pot=nvram_adr[9];
}
void cfg_save() // nvram_cfg <- ram_cfg
{
int i;
U32 buf[128];
// blank
for (i=0;i<128;i++) buf[i]=0xFFFFFFFF;
// cfg
buf[8]=ram_cfg.writes;
buf[9]=ram_cfg.irc_pot;
// Bootloader default cfg
buf[126]=0x929E0B79;
buf[127]=0xE11EFFD7;
flashcdw_memcpy(nvram_adr ,buf ,256,true); // write data -> nvram_cfg with erase
flashcdw_memcpy(nvram_adr+64,buf+64,256,false); // write data -> nvram_cfg without erase (fucking ASF cant write more than 256Bytes at once but erases whole page !!!)
}
我不得不从 ASF 3.48.0.98
更新 flashcdw.c,flashcdw.h
以便能够写入完整的 512 字节,因为旧的 ASF 只编程了最多 256 字节,但擦除整个页面造成一团糟。我还必须存储整页(而不是由于擦除而只存储 8 个字节),并且像往常一样,由于 ASF 错误,我需要按原样存储,而不是只调用 flashcdw_memcpy
一次...
它现在可以工作了,但我发现有些地址导致了奇怪的行为。当 0xFF
不在某个地址上时,设备将不再正常复位(但仍然直到复位 运行s OK)。在非引导加载程序重置时,它会启动固件代码,但在几次 [ms]
之后它会再次重置,并且这种情况会永远持续下去。需要明确的是,RESET 发生在这部分代码中(在我的例子中):
for (U8 i=0;i<4;i++)
{
gpio_tgl_gpio_pin(_LED);
wait_ms(200);
}
系统配置后 LED 的简单闪烁(PLL CPU 时钟、已配置的定时器和 ISR,但中断仍被禁用)。 LED 闪烁几次(PLL 以正确的速度工作)但在循环完成之前发生重置。等待很简单:
//------------------------------------------------------------------------------------------------
#define clk_cpu 50000000
#define RDTSC_mask 0x0FFFFFFF
void wait_ms(U32 dt)
{
U32 t0,t1;
t0=Get_system_register(AVR32_COUNT);
static const U32 ms=(clk_cpu+999)/1000;
t0&=RDTSC_mask;
for (;dt>0;)
{
t1=Get_system_register(AVR32_COUNT);
t1&=RDTSC_mask;
if (t0>t1) t1+=RDTSC_mask+1;
if ((t1-t0)>=ms)
{
dt--;
t0+=ms;
t0&=RDTSC_mask;
continue;
}
}
}
//------------------------------------------------------------------------------------------------
更奇怪的是,如果我启动到 Bootloader 然后再次正常重置设备正确重置并且固件再次工作(没有任何 erasing/programing)但是如果我再次正常重置重置循环再次发生......
如果我使用 BatchISP(翻转)将 .userpage
NVRAM 重新编程回原始状态,芯片将再次正常工作。
最后是问题:
NVRAM 用户页面中的哪些地址导致此问题或应该 reserved/avoided 更改?
我知道最后 8 个字节是 Bootloader 配置。我怀疑有问题的地址是前 16 个字节。 .userpage
应该是用户数据,不包含保险丝。
发生了什么事?
它是某种看门狗之类的吗?我以为那些是存放在别处的保险丝内。我在数据表中没有看到任何内容。
这里是原来的十六进制 .userpage
:
:020000048080FA
:10000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
:10001000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
:10002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
:10008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:10009000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
:1000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:1000B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
:1000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
:1000D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
:1000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
:1000F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
:10010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
:10011000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
:10012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
:10013000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
:10014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
:10015000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
:10016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
:10017000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
:10018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
:10019000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
:1001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
:1001B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
:1001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
:1001D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
:1001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
:1001F000FFFFFFFFFFFFFFFF929E0B79E11EFFD77E
:00000001FF
我使用这些(翻转命令)来获取和恢复它:
Batchisp -device AT32UC3L0256 -hardware RS232 -port COM1 -baudrate 115200 -operation memory user read savebuffer cfg_userpage.hex hex386 start reset 0
Batchisp -device AT32UC3L0256 -hardware RS232 -port COM1 -baudrate 115200 -operation onfail abort memory user loadbuffer cfg_userpage.hex program start reset 0
有问题的Bootloader是USART版本:1.0.2
具有此行为的固件使用 PLL、TC、GPIO、PWMA、ADC 模块,但是在任何 ISR 和/或 ADC、PWM、TC 使用之前发生重置。
[Edit1] 看门狗
根据 this,NVRAM .userpage
中的第一个字是看门狗的保险丝,它解释了在几次 ms
后将数据修复为原始值并禁用WDT
重置停止。但是,现在启动的不是引导程序,而是引导加载程序,因此仍然有些可疑。 Bootloader 引脚选择在最后 8 个字节
我还查看了 USART Bootloader ver: 1.0.2 源代码,发现他们使用 FLASHC
而不是 FLASHCDW
并强制使用看门狗启动(这可能会重置其状态并启用我的程序以某种方式再次 运行)。
[Edit2] 已查出错误
我终于发现问题是写入512字节的最后32位字引起的.userpage
:
U32 btldr[2]={0x929E0B79,0xE11EFFD7};
flashcdw_memcpy(&nvram_adr[127],(U32*)&btldr[1] ,4,false);
这是一个大问题,因为为了正确存储数据,我必须使用 erase 擦除整个页面,无论如何,为了仍然能够正确引导到 Bootloader 或我的固件,我必须恢复 Bootloader 配置数据:
U32 btldr[2]={0x929E0B79,0xE11EFFD7};
flashcdw_memcpy(&nvram_adr[126],(U32*)&btldr[0] ,4,false);
flashcdw_memcpy(&nvram_adr[127],(U32*)&btldr[1] ,4,false);
我需要找到一个解决方法来将芯片恢复到功能状态。也许从引导加载程序复制看门狗重置(但这在我的应用程序中会非常有问题甚至有风险)因为即使没有任何闪烁它也会恢复芯片...
所以现在地图:
:020000048080FA
:10000000---WDT--FFFFFFFFFFFFFFFFFFFFFFFF00
:10001000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
:10002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
:10008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:10009000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
:1000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:1000B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
:1000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
:1000D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
:1000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
:1000F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
:10010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
:10011000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
:10012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
:10013000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
:10014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
:10015000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
:10016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
:10017000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
:10018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
:10019000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
:1001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
:1001B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
:1001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
:1001D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
:1001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
:1001F000FFFFFFFFFFFFFFFF------BTLDR-----7E
:00000001FF
[Edit3] 解决方法
我成功地write/read 程序存储器 FLASH(用 BatchISP 和 MCU 本身检查)它也可以启动。
这里是我测试的代码:
// **** test ****
volatile static U32 *flash_adr=(U32*)(void*)0x00000000;
const U32 buf[4]=
{
0xDEADBEEF,0x00112233,
0xDEADBEEF,0x44556677,
};
volatile static U32 *adr=(U32*)(void*)0x80030000;
flashcdw_memcpy(&adr[0],(U32*)buf,4*4,true ); // erase
flash_adr=(U32*)(void*)0x80000000;
for (U32 i=0;i<0x08000000;i++,flash_adr++)
{
if (flash_adr!=buf)
if (flash_adr[0]==buf[0])
if (flash_adr[1]==buf[1])
if (flash_adr[2]==buf[2])
if (flash_adr[3]==buf[3])
{ break; }
if ((i&0xFFF)==0) gpio_tgl_gpio_pin(_LED);
}
其中 flash_adr
是找到的地址,其内容与 buf[]
签名匹配...(我将其打印在 LCD 上,以便查看它是否符合我的预期)并且它最终符合 :) .所以我会用这个代替 .userpage
.
但是 .userpage
引导问题修复仍然是未决问题
在 main 函数的早期禁用 wdt
wdt_disable();
另外我认为你不需要每次都写整页。
flashc_memcpy 在保持其他数据不变的情况下写入字节长度。
unsigned char buf[AVR32_FLASHCDW_PAGE_SIZE];
void* flash_addr = AVR32_FLASHCDW_USER_PAGE;
memcpy(buf, flash_addr, 512);
// modify data in buf
flashcdw_memcpy(flash_addr, buf,AVR32_FLASHCDW_PAGE_SIZE,TRUE);
或者直接使用
int mydata = 123;
int *nvmydata=AVR32_FLASHCDW_USER_PAGE + 16 // offset
flashcdw_memcyp(nvmydata,&mydata,sizeof(mydata),TRUE);
flashcdw_memcpy
volatile void* flashcdw_memcpy(volatile void* dst, const void* src, size_t nbytes, Bool erase)
{
// Use aggregated pointers to have several alignments available for a same address.
UnionCVPtr flash_array_end;
UnionVPtr dest;
UnionCPtr source;
StructCVPtr dest_end;
UnionCVPtr flash_page_source_end;
Bool incomplete_flash_page_end;
Union64 flash_dword;
Bool flash_dword_pending = FALSE;
UnionVPtr tmp;
unsigned int error_status = 0;
unsigned int i, j;
// Reformat arguments.
flash_array_end.u8ptr = AVR32_FLASH + flashcdw_get_flash_size();
dest.u8ptr = dst;
source.u8ptr = src;
dest_end.u8ptr = dest.u8ptr + nbytes;
// If destination is outside flash, go to next flash page if any.
if (dest.u8ptr < AVR32_FLASH)
{
source.u8ptr += AVR32_FLASH - dest.u8ptr;
dest.u8ptr = AVR32_FLASH;
}
else if (flash_array_end.u8ptr <= dest.u8ptr && dest.u8ptr < AVR32_FLASHCDW_USER_PAGE)
{
source.u8ptr += AVR32_FLASHCDW_USER_PAGE - dest.u8ptr;
dest.u8ptr = AVR32_FLASHCDW_USER_PAGE;
}
// If end of destination is outside flash, move it to the end of the previous flash page if any.
if (dest_end.u8ptr > AVR32_FLASHCDW_USER_PAGE + AVR32_FLASHCDW_USER_PAGE_SIZE)
{
dest_end.u8ptr = AVR32_FLASHCDW_USER_PAGE + AVR32_FLASHCDW_USER_PAGE_SIZE;
}
else if (AVR32_FLASHCDW_USER_PAGE >= dest_end.u8ptr && dest_end.u8ptr > flash_array_end.u8ptr)
{
dest_end.u8ptr = flash_array_end.u8ptr;
}
// Align each end of destination pointer with its natural boundary.
dest_end.u16ptr = (U16*)Align_down((U32)dest_end.u8ptr, sizeof(U16));
dest_end.u32ptr = (U32*)Align_down((U32)dest_end.u16ptr, sizeof(U32));
dest_end.u64ptr = (U64*)Align_down((U32)dest_end.u32ptr, sizeof(U64));
// While end of destination is not reached...
while (dest.u8ptr < dest_end.u8ptr)
{
// Clear the page buffer in order to prepare data for a flash page write.
flashcdw_clear_page_buffer();
error_status |= flashcdw_error_status;
// Determine where the source data will end in the current flash page.
flash_page_source_end.u64ptr =
(U64*)min((U32)dest_end.u64ptr,
Align_down((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE) + AVR32_FLASHCDW_PAGE_SIZE);
// Determine if the current destination page has an incomplete end.
incomplete_flash_page_end = (Align_down((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE) >=
Align_down((U32)dest_end.u8ptr, AVR32_FLASHCDW_PAGE_SIZE));
// If destination does not point to the beginning of the current flash page...
if (!Test_align((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE))
{
// Fill the beginning of the page buffer with the current flash page data.
// This is required by the hardware, even if page erase is not requested,
// in order to be able to write successfully to erased parts of flash
// pages that have already been written to.
for (tmp.u8ptr = (U8*)Align_down((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE);
tmp.u64ptr < (U64*)Align_down((U32)dest.u8ptr, sizeof(U64));
tmp.u64ptr++)
{
* tmp.u32ptr = *tmp.u32ptr;
* (tmp.u32ptr + 1) = *(tmp.u32ptr + 1);
}
// If destination is not 64-bit aligned...
if (!Test_align((U32)dest.u8ptr, sizeof(U64)))
{
// Fill the beginning of the flash double-word buffer with the current
// flash page data.
// This is required by the hardware, even if page erase is not
// requested, in order to be able to write successfully to erased parts
// of flash pages that have already been written to.
for (i = 0; i < Get_align((U32)dest.u8ptr, sizeof(U64)); i++)
flash_dword.u8[i] = *tmp.u8ptr++;
// Fill the end of the flash double-word buffer with the source data.
for (; i < sizeof(U64); i++)
flash_dword.u8[i] = *source.u8ptr++;
// Align the destination pointer with its 64-bit boundary.
dest.u64ptr = (U64*)Align_down((U32)dest.u8ptr, sizeof(U64));
// If the current destination double-word is not the last one...
if (dest.u64ptr < dest_end.u64ptr)
{
// Write the flash double-word buffer to the page buffer.
*dest.u32ptr++ = flash_dword.u32[0];
*dest.u32ptr++ = flash_dword.u32[1];
}
// If the current destination double-word is the last one, the flash
// double-word buffer must be kept for later.
else flash_dword_pending = TRUE;
}
}
// Read the source data with the maximal possible alignment and write it to
// the page buffer with 64-bit alignment.
switch (Get_align((U32)source.u8ptr, sizeof(U32)))
{
case 0:
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
{
*dest.u32ptr++ = *source.u32ptr++;
*dest.u32ptr++ = *source.u32ptr++;
}
break;
case sizeof(U16) :
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
{
for (j = 0; j < sizeof(U64) / sizeof(U16); j++) flash_dword.u16[j] = *source.u16ptr++;
* dest.u32ptr++ = flash_dword.u32[0];
* dest.u32ptr++ = flash_dword.u32[1];
}
break;
default:
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
{
for (j = 0; j < sizeof(U64); j++) flash_dword.u8[j] = *source.u8ptr++;
dest.u32ptr++ = flash_dword.u32[0];
dest.u32ptr++ = flash_dword.u32[1];
}
}
// If the current destination page has an incomplete end...
if (incomplete_flash_page_end)
{
// If the flash double-word buffer is in use, do not initialize it.
if (flash_dword_pending) i = Get_align((U32)dest_end.u8ptr, sizeof(U64));
// If the flash double-word buffer is free...
else
{
// Fill the beginning of the flash double-word buffer with the source data.
for (i = 0; i < Get_align((U32)dest_end.u8ptr, sizeof(U64)); i++)
flash_dword.u8[i] = *source.u8ptr++;
}
// This is required by the hardware, even if page erase is not requested,
// in order to be able to write successfully to erased parts of flash
// pages that have already been written to.
{
tmp.u8ptr = (volatile U8*)dest_end.u8ptr;
// If end of destination is not 64-bit aligned...
if (!Test_align((U32)dest_end.u8ptr, sizeof(U64)))
{
// Fill the end of the flash double-word buffer with the current flash page data.
for (; i < sizeof(U64); i++)
flash_dword.u8[i] = *tmp.u8ptr++;
// Write the flash double-word buffer to the page buffer.
* dest.u32ptr++ = flash_dword.u32[0];
* dest.u32ptr++ = flash_dword.u32[1];
}
// Fill the end of the page buffer with the current flash page data.
for (; !Test_align((U32)tmp.u64ptr, AVR32_FLASHCDW_PAGE_SIZE); tmp.u64ptr++)
{
tmp.u32ptr = *tmp.u32ptr;
* (tmp.u32ptr + 1) = *(tmp.u32ptr + 1);
}
}
}
// If the current flash page is in the flash array...
if (dest.u8ptr <= AVR32_FLASHCDW_USER_PAGE)
{
// Erase the current page if requested and write it from the page buffer.
if (erase)
{
flashcdw_erase_page(-1, FALSE);
error_status |= flashcdw_error_status;
}
flashcdw_write_page(-1);
error_status |= flashcdw_error_status;
// If the end of the flash array is reached, go to the User page.
if (dest.u8ptr >= flash_array_end.u8ptr)
{
source.u8ptr += AVR32_FLASHCDW_USER_PAGE - dest.u8ptr;
dest.u8ptr = AVR32_FLASHCDW_USER_PAGE;
}
}
// If the current flash page is the User page...
else
{
// Erase the User page if requested and write it from the page buffer.
if (erase)
{
flashcdw_erase_user_page(FALSE);
error_status |= flashcdw_error_status;
}
flashcdw_write_user_page();
error_status |= flashcdw_error_status;
}
}
// Update the FLASHC error status.
flashcdw_error_status = error_status;
// Return the initial destination pointer as the standard memcpy function does.
return dst;
}
好的,这是来自 Atmel/Microchip 的官方决议:
Proposed Resolution:
Looks like the DFU word being restored doesn't
have the boot flag + CRC change. For the customer's application where
userpage data is not constant, it's preferable to flash instead for
non-volatile storage.
所以我理解它是一个硬件错误(在较新的芯片上,如 AT32UC3L0256)并且无法解决......除了使用不同的非易失性存储器(如闪存)作为程序(就像我最终做的那样)首先)。
[edit1] Atmel/Microchip 刚刚确认了它的硬件错误
我最近需要在 AT32UC3L0256 的构建 NVRAM/EEPROM 中使用来存储一些配置数据。我终于设法使用 MCU 的用户页面 NVRAM(经过几天的反复试验和诅咒 GCC 忽略 noinit
指令并像往常一样修复和解决 ASF 中的错误)到这样的事情:
typedef struct
{
int writes; // write cycles counter
int irc_pot; // IRC_POT_IN position state
} _cfg;
volatile static int *nvram_adr=(int*)(void*)0x80800000; // user page NVRAM
volatile static _cfg ram_cfg; // RAM copy of cfg
void cfg_load() // nvram_cfg -> ram_cfg
{
ram_cfg.writes =nvram_adr[8];
ram_cfg.irc_pot=nvram_adr[9];
}
void cfg_save() // nvram_cfg <- ram_cfg
{
int i;
U32 buf[128];
// blank
for (i=0;i<128;i++) buf[i]=0xFFFFFFFF;
// cfg
buf[8]=ram_cfg.writes;
buf[9]=ram_cfg.irc_pot;
// Bootloader default cfg
buf[126]=0x929E0B79;
buf[127]=0xE11EFFD7;
flashcdw_memcpy(nvram_adr ,buf ,256,true); // write data -> nvram_cfg with erase
flashcdw_memcpy(nvram_adr+64,buf+64,256,false); // write data -> nvram_cfg without erase (fucking ASF cant write more than 256Bytes at once but erases whole page !!!)
}
我不得不从 ASF 3.48.0.98
更新 flashcdw.c,flashcdw.h
以便能够写入完整的 512 字节,因为旧的 ASF 只编程了最多 256 字节,但擦除整个页面造成一团糟。我还必须存储整页(而不是由于擦除而只存储 8 个字节),并且像往常一样,由于 ASF 错误,我需要按原样存储,而不是只调用 flashcdw_memcpy
一次...
它现在可以工作了,但我发现有些地址导致了奇怪的行为。当 0xFF
不在某个地址上时,设备将不再正常复位(但仍然直到复位 运行s OK)。在非引导加载程序重置时,它会启动固件代码,但在几次 [ms]
之后它会再次重置,并且这种情况会永远持续下去。需要明确的是,RESET 发生在这部分代码中(在我的例子中):
for (U8 i=0;i<4;i++)
{
gpio_tgl_gpio_pin(_LED);
wait_ms(200);
}
系统配置后 LED 的简单闪烁(PLL CPU 时钟、已配置的定时器和 ISR,但中断仍被禁用)。 LED 闪烁几次(PLL 以正确的速度工作)但在循环完成之前发生重置。等待很简单:
//------------------------------------------------------------------------------------------------
#define clk_cpu 50000000
#define RDTSC_mask 0x0FFFFFFF
void wait_ms(U32 dt)
{
U32 t0,t1;
t0=Get_system_register(AVR32_COUNT);
static const U32 ms=(clk_cpu+999)/1000;
t0&=RDTSC_mask;
for (;dt>0;)
{
t1=Get_system_register(AVR32_COUNT);
t1&=RDTSC_mask;
if (t0>t1) t1+=RDTSC_mask+1;
if ((t1-t0)>=ms)
{
dt--;
t0+=ms;
t0&=RDTSC_mask;
continue;
}
}
}
//------------------------------------------------------------------------------------------------
更奇怪的是,如果我启动到 Bootloader 然后再次正常重置设备正确重置并且固件再次工作(没有任何 erasing/programing)但是如果我再次正常重置重置循环再次发生......
如果我使用 BatchISP(翻转)将 .userpage
NVRAM 重新编程回原始状态,芯片将再次正常工作。
最后是问题:
NVRAM 用户页面中的哪些地址导致此问题或应该 reserved/avoided 更改?
我知道最后 8 个字节是 Bootloader 配置。我怀疑有问题的地址是前 16 个字节。
.userpage
应该是用户数据,不包含保险丝。发生了什么事?
它是某种看门狗之类的吗?我以为那些是存放在别处的保险丝内。我在数据表中没有看到任何内容。
这里是原来的十六进制 .userpage
:
:020000048080FA
:10000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
:10001000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
:10002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
:10008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:10009000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
:1000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:1000B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
:1000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
:1000D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
:1000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
:1000F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
:10010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
:10011000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
:10012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
:10013000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
:10014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
:10015000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
:10016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
:10017000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
:10018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
:10019000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
:1001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
:1001B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
:1001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
:1001D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
:1001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
:1001F000FFFFFFFFFFFFFFFF929E0B79E11EFFD77E
:00000001FF
我使用这些(翻转命令)来获取和恢复它:
Batchisp -device AT32UC3L0256 -hardware RS232 -port COM1 -baudrate 115200 -operation memory user read savebuffer cfg_userpage.hex hex386 start reset 0
Batchisp -device AT32UC3L0256 -hardware RS232 -port COM1 -baudrate 115200 -operation onfail abort memory user loadbuffer cfg_userpage.hex program start reset 0
有问题的Bootloader是USART版本:1.0.2 具有此行为的固件使用 PLL、TC、GPIO、PWMA、ADC 模块,但是在任何 ISR 和/或 ADC、PWM、TC 使用之前发生重置。
[Edit1] 看门狗
根据 this,NVRAM .userpage
中的第一个字是看门狗的保险丝,它解释了在几次 ms
后将数据修复为原始值并禁用WDT
重置停止。但是,现在启动的不是引导程序,而是引导加载程序,因此仍然有些可疑。 Bootloader 引脚选择在最后 8 个字节
我还查看了 USART Bootloader ver: 1.0.2 源代码,发现他们使用 FLASHC
而不是 FLASHCDW
并强制使用看门狗启动(这可能会重置其状态并启用我的程序以某种方式再次 运行)。
[Edit2] 已查出错误
我终于发现问题是写入512字节的最后32位字引起的.userpage
:
U32 btldr[2]={0x929E0B79,0xE11EFFD7};
flashcdw_memcpy(&nvram_adr[127],(U32*)&btldr[1] ,4,false);
这是一个大问题,因为为了正确存储数据,我必须使用 erase 擦除整个页面,无论如何,为了仍然能够正确引导到 Bootloader 或我的固件,我必须恢复 Bootloader 配置数据:
U32 btldr[2]={0x929E0B79,0xE11EFFD7};
flashcdw_memcpy(&nvram_adr[126],(U32*)&btldr[0] ,4,false);
flashcdw_memcpy(&nvram_adr[127],(U32*)&btldr[1] ,4,false);
我需要找到一个解决方法来将芯片恢复到功能状态。也许从引导加载程序复制看门狗重置(但这在我的应用程序中会非常有问题甚至有风险)因为即使没有任何闪烁它也会恢复芯片...
所以现在地图:
:020000048080FA
:10000000---WDT--FFFFFFFFFFFFFFFFFFFFFFFF00
:10001000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
:10002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
:10008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:10009000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
:1000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:1000B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
:1000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
:1000D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
:1000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
:1000F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10
:10010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
:10011000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
:10012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
:10013000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
:10014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
:10015000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
:10016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
:10017000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
:10018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
:10019000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F
:1001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
:1001B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F
:1001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
:1001D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
:1001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
:1001F000FFFFFFFFFFFFFFFF------BTLDR-----7E
:00000001FF
[Edit3] 解决方法
我成功地write/read 程序存储器 FLASH(用 BatchISP 和 MCU 本身检查)它也可以启动。
这里是我测试的代码:
// **** test ****
volatile static U32 *flash_adr=(U32*)(void*)0x00000000;
const U32 buf[4]=
{
0xDEADBEEF,0x00112233,
0xDEADBEEF,0x44556677,
};
volatile static U32 *adr=(U32*)(void*)0x80030000;
flashcdw_memcpy(&adr[0],(U32*)buf,4*4,true ); // erase
flash_adr=(U32*)(void*)0x80000000;
for (U32 i=0;i<0x08000000;i++,flash_adr++)
{
if (flash_adr!=buf)
if (flash_adr[0]==buf[0])
if (flash_adr[1]==buf[1])
if (flash_adr[2]==buf[2])
if (flash_adr[3]==buf[3])
{ break; }
if ((i&0xFFF)==0) gpio_tgl_gpio_pin(_LED);
}
其中 flash_adr
是找到的地址,其内容与 buf[]
签名匹配...(我将其打印在 LCD 上,以便查看它是否符合我的预期)并且它最终符合 :) .所以我会用这个代替 .userpage
.
但是 .userpage
引导问题修复仍然是未决问题
在 main 函数的早期禁用 wdt
wdt_disable();
另外我认为你不需要每次都写整页。 flashc_memcpy 在保持其他数据不变的情况下写入字节长度。
unsigned char buf[AVR32_FLASHCDW_PAGE_SIZE];
void* flash_addr = AVR32_FLASHCDW_USER_PAGE;
memcpy(buf, flash_addr, 512);
// modify data in buf
flashcdw_memcpy(flash_addr, buf,AVR32_FLASHCDW_PAGE_SIZE,TRUE);
或者直接使用
int mydata = 123;
int *nvmydata=AVR32_FLASHCDW_USER_PAGE + 16 // offset
flashcdw_memcyp(nvmydata,&mydata,sizeof(mydata),TRUE);
flashcdw_memcpy
volatile void* flashcdw_memcpy(volatile void* dst, const void* src, size_t nbytes, Bool erase)
{
// Use aggregated pointers to have several alignments available for a same address.
UnionCVPtr flash_array_end;
UnionVPtr dest;
UnionCPtr source;
StructCVPtr dest_end;
UnionCVPtr flash_page_source_end;
Bool incomplete_flash_page_end;
Union64 flash_dword;
Bool flash_dword_pending = FALSE;
UnionVPtr tmp;
unsigned int error_status = 0;
unsigned int i, j;
// Reformat arguments.
flash_array_end.u8ptr = AVR32_FLASH + flashcdw_get_flash_size();
dest.u8ptr = dst;
source.u8ptr = src;
dest_end.u8ptr = dest.u8ptr + nbytes;
// If destination is outside flash, go to next flash page if any.
if (dest.u8ptr < AVR32_FLASH)
{
source.u8ptr += AVR32_FLASH - dest.u8ptr;
dest.u8ptr = AVR32_FLASH;
}
else if (flash_array_end.u8ptr <= dest.u8ptr && dest.u8ptr < AVR32_FLASHCDW_USER_PAGE)
{
source.u8ptr += AVR32_FLASHCDW_USER_PAGE - dest.u8ptr;
dest.u8ptr = AVR32_FLASHCDW_USER_PAGE;
}
// If end of destination is outside flash, move it to the end of the previous flash page if any.
if (dest_end.u8ptr > AVR32_FLASHCDW_USER_PAGE + AVR32_FLASHCDW_USER_PAGE_SIZE)
{
dest_end.u8ptr = AVR32_FLASHCDW_USER_PAGE + AVR32_FLASHCDW_USER_PAGE_SIZE;
}
else if (AVR32_FLASHCDW_USER_PAGE >= dest_end.u8ptr && dest_end.u8ptr > flash_array_end.u8ptr)
{
dest_end.u8ptr = flash_array_end.u8ptr;
}
// Align each end of destination pointer with its natural boundary.
dest_end.u16ptr = (U16*)Align_down((U32)dest_end.u8ptr, sizeof(U16));
dest_end.u32ptr = (U32*)Align_down((U32)dest_end.u16ptr, sizeof(U32));
dest_end.u64ptr = (U64*)Align_down((U32)dest_end.u32ptr, sizeof(U64));
// While end of destination is not reached...
while (dest.u8ptr < dest_end.u8ptr)
{
// Clear the page buffer in order to prepare data for a flash page write.
flashcdw_clear_page_buffer();
error_status |= flashcdw_error_status;
// Determine where the source data will end in the current flash page.
flash_page_source_end.u64ptr =
(U64*)min((U32)dest_end.u64ptr,
Align_down((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE) + AVR32_FLASHCDW_PAGE_SIZE);
// Determine if the current destination page has an incomplete end.
incomplete_flash_page_end = (Align_down((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE) >=
Align_down((U32)dest_end.u8ptr, AVR32_FLASHCDW_PAGE_SIZE));
// If destination does not point to the beginning of the current flash page...
if (!Test_align((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE))
{
// Fill the beginning of the page buffer with the current flash page data.
// This is required by the hardware, even if page erase is not requested,
// in order to be able to write successfully to erased parts of flash
// pages that have already been written to.
for (tmp.u8ptr = (U8*)Align_down((U32)dest.u8ptr, AVR32_FLASHCDW_PAGE_SIZE);
tmp.u64ptr < (U64*)Align_down((U32)dest.u8ptr, sizeof(U64));
tmp.u64ptr++)
{
* tmp.u32ptr = *tmp.u32ptr;
* (tmp.u32ptr + 1) = *(tmp.u32ptr + 1);
}
// If destination is not 64-bit aligned...
if (!Test_align((U32)dest.u8ptr, sizeof(U64)))
{
// Fill the beginning of the flash double-word buffer with the current
// flash page data.
// This is required by the hardware, even if page erase is not
// requested, in order to be able to write successfully to erased parts
// of flash pages that have already been written to.
for (i = 0; i < Get_align((U32)dest.u8ptr, sizeof(U64)); i++)
flash_dword.u8[i] = *tmp.u8ptr++;
// Fill the end of the flash double-word buffer with the source data.
for (; i < sizeof(U64); i++)
flash_dword.u8[i] = *source.u8ptr++;
// Align the destination pointer with its 64-bit boundary.
dest.u64ptr = (U64*)Align_down((U32)dest.u8ptr, sizeof(U64));
// If the current destination double-word is not the last one...
if (dest.u64ptr < dest_end.u64ptr)
{
// Write the flash double-word buffer to the page buffer.
*dest.u32ptr++ = flash_dword.u32[0];
*dest.u32ptr++ = flash_dword.u32[1];
}
// If the current destination double-word is the last one, the flash
// double-word buffer must be kept for later.
else flash_dword_pending = TRUE;
}
}
// Read the source data with the maximal possible alignment and write it to
// the page buffer with 64-bit alignment.
switch (Get_align((U32)source.u8ptr, sizeof(U32)))
{
case 0:
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
{
*dest.u32ptr++ = *source.u32ptr++;
*dest.u32ptr++ = *source.u32ptr++;
}
break;
case sizeof(U16) :
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
{
for (j = 0; j < sizeof(U64) / sizeof(U16); j++) flash_dword.u16[j] = *source.u16ptr++;
* dest.u32ptr++ = flash_dword.u32[0];
* dest.u32ptr++ = flash_dword.u32[1];
}
break;
default:
for (i = flash_page_source_end.u64ptr - dest.u64ptr; i; i--)
{
for (j = 0; j < sizeof(U64); j++) flash_dword.u8[j] = *source.u8ptr++;
dest.u32ptr++ = flash_dword.u32[0];
dest.u32ptr++ = flash_dword.u32[1];
}
}
// If the current destination page has an incomplete end...
if (incomplete_flash_page_end)
{
// If the flash double-word buffer is in use, do not initialize it.
if (flash_dword_pending) i = Get_align((U32)dest_end.u8ptr, sizeof(U64));
// If the flash double-word buffer is free...
else
{
// Fill the beginning of the flash double-word buffer with the source data.
for (i = 0; i < Get_align((U32)dest_end.u8ptr, sizeof(U64)); i++)
flash_dword.u8[i] = *source.u8ptr++;
}
// This is required by the hardware, even if page erase is not requested,
// in order to be able to write successfully to erased parts of flash
// pages that have already been written to.
{
tmp.u8ptr = (volatile U8*)dest_end.u8ptr;
// If end of destination is not 64-bit aligned...
if (!Test_align((U32)dest_end.u8ptr, sizeof(U64)))
{
// Fill the end of the flash double-word buffer with the current flash page data.
for (; i < sizeof(U64); i++)
flash_dword.u8[i] = *tmp.u8ptr++;
// Write the flash double-word buffer to the page buffer.
* dest.u32ptr++ = flash_dword.u32[0];
* dest.u32ptr++ = flash_dword.u32[1];
}
// Fill the end of the page buffer with the current flash page data.
for (; !Test_align((U32)tmp.u64ptr, AVR32_FLASHCDW_PAGE_SIZE); tmp.u64ptr++)
{
tmp.u32ptr = *tmp.u32ptr;
* (tmp.u32ptr + 1) = *(tmp.u32ptr + 1);
}
}
}
// If the current flash page is in the flash array...
if (dest.u8ptr <= AVR32_FLASHCDW_USER_PAGE)
{
// Erase the current page if requested and write it from the page buffer.
if (erase)
{
flashcdw_erase_page(-1, FALSE);
error_status |= flashcdw_error_status;
}
flashcdw_write_page(-1);
error_status |= flashcdw_error_status;
// If the end of the flash array is reached, go to the User page.
if (dest.u8ptr >= flash_array_end.u8ptr)
{
source.u8ptr += AVR32_FLASHCDW_USER_PAGE - dest.u8ptr;
dest.u8ptr = AVR32_FLASHCDW_USER_PAGE;
}
}
// If the current flash page is the User page...
else
{
// Erase the User page if requested and write it from the page buffer.
if (erase)
{
flashcdw_erase_user_page(FALSE);
error_status |= flashcdw_error_status;
}
flashcdw_write_user_page();
error_status |= flashcdw_error_status;
}
}
// Update the FLASHC error status.
flashcdw_error_status = error_status;
// Return the initial destination pointer as the standard memcpy function does.
return dst;
}
好的,这是来自 Atmel/Microchip 的官方决议:
Proposed Resolution:
Looks like the DFU word being restored doesn't have the boot flag + CRC change. For the customer's application where userpage data is not constant, it's preferable to flash instead for non-volatile storage.
所以我理解它是一个硬件错误(在较新的芯片上,如 AT32UC3L0256)并且无法解决......除了使用不同的非易失性存储器(如闪存)作为程序(就像我最终做的那样)首先)。
[edit1] Atmel/Microchip 刚刚确认了它的硬件错误