svatoslav_p 0 4 июня, 2018 Опубликовано 4 июня, 2018 (изменено) · Жалоба Добрый день! Разбираю тему написания 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 выставил фьюзы как на скриншоте (т.е. загрузочная область у меня 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) это конец файла .hex считанного с флэш МК на скриншоте видно, что с адреса FE00 ничего нет, а должно быть так как загрузчик установлен с этого адреса. Так вот есть два вопроса: 1. Как сгенерировать прошивку так, что бы она писалась именно в нужную область флэш памяти (при том, что фьюзы выставлены согласно скриншоту выше) 2. В чем может быть проблема с записью во флэш в приведенном примере Изменено 4 июня, 2018 пользователем svatoslav Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 43 4 июня, 2018 Опубликовано 4 июня, 2018 · Жалоба Надо не только FUSE выставить, но и указать линкеру куда размещать код программы загрузчика. Это делается в настройках проекта. В настройки линкера надо добавить следующий ключик LDFLAGS += -Wl,-section-start=.text=0x1800 Где 0x1800 заменить на адрес начала вашего загрузчика. Только там этот адрес то ли делить на 2, то ли умножать на 2 надо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
svatoslav_p 0 5 июня, 2018 Опубликовано 5 июня, 2018 · Жалоба Только там этот адрес то ли делить на 2, то ли умножать на 2 надо. Нужно умножить на 2. Спасибо за совет действительно так генерируется в нужную область памяти. Проверить запись во флэш смогу завтра, надеюсь дело в этом (предполагаю, что так как прошивка находится не в разделе BOOT поэтому он и не может писать данные, по результатам отпишу, может кому пригодится информация) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
svatoslav_p 0 8 июня, 2018 Опубликовано 8 июня, 2018 · Жалоба Добрый день. В продолжении темы о Bootloader. Не получается перенести векторы прерывания в область Bootloader по документации на atmega128a написано, что нужно сделать так: /* Enable change of Interrupt Vectors */ MCUCR = (1<<IVCE); /* Move interrupts to boot Flash section */ MCUCR = (1<<IVSEL); но прерывания все равно не срабатывают. При этом в регистре MCUCR я не вижу, что нужные биты выставлены . 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 устанавливаю, когда прошиваю в раздел загрузчика Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 123 8 июня, 2018 Опубликовано 8 июня, 2018 · Жалоба А если так: /* Enable change of Interrupt Vectors */ MCUCR = (1<<IVCE); /* Move interrupts to boot Flash section */ MCUCR = (1<<IVCE)|(1<<IVSEL); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
svatoslav_p 0 8 июня, 2018 Опубликовано 8 июня, 2018 · Жалоба но прерывания все равно не срабатывают Решил проблему, оказалось необходимо выставить флаг оптимизации -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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться