英文:
ATMEGA4809 UART bootloader block gets corrupted randomly
问题
我正在开发一个基于Microchip示例的简单UART引导加载程序,示例链接如下:text
它按预期工作,但是有时设备会突然无法启动...变成了一个砖头 - 它不会启动到引导加载程序或应用程序。我们需要再次通过UPDI接口重新编程它才能让它重新工作。
我们正在使用FT232 USB-UART与ATMEGA进行通信。
这个问题发生时没有特定的模式,但总是在设备的USB电缆被重新启动后发生。
在这个问题发生后,我读取了程序内存,并发现从地址0x00E0到0x00FF有一部分字节被覆盖 - 就像一个页面被擦除和写入一样。但是如果我没有发出写命令,这怎么可能发生呢?
以下是代码:
#define F_CPU_RESET (20E6/6)
#include <avr/io.h>
#include <assert.h>
#include <stdbool.h>
/* 波特率配置 */
#define BOOT_BAUD (115200)
/* 存储器配置
* BOOTEND_FUSE * 256 必须高于引导加载程序的程序内存使用,这是194字节,在优化级别-O3下,所以BOOTEND_FUSE = 0x01
*/
#define BOOTEND_FUSE (0x02)
#define BOOT_SIZE (BOOTEND_FUSE * 0x100)
#define MAPPED_APPLICATION_START (MAPPED_PROGMEM_START + BOOT_SIZE)
#define MAPPED_APPLICATION_SIZE (MAPPED_PROGMEM_SIZE - BOOT_SIZE)
/* 保险丝配置
* BOOTEND 设置引导段的大小(末尾)为256字节块。
* APPEND = 0x00 定义从BOOTEND*256到Flash末尾的部分作为应用程序代码。
* 剩余的保险丝使用默认配置。
*/
FUSES = {
.OSCCFG = FREQSEL_20MHZ_gc,
.SYSCFG0 = 0xC8,
.SYSCFG1 = SUT_0MS_gc,
.APPEND = 0x00,
.BOOTEND = BOOTEND_FUSE
};
/* 定义应用程序指针类型 */
typedef void (*const app_t)(void);
//uint8_t EEMEM user_byte = 0;
/* 接口函数原型 */
static bool is_bootloader_requested(void);
static void init_uart(void);
static uint8_t uart_receive(void);
static void uart_send(uint8_t byte);
static void init_status_led(void);
static void toggle_status_led(void);
static void initPins(void);
static inline void wait_50_ms(void);
/*
* 主要引导函数
* 放在构造函数部分(.ctors)以节省闪存。
* 使用naked属性,因为函数的前导和尾部是未使用的
*/
__attribute__((naked)) __attribute__((section(".ctors"))) void boot(void)
{
/* 初始化系统以支持AVR GCC,期望r1 = 0 */
asm volatile("clr r1");
initPins();
//这远远超过了50毫秒,但没关系
wait_50_ms();
/* 检查是否进入应用程序或继续引导加载程序 */
if(!is_bootloader_requested()) {
/* 启用引导段锁定 */
NVMCTRL.CTRLB = NVMCTRL_BOOTLOCK_bm;
/* 转到应用程序,位于引导段之后 */
app_t app = (app_t)(BOOT_SIZE / sizeof(app_t));
app();
}
/* 初始化通信接口和LED */
init_uart();
init_status_led();
VPORTB.OUT |= PIN5_bm;
/*
* 从应用程序段的开始开始编程
* 在条件中减去MAPPED_PROGMEM_START以处理大闪存大小的溢出
*/
uint8_t *app_ptr = (uint8_t *)MAPPED_APPLICATION_START;
while(app_ptr - MAPPED_PROGMEM_START <= (uint8_t *)PROGMEM_END) {
/* 接收并回显数据以加载到内存 */
uint8_t rx_data = uart_receive();
uart_send(rx_data);
/* 在写入Flash之前对页面缓冲区进行增量加载 */
*app_ptr = rx_data;
app_ptr++;
if(!((uint16_t)app_ptr % MAPPED_PROGMEM_PAGE_SIZE)) {
/* 达到页面边界,提交页面到Flash */
_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
while(NVMCTRL.STATUS & NVMCTRL_FBUSY_bm);
toggle_status_led();
}
}
//将USERROW设置回0xff,以便在接下来的重启后进入应用程序
USERROW.USERROW31 = 0xff;
_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
while(NVMCTRL.STATUS & NVMCTRL_EEBUSY_bm);
//关闭LED,以防更新后仍然开启
VPORTB.OUT &= ~PIN5_bm;
// /* 发出系统复位 */
_PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRE_bm);
}
/*
* 引导访问请求函数
*/
static bool is_bootloader_requested(void)
{
if(USERROW.USERROW31 == 0x55) {
return true;
}
return false;
}
/*
* 通信接口函数
*/
static void init_uart(void)
{
/* 配置UART */
USART3.CTRLB = USART_RXEN_bm | USART_TXEN_bm;
/* 从数据表中:
* 用工厂存储的频率误差补偿波特率
* 异步通信,无自动波特率(同步字段)
* 20MHz时钟,3V
*/
int32_t baud_reg_val = (F_CPU_RESET * 64) / (BOOT_BAUD * 16); // 理想的BAUD寄存器值
assert(baud_reg_val >= 0x4A); // 验证最小合法BAUD寄存器值与最大负补偿
int8
<details>
<summary>英文:</summary>
I'm working on a simple UART bootloader based off the example by Microchip here:[text](https://ww1.microchip.com/downloads/en/Appnotes/AN2634-Bootloader-for-tinyAVR-and-megaAVR-00002634C.pdf)
It works as intended, except randomly the device just won't boot up...it becomes a brick - it doesn't boot into the bootloader or the app. We need to reprogram it over the UPDI interface again to get it working again.
We're using a FT232 USB-UART to interface with the ATMEGA.
This issue happens without any particular pattern, but always when the device USB cable has been rebooted
I've read back the program memory after this issue happens, and there is a section of bytes that gets overwritten from address 0x00E0 to 0x00FF - like if a page was erased and written. But how can that happen without me issuing a write command?
Here is the code:
#define F_CPU_RESET (20E6/6)
#include <avr/io.h>
#include <assert.h>
#include <stdbool.h>
/* Baud rate configuration */
#define BOOT_BAUD (115200)
/* Memory configuration
- BOOTEND_FUSE * 256 must be above Bootloader Program Memory Usage,
- this is 194 bytes at optimization level -O3, so BOOTEND_FUSE = 0x01
*/
#define BOOTEND_FUSE (0x02)
#define BOOT_SIZE (BOOTEND_FUSE * 0x100)
#define MAPPED_APPLICATION_START (MAPPED_PROGMEM_START + BOOT_SIZE)
#define MAPPED_APPLICATION_SIZE (MAPPED_PROGMEM_SIZE - BOOT_SIZE)
/* Fuse configuration
- BOOTEND sets the size (end) of the boot section in blocks of 256 bytes.
- APPEND = 0x00 defines the section from BOOTEND*256 to end of Flash as application code.
- Remaining fuses have default configuration.
*/
FUSES = {
.OSCCFG = FREQSEL_20MHZ_gc,
.SYSCFG0 = 0xC8,
.SYSCFG1 = SUT_0MS_gc,
.APPEND = 0x00,
.BOOTEND = BOOTEND_FUSE
};
/* Define application pointer type */
typedef void (*const app_t)(void);
//uint8_t EEMEM user_byte = 0;
/* Interface function prototypes */
static bool is_bootloader_requested(void);
static void init_uart(void);
static uint8_t uart_receive(void);
static void uart_send(uint8_t byte);
static void init_status_led(void);
static void toggle_status_led(void);
static void initPins(void);
static inline void wait_50_ms(void);
/*
-
Main boot function
-
Put in the constructors section (.ctors) to save Flash.
-
Naked attribute used since function prologue and epilogue is unused
/
attribute((naked)) attribute((section(".ctors"))) void boot(void)
{
/ Initialize system for AVR GCC support, expects r1 = 0 */
asm volatile("clr r1");initPins();
//this is a lot more than 50ms, but that's ok
wait_50_ms();/* Check if entering application or continuing to bootloader /
if(!is_bootloader_requested()) {
/ Enable Boot Section Lock */
NVMCTRL.CTRLB = NVMCTRL_BOOTLOCK_bm;/* Go to application, located immediately after boot section */ app_t app = (app_t)(BOOT_SIZE / sizeof(app_t)); app();
}
/* Initialize communication interface and LED */
init_uart();
init_status_led();
VPORTB.OUT |= PIN5_bm;/*
-
Start programming at start for application section
-
Subtract MAPPED_PROGMEM_START in condition to handle overflow on large flash sizes
*/
uint8_t *app_ptr = (uint8_t *)MAPPED_APPLICATION_START;
while(app_ptr - MAPPED_PROGMEM_START <= (uint8_t )PROGMEM_END) {
/ Receive and echo data before loading to memory */
uint8_t rx_data = uart_receive();
uart_send(rx_data);/* Incremental load to page buffer before writing to Flash */
app_ptr = rx_data;
app_ptr++;
if(!((uint16_t)app_ptr % MAPPED_PROGMEM_PAGE_SIZE)) {
/ Page boundary reached, Commit page to Flash */
_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
while(NVMCTRL.STATUS & NVMCTRL_FBUSY_bm);toggle_status_led();
}
}
//change USERROW back to 0xff, so we go into app after upcoming reboot
USERROW.USERROW31 = 0xff;
_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
while(NVMCTRL.STATUS & NVMCTRL_EEBUSY_bm);//turn off led, just in case it's still on after update
VPORTB.OUT &= ~PIN5_bm; -
// /* Issue system reset */
_PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRE_bm);
}
/*
- Boot access request function
*/
static bool is_bootloader_requested(void)
{
if(USERROW.USERROW31 == 0x55) {
return true;
}
return false;
}
/*
-
Communication interface functions
/
static void init_uart(void)
{
/ Configure UART */
USART3.CTRLB = USART_RXEN_bm | USART_TXEN_bm;/* From datasheet:
- Baud rate compensated with factory stored frequency error
- Asynchronous communication without Auto-baud (Sync Field)
- 20MHz Clock, 3V
*/
int32_t baud_reg_val = (F_CPU_RESET * 64) / (BOOT_BAUD * 16); // ideal BAUD register value
assert(baud_reg_val >= 0x4A); // Verify legal min BAUD register value with max neg comp
int8_t sigrow_val = SIGROW.OSC20ERR5V; // read signed error
baud_reg_val *= (1024 + sigrow_val); // sum resolution + error
baud_reg_val += 512; // compensate for rounding error
baud_reg_val /= 1024; // divide by resolution
USART3.BAUD = (int16_t) baud_reg_val; // set adjusted baud rate
/* Set TxD (PB2) as output */
VPORTB.DIR |= PIN0_bm;
}
static uint8_t uart_receive(void)
{
/* Poll for data received */
while(!(USART3.STATUS & USART_RXCIF_bm));
return USART3.RXDATAL;
}
static void uart_send(uint8_t byte)
{
/* Data will be sent when TXDATA is written */
USART3.TXDATAL = byte;
}
static void init_status_led(void)
{
/* Set LED0 (PB4) as output */
VPORTB.DIR |= PIN5_bm;
}
static void toggle_status_led(void)
{
/* Toggle LED0 (PB4) */
VPORTB.OUT ^= PIN5_bm;
}
static inline void wait_50_ms(void)
{
for(uint32_t count = 0; count < 500000; count++){
asm volatile ("nop");
}
}
static void initPins(void){
VPORTA.DIR = 0x00;
VPORTB.DIR = 0x20;
VPORTC.DIR = 0x00;
VPORTD.DIR = 0x00;
VPORTE.DIR = 0x00;
VPORTF.DIR = 0x00;
}
I've tried adding delays to the bootloader start up, making all pins (except LED) inputs immediately on bootup, and changing fuse values. None helped. I've also tried moving my NVMCTRL_BOOTLOCK_bm call to right at the beginning, but then the bootloader never gets to the app jump...
I've tried my device with a 3rd party bootloader (optiboot) to see if it's something in my hardware, but that worked fine - so it must be my bootloader code.
Any help would be greatly appreciated.
Thanks!
</details>
# 答案1
**得分**: 0
看起来这是由于没有设置BOD保险丝引起的数据损坏。因此,当它上电时,没有什么可以阻止控制器在电压足够高之前尝试运行。
我已经在上面的原始代码中添加了以下内容,以便对BOD进行采样,并且只有在我们处于安全工作电压时才启动控制器:
.BODCFG = 0xFE
我在Stack Exchange上找到了一个类似的问题,提醒我关于BOD:[链接][1]
在适当添加BOD保险丝后,问题再次没有发生。
[1]: https://electronics.stackexchange.com/questions/116607/avr-flash-memory-corruption
<details>
<summary>英文:</summary>
It appears to have been corruption due to not setting the BOD fuses. So when it powered up, there was nothing stopping the controller from trying to run before the voltage was high enough.
I've added the following to my original code above, so that BOD is sampled and the controller only boots up once we're in a safe operating voltage:
.BODCFG = 0xFE
I had found a similar question at Stack Exchange that tipped me off about BOD: [Link][1]
After adding the BOD fuses appropriately, the issue hasn't occurred again.
[1]: https://electronics.stackexchange.com/questions/116607/avr-flash-memory-corruption
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论