STM32 SD KART ÜZERİNDEN BOOT ETMEK

Merhabalar, daha önce burada ve burada bulunan yazılarımı okumadıysanız, bakmanızda fayda olabilir. Daha önce o başlıklarda bahsetmiş olduğum kısımlara tekrar tekrar değinmeyeceğim için kopukluklar yaşanabilir.

Bu yazıda bahsetmeyi çok istediğim ama bahsetmeyeceğim şunlar var :

  • Secure Bootloader (Bilmediğim bir konu)
  • Başlıktan da anlaşılacağı gibi sd kart üzerinden boot edeceğiz ama o dosya sd karta nasıl geldi ? (Bundan başka bir yazıda OTA ile ilgili üretmeyi planlığım içerikte bahsedeceğim babajım)

Bu yazıda bahsedeceğim ve olası çözümlerini implemente ettiğimi düşündüğüm şunlar var :

  • Dosyanın bütünlüğünü(integrity) kontrol ettiğimiz bir yapı.
  • Cihazın anlık enerji kesintilerinde yazma işlemlerinin yarım kalmasına karşın durumu handle edebilmesi. Daha önce yukarda belirttiğim linklerde bulunan st’nin kendi sistem bootloader programlarında bu bir problemdi. (Burada bir çok case olduğu için, ya şöyle bir şey olursa diye aklınıza bir şey gelirse ve o durum atlanmışsa bildirirseniz çok sevinirim. Kahve ısmarlarım bende)
  • Yazılımın versiyonun kontrolü işlemi.

Enerjin düşük ise ve 30 saniye vaktin var ise şuraya tıklayarak hayata dönmene yardımcı olabilirim.

Bildiğiniz, karşılaştığınız gibi bir cihazın kutulandıktan ya da fiziksel olarak sizden ayrıldıktan sonra yazılımın doğası gereği yapılan yeni geliştirmelere her zaman açık olması gerekebiliyor.

Uygulamanın yapmaya çalıştığı işi şu resim ile özetleyebiliriz.

ST’nin flash bölümünde birden çok ve farklı boyutlarda olmak üzere sektörler mevcut. Kullandığım mikrodenetleyici STM32F446xx serisine ait. Ve bu seri için 8 farklı sektör mevcut. Ben uygulama kodumu 128kb olarak parçalanmış 7.sektörde çalıştırırken, bootloader uygulamamı ise 0.sektörde konumlandıracağım. Bu konumlandırma işleminin KEIL IDE’si üzerinde nasıl gerçekleştirileceğini daha önce yukarda belirttiğim linklerde bahsetmiştim.

İlgili dosyanın metadatası ve proje özelinde oluşturduğum fonksiyonlar şu şekilde.

#ifndef BOOT_H_INCLUDED
#define BOOT_H_INCLUDED

#include "stdint.h"

#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */

#define APP_ADDRESS (ADDR_FLASH_SECTOR_7)						 		/* Image Location */

#define FLASH_USER_START_ADDR   (APP_ADDRESS)   /* Start @ of user Flash area */

#define FLASH_USER_END_ADDR     ((uint32_t)0x0807FFFF)    /* End @ of user Flash area */
																					 
#define VERSION_ADDR ((uint32_t)(FLASH_USER_END_ADDR - 3))

typedef struct meta_data meta_data_t;
struct meta_data
{
	uint8_t crc_val[4];
	
	uint8_t version[4];
};	

void jump_to_app(void);

int erase_flash_sector(void);

#endif
#include "stm32f4xx.h"                  // Device header
#include "boot.h"

static uint8_t get_sector(uint32_t address)
{	
	uint8_t sector = 0;
  
  if((address < ADDR_FLASH_SECTOR_1) && (address >= ADDR_FLASH_SECTOR_0))
  {
    sector = FLASH_SECTOR_0;  
  }
  else if((address < ADDR_FLASH_SECTOR_2) && (address >= ADDR_FLASH_SECTOR_1))
  {
    sector = FLASH_SECTOR_1;  
  }
  else if((address < ADDR_FLASH_SECTOR_3) && (address >= ADDR_FLASH_SECTOR_2))
  {
    sector = FLASH_SECTOR_2;  
  }
  else if((address < ADDR_FLASH_SECTOR_4) && (address >= ADDR_FLASH_SECTOR_3))
  {
    sector = FLASH_SECTOR_3;  
  }
  else if((address < ADDR_FLASH_SECTOR_5) && (address >= ADDR_FLASH_SECTOR_4))
  {
    sector = FLASH_SECTOR_4;  
  }
  else if((address < ADDR_FLASH_SECTOR_6) && (address >= ADDR_FLASH_SECTOR_5))
  {
    sector = FLASH_SECTOR_5;  
  }
  else if((address < ADDR_FLASH_SECTOR_7) && (address >= ADDR_FLASH_SECTOR_6))
  {
    sector = FLASH_SECTOR_6;  
  }	
  else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11) */
  {
    sector = FLASH_SECTOR_7;
  }

  return sector;
}

void jump_to_app(void)
{
	uint32_t* image_loc_addr = (uint32_t*)APP_ADDRESS;
	
	uint32_t stack_pointer = image_loc_addr[0];
	
	uint32_t jump_addr = image_loc_addr[1];
		
	typedef void (*jump_func)(void);
	
	jump_func jump = (jump_func) jump_addr;
	
	HAL_RCC_DeInit();
	
	HAL_DeInit();
	
	SysTick -> CTRL = 0;
	SysTick -> LOAD = 0;
	SysTick -> VAL = 0;
	
	__set_MSP(stack_pointer);
	
	jump();	
	
}

int erase_flash_sector(void)
{
	FLASH_EraseInitTypeDef EraseInitStruct;
	
	int ret = 1;
	
	uint8_t sec = get_sector(FLASH_USER_START_ADDR);
	
	uint8_t number_of_sector = get_sector(FLASH_USER_END_ADDR) - sec + 1;
	
	EraseInitStruct.TypeErase = TYPEERASE_SECTORS;
	EraseInitStruct.VoltageRange = VOLTAGE_RANGE_3;
	EraseInitStruct.Sector = sec;
	EraseInitStruct.NbSectors = number_of_sector;
	
	uint32_t sector_error;
	
	if (HAL_FLASHEx_Erase(&EraseInitStruct, &sector_error) != HAL_OK)
	{
		ret = -1;
	}	
	
	return ret;
}

ST ailesinin işlemcilerinde flasha yazabileceğiniz data tipleri ya da gerilim seviyesine göre yazabileceğiniz data tipleri her zaman standart olmuyor gördüğüm kadarıyla. (Daha önce tecrübe ettim). Bu yüzden kendi kullandığınız işlemcinin datasheetinde şu tabloyu kontrol etmenizde fayda var.

    if ((fr = f_mount(&fs, SDPath, 1)) != FR_OK)
	{
		goto exit;
	}	
	
	if ((fr = f_open(&file, "test.bin", FA_READ)) == FR_OK)
	{
		//4 offset for CRC
		if (f_size(&file) - 4 > (FLASH_USER_END_ADDR - FLASH_USER_START_ADDR))
		{
			f_close(&file);
			
			goto exit; 
		}
		
		uint8_t meta_data_buf[8] = { 0 };

		uint32_t crc_val = 0;
	
		unsigned int number_of_bytes_read = 0;
		
		f_lseek(&file, (f_size(&file) - sizeof(struct meta_data)));
				
		f_read(&file, meta_data_buf, sizeof(meta_data_buf), &number_of_bytes_read);
		
		meta_data_t* mdb = (meta_data_t*) meta_data_buf;		
		
		f_lseek(&file, 0);
		
		do 
		{
			unsigned int number_of_bytes_read = 0;
			
			uint8_t buffer[4] = { 0 };
			
			fr = f_read(&file, buffer, sizeof(buffer), &number_of_bytes_read);
		
			for (int i = 0; i < 4; ++i)
			{
				crc_val = get_crc32(crc_val, buffer[i]); 
			}
					
		} while (number_of_bytes_read > 0 && f_tell(&file) + sizeof(struct meta_data) + 4 <= f_size(&file) && fr == FR_OK);
		
		uint32_t match_crc = (mdb -> crc_val[0] << 24) | 
												 (mdb -> crc_val[1] << 16) | 
												 (mdb -> crc_val[2] << 8)  | 
												 (mdb -> crc_val[3]);
		
		uint32_t version =   (mdb -> version[0] << 24) | 
												 (mdb -> version[1] << 16) | 
												 (mdb -> version[2] << 8)  | 
												 (mdb -> version[3]);
		
		/* CRC is valid and version is ok */
		if (crc_val == match_crc && ((version > (*(uint32_t*)VERSION_ADDR)) || ((*(uint32_t*)VERSION_ADDR) == 0xFFFFFFFF)))
		{
			fr = f_lseek(&file, 0);
			
			HAL_FLASH_Unlock();
			
			if (erase_flash_sector() < 0)
			{
				goto exit;
			}		
			
			static unsigned int address_offset = 0;
			
			do 
			{	
				uint8_t buffer_flash[4] = { 0 };

				unsigned int number_of_bytes_read = 0;
				
				uint32_t written_data = 0;
				
				fr = f_read(&file, buffer_flash, sizeof(buffer_flash), &number_of_bytes_read);
				
				written_data = *(uint32_t*)buffer_flash;
				
				HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_ADDRESS + address_offset, written_data);
				
				if (written_data != (*(__IO uint32_t*)(APP_ADDRESS + address_offset)))
				{
					/* Whatever you want depend on your logic */
					while(1);
				}
				
				address_offset = address_offset + 4;
			
			} while (number_of_bytes_read > 0 && f_tell(&file) + sizeof(struct meta_data) + 4 <= f_size(&file) && fr == FR_OK);
						
			HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, VERSION_ADDR , version);
			
			f_close(&file);	
			
			HAL_FLASH_Lock();
			
			//TODO: Delete related file if you want
			
			goto exit;
						
		}//(crc_val == match_crc)
		
		else
		{
			goto exit;		
		}
	}
	
	else
	{
		goto exit;
	}
	
	exit:
		
		if (*(uint32_t*)APP_ADDRESS != 0xFFFFFFFF && *(uint32_t*)VERSION_ADDR != 0xFFFFFFFF)
		{
			jump_to_app();
		}
			
		else
		{
			/* Whatever you want depend on your logic */
			while(1);
			//NVIC_SystemReset();
		}

Edit : Flowchartta belirtilmemiş ama ilgili dosyanın boyutu, uygulamanın çalışacağı boyuttan yüksek olması durumunda yazma işlemi yapılmamakta.

Bu konuda Türkçe kaynak oluşturmak için bu yazıyı paylaştığımı belirtmek ister ve bu yapıyı kullanırken karşılacağınız sorunlar olur ise paylaşmanızı rica ederek, yazıyı sonlandırıyorum. İşleriniz rast gitsin hocamlar.

2 comments

  1. Merhaba Hocam,
    Verdiğiniz bilgiler için teşekkürler. Bu konuyu doğrudan ilgilendirmiyor ama söyle bir sorunla karşılaştım F0 işlemcisi için bootloader yazılımını yaptım. Flasha yazma ve ana programa dallamada problem yok. Eeprom olmadığı için bazı dataları Flash da ayırdığım alanda tutuyor programın başında flashtan okuma yaptığım zaman usart kesmesi aktif olmuyor. Flash dan okuma kısmını kapattığım zaman sorunsuz çalışıyor. Böyle bir durumla karşılaştınız mı yada bilginiz var mı

    Liked by 1 kişi

Bir Cevap Yazın

Please log in using one of these methods to post your comment:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s