Обсуждаем контроллеры компании Atmel.
Ответить

Загрузчик для ATmega32 с прошивкой по I2C

Пн ноя 06, 2023 22:31:10

Всем привет!

Пытаюсь написать загрузчик для ATmega32. Мне нужна прошивка по I2C, так как ATmega32 - ведомый контроллер.
Пишу из Atmel Studio 7. Выставил фьюза High 0xD8, Low 0xBF, загрузчик размером до 4 КБ. Так же выведен UART для отладочного вывода.
Проблема в том, что нет вывода, который показывает, что ATmega32 перешла в режим загрузчика. И есть подозрение, что загрузчик не работает.

Что может быть не так?

Код:
Спойлер
Код:
/*
 * atmega32_slave_i2c_bootloader.c
 *
 * Created: 31.10.2023 21:11:47
 * Author : dmitr
 */

#include <avr/io.h>
#include <avr/boot.h>
#include "TWI_slave.h"
#include "systick.h"
#include "bootloader_config.h"
#include <avr/interrupt.h>
#include <compat/twi.h>
#include <stdio.h>
#include <inttypes.h>
#include <util/delay.h>
#include <string.h>
#include <avr/pgmspace.h>


#define LEN 32
#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

#define BOOTLOADER_GET_VERSION                              0x01
#define BOOTLOADER_IS_BOOTLOADER_MODE                        0x02
#define BOOTLOADER_START_FLASH                              0x10
#define BOOTLOADER_SEND_FLASH_CHUNK                           0x11
#define BOOTLOADER_GET_LAST_TRANSITION_STATUS                  0x12
#define BOOTLOADER_END_FLASH                              0x13
#define BOOTLOADER_BOOT_TO_PROGRAM                           0x20

/* device compatibility: */
#ifndef GICR    /* ATMega*8 don&#39;t have GICR, use MCUCR instead */
#   define GICR     MCUCR
#endif

/* ---------- */


unsigned char regaddr; // Store the Requested Register Address
unsigned char regdata; // Store the Register Address Data

char buffer[LEN];

register unsigned char IT asm("r16");
volatile unsigned char done;
volatile unsigned char IDX;

static void jump_to_main_program() __attribute__((__noreturn__));

