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

Обсуждаем контроллеры компании Atmel.
Ответить
Открыл глаза
Сообщения: 43
Зарегистрирован: Ср дек 25, 2019 23:41:33

Сообщение dmitrij999 »

Всем привет!

Пытаюсь написать загрузчик для 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'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 == '\n')
	uart_putchar('\r', 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 == ':' || 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, как будто не прошивался контроллер
Реклама
Открыл глаза
Сообщения: 43
Зарегистрирован: Ср дек 25, 2019 23:41:33

Сообщение dmitrij999 »

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

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

Вернуться в «AVR»