C++ Kursu Notlarım


C ve Sistem Programcıları Derneği üzerinden almış olduğumuz C++ kursu ile ilgili notlarımı notion üzerinde toplamaya başladım. Buradan erişim sağlayabilirsiniz.

  • Notlar içerisinde olabilecek hatalardan eğitmen değil ben sorumluyumdur. Hataları bildirirseniz çok sevinirim.
  • Kursun eğitmeni Necati Ergin’dir.
  • Her hafta minumum 2 not eklemek hedefim.
  • Notları incelemek dili öğrenmek için yeterli olmayacaktır. Her konuda olduğu gibi pratik şart.)

OOP_C[0] = “Polymorphism”



We can say that polymorphism means having many forms. I can hear you say, “C language is not an OOP language”. Yes you are right, but there is a very fine line in this answer;

Answer can be yes or no, depending on:

if you ask “is C an object oriented language?”, the answer is “no” because it do not have object oriented constructors, keywords, semantic etc…

if you intend “can I realize OOP in C?”, the answer is yes, because OOP is not only a requirement of a language, but also a way of “thinking”, an approach to programming, before to touch some language or another. However the implementation of OOP in C (or any other language not natively designed to be OOP) will be surely “forced” and much hard to manage then any other OOP language, so also some limitation shall be expected.

There are some techniques in this subject.

taken from geeksforgeeks

Today, we will try to implement the structure at run time with virtual functions.(Dynamic or late binding)

#include "stdio.h"
#include "stdbool.h"

struct which_series
{
    const struct vtable* vptrinc;
    const char* name;    
};

struct vtable
{
    void (*is_watched)(struct which_series* self);
};

static void is_watched(struct which_series* self)
{
    printf("\r\n Yes, %s watched ", self -> name);
}

void is_watched_for_client(struct which_series* self)
{
    const struct vtable* vptr = *(&self -> vptrinc);
    
    if (vptr -> is_watched != NULL)
    {
        vptr -> is_watched(self);
    }

    else
    {
        printf("\r\n No, %s not watched yet ", self -> name);
    }    
}

int main()
{
    struct vtable spycraft = { &is_watched };
    struct which_series spycraft_series  = { &spycraft, "Spycraft" };

    struct vtable lupin = { (void*)0 };
    struct which_series lupin_series = { &lupin, "Lupin" };

    is_watched_for_client(&spycraft_series);
    is_watched_for_client(&lupin_series);
}

How it is works ?

NOTE: The alignment of the Rectangle structure and the inherited attributes from the Shape
structure is guaranteed by the C Standard WG14/N1124. Section 6.7.2.1.13 of this Standard, says:
“… A pointer to a structure object, suitably converted, points to its initial member. There may be
unnamed padding within a structure object, but not at its beginning”.

Again, since “vptrinc” is the first member in the “which_series” structure it actually has the same address as the instance self.

printf("\r\n Addr in the structure < %x > ", self -> vptrinc);
printf("\r\n spycraft addr < %x > ", &spycraft);
//gives same address

Nuvoton from scratch, the bare minimum


I do not use register based programming actively in my daily life. However, if i need to meet a new platform, i try this way. In this way, i can understand whether the datasheet of the relevant platform is good.  To be honest, the datasheet of the Nuvoton products came out poor. I could not find some generalized keywords like “register boundary address ” in the datasheet. Finding relevant addresses and offset values was a waste of time really. Today, we will build a project from scratch. We will use GNU ARM Embedded as toolchain. We will write the linker file. After the compilation and linking process, we will transfer the .bin extension file that we will obtain through the utility to our development card with program named NUMicro ICP Programming Tool. If you have used the stm32 series before you can understand it easily. The development card i have (NuMaker – PFM – M487) using Cortex-M4.

Let’s start with startup file as named startup.c

#include "stdint.h"

#define sp 0x20000800 

#define __IO volatile

typedef struct
{
    __IO uint32_t MODE;          /* Offset: 0x00/0x40/0x80/0xC0/0x100/0x140/0x180/0x1C0  Port A-H I/O Mode Control                       */
    __IO uint32_t DINOFF;        /* Offset: 0x04/0x44/0x84/0xC4/0x104/0x144/0x184/0x1C4  Port A-H Digital Input Path Disable Control     */
    __IO uint32_t DOUT;          /* Offset: 0x08/0x48/0x88/0xC8/0x108/0x148/0x188/0x1C8  Port A-H Data Output Value                      */
    __IO uint32_t DATMSK;        /* Offset: 0x0C/0x4C/0x8C/0xCC/0x10C/0x14C/0x18C/0x1CC  Port A-H Data Output Write Mask                 */
    __IO uint32_t PIN;           /* Offset: 0x10/0x50/0x90/0xD0/0x110/0x150/0x190/0x1D0  Port A-H Pin Value                              */
    __IO uint32_t DBEN;          /* Offset: 0x14/0x54/0x94/0xD4/0x114/0x154/0x194/0x1D4  Port A-H De-Bounce Enable Control Register      */
    __IO uint32_t INTTYPE;       /* Offset: 0x18/0x58/0x98/0xD8/0x118/0x158/0x198/0x1D8  Port A-H Interrupt Trigger Type Control         */
    __IO uint32_t INTEN;         /* Offset: 0x1C/0x5C/0x9C/0xDC/0x11C/0x15C/0x19C/0x1DC  Port A-H Interrupt Enable Control Register      */
    __IO uint32_t INTSRC;        /* Offset: 0x20/0x60/0xA0/0xE0/0x120/0x160/0x1A0/0x1E0  Port A-H Interrupt Source Flag                  */
    __IO uint32_t SMTEN;         /* Offset: 0x24/0x64/0xA4/0xE4/0x124/0x164/0x1A4/0x1E4  Port A-H Input Schmitt Trigger Enable Register  */
    __IO uint32_t SLEWCTL;       /* Offset: 0x28/0x68/0xA8/0xE8/0x128/0x168/0x1A8/0x1E8  Port A-H High Slew Rate Control Register        */
    __IO uint32_t RESERVE0[1];
    __IO uint32_t PUSEL;         /* Offset: 0x30/0x70/0xB0/0xF0/0x130/0x170/0x1B0/0x1F0  Port A-H Pull-up and Pull-down Enable Register  */

} GPIO_T;

