STM32 SD Kart Bootloader problemi

Başlatan baran123, 28 Ağustos 2017, 17:16:42

baran123

STM32F429-DISCO ile çalışıyorum.Amacım bir SD Kart bootloader yazmak.
Flashın son page'ine bir bootloader koydum. Normal adresten buton ile bootloader'ı atlayınca for döngüsünde 1 satır kod yazıyorum ve arkasına yazdığım kodlar çalışmıyor.

Atlamak için kullandığım fonksiyon. Bunu mainde bir buton ile çağırdığımda 0x081E 0000 adresine atlıyorum.
void Bootloader_Jump(uint32_t addr)
{
	typedef void(*pFunction)(void);
	
	uint32_t  JumpAddress = *(__IO uint32_t*)(addr + 4);
	pFunction Jump = (pFunction)JumpAddress;
	
	__disable_irq();
	
	RCC_DeInit();
	
	SysTick->CTRL = 0;
	SysTick->LOAD = 0;
	SysTick->VAL = 0;
	
	SCB->VTOR = addr;
	
	__set_MSP(*(__IO uint32_t*)addr);
	
	Jump();
}


Atladığım adreste şu program var.
int main(void)
{
    SD_Result_t result = SD_Result_Error;
    
    SystemInit();
    TM_DELAY_Init();
    TM_ILI9341_Init();
    TM_DISCO_LedInit();
    TM_DISCO_ButtonInit();
    TM_ILI9341_SetLayer1();
    TM_ILI9341_Fill(ILI9341_COLOR_RED);
    TM_ILI9341_Puts(0, 0, "BOOTLOADER MODE", &TM_Font_16x26, ILI9341_COLOR_WHITE, ILI9341_COLOR_RED);
	
    TM_DISCO_LedOn(LED_RED);
	
    for (;;)
    {
        SD_ReadBinFile("test.bin"); // 
	TM_DISCO_LedOn(LED_GREEN); // Bu led yanmıyor.
    }
}

Fakat led yanmıyor.
Programın fonksiyonda takıldığını düşünebilirsiniz fakat bu fonksiyonda hiç bir bekleme veya while döngüsü yok. Sadece bir if ile kart mount edilmiş mi ona bakıyor.
Debug ile baktığımda ise bu fonksiyon içinde if içine bir türlü giremiyor yani sd kartı bir türlü açamıyorum.

Ben nerede hata yapıyorum ?
Ana programdada ekran ve sd kart kullanıyorum bu bir problem teşkil eder mi ?


1n4001

Bootloader'a atladıktan sonra SD kart okuma ile ilgili donanımları, FatFs'yi tekrar kurdunuz mu? MCU bir şekilde ulaşamadığı bir alana gitmeye çalışıp Hard_Fault'a düşüyor olabilir. Bootloader kısmında kullandığınız fonksiyonları temizleyip deneyin derim. Mesela sadece for döngüsünün içinde led yakın diğer init fonksiyonlarını kapatın.

Klein

Gördüğüm kadarıyla siz Bootloader'e sıçrama yapmıyor, bulunduğunuz noktada , oradaki "int main(void)" fonksiyonunu çalıştırıyorsunuz.
Main fonksiyonunu başka bir projede derleyip , CPU ya atarken farklı bir adrese mi atıyorsunuz? Biraz daha detay verirsaniz, durumu anlamaya çalışayım. 

baran123

Tek proje var.
Önce maini derliyorum 0x800 0000 e yüklüyorum.
Daha sonra bootloader programını yazıp keilin ayarlarından en son page yani 0x81E 0000 adresine yüklüyorum


Çalıştırdığımda ana programdan 0x81E 0000 adresine jump ediyorum.
Atladığımda ise yeni main fonksiyonumda tekrar her şeyi init edip sd kartı açmaya çalışıyorum fakat açılmıyor.

Klein

