12

STM32-Flash Hafızaya Veri Yazma ve Okuma

Bazı Gömülü Sistem projelerinde kullanıcı tarafından değiştirilen bir değere (kullanıcı adı,şifre vb.), sistemin enerjisi kesilip tekrar geldiğinde veya sistem resetlendiğinde tekrar erişmek gerekebiliyor. Böyle durumlarda mikrodenetleyicinin programcılar için ayrılan adreslerine veri yazılıp ve gerektiğinde o adrese erişerek veriler çekilebilir. Flash hafızanın bir yazma sınırı olduğunu unutmayalım. Yani sürekli flash hafızaya veri yazılıp okunduğu bir uygulama önerilmemektedir. Bu yazıda STM32F1 serisi için Flash Hafızaya veri yazma veya Flash Hafızadan veri okuma işleminin bir örnek uygulamasından bahsedeceğim.
Öncelikle STM32f1 de flash adrese veri yazma ve okuma işlemi 16 bitlik(half-word) değişkenler ile yapılır. İlk olarak en basit olanından yani veri okuma fonksiyonundan başlayalım. Aşağıda görüldüğü gibi bu fonksiyon iki satırdan oluşmaktadır. C programlamadan hatırlayacağınız üzere pointerlar ile adrese erişip adresteki veriyi alma mantığına dayanır.

uint16_t Read_Flash(uint32_t  adr)
{
  uint16_t * Pntr = (uint16_t *)adr;
  return(*Pntr); 
}

Flash adrese veri yazmak ve flash adresteki veriyi silmek , okumaya göre biraz daha farklıdır. STM32 de bu işlemleri yapabilmek için öncelikle flash kilidini açmak gerekir. Bunun için STM32 programming manualden  yararlanabiliriz. Flash kilidini açmak için FLASH->KEYR registerına programming manualdeki key1 ve key2 sırası ile gönderilmelidir. Böylece flash kilidi açılmış olur. Yazma veya silme işlemi bittikten sonra FLASH_CR registerı resetlendiği zaman flash tekrar kilitlenmiş olur ve yazma,silme işlemi yapılamaz. Aşağıda programming manuel den alınan flash key değerlerine ait değerler verilmiştir.

STM32f1 Flash Keys

Flash Keys

Peki hangi adrese veri yazacağız. Bunu da yine programming manual veya reference manual’deki “flash memory map” tablosundan bulabiliriz. Aşağıda bu tablo verilmiştir.

stm32 flash memory map

STM32F1 Memory Map

Dikkat edilirse main memory 128 adet 1 kbyte’lık sayfalardan oluşmaktadır. Burada önemli bir noktaya değinmek gerekir. Yazma işlemi yapılırken tek bir adrese yazılır fakat silme işlemi tüm sayfayı siler.

void Erase_Flash (uint32_t adr)
{
	FLASH->KEYR=0x45670123;  					// Silme veya yazma islemi yapilmadan önce Flash kilidi mutlaka açilmalidir.
  FLASH->KEYR=0xCDEF89AB;						// FLASH->KEYR registerine KEY1 ve KEY2 degerleri atandiginda Flash kilidi açilir.
  FLASH->CR|=0x00000002;            //PER enable
  FLASH->AR=adr;                    //FLASH->AR registerine silinmek istenen adres yazilir
  FLASH->CR|=0x00000040;            //STRT anable
  while((FLASH->SR&0x00000001));    //Islem bitene kadar bekle(BUSY kontrol ediliyor)
  FLASH->CR &= ~0x00000042;         //FLASH->CR ilk durumuna aliniyor (kilit hala açik!)
	FLASH->CR=0x00000080;  						//FLASH_CR registeri resetlendiginde FLASH kiltlenmis olur
}
void Write_Flash (uint32_t adr, uint16_t data)
{
	FLASH->KEYR=0x45670123;  					// Silme veya yazma islemi yapilmadan önce Flash kilidi mutlaka açilmalidir.
  FLASH->KEYR=0xCDEF89AB;						// FLASH->KEYR registerine KEY1 ve KEY2 degerleri atandiginda Flash kilidi açilir.
  FLASH->CR|=0x00000001;            //PG enable
  *(__IO uint16_t*)adr = data;      //istenen adrese istenen data yaziliyor
  while((FLASH->SR&0x00000001));    //Islem bitene kadar bekle(BUSY kontrol ediliyor)
	FLASH->CR=0x00000080;  					  //FLASH_CR registeri resetlendiginde FLASH kiltlenmis olur
}

Yukarıda yazma ve silme işlemleri için yazılan fonksiyonlar verilmiştir. Aşağıdaki kodda dikkat edilirse yazma adresi olarak Page 127‘nin başlangıç adresi seçilmiştir.

