Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Bootloader Atmega128a
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
svatoslav
Добрый день! Разбираю тему написания bootloadera для мк. в Atmel Studio 7 есть библиотека boot.h для работы с флэш и EEPROM. Использую от туда пример от Atmel для записи данных во флэш
CODE
void boot_program_page (uint32_t page, uint8_t *buf)
{
uint16_t i;
uint8_t sreg;

// Disable interrupts.

sreg = SREG;
cli();

eeprom_busy_wait ();

boot_page_erase (page);
boot_spm_busy_wait (); // Wait until the memory is erased.

for (i=0; i<SPM_PAGESIZE; i+=2)
{
// Set up little-endian word.

uint16_t w = *buf++;
w += (*buf++) << 8;

boot_page_fill (page + i, w);
}

boot_page_write (page); // Store buffer in flash page.
boot_spm_busy_wait(); // Wait until the memory is written.

// Reenable RWW-section again. We need this if we want to jump back
// to the application after bootloading.

boot_rww_enable ();

// Re-enable interrupts (if they were ever enabled).

SREG = sreg;
}

но данные не пишутся! Чтение проходит отлично, что из флэш, что из EEPROM, а запись не работает. Пишу для Atmega128a выставил фьюзы как на скриншоте
Нажмите для просмотра прикрепленного файла (т.е. загрузочная область у меня 512 слов и вектор сброса перенесен в область старта bootloadera) генерирую .hex (.elf) прошиваю через jtag (программатор ATmel ICE) код стартует, но данные не пишутся, пробовал под отладкой пройтись, в функцию все передается верно. Ниже приведен полный пример для записи (не судите строго, код только для проверки записи). Если кратко заполняю массив на 256 элементов значениями 0x5F, и пишу эти данные по адресу скажем addr = 0x0AB3, все делаю в режиме отладки после того как данные записаны останавливаю программу (дабы не пошло все на второй цикл) считываю флэш через программатор и открываю в hex-редакторе и вижу, что ничего не записалось из этого.
CODE

#ifndef F_CPU
#define F_CPU 8000000UL // рабочая частота МК (8МГц)
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/boot.h>
#include <avr/pgmspace.h>

#include "main.h"

/
static uint8_t buf[SPM_PAGESIZE];
static uint16_t addr;

void boot_program_page (uint32_t page, uint8_t *buf)
{
uint16_t i;
uint8_t sreg;

// Disable interrupts.

sreg = SREG;
cli();

eeprom_busy_wait ();

boot_page_erase (page);
boot_spm_busy_wait (); // Wait until the memory is erased.

for (i=0; i<SPM_PAGESIZE; i+=2)
{
// Set up little-endian word.

uint16_t w = *buf++;
w += (*buf++) << 8;

boot_page_fill (page + i, w);
}

boot_page_write (page); // Store buffer in flash page.
boot_spm_busy_wait(); // Wait until the memory is written.

// Reenable RWW-section again. We need this if we want to jump back
// to the application after bootloading.

boot_rww_enable ();

// Re-enable interrupts (if they were ever enabled).

SREG = sreg;
}

int main(void)
{
sei();
addr = 0x0AB3;
for(int i=0;i<256;i++){
buf[i] = 0x5F;
}
boot_program_page(addr, buf);
}

Есть подозрение, что прошивка не записывается в раздел bootloadera вообще потому как если открыть считанный *.hex после того как прошил проект он расположен в начале памяти мк, а после вплоть до самого конца пусто (см. спойлер привел начало hex перед тем как записать прошивку полностью вытер flash)
Нажмите для просмотра прикрепленного файла
это конец файла .hex считанного с флэш МК
Нажмите для просмотра прикрепленного файла
на скриншоте видно, что с адреса FE00 ничего нет, а должно быть так как загрузчик установлен с этого адреса. Так вот есть два вопроса:
1. Как сгенерировать прошивку так, что бы она писалась именно в нужную область флэш памяти (при том, что фьюзы выставлены согласно скриншоту выше)
2. В чем может быть проблема с записью во флэш в приведенном примере
dimka76
Надо не только FUSE выставить, но и указать линкеру куда размещать код программы загрузчика. Это делается в настройках проекта.
В настройки линкера надо добавить следующий ключик
Код
LDFLAGS += -Wl,-section-start=.text=0x1800