#define PERIPH_BASE                 (( uint32_t)0x40000000)     /*!< Perip. Control Register */
#define GPIOH_BASE                  (PERIPH_BASE + 0x041C0UL)   /*!< PORT H Control Register */
#define PH                          ((GPIO_T *)  GPIOH_BASE)    /*!< Casting to PORT H Control Register */
#define GPIO_MODE_OUTPUT            (0x1UL)                     /*!< Output Mode ideinitializer */
#define BIT0                        (0x00000001UL)              //< Bit 0 mask of an 32 bit integer

int main(void);

uint32_t *vector_table[2] __attribute__  ((section("vectors"))) = 
{
    (uint32_t*) sp, //stack pointer
    (uint32_t*) main,
};

int main()
{    
    PH -> MODE |= BIT0; //PH0 Output Mode
    
    while(1)
    {
        PH -> DOUT = 0x00; //low PH-0.bit
        for (int i = 0; i < 50000; ++i); //for delay
        PH -> DOUT = 0x01; //high PH-0.bit
        for (int i = 0; i < 50000; ++i); //for delay
    }       
}


Since the stack is a downward-going structure and the ram start address is 0x20000000. I’ve set a small stack. Now we can compile this file, so tiny : just 84 bytes.

arm-none-eabi-gcc -I. -c -fno-common -O0 -g -mcpu=cortex-m4 -mthumb startup.c
MEMORY
{
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
    rom (rx)  : ORIGIN = 0x00000000, LENGTH = 40K
}

SECTIONS
{
    .  = 0x0;         /* From 0x00000000 */
    .text :
    {
        *(vectors)    /* Vector table */
        *(.text)      /* Program code */
        *(.rodata)    /* Read only data */
    } >rom

    .  = 0x20000000;  /* From 0x20000000 */
    .data :
    {
        *(.data)      /* Data memory */
    } >ram AT > rom

    .bss :
    {
        *(.bss)       /* Zero-filled run time allocate data memory */
    } >ram AT > rom
}


I did not write the original ram and rom sizes, I did not need that much, so I used my existing linker file. Lets linking.

arm-none-eabi-ld -Tnuvoton.ld -nostartfiles -o startup.elf startup.o

Converting the binary file.

arm-none-eabi-objcopy -Obinary startup.elf startup.bin


After uploading the file to our development card with the numicro program, we can see that the red led on the card flashes.

Best Regards,
Volkan

Gömülü Cihazlar ve Big-O Notasyonu


Literatürde bulunan bir çok farklı algoritmanın, farklı yazılım dillerinde implementasyonlarını yapmak mümkün. Bu algoritmaların koşacağı birbirinden farklı sistem kaynaklarının olduğunu düşündüğümüzde ise bu algoritmaların koştuğu sistem üzerindeki davranışını değil de, söz konusu olan algoritmanın karmaşıklığının maliyetini belirlemek için kullandığımız bir notasyon olan BIG-O notasyonunu bugün bir örnekle ele alacağım ve bu konuyu güzel bir şekilde ele alan bir udemy eğitiminden bahsedeceğim.

Çeşitli veri yapılarının ve operasyonların algoritma karmaşıklıklarına dair derli toplu bir yer arıyorsanız şuraya bakabilirsiniz.

Bugün şu soruyu irdeleyeceğiz,

N elemandan oluşan bir dizide negatif karşılığı bulunan tüm pozitif sayıları bulunuz.
Örneğin [2, 5, 1, -1 , -3, 7, 4 , -2] dizisi için sonuç [2, 1] olmalıdır.

