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

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

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


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

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...