Где 0x1800 заменить на адрес начала вашего загрузчика.
Только там этот адрес то ли делить на 2, то ли умножать на 2 надо.
svatoslav
Цитата(dimka76 @ Jun 4 2018, 19:40) *
Только там этот адрес то ли делить на 2, то ли умножать на 2 надо.

Нужно умножить на 2. Спасибо за совет действительно так генерируется в нужную область памяти. Проверить запись во флэш смогу завтра, надеюсь дело в этом (предполагаю, что так как прошивка находится не в разделе BOOT поэтому он и не может писать данные, по результатам отпишу, может кому пригодится информация)
svatoslav
Добрый день. В продолжении темы о Bootloader. Не получается перенести векторы прерывания в область Bootloader по документации на atmega128a написано, что нужно сделать так:
Код
/* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVSEL);

но прерывания все равно не срабатывают. При этом в регистре MCUCR я не вижу, что нужные биты выставлены
Нажмите для просмотра прикрепленного файла.
CODE
int main(void)
{
/*************************************************************
* Инициализация МК *
**************************************************************/
//Перемещаем все векторы прерываний в раздел с Bootloader-ом
/* Enable change of Interrupt Vectors */
MCUCR = (1<<IVCE);
/* Move interrupts to boot Flash section */
MCUCR = (1<<IVSEL);

