реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Bootloader Atmega128a, правильная генерация hex
svatoslav
сообщение Jun 4 2018, 14:55
Сообщение #1





Группа: Участник
Сообщений: 12
Регистрация: 22-06-17
Пользователь №: 97 796



Добрый день! Разбираю тему написания 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. В чем может быть проблема с записью во флэш в приведенном примере

Сообщение отредактировал svatoslav - Jun 4 2018, 14:56
Go to the top of the page
 
+Quote Post
dimka76
сообщение Jun 4 2018, 18:40
Сообщение #2


developer
****

Группа: Свой
Сообщений: 886
Регистрация: 12-04-06
Из: Казань
Пользователь №: 16 032



Надо не только FUSE выставить, но и указать линкеру куда размещать код программы загрузчика. Это делается в настройках проекта.
В настройки линкера надо добавить следующий ключик
Код
LDFLAGS += -Wl,-section-start=.text=0x1800


Где 0x1800 заменить на адрес начала вашего загрузчика.
Только там этот адрес то ли делить на 2, то ли умножать на 2 надо.


--------------------
Все может быть и быть все может, и лишь того не может быть-чего уж точно быть не может, хотя..и это может быть.
Go to the top of the page
 
+Quote Post
svatoslav
сообщение Jun 5 2018, 06:01
Сообщение #3





Группа: Участник
Сообщений: 12
Регистрация: 22-06-17
Пользователь №: 97 796



Цитата(dimka76 @ Jun 4 2018, 19:40) *
Только там этот адрес то ли делить на 2, то ли умножать на 2 надо.

Нужно умножить на 2. Спасибо за совет действительно так генерируется в нужную область памяти. Проверить запись во флэш смогу завтра, надеюсь дело в этом (предполагаю, что так как прошивка находится не в разделе BOOT поэтому он и не может писать данные, по результатам отпишу, может кому пригодится информация)
Go to the top of the page
 
+Quote Post
svatoslav
сообщение Jun 8 2018, 07:25
Сообщение #4





Группа: Участник
Сообщений: 12
Регистрация: 22-06-17
Пользователь №: 97 796



Добрый день. В продолжении темы о 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 устанавливаю, когда прошиваю в раздел загрузчика
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jun 8 2018, 07:32
Сообщение #5


Гуру
******

Группа: Модераторы
Сообщений: 8 313
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



А если так:
CODE
    /* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVCE)|(1<<IVSEL);


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
svatoslav
сообщение Jun 8 2018, 07:53
Сообщение #6





Группа: Участник
Сообщений: 12
Регистрация: 22-06-17
Пользователь №: 97 796



Цитата(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
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th June 2018 - 15:13
Рейтинг@Mail.ru


Страница сгенерированна за 0.01308 секунд с 7
ELECTRONIX ©2004-2016