jump() fonksiyonunun içeriği nedir?  ( jump'ın fonksiyon işaretçisi olduğunun farkındayım. Gösterdiği adresteki fonksiyonun içeriği asıl sormak istediğim)

Doğru anlamış mıyım acaba?
1- main() fonksiyonu ve daha bir çok fonksiyon içeren bir programınız var.   bunu derleyip çipe yüklüyorsunuız.
2- daha sonra bu main bloğunu silip ( ya da comment yapıp) başka bir main() bloğu ile yeniden derleme yapıyorsunuz.
     fakat bu derlemeyi 0x81E0000 adresinden başlayacak şekilde derliyor ve çıkan hex kodu da çipin o bölgesine yüklüyorsunuz.
   
     Önce ilk derlediğiniz Main program çalışıyor. eğerr boot modunda iseniz ,
      0x81E 0000 jump ediyorsunuz. 

    Bootloader_Jump() fonksiyonunda ben bir jump göremedim. Burası kafamı karıştırdı.  Orada verdiğiniz adresteki bir fonksiyona çağrı var. Atladığım bir şey mi var?
    Bootloader_jump() fonksiyonuna verdiğiniz adres 0x81E 0000 adresi mi?

baran123

#5
Hocam maine ana programın içine şunu koyuyorum.
for (;;)
    {
        if (TM_DISCO_ButtonPressed()) 
        {
            Bootloader_Jump(BOOLOADER_ADDRESS);   //  BOOLOADER_ADDRESS = 0x081E 0000
        }

Butona bastım ve bootloader kısmına geçmiş olmam gerek dimi ? Benim yaptığım yazılımsal. ST'nin kendi bootloader bölümünü kullanmıyorum.

Bootloaderda tekrar her şeyi init yapıp kartı okumaya çalışıyorum fakat okuyamıyorum saçma bir şekilde takılma oluyor döngüde.

Klein

Butona bastığınızda Bootloader kısmına geçmiş olmuyorsunuz tam olarak.
0x081E 0000 adresindeki fonksiyonu çağırıyorsunuz.  Çağırmadan önceki regidter değerleri, yerel değilşkenler, fonksiyon dönüş adresi vs... hepsi yığına atılıyor.
Fonksiyonla işiniz bittiğinde tekrar butona bastığınız noktaya dönüp oradan işine devam edecek. 
Fakat main() özel bir fonksiyon. Main fonksiyonuna bu şekilde çağrı yapmak nelere sebep olur tam emin değilim.  Ayrıca programı derlediğinizde kodlarınız haricinde bir sürü adres, lirteral vs.. belleğin bir yerlerine yerleştiriliyor. Resetten main() fonksiyonu nun çalışması aşamasına kadar onlarca init işlemi yapılıyor. 
Örneğin stack.  Asıl programı derlediğinizde stack nerede?  bootloader  derlemesinde stack adresi ne? belki stack adresleri aynı ve çakışıyor. Belki literaller vs.. ortak alan kullanıyor.
İki ayrı derlemenin .map dosyasını derinlemesine incelemeden bir şey söylemek zor.

baran123

@Klein hocam Stack vs alanda herhangi bir ayırma yapmadım. Peki atladığımız adreste kodlar işlenmeye başlamadan önce RAM, stack heap vs hepsini temizlersek bu durumu düzeltebilir miyiz ?
Bu şekilde bir yazılımsal bootloader yapmanın en iyi yolu nedir ?


Klein

Yok daha da karıştırabilirsin.
Bootloader yazmaktaki amaca bağlı biraz. 
SD karttan mevcut yazılıma güncelleme mi yapmak? yoksa herhangi bir yazılımı kendi bootloaderiniz üzerinden atmak mı?

Eğer versiyon güncellemesi ise işler nispeten basit.


baran123

kendi programını güncelleyeceğim hocam.
içine yüklediğim bin uzantılı dosyaları tft ekranda listeleyip seçimi yükleyeceğim.

Klein

şöyle basit bir mantık işini kolaylaştırabilir.

Önce programının tamamını yaz bitir.  sd karttan dosyaları okuyan ,  flasha yazan , display'i güncelleyen tüm rutinlerinle birlikte komple programı bitir.

Sonra güncellenmeyecek olan kısımlar ( flash yazma , dosya okuma , temel display işlemleri) kalsın,  diğer tüm rutinleri flash'ın başka bir yerine taşı.

Fonksiyonları ve değişkenleri belleğin istediğin bir bölgesine yerleştirme konusunda aşağıdaki link işine yarayabilir.

http://blog.atollic.com/using-gnu-gcc-on-arm-cortex-devices-placing-code-and-data-on-special-memory-addresses-using-the-gnu-ld-linker

Programı bu şekilde başarılı bir şekilde çalıştırdıktan sonra iş kolay.

Versiyon güncellemesi yapacağın zaman programın sadece özel alana yerleştirdiğin kısımlarında değişiklik yap, bootloader (sd kart, display temel rutinler, flash yazma ...) kısmına dokunma.

programı derle.

SD karttan okuduğun verinin tamamını değil , sadece bootloader sektöründen sonraki sektörleri güncelle.


Test etmedim. Fakat işe yarayacağını düşünüyorum.

MrDarK

#11
Memory içindeki başka bir hex uygulamasına geçmek için aşağıdaki kodu kullanıyorum. Örnekte gideceğim uygulamanın bulunduğu adres 0x08003000 adresidir.

#define ApplicationAddress    0x08003000
typedef void(*pFunction)(void);

/* Jump Parameters */
pFunction Jump_To_Application;
uint32_t JumpAddress;

JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();


Ayrıca gideceğiniz uygulamanın systeminit fonksiyonunda vector table reloaction işlemini yapmanız gerekiyor. Eğer bu işlemi yapmazsanız interrupt rutinleri çalışmayacaktır. Relocation işlemi için aşağıdaki fonksiyonu kullanabilirsiniz. Veya SCB->VTOR adresine ilgili değeri yükleyin.

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x3000);

Picproje Eğitim Gönüllüleri ~ MrDarK

baran123

@Klein hocam böyle bir şey aklıma geldi ama benim asıl merak edip öğrenmek istediğim hafızanın başka bir yerine yüklenmiş hex, bin dosyasını alıp ana program yerine yazarak bunu çalıştırmak.

@MrDarK hocam bende şu şekilde bir fonksiyon kullanıyorum fakat benim bootloader'ım hafızanın sonunda bu bir fark yaratır mı ?
Dediğiniz düzenlemeyi yaptım şu şekilde Jump etmeye çalışıyorum.

void Bootloader_Jump(uint32_t addr)
{
	typedef void(*pFunction)(void);
	
	pFunction Jump_To_Application;
	uint32_t JumpAddress;

	NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x1E0000);
	
	JumpAddress = *(__IO uint32_t*) (addr + 4);
	Jump_To_Application = (pFunction) JumpAddress;
	__set_MSP(*(__IO uint32_t*) addr);
	Jump_To_Application();
}