Bu soru karşımıza bir mülakat esnasında gelebileceği gibi, bu tarz soruların üzerinde düşünmenin bizi daha dinç tuttuğunu düşünüyorum. Bu soruyu gördüğümüzde aklımızda hemen farklı yaklaşımlar belirebilir. Benim aklıma HackerRank sitesinde soruları çözerken kazandığım bir idiom gelmekte ve elemanların değerlerinin başka bir dizi üzerinde index olarak yapılandırması yolunu tercih ediyorum. Eğer daha önce HackerRank gibi platformlara göz gezdirmediyseniz ve problem çözmeyi seviyorsanız mutlaka bakın. Hem kodlama pratiği açısından oldukça güzel bir site hemde teknik mülakatlarda bulanacağımız zaman bize artı kattığını düşünüyorum uzun vadede.

Kodlamaya başlamadan önce size verilen veri setinden daha fazlasını düşünmek zorundasınız, yukarda gördüğünüz gibi 0’a tümleyen negatif sayıların mutlak karşılığı olan pozitif sayılar daha önce gelmekte. Kurduğunuz algoritma bunun tersi durumları handle edebiliyor mu ? Yine aynı şekilde yaklaşımınız mutlak değerleri eşit olan fakat aynı işaretli sayılar için nasıl davranıyor ? Benim yukarda bulunan soru için çözümüm bu şekilde oldu, eksik gördüğünüz noktalar ya da denediğiniz test durumları için yanlış bir sonuç verirse lütfen bilgilendirin. Eğer mobilden bu yazıyı okuyorsanız şuradan kodun biçimlendirilmiş haline ulaşabilirsiniz.

//NOT : SIZE verilen dizideki en yüksek değerden daha yüksek seçilmiştir.
#include <stdio.h>
#include <stdlib.h>

#define SIZE 10

const int test_array[SIZE] = {-2, 5, 1, -1, -3, -7, 7, 2, 3, 5};

int main(void) {

  int emulate_array[SIZE] = { 0 };
 
  for (int i = 0; i < SIZE; ++i)
  {
    //Dizide aynı sayının tekrarı olması durumu göz önünde alındı. Örnek dizi de -7 -7 ya da  5 5 gibi aynı ranka sahipse elenir.
    if (test_array[i] < 0 && emulate_array[abs(test_array[i])] == 1)
    {
      printf("\r\n Bulundu = %d", abs(test_array[i]));      
    }

    else if (test_array[i] > 0 && emulate_array[test_array[i]] == -1)
    {
      printf("\r\n Bulundu = %d", abs(test_array[i]));
    }   

    if (test_array[i] > 0)
    {      
      emulate_array[test_array[i]] = 1;
    } 

    //Negatif sayi dizide daha önce gelebilir.
    else
    { 
      emulate_array[abs(test_array[i])] = -1;
    }
  }
  
  return 0;
}

Sergilediğimiz bu yaklaşımı biraz daha irdeleyecek olursak, farkettiğiniz gibibellek kullanımı(space complexity) artmış durumda. Ve algoritmamızın karmaşıklığı şu anda O(n) durumunda ve bu hoş. Burada aklıma gelen ilk şey şu oldu bu yazdığım algoritma bir mikrodenetleyici içeren sistem üzerinde mi koşacak ? Yarın öbür gün veri setinin boyutunda gerçekleşebilecek bir artış benim donanımda kullandığım çipin tahsis edemeyeceği bir bellek boyutu durumuna gelebilir mi ? Farklı yaklaşımlar neler olabilir ve onların algoritma karmaşıklığının daha fazla olması fakat bellek kullanımının daha az olması sistemim için daha mı tercih edilebilir bir yöntem ? Veri setinin büyümesi sonucunda algoritma karmaşıklığı diğer sistemleri bloklar mı ? Bu ve benzer bir çok soru aklımıza gelebilir ve gelmesi önemli bence.

Anlatmaya çalıştığım gibi bu soruyu yapılabilecek farklı yaklaşım yöntemleri mevcut, belki öncesinde diziyi buble, quick sort gibi farklı maliyetlere sahip algoritmalar ile sıralayabilir ve sonrasında da işlem yapabilirsiniz. Benim yöntemim doğru diğer yaklaşımlar yanlış demek çok mümkün değil ve yanlış bir bakış açısı olur.

Yazının başında belirttiğim gibi bu konuları içerisinde barındıran henüz bitirmedim ama geldiğim noktaya kadar yeni şeyler öğrendiğim ve mevcut öğrendiklerimi tazelediğim bir udemy eğitiminin promosyonlu linkine buradan erişebilirsiniz. Bir eğitim alırken dikkat ettiğim bir konu var, ben tecrübeye para ödemeyi seçiyorum, zira udemy üzerinde internet üzerinden topladığı bilgileri henüz kendi süzgecinden geçirmeden paylaşan bir çok birey var. Bu bana çok doğru gelmiyor. Eğitim linkini bıraktığım videoları hazırlayan kişilerin hem kendileri aday pozisyonunda hemde adayları değerlendirdiği bir pozisyonda bulunduğunu düşünürsek kazandıkları deneyimler için bu kadar cüzzi bir miktar ödemek bence çok iyi. Eğer öğrenciyseniz ve bu eğitimi almak bütçeniz için uygun değilse benimle .edu uzantılı mail adresinizden iletişime geçebilirsiniz.

Kolaylıklar dilerim.

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.