Перейти к содержанию

    

Bootloader Atmega128a

Добрый день! Разбираю тему написания bootloadera для мк. в Atmel Studio 7 есть библиотека boot.h для работы с флэш и EEPROM. Использую от туда пример от Atmel для записи данных во флэш

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 выставил фьюзы как на скриншоте

post-97796-1528122144_thumb.jpg

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

#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)

post-97796-1528123400_thumb.jpg

это конец файла .hex считанного с флэш МК

post-97796-1528123833_thumb.jpg

на скриншоте видно, что с адреса FE00 ничего нет, а должно быть так как загрузчик установлен с этого адреса. Так вот есть два вопроса:

1. Как сгенерировать прошивку так, что бы она писалась именно в нужную область флэш памяти (при том, что фьюзы выставлены согласно скриншоту выше)

2. В чем может быть проблема с записью во флэш в приведенном примере

Изменено пользователем svatoslav

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Надо не только FUSE выставить, но и указать линкеру куда размещать код программы загрузчика. Это делается в настройках проекта.

В настройки линкера надо добавить следующий ключик

LDFLAGS += -Wl,-section-start=.text=0x1800

 

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

Только там этот адрес то ли делить на 2, то ли умножать на 2 надо.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Только там этот адрес то ли делить на 2, то ли умножать на 2 надо.

Нужно умножить на 2. Спасибо за совет действительно так генерируется в нужную область памяти. Проверить запись во флэш смогу завтра, надеюсь дело в этом (предполагаю, что так как прошивка находится не в разделе BOOT поэтому он и не может писать данные, по результатам отпишу, может кому пригодится информация)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Добрый день. В продолжении темы о Bootloader. Не получается перенести векторы прерывания в область Bootloader по документации на atmega128a написано, что нужно сделать так:

/* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVSEL);

но прерывания все равно не срабатывают. При этом в регистре MCUCR я не вижу, что нужные биты выставлены

post-97796-1528441713_thumb.jpg

.

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 устанавливаю, когда прошиваю в раздел загрузчика

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если так:

    /* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVCE)|(1<<IVSEL);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
но прерывания все равно не срабатывают

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

 

А если так:

    /* Enable change of Interrupt Vectors */
    MCUCR = (1<<IVCE);
    /* Move interrupts to boot Flash section */
    MCUCR = (1<<IVCE)|(1<<IVSEL);

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация