0

我最近需要在AT32UC3L0256的 build 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 !!!)
    }

为了能够写入完整的 512 字节,我必须进行更新flashcdw.c,flashcdw.h,因为旧 ASF 确实编程了最多 256 字节,但擦除了整个页面,弄得一团糟。ASF 3.48.0.98我还必须存储整个页面(而不是因为擦除而只存储 8 个字节),并且像往常一样由于 ASF 错误,我需要按原样进行操作,而不是只调用flashcdw_memcpy一次...

它现在可以工作,但我发现某些地址会导致奇怪的行为。当0xFF不在某个地址上时,设备将不再正常复位(但在复位运行正常之前仍然如此)。在非引导加载程序重置时,它会启动固件代码,但在几次之后[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;
            }
        }
    }
//------------------------------------------------------------------------------------------------

更奇怪的是,如果我启动引导加载程序然后再次正常重置,设备正在正确重置并且固件再次工作(没有任何擦除/编程)但是如果我再次正常重置,重置循环再次发生......

如果我.userpage使用 BatchISP(翻转)将 NVRAM 重新编程回原始状态,则芯片会再次正常工作。

所以最后的问题:

  1. NVRAM 的用户页中的哪些地址会导致此问题或应保留/避免更改?

    我知道最后 8 个字节是引导加载程序配置。我怀疑有问题的地址是前 16 个字节。应该用于用户数据并且.userpage不包含保险丝。

  2. 怎么了?

    它是某种看门狗还是什么?我以为那些是存放在其他地方的保险丝。我在数据表中看不到任何内容。

这里是原始的十六进制.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

有问题的引导加载程序是 USART 版本:1.0.2 并且具有此行为的固件使用PLL、TC、GPIO、PWMA、ADC模块,但是在任何 ISR 和/或 ADC、PWMA、TC 使用之前发生复位。

[Edit1] 看门狗

据此,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);

这是一个巨大的问题,因为为了正确存储数据,我必须使用擦除来擦除整个页面,无论如何,为了仍然能够正确引导到引导加载程序或我的固件,我必须恢复引导加载程序配置数据:

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] 解决方法

我设法成功地写入/读取程序存储器闪存(用 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引导问题修复仍然是悬而未决的问题

4

2 回答 2

1

在 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;
}
于 2020-05-26T11:34:31.393 回答
0

好的,这是来自 Atmel/Microchip 的官方解决方案:

建议的解决方案:
看起来正在恢复的 DFU 字没有引导标志 + CRC 更改。对于用户页面数据不固定的客户应用程序,最好使用闪存代替非易失性存储。

所以我理解它是一个硬件错误(在 AT32UC3L0256 等较新的芯片上)并且无法解决......除了使用不同的非易失性存储器(如闪存)进行程序(就像我最终做的那样) .

[edit1] Atmel/Microchip 刚刚确认了其明确的硬件错误

于 2021-05-13T07:39:08.700 回答