data = 10;
Erase_Flash(0x0801FC00); // adresteki değer silindi. 
Write_Flash(0x0801FC00,data); // 16 bitlik veri adrese yazıldı. 
HAL_Delay(2000); // flash_data degiskeninin baslangiçta sifir oldugunu görebilmek için gecikme eklendi. 
flash_data = Read_Flash(0x0801FC00);

Kod yüklendikten sonra değişkenlerdeki değişimleri STMStudio ile izleyebilirsiniz.

Read_Flash Öncesi

Read_Flash Sonrası

Kodların Tamamı

/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2019 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint16_t data,flash_data=0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/*######################## Adresten Veri Okuma Fonksiyonu ####################*/
uint16_t Read_Flash(uint32_t  adr)
{
  uint16_t * Pntr = (uint16_t *)adr;
  return(*Pntr); 
}
/*######################## Verilen adresteki veriyi silmek için yazilan fonksiyon ############*/
void Erase_Flash (uint32_t adr)
{
	FLASH->KEYR=0x45670123;  					// Silme veya yazma islemi yapilmadan önce Flash kilidi mutlaka açilmalidir.
  FLASH->KEYR=0xCDEF89AB;						// FLASH->KEYR registerine KEY1 ve KEY2 degerleri atandiginda Flash kilidi açilir.
  FLASH->CR|=0x00000002;            //PER enable
  FLASH->AR=adr;                    //FLASH->AR registerine silinmek istenen adres yazilir
  FLASH->CR|=0x00000040;            //STRT anable
  while((FLASH->SR&0x00000001));    //Islem bitene kadar bekle(BUSY kontrol ediliyor)
  FLASH->CR &= ~0x00000042;         //FLASH->CR ilk durumuna aliniyor (kilit hala açik!)
	FLASH->CR=0x00000080;  						//FLASH_CR registeri resetlendiginde FLASH kiltlenmis olur
}
/*################ Verilen adrese veri yazma fonksiyonu ##############################*/
void Write_Flash (uint32_t adr, uint16_t data)
{
	FLASH->KEYR=0x45670123;  					// Silme veya yazma islemi yapilmadan önce Flash kilidi mutlaka açilmalidir.
  FLASH->KEYR=0xCDEF89AB;						// FLASH->KEYR registerine KEY1 ve KEY2 degerleri atandiginda Flash kilidi açilir.
  FLASH->CR|=0x00000001;            //PG enable
  *(__IO uint16_t*)adr = data;      //istenen adrese istenen data yaziliyor
  while((FLASH->SR&0x00000001));    //Islem bitene kadar bekle(BUSY kontrol ediliyor)
	FLASH->CR=0x00000080;  					  //FLASH_CR registeri resetlendiginde FLASH kiltlenmis olur
}
/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();

  /* USER CODE BEGIN 2 */
	data = 10;
	Erase_Flash(0x0801FC00);                        // adresteki değer silindi.
	Write_Flash(0x0801FC00,data);                   // 16 bitlik veri adrese yazıldı.
	HAL_Delay(2000);												// flash_data degiskeninin baslangiçta sifir oldugunu görebilmek için gecikme eklendi.
	flash_data = Read_Flash(0x0801FC00);		// addresten 16 bitlik veri okundu.
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void _Error_Handler(char * file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1) 
  {
  }
  /* USER CODE END Error_Handler_Debug */ 
}

#ifdef USE_FULL_ASSERT

/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */

}

#endif

/**
  * @}
  */ 

/**
  * @}
*/ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 HAL Kütüphanesi ile Flash Hafıza Örneği

Bir örnekte HAL kütüphanesi ile yapalım. Bu örnekte silme işlemi ile yazma işlemini tek bir fonksiyon içerisinde yapacağız. Silme işlemi bir kaç satırdan oluştuğu için aynı fonksiyon içinde aşağıdaki gibi kullanabiliriz.

void Write_Flash( uint32_t address , uint16_t data)
{
    FLASH_EraseInitTypeDef EraseStruct;
    uint32_t PageError;
	
    EraseStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
    EraseStruct.PageAddress = address;
    EraseStruct.NbPages	    = 1;
	
    HAL_FLASH_Unlock();
    HAL_FLASHEx_Erase(&EraseStruct,&PageError);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,address,data);
    HAL_FLASH_Lock();
}

Bu fonksiyon içerisinde HAL_FLASH_Program() fonksiyonu ile istersek 32 bit veya 64 bitlik değerleri hafızaya yazabiliriz. Tabi arka planda değerler yine 16 bit olarak hafızaya yazılır. HAL_FLASHx_Erase() fonskiyonu bizden bir hata parametresi bekler. Eğer silme işleminde hata oluşur ise bu parametreye girilen değişkeni hangi adresi silerken hata oluştu ise o adresin değerine eşitler.

Write_Flash(Flash_address,0x0001);

NOT: Ben bu örneği STM32F103C8T6(Blue Pill) üzerinde denedim. Yazının başında verdiğim tabloda STM32F1 serisinin Memory Map tablosunda 128 sayfa olduğu görülmektedir fakat her f1 modeli 128 sayfalık yani 128 kb flash hafızaya sahip değildir. STM32F103C8T6 da C den sonra gelen 8 kodu 64 kb lık bir flash hafızaya sahip olduğunu belirtmektedir.

Memory Map tablosunda 63. sayfanın hangi adreste olduğu yazmamaktadır. Peki herhangi bir sayfanın adresini nasıl hesaplayabiliriz? Bunu basitçe aşağıdaki gibi hesaplayabiliriz.

#define Flash_address (uint32_t)(0x08000000+1024*63) // page 63 -> 0x0800FC00

Memory map’ten page 0′ ın başlangıç adresini ve her sayfanın 1 kb(1024 byte) olduğunu biliyoruz. Buna göre istenilen sayfanın başlangıç adresini yukarıdaki gibi hesaplayabiliriz.

Kaynak: http://www.picproje.org/

Mehmet Topuz

12 Comments

    • Evet doğrudur. Flash hafızanın boş olduğu durum 0xFF değeridir. Aynı zamanda hafızadan veri silindiğinde değeri 0xFF olmalıdır.

  1. Mehmet bey merhabalar, öncelikle verdiğiniz bilgiler için teşekkür ederim. Takıldığım bir nokta var. STM32F103CBT6 kodlu işlemci kullanıyorum. Float değerim var ve flash hafızaya kaydetmek istiyorum. Nasıl bir yol izleyebilirim?

    • Merhaba, rica ederim. Öncelikle float 32 bitlik bir değişkendir. Flash hafızaya yazarken 16 bitlik değerler yazabiliyoruz. Float değişkenlerde bit kaydırma işlemi(<<) yapılmadığı için float değişkeni 16 bitlik iki değişkene parçalamak zor bir iş. Bunun yerine float değişkenin virgülden sonraki basamağını sabit kabul ederek virgülden önceki ve virgülden sonraki sayıyı iki ayrı 16 bitlik değişkene atayıp hafızaya kaydedebilirsin. Örneğin; Float değişkeni şöyle iki değişkene atayabilirsin.

      float a = 13.35;

      uint16_t b = a; // b = 13

      uint16_t c = a*100;

      c = c%100; // c = 35

      Burada değişkenin birini örnek olarak 0x0801FC00 adresine diğerini ise 0x0801FC02 adresine kaydedebilirsin. Daha sonra bu adreslerden veri okuduktan sonra tekrar bu iki sayıyı float tipine dönüştürmek istiyorsan bunu da şu şekilde yapabilirsin.

      float d = b; // d = 13.0

      d = d+ (float)c/100; // d = 13.35000

      Bunu böyle bir deneyiniz. Eğer kod çalışırsa buraya tekrar yazarsanız sevinirim. Sonucu ben de merak ediyorum.

  2. Sorunumu şu şekilde çözüme ulaştırdım. Belki ihtiyacı olan arkadaşlar olabilir;
    typedef union
    {
    uint16_t dataBytes[2];
    float f;
    }floatToChar_t ;

    fonksiyon olarak kullanımı;
    Write_Flash(carpanDegeriAdress, degisken.dataBytes[0]);
    Write_Flash(carpanDegeriAdress1, degisken.dataBytes[1]);

    ve;
    degisken.dataBytes[0] = Read_Flash(carpanDegeriAdress);
    degisken.dataBytes[1] = Read_Flash(carpanDegeriAdress1);

    şeklinde. (Read_Flash ve Write_Flash fonksiyonları sizin yukarıda yazmış olduğunuz fonksiyonlar.)

    • Merhaba Gökhan Bey,
      Çözümü bir örnek üzerinde detaylı olarak gösterebilir misiniz ?

  3. İşlemler bittikten sonra Flash_Lock komutunun önemi nedir?
    Resetten sonra flash’ı unlock etsek ve bir daha hiç Lock etmesek, veri kaybı yaşar mıyız? Veya veri kaybı yaşama olasılığımız ne oranda etkilenir?

    • Bu komut sadece flasha yazmayı engeller. Genelde yanlışlıkla program kodları değiştirilmesin diye kullanılır. Mikrodenetleyici üzerinde çalışan kodlar da flash hafızda tutulur. Bu kodlardan herhangi bir byte silinse mikrodenetleyicide bir süre sonra hata oluşur. Bunun önüne geçmek için konulmuş bir önlem olabilir. Flashı lock etmesende bir sıkıntı çıkacağını zannetmiyorum program hafızasına veri yazmadığın sürece tabi. Mikrodenetleyici resetlendiğinde tekrar otomatik kilitlenecektir.

  4. Merhabalar. Bir sorum olacak. Flash hafızaya aynı sector’e mesela 11.sector’e farklı adreslerine birçok değer atamak istiyorum. Fakat Flash_Write komutu her çalıştığında sector11’i tamamen sildiği için ilk yazdığım veriler siliniyor. Doğal olarak 10 tane veri kaydetmek istesem sadece sonuncu kalıyor. Diğerleri tüm sector silindiği için siliniyor. Bunun için bir öneriniz var mı ? Yardımcı olursanız çok sevinirim.

    • Merhaba, Daha önce bu sorunu şu şekilde çözmüştüm. Atıyorum aynı sayfadaki 10 adet verilerden sadece birini silip yerine başka bir tane kaydetmek istedik. Tüm sayfa silineceği için önce 10 adet veriyi hafızadan sırası ile okuyup bir diziye kaydettim. Daha sonra kaydedecek olduğum yeni veriyi dizideki yerine kaydettim. Ardından flash hafızadaki sayfayı silip, bütün diziyi tekrar ilgili sayfaya yazmıştım. Burada verilerin yazılacağı adresi ardışık seçmeniz daha iyi olacaktır. Örneğin; 16 bitlik veriler kaydediyorsanız adresi 2 arttırarak kaydedin.32bitlik değerler kaydediyorsanız adresi 4 arttırarak kaydedin.Böylece basit bir for döngüsü ile bütün verileri okuyup kaydedebilirsiniz. Normalde flash write fonskiyonundan sonra hafızayı silmez. Yazma ve silme fonksiyonlarını ayrı ayrı oluşturmuştum. Sadece HAL kütüphanesi ile yazılmış örnekte write fonksiyonu önce silip sonra yazar. Bu fonksiyonuda yazma ve silme olarak ikiye fonksiyon haline getirebilirsiniz.

  5. Hocam merhaba, kafamı kurcalayan bir soru var. Flash hafızaya yazdırma işlemi yaparken datasheette verilen adreslerden page1’de bulunan 0x08000800 adresine yazdırmak istedim ve başardım da ancak sistem enerjisini kesip yeniden başlattıtğımda bu değer yine ilk değerini aldı ve benim sonradan yazdığım değeri silmiş. ancak sizin kullandıgınız adresi kullandığımda düzgün çalıştığını gördüm. ayrıca c8 ifadesinin 64kb hafıza ifade ettiğini yazmışsınız, o halde datasheette verilen son 64kblık adresi mi kullanmam gerekiyor. yani 64. sayfadan 127. sayfaya kadar olan kısmı? datasheette böyle bir yazı göremedim veya kaçırdım.

    • Merhaba, Öncelikle flash hafızaya veri yazarken ilk sayfalara yazmak önerilmez çünkü ilk sayfalarda mikrodenetleyici üzerinde çalışan program kodu bulunur. Bu yüzden son sayfalara yazmak daha mantıklıdır. Fakat şöyle bir durumda var; Yazdığınız kodların derlendikten sonraki boyutu atıyorum 8kb ise flash hafızaya bu kodlar 8. sayfaya kadar yüklenecektir. Yani 8. sayfadan sonrası halen boştur. Bu yüzden bu örnekte 9. sayfa da kullanılabilir ama yazıp sildiğiniz sayfada program kodları bulunmamalı çünkü progam kodunun tek bir byte’ının silinmesi çalışma zamanında hataya sebep olur. stm32f103c8t6 daki 8 değeri aslında 64kb olduğunu söyler. Yukarıda paylaştığım resmi şu linkteki-> https://www.st.com/resource/en/datasheet/stm32f103c8.pdf datasheet’in 108. sayfasından aldım. Siz hangi seriyi kullanıyorsunuz bilmiyorum ama her seride flash hafıza büyüklüğü farklıdır. Kullandığınız stm32’nin datasheetinde aynı tablonun olması gerekir. Eğer 64 kb hafıza diyorsa 64kb’tan ilerisine yazamassınız. İlk örnekte farklı bir bluepill kullanmıştım ve hata vermemişti. O zaman bluepill üzerindeki mcu’nun paket tipine detaylı bakmamıştım demekki o c8t6 değilmiş, 128kb hafızaya sahip farklı bir f1 serisi de olabilir.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir