STM32F4. Обновление прошивки с карты памяти (Bootloader SD)

Микроконтроллер STM32 имеет довольно много способов обновления прошивки, лично я использую SWD, иногда UART. Тем не менее, для прошивки этими способами нужно как минимум программатор или TTL переходник, плюс нужно устанавливать специальный софт и т.д., что составляет трудности людям, не обладающим необходимыми техническими знаниями. Собственно, с моем случае нужно было максимально упростить процесс обновления прошивки. Устройство на базе STM32F407VG и имеет карту памяти, подключенную к SDIO, обновление прошивки решил сделать через карту памяти.

Для этого нужно создать бутлоадер. Алгоритм следующий:

  1. При включении бутлоадер проверяет наличие на карте памяти файла с определенным названием, пусть называется «FLASH». Файл обязательно должен быть бинарным, без всякой служебной информации. Обычно у него разрешение *.bin, а hex и elf не подходят, но есть программы, которые могут конвертировать эти форматы в bin.
  2. Если найдено, считываем и записываем в постоянную память микроконтроллера (флеш-память).
  3. Переходим к основному приложению.
  4. В основном приложении проверяем наличие этого файла, если он существует, удаляем, чтобы после перезагрузки перепрошивка не началась повторно.


Схема подключения карты памяти к STM32:

Память в STM32F4 разделена на сектора, в STM32F с 1Мб флеша их 12:

0. 0x08000000-0x08003FFF (16 кБ)
1. 0x08004000-0x08007FFF (16 кБ)
2. 0x08008000-0x0800BFFF (16 кБ)
3. 0x0800C000-0x0800FFFF (16 кБ)
4. 0x08010000-0x0801FFFF (64 кБ)
5. 0x08020000-0x0803FFFF (128 кБ)
6. 0x08040000-0x0805FFFF (128 кБ)
7. 0x08060000-0x0807FFFF (128 кБ)
8. 0x08080000-0x0809FFFF (128 кБ)
9. 0x080A0000-0x080BFFFF (128 кБ)
10. 0x080C0000-0x080DFFFF (128 кБ)
11. 0x080E0000-0x080FFFFF (128 кБ)

Забегая наперед, скажу, что наш бутлоадер будет весить почти 13Кб, Поэтому достаточно одного сектора, но для запаса возьмем 2 (первые), тоесть, на бутлоадер у нас уходит 32Кб, что мелочь в данном случае. Основное приложение будет начинаться со второго сектора, тоесть, с адреса 0x08008000.

Код бутлоадера (лишнее опустил, смотрите в прикрепленных файлах):

// буффер для чтения данных с карты. Будем читать по 1Кб
#define PAGE_SIZE	1024
uint8_t	buff[PAGE_SIZE];
 
#define	APPLICATION_BEGIN	(0x08008000)	// адрес начала основного приложения (FLASH_Sector_2)
 
// Переход к основному приложению, которое по адресу APPLICATION_BEGIN
void jumpToApplication(uint32_t addr)
{
	typedef  void (*pFunction)(void);
	pFunction Jump_To_Application;
	uint32_t JumpAddress;
 
	JumpAddress = *(uint32_t*) (addr + 4);
	Jump_To_Application = (pFunction) JumpAddress;
	//	Initialize user application's Stack Pointer
	__set_MSP(*(vu32*) addr);
	Jump_To_Application();
}
 
void startFlashing() {
	printf("Flashing begin\n\r");
	FLASH_Unlock();	// разблокировка flash, без этого нельзя будет сделать запись
 
	uint32_t	fsize = fil_obj.fsize;
	printf("File size: %dKb\n\r", (int)(fsize/1024));
 
	// очистка флеша с сектора FLASH_Sector_2 по FLASH_Sector_11
	printf("Clear. ");
	for (uint32_t sector = FLASH_Sector_2; sector <= FLASH_Sector_11; sector += FLASH_Sector_1) {
		FLASH_Status	s = FLASH_EraseSector(sector, VoltageRange_3);
		if (s == FLASH_COMPLETE) continue;
		// если ошибка
		printf("Error %d\n\r", s);
		while(1);
	}
	printf("OK\n\r");
 
	// программирование
	printf("Programming. ");
	uint32_t	flash_position = 0;	// счетчик записаных во флеш байтов
	while (flash_position < fsize) {
		// читаем 1024 байта (может быть считано и меньше, если размер прошивки не кратен 1024)
		if (f_read(&fil_obj, buff, PAGE_SIZE, &ByteWriteRead) != FR_OK) {
			printf("Flashing error on read file");
			while(1);
		}
 
		// записаваем считаные данные во флеш
		for (uint32_t i=0; i < ByteWriteRead; i++) {
			FLASH_Status	s = FLASH_ProgramByte(APPLICATION_BEGIN + (flash_position++), buff[i]);
			if (s == FLASH_COMPLETE) continue;
			// если ошибка
			printf("Error %d\n\r", s);
			while(1);
		}
	}
	printf("OK\n\r");
 
	FLASH_Lock(); // блокируем флеш
	printf("Flashing end\n\r");
}
 
int main(void)
{
	SystemInit();
 
	NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x00);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
 
	st_init();
	printf("\n\rBootloader started\n\r");
 
	printf("SD Card: ");
	SD_NVIC_Init();
	while (!mmc_mount());
	printf("OK\n\r");
 
	if (f_open(&fil_obj, "FLASH", FA_READ | FA_OPEN_EXISTING) == FR_OK) {	// открываем файл
		startFlashing();
		f_close(&fil_obj);	//закрываем файл
	}
 
	// Переходим к основному приложению
	printf("Jump to application\n\r\n\r");
	jumpToApplication(APPLICATION_BEGIN);
	printf("---ERROR!!!---\n\r");	// если перейти не получилось, но реально врятли сюда дойдет, так как если прошивка не залита, то на предыдущей функции может зависнуть
	while(1);
}

Теперь по поводу основной прошивки. Стоит учесть, что она должна начинаться с адреса 0x08008000. В CooCox это делается вот так:
Напоминаю, не забудьте в основной прошивке стереть этот файл с карты.

Как видите, не сложно, старался максимально упростить. Ещё можно добавить шифрование прошивки, чтобы у Вас её не стырили и защиту памяти от считывания (в STM32, в отличии от AVR, это делается программно).

Скачать файлы статьи

2 комментария STM32F4. Обновление прошивки с карты памяти (Bootloader SD)

Leave a Reply