#if DEBUG_EN
DDRA= (1<<DDA0);
PORTA= (1<<PORTA0);
#endif
//Настроим таймер 1 с частотой F_CPU/1024 и прерыванием по переполнению OVF interrupt
//TCCR0 = (1<<CS02) | (1<<CS01) | (1<<CS00);
//TIMSK = (1<<TOIE0);
TWIinitSlave();
//Разрешим глобальные прерывания
sei();
while (1)
{
#if DEBUG_EN
PORTA= (1<<PORTA0);
_delay_ms(100);
PORTA= (0<<PORTA0);
_delay_ms(100);
#endif
//jump_to_app();
//Разместим таймер
}

}
ISR(TWI_vect) {
static uint8_t tw_state_machine;
uint8_t tw_status_reg;
cli();//выключаем все прерывания
tw_status_reg=TW_STATUS;//считываем статус TWI(I2C) в сети

//строим конечный автомат
switch(tw_status_reg) {
case TW_SR_SLA_ACK: // если на шине выставлен наш адрес устройства TWI_SLAVE_ADDR (0x60: SLA+W received, ACK returned)
tw_state_machine = SM_TW_ADDR_WR;
break;

case TW_SR_DATA_ACK: //начинаем принимать данные в режиме "ЗАПИСЬ" от мастера (0x80: data received, ACK returned)
if (tw_state_machine == SM_TW_ADDR_WR)
{
cmd = TWDR; // сохраняем первый бит в регистре комманд
tw_state_machine = SM_TW_DATA_WR;// переводим следующие биты в регистр данных
}
else
{
twi_buf[twi_buf_index] = TWDR; // сохраняем байт в регистре данных
twi_buf_index++;//готовим следующий элемент массива примного буфера
tw_state_machine = SM_TW_WAIT_STOP;//если есть еще данные ждем и пишем, если нет то заканчиваем транзакцию(если в tw_status_reg = TW_SR_STOP(0xA0))
}
break;

case TW_SR_STOP: // если пришел сигнал СТОП заканчиваем транзакцию(0xA0: stop or repeated start condition received while selected)
if (tw_state_machine == SM_TW_WAIT_STOP)
{
TW_slave_action(1); // вызываем функцию которая будет обрабатывать принятые данные с параметром "ЗАПИСЬ"
tw_state_machine = SM_TW_RESET; // сбросим статус конечного автомата
twi_buf_index = 0;
}
break;

case TW_ST_SLA_ACK: //если пришел запрос на чтение подготовим ответ (0xA8: data transmitted, ACK received)
if (tw_state_machine == SM_TW_DATA_WR)//проверяем принят ли адрес желаемого регистра от мастера
{
TW_slave_action(0); // вызываем функцию которая будет обрабатывать запрос на чтение, заполним все необходимое (rw_status = 0)
TWDR = twi_buf[twi_buf_index]; // положим первый байт для последующей отправки
twi_buf_index++;//на случай если попросят еще байт
tw_state_machine = SM_TW_DATA_OK_TO_SEND;
}
break;
case TW_ST_DATA_ACK: /* если мастер просит передать байт и говорит, что попросит еще (0xB8: data transmitted, ACK received)
передадим байт который подготовили на этапе TW_ST_SLA_ACK*/
if (tw_state_machine == SM_TW_DATA_OK_TO_SEND)//проверим готовы ли данные для передачи, которые должны были сформироваться в TW_ST_SLA_ACK
{
TWDR = twi_buf[twi_buf_index];// положим второй байт для последующей отправки
twi_buf_index++;//на случай если попросят еще байт

}
else//если данные не готовы отправим назад команду, которую запрашивал мастер
{
TWDR = !cmd;
}

break;

case TW_ST_DATA_NACK: /*если мастер просит передать ему последний байт или единственный (0xC0: data transmitted, NACK received)
передаем байт подготовленный на этапе TW_ST_SLA_ACK или TW_ST_DATA_ACK (если просят более 1-го байта)*/
if (tw_state_machine == SM_TW_DATA_OK_TO_SEND)//проверим готовы ли данные для передачи, которые должны были сформироваться в TW_ST_SLA_ACK
{
twi_buf_index = 0;//так как попросили последний байт, готовим массив для следующего цикла
tw_state_machine = SM_TW_RESET;//сбросим конечный автомат
}
else
{
TWDR = !cmd;
}
break;
//case TW_ST_LAST_DATA: // 0xC8: last data byte transmitted, ACK received
//case TW_BUS_ERROR: // 0x00: illegal start or stop condition
default:
tw_state_machine = SM_TW_RESET; // вернем в начальное положение конечный автомат
twi_buf_index = 0;
break;
}
// Очистим TWINT Flag
TWCR |= (1<<TWINT);//настроим статусный регистр
// Разрешим все прерывания
sei();
}

Провел опыт. Загрузил эту программу не в бут, а в раздел пользовательских приложений с нулевого адреса флэш. И прерывание срабатывает т.е.
/* Enable change of Interrupt Vectors */
MCUCR = (1<<IVCE);
/* Move interrupts to boot Flash section */
MCUCR = (1<<IVSEL);
почему то не переносит вектор прерывания в раздел загрузчика фьюз BOOTRST устанавливаю, когда прошиваю в раздел загрузчика
Сергей Борщ
А если так:
CODE
    /* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVCE)|(1<<IVSEL);
svatoslav
Цитата(svatoslav @ Jun 8 2018, 08:25) *
но прерывания все равно не срабатывают

Решил проблему, оказалось необходимо выставить флаг оптимизации -0s иначе не работает! Думаю связано с тем что с другими флагами не успевает записаться за 4 цикла как написано в документации

Цитата(Сергей Борщ @ Jun 8 2018, 08:32) *
А если так:
Код
    /* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVCE)|(1<<IVSEL);

так все же тоже не получается, возможно нужно сделать ASM вставкой в си код, если кто нибудь подскажет как правильно это сделать в Atmel Studio попробую, может кому пригодится такое решение. вот код из документации на ASM:

CODE
Move_interrupts:
; Enable change of Interrupt Vectors
ldi r16, (1<<IVCE)
out MCUCR, r16
; Move interrupts to boot Flash section
ldi r16, (1<<IVSEL)
out MCUCR, r16
ret
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.