void USART_Init( unsigned int ubrr)
{
   /*Set baud rate */
   UBRRH = (unsigned char)(ubrr>>8);
   UBRRL = (unsigned char)ubrr;
   //Enable receiver and transmitter */
   UCSRB = (1<<RXEN)|(1<<TXEN);
   /* Set frame format: 8data, 1stop bit */
   UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

void USART_Deinit() {
   UCSRB = (0<<RXEN)|(0<<TXEN);
}

static int uart_putchar(char c, FILE *stream)
{
   if (c == &#39;\n&#39;)
   uart_putchar(&#39;\r&#39;, stream);
   loop_until_bit_is_set(UCSRA, UDRE);
   UDR = c;
   return 0;
}

inline void clearStr(char* str)
{
   for(IT=0;IT<LEN;IT++)
   str[IT]=0;
}

ISR(USART_RXC_vect)
{
   char bf= UDR;
   buffer[IDX]=bf;
   IDX++;

   if (bf == &#39;:&#39; || IDX >= LEN)
   {
      IDX=0;
      done=1;
   }

}

void int_out_send_interrupt() {
   BOOTLOADER_CONFIG_INT_OUT_PORT |= (1<<BOOTLOADER_CONFIG_INT_OUT_PIN);
   _delay_us(100);
   BOOTLOADER_CONFIG_INT_OUT_PORT &= ~(1<<BOOTLOADER_CONFIG_INT_OUT_PIN);
   _delay_us(100);
}

void jump_to_main_program() {
   cli();
   int_out_send_interrupt();
   USART_Deinit();
   sysTimerDeinit();
   GICR |= (1<<IVCE);
   GICR &= ~(1<<IVSEL);
   asm("jmp 0x0000");
}

void TWI_Act_On_Failure_In_Last_Transmission ();

void IO_Init() {
   BOOTLOADER_CONFIG_INT_OUT_DDR |= (1<<BOOTLOADER_CONFIG_INT_OUT_PIN);
   BOOTLOADER_CONFIG_INT_OUT_PORT &= ~(1<<BOOTLOADER_CONFIG_INT_OUT_PIN);
   
   //TWI Pull UP
   PORTC |= ((1<<PINC0) | (1<<PINC1));
}

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)
{
   uint8_t temp=GICR;
   GICR|=(1<<IVCE);
   GICR|=(1<<IVSEL);
   
   IO_Init();
   sysTimerInit();
   USART_Init(MYUBRR);
   TWI_Slave_Initialise(BOOTLOADER_CONFIG_I2C_SLAVE_ADDRESS);
   unsigned char messageBuf[TWI_BUFFER_SIZE];
   unsigned char flash_page_buf[SPM_PAGESIZE];
   
   uint32_t bootloader_react_millis = millis();
   uint8_t bootloader_countdown = 1;
   uint8_t quit_from_bootloader = 0;
   uint8_t flash_allowed = 0;
   uint8_t flash_last_transition_status_ok = 0;
   uint16_t flash_page_start_address = 0;
   uint16_t page_received_cs = 0;
   uint16_t page_received_cs_calc = 0;
   
   int_out_send_interrupt();
   sei();
   /* Replace with your application code */
   printf("Send any I2C command within 1 sec to get into bootloader mode\n");
    while (1)
    {
      // Check if the TWI Transceiver has completed an operation.
      if ( ! TWI_Transceiver_Busy() )
      {
         // Check if the last operation was successful
         if ( TWI_statusReg.lastTransOK )
         {
            // Check if the last operation was a reception
            if ( TWI_statusReg.RxDataInBuf )
            {
               // byte 0 - cmd
               // byte 1 - data
               TWI_Get_Data_From_Transceiver(messageBuf, TWI_BUFFER_SIZE);
               // Check if the last operation was a reception as General Call
               if ( TWI_statusReg.genAddressCall )
               {
                  // Not needed to react on broadcast address
                  // Put data received out to PORTB as an example.
                  //PORTB = messageBuf[0];
               }
               else // Ends up here if the last operation was a reception as Slave Address Match
               {
                  // Example of how to interpret a command and respond.
                  regaddr = messageBuf[0];
                  bootloader_countdown = 0;
                  printf("Bootloader mode\n");
                  switch(regaddr) {
                     case BOOTLOADER_GET_VERSION:
                        printf("Firmware version is %s \n", BOOTLOADER_CONFIG_FIRMWARE_VERSION_STRING);
                        TWI_Start_Transceiver_With_Data(BOOTLOADER_CONFIG_FIRMWARE_VERSION_STRING, sizeof(BOOTLOADER_CONFIG_FIRMWARE_VERSION_STRING));
                        break;
                     case BOOTLOADER_IS_BOOTLOADER_MODE:
                        memset(messageBuf, 0, sizeof(messageBuf));
                        messageBuf[0] = BOOTLOADER_IS_BOOTLOADER_MODE;
                        messageBuf[1] = 1;
                        TWI_Start_Transceiver_With_Data(messageBuf, 2);
                     case BOOTLOADER_START_FLASH:
                        flash_allowed = 1;
                        flash_page_start_address = 0;
                        break;
                     case BOOTLOADER_SEND_FLASH_CHUNK:
                        if (!flash_allowed) break;
                        flash_last_transition_status_ok = 0;
                        page_received_cs = 0;
                        for(int i = 0; i < TWI_BUFFER_SIZE-2; i++) page_received_cs += messageBuf[i];
                        page_received_cs_calc = (uint16_t)(messageBuf[TWI_BUFFER_SIZE-2]<<8) | (uint16_t)(messageBuf[TWI_BUFFER_SIZE-1]);;
                        if (page_received_cs_calc - page_received_cs == 0) {
                           flash_page_start_address = (uint16_t)(messageBuf[1]<<8) | (uint16_t)(messageBuf[2]);
                           for (int i = 0; i < SPM_PAGESIZE; i++) flash_page_buf[i] = messageBuf[i+3];
                           boot_program_page((uint32_t)flash_page_start_address, flash_page_buf);
                           flash_last_transition_status_ok = 1;
                        }
                        int_out_send_interrupt();
                        break;
                     case BOOTLOADER_END_FLASH:
                        flash_page_start_address = 0;
                        flash_allowed = 0;
                        break;
                     case BOOTLOADER_BOOT_TO_PROGRAM:
                        quit_from_bootloader = 1;
                        break;
                     default:
                        break;
                  }
               }
            }
            else // Ends up here if the last operation was a transmission
            {
               asm("nop");   // Put own code here.
            }
            // Check if the TWI Transceiver has already been started.
            // If not then restart it to prepare it for new receptions.
            if ( ! TWI_Transceiver_Busy() )
            {
               TWI_Start_Transceiver();
            }
         }
         else // Ends up here if the last operation completed unsuccessfully
         {
            TWI_Act_On_Failure_In_Last_Transmission();
         }
      }
      
      if (quit_from_bootloader) break;
      
      if (bootloader_countdown && (millis()-bootloader_react_millis>1000))
         break;
    }
   jump_to_main_program();
}

void TWI_Act_On_Failure_In_Last_Transmission ()
{
   //PORTB = TWIerrorMsg;
   TWI_Get_State_Info();
   TWI_Start_Transceiver();
   
   //return TWIerrorMsg;
}


При этом, когда прошиваю обычную программу, то она исполняется нормально при тех же фьюзах

Добавлено after 2 hours 37 minutes 36 seconds:
Забыл уточнить

В Atmel Studio прописал .text=0x3800
Добавил F_CPU=8000000, но вывода UART все нет

Но вижу, что пин BOOTLOADER_CONFIG_INT_OUT_PIN дергается, значит, Atmega32 находится в режиме загрузчика
Попробую повзаимодействовать через I2C и прошить

Добавлено after 19 minutes:
Забыл добавить
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
и
stdout = &mystdout; в main

Теперь в терминал выводится Se и все, а пин BOOTLOADER_CONFIG_INT_OUT_PIN дергается

Добавлено after 2 hours 45 minutes 54 seconds:
Когда прописал
GICR=0x02;
тогда логика загрузчика заработала правильно
Видимо, переключение таблицы прерываний происходит в пределах 4 тактов

Добавлено after 3 hours 53 minutes 7 seconds:
Так, логика вроде работает, но в какой-то момент программа загрузчика зависает
В основном, это происходит при взаимодействии по I2C
Плюс, когда прошиваю ведомый контроллер с помощью загрузчика, то при извлечении дампа флешки в начале идут FF, как будто не прошивался контроллер

Re: Загрузчик для ATmega32 с прошивкой по I2C

Вс ноя 12, 2023 01:59:25

Так, удалось разобраться: неправильно подавал команды на прошивку очередной страницы
После правильной конвертации hex в bin программа теперь запускается корректно после прошивки через I2C
Проблема со случайным зависанием загрузчика пока остается
Чаще всего, зависает в процессе прошивки, когда уже отправил страницу, но может зависнуть, если несколько раз отправить одну и ту же команду

Добавлено after 42 seconds:
Конвертировал основную программу, добавив в Post-build event command
avr-objcopy.exe -I ihex -O binary ATmega32_slave.hex ATmega32_slave.bin
Ответить