Sanırım siz Bootloader ile başlayıp Application'a zıplıyorsunuz.
Benim yaptığım tam tersi :)

Bootloader'a zıpladığım da tekrar main içinde init yaparken SysInitde takılıyor.

Sanırım bu iş biraz zor olacak.

MrDarK

#13
Hafızanın sonunda olması veya başında olması herhangi bir şeyi değiştirmez. Lakin Jump fonksiyonunun içinde relocation kodunu çalıştırmayacaksın. Her uygulama kendi systeminit fonksiyonunda kendi vector adresine göre relocation yapması gerekli. Bu hem bootloader hemde Application programında yapılması gereken bir işlem.  Atladığın hex'in içindeki systeminit fonksiyonunun sonunda çalıştırman yeterli. Lakin dikkat et eğer systeminit fonksiyonun interrupt rutinlerinden birini kullanıyor ise programın çakılır çünkü henüz relocation kodunu işletmedin. Duruma göre eğer clock vb durumlar için systick vb bişeyi aktif ediyorsan bu relocation kodunu yukarı alabilirsin veya interruptları disable edip işlem bitiminde enable edebilirsin. Birde relocation adresini doğru vermelisin. 0x1E0000 adresi biraz ilginç geldi bana.

Eğer ki Klein hocanın da dediği gibi map dosyalarındaki hex oluşumlarının adreslerini start end şeklinde verebilirsen daha doğru yorumlayabiliriz. Kolaylık olması açısından systeminit fonksiyonlarını hem bootloader hemde application için paylaşırsan ilgili düzeltmeleri yapabiliriz.
Picproje Eğitim Gönüllüleri ~ MrDarK

baran123

Kusura bakmayın yoğunluktan yazamadım.
Hocam şimdi projeleri ayırdım daha rahat olacak.

Önce bootloader projesinde system_stm32f4xx.c dosyasında şu tanımlamayı yaptım.
#define VECT_TAB_OFFSET  0x1E0000

Bu arada benim bootloaderım bu adresten başlıyor "0x1E0000" (Bu son sektörün başlangıç adresi)
VECT Relocation olarak bunu vermeliyim dimi ?

Daha sonra Bootloader için main fonksiyonunda interruptları gecici olarak kapattım.
int main(void)
{
	SD_Result_t result = SD_Result_Error;
	
	__disable_irq();
	SystemInit();
	__enable_irq();
       ...


Şimdi bootloader'a geçtiğimde sd karta erişip dosya okuyabiliyorum. Hatta bootloader'ı geçince ana programı silebiliyorum :)
Buraya kadar problem yok gibi. Sizce buraya kadar bir hata var mı ?
Yok ise bundan sonra bin dosyası okuyup programlama yapmaya çalışacağım.