STM32 iki main fonksiyonu

Başlatan mr.engineer, 23 Ocak 2020, 23:27:01

mr.engineer

Merhaba,
STM32 mikrodenetleyicisi (cortex M3) ile içiresinde iki adet uygulama (iki main fonksiyonu) bulunduran bir uygulama yapmak istiyorum. Bootloder'dan jump yapmam gerektiğini öğrendim. Yardım edebilecek olursa sevinirim.

SpeedyX

İki main demek iki sonsuz döngülü fonksiyon demek.
Açılışta bir pine, eepromdaki bir değere yada neye istersen bakarak hangini çağıracağına karar verip gir birine.

mr.engineer

Alıntı yapılan: SpeedyX - 23 Ocak 2020, 23:55:35İki main demek iki sonsuz döngülü fonksiyon demek.
Açılışta bir pine, eepromdaki bir değere yada neye istersen bakarak hangini çağıracağına karar verip gir birine.


Bootlader kullanarak yapmak istedim. Bir jump fonksiyonu ile bootloader'a daha sonra da diğer main'e geçmeyi düşünmüştüm.

e-zeki

Alıntı yapılan: mr.engineer - 24 Ocak 2020, 00:04:01Bootlader kullanarak yapmak istedim. Bir jump fonksiyonu ile bootloader'a daha sonra da diğer main'e geçmeyi düşünmüştüm.

Böyle bir işlem için Bootloader'a gidip gelmene gerek yok.
Eğer RAM ve ROM yeterliyse @SpeedyX 'in dediği gibi uğraşmadan 2 sonsuz döngü ile işini görürsün.
Yok illa 2 ayrı hex dosyası tek işlemcide koşsun istiyorsan ve bu kodlar güncellenmeyecekse yine bootloader'a ihtiyacın yok. Sadece Jump fonksiyonu kullanarak 2 hex arasında gidip gelebilirsin.
Jump kod için dün @MrDarK 'ın verdiği örnek kodu kullanabilirsin.

z

Bu iki program fakli kisilerce bagimsiz gelistirilecekse biraz sikinti var.

Eger ortaklasa yazilacaksa sorun yok.

Sorun int vector tablosunda olacak gibi gorunuyor.

Her program parcasi kendi int tablosuna sahip olmali. Eger ortaklasa calisma yapilacaksa bu int fonksiyonlari birlikte yazilmali.

Eger iki ayri vector tablosu yapilacaksa bu kez program gecislerinde vector tablosunun adresinin de set edilmesi gerekecek.
Bana e^st de diyebilirsiniz.   www.cncdesigner.com

MrDarK

#5
- İki ayrı uygulama denince iki ayrı proje deniyor ise her projeyi ayrı ayrı oluşturacaksınız.
- Her uygulama için ROM alanını hesalayıp haritalandıracaksınız. Örn 0 - 0x100 arası 1.uygulama, 0x100 - 0x200 arası 2. uygulama gibi
- Sonrasında her projenin .ld (attolic/gcc için) uzantılı dosyasını açıp yukarıda belirttiğim rom alanlarını güncelleyeceksiniz.
- Sonrasında ana uygulama seçici kod talebe göre hangi projeye jump yapacağına karar verecek.

Eğer projelerde interrupt kullanılacak ise (normalde de gerekli olabilir emin değilim) Vector Table Relocation işlemi yapılması gerekiyor. Bunu da interrupt'ları aktif etmeden SystemInit fonksiyonunda veya main.c'nin ilk işlemi olarak aşağıdaki kodu işletebilirsiniz.

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x..... (Burası uygulama adresi olacak))

Jump kodu için nette bulduğum örneği buraya koyuyorum.

/**
 * @brief   Actually jumps to the user application.
 * @param   void
 * @return  void
 */
void flash_jump_to_app(void)
{
  /* Function pointer to the address of the user application. */
  fnc_ptr jump_to_app;
  jump_to_app = (fnc_ptr)(*(volatile uint32_t*) (FLASH_APP_START_ADDRESS+4u));
  HAL_DeInit();
  /* Change the main stack pointer. */
  __set_MSP(*(volatile uint32_t*)FLASH_APP_START_ADDRESS);
  jump_to_app();
}

Bu tasarım yapılacak ise rom haritalandırma işinde mutlaka 32bit'in katlarına göre ayrım yaparsanız faydanıza olur.
Picproje Eğitim Gönüllüleri ~ MrDarK

sımışka

Her iki programın koşacağı bellek bölgelerini linker ile ayarlaman gerekiyor. Sırasıyla bir jump fonksiyonu ile vektor tablosunu, stack ve program counterı güncellemen gerekiyor. Bu iş için bootloadera gerek yok fakat , mainleri güncellemek gerekeceği ve bootloader ile güncellemek istiyorsan gerekli. Bu iş için ufak bir program yazıp , sabit olarak belleğe gömüp başlangıçta bu yazılım ile ayağa kaldırırsan (asm düzeyinde bir kod ile kısıtlı ilklendirme yaparak), hangi maine geçiş yapacağına bu yazılım ile karar verebilirsin.

mr.engineer

Alıntı yapılan: MrDarK - 24 Ocak 2020, 10:16:18- İki ayrı uygulama denince iki ayrı proje deniyor ise her projeyi ayrı ayrı oluşturacaksınız.
- Her uygulama için ROM alanını hesalayıp haritalandıracaksınız. Örn 0 - 0x100 arası 1.uygulama, 0x100 - 0x200 arası 2. uygulama gibi
- Sonrasında her projenin .ld (attolic/gcc için) uzantılı dosyasını açıp yukarıda belirttiğim rom alanlarını güncelleyeceksiniz.

Herkese teşekkürler.

İlk söylediğiniz ROM alanı ayırma işlemi için Keil'de options for target kısmında target ve linker sekmelerinden bu ayırma işlemini yapacağım galiba. Yani kodda ROM için bir şey yazılmayacak. Peki bu ROM boyutu ( target sekmesinde IROM1 0x800000-0x20000 yazıyor.) neye göre belirleniyor. İkinci projeye ben de 0x8020001-0x20000 yazabilir miyim? (Burada 0x80.. start, 0x20000 ise size oluyor.) 

Alıntı yapılan: MrDarK - 24 Ocak 2020, 10:16:18- Sonrasında her projenin .ld (attolic/gcc için) uzantılı dosyasını açıp yukarıda belirttiğim rom alanlarını güncelleyeceksiniz.
- Sonrasında ana uygulama seçici kod talebe göre hangi projeye jump yapacağına karar verecek.

Burada bahsettiğiniz .ld dosyası asssembly dosaysı mı oluyor. Keil'da farklı uzantıya sahip. Bu rom alalarını yazdıktan sonra en son birinci projeme jump fonksiyonumu ekleyeceğim ve MCU'ya kodu yükleyeceğim. Daha sonra ikinci projeyi açıp onu da MCU'ya yükleyeceğim. Bu şekilde bir buton yardımıyla istediğim projeyi seçebilir miyim?

Yardımlarınız için teşekkürler. Daha önce böyle bir şey hiç yapmadığım için kafam karışık ve internette bu jump fonksiyonunu yazanları gördüm ama her şeyi nasıl yaptığını anlatan birini göremedim. Meşgul ediyorum sizi de kusura bakmayın. 


e-zeki

Öncelikle kullandığın mcu'nun refference manuel'inden o işlemcinin flash memeory yapısına bakman gerekiyor flash page mi sector mu ona göre bir adres belirlemelisin. sector yapısında olan mcu'larda sector boyutları birbirinden farklı ve yazma silme işlemi esnasında tüm sektörü tek kabul eder. sen 2 hex dosyasını aynı sektörde bulunan adres aralığına yazarsan 2. hexi yüklerken her zaman 1. hexi de siler.
Alıntı yapılan: mr.engineer - 24 Ocak 2020, 13:17:51Peki bu ROM boyutu ( target sekmesinde IROM1 0x800000-0x20000 yazıyor.) neye göre belirleniyor. İkinci projeye ben de 0x8020001-0x20000 yazabilir miyim? (Burada 0x80.. start, 0x20000 ise size oluyor.) 
Bu kısım senin kullandığım mcu'nun ROM boyutu ve başlangıç adresini belirtir. zaten orada da start - size olarak görünür. Projelerinin boyutlarına göre size belirlemelisin ve page / sektör durumuna göre de start adress belirlemelisin.
Bundan sonra jump fonksiyonundan önce 2. hexin (Memory'de 0x08000000'den başka bir adrese yazılacak olan) için vector table'ın adresini değiştirmen gerekiyor.
bu da  #define VECT_TAB_OFFSET şeklinde dosyanın birinde tanımlı.
Buraya kadar geldikten sonra jump fonksiyon kısmına geçebilirsin.

z

#9
Bu iki C projesi icin gecis islemlerini startup dosyasinda halletmekte ayrica fayda var.

Soyleki;

Reset islemi ardindan stack tanimi, stack pointerlerin alacagi degerler, startup islemleri ve C icin init islemleri (global degiskenlere ilk deger atama vs) main fonksiyonu calismadan once yapilir.

Bir C projesi calistiktan sonra diger C projesine gecilecekse tum bu init islemlerinin yeniden yapilmasi, bas agrimamasi icin onemlidir.

Bu tur mudahaleler icin en iyi yer de startup kodlari olacaktir. Fakat bu kisma asm yazmaktan baska yol var mi bilmiyorum.
Bana e^st de diyebilirsiniz.   www.cncdesigner.com

mr.engineer

#10
Alıntı yapılan: e-zeki - 24 Ocak 2020, 14:41:21Öncelikle kullandığın mcu'nun refference manuel'inden o işlemcinin flash memeory yapısına bakman gerekiyor flash page mi sector mu ona göre bir adres belirlemelisin. sector yapısında olan mcu'larda sector boyutları birbirinden farklı ve yazma silme işlemi esnasında tüm sektörü tek kabul eder. sen 2 hex dosyasını aynı sektörde bulunan adres aralığına yazarsan 2. hexi yüklerken her zaman 1. hexi de siler.Bu kısım senin kullandığım mcu'nun ROM boyutu ve başlangıç adresini belirtir. zaten orada da start - size olarak görünür. Projelerinin boyutlarına göre size belirlemelisin ve page / sektör durumuna göre de start adress belirlemelisin.
Bundan sonra jump fonksiyonundan önce 2. hexin (Memory'de 0x08000000'den başka bir adrese yazılacak olan) için vector table'ın adresini değiştirmen gerekiyor.
bu da  #define VECT_TAB_OFFSET şeklinde dosyanın birinde tanımlı.
Buraya kadar geldikten sonra jump fonksiyon kısmına geçebilirsin.

Yardımlarınız için teşekkürler. Birkaç gündür bakamamıştım bugün dediklerinizi yapmaya çalışıyorum.

Vector table offset kısmında takıldım. Derleyici de VTOR registerının tanımlı olmadığını gördüm. Daha sonra cortex M0 da VTOR registerının bulunmadığını öğrendim yani vector table her zaman 0x00000 adresindeymiş. Bu durumda ne yapmam gerekiyor?

Bir diğer anlamadığım yer vector table 0x0000 adresinde mi olur yoksa 0x08000 (flash memory başlangıç adresi) adresinde mi?

Benim anladığım kadarıyla Vector table 0x08000 adresinde bulunuyor ve istersek  0x0000 adresinden de ulaşabiliyoruz. (aliasing) Memory mapin en altında flash, system memory, SRAM boot configuration yazıyor.

HAKKITANIR

#11
Doğrumu anlamışım?
işlemci ilk çalışmasına başladı ve int main(void) içerisi işletilip, tüm ayarlamalar yapılıp,
While(1) içerine girildi.
Bu aşamadan sonra şu yapılmak isteniyor;
herhangibir şart gerçekleştiğinde, işlemcinin çalışması durdurulmadan,
tıpkı yukarıda ilk açılışta işletilen, int main(void) fonksiyonu gibi, ama daha farklı özelliklerdeki ve farklı ayarlamalardaki, ikinci bir int main(void) fonksiyuna veya adı başkada olabilir, atlanılacak ve bu fonksiyon işletilip tekrar, While(1) içerisinde kalınan yere dönülüp program buradan çalışmasına devam edecek.
iki main içinde tek bir while var galiba, anladığım kadarıyla.

Bence bu olur, ama ikinci main fonksiyonun adı farklı olup, adı bizim tanımlayacağımız bir şey olur,
ve while(1) içerisinde gerekli şartlar oluşana kadar bekler. yani bu ikinci main işlevini yapacak fonksiyon,
while(1) içerisinde olmalı.
Bootlooderi bilmem ama, Yanlış değilsem programda jump label tarzı işlemlerle while(1) içerisine girdikten sonra, while(1) dışına atlanılamıyordur diye düşünüyorum.
Atlanılabiliyorsa sizin iş zaten olmuş bitmiş görünüyor.
Atlanılamıyorsa bir kaç satır üst de, dediğim gibi bir işlem yapılsa işiniz  görülmez mi?

yalnız her iki durumdada mesela devam eden bir pwm çıkış döngünüz, devam eden bir dac çıkışı, Ic girişi, ayarlanan bir timer çalışması veya seri haberleşme tarzında bir yapı varsa, kısaca çalışan işlemci modüllerinin kontrol registerlerine, port konfigürasyonlarına,
yeniden dokunduğunuz anda ortalık karışabilir. bu kısımlarıda göze almak ve önlem almak gerekebilir.
veri kaybı ve çalışmaların sekteye uğrayacağı kesin, sonuç da ikinci bir main gibi bir şey oluşturacaksınız.
modüllerin yedeği yok bunlar iki main içinde aynı ve ortak olacaklar.

bu kısımları belirtenler olmuş üstde ki mesajlarda.

ben ikinci bir main fonksiyonunun çalışma zamanında kesinlikle gerekip gerekmediği noktasında tereddütte kaldım.
yapılacakta kesin gereklmi dir böyle bir şey, olmazsa olmazmı dır yani?
başka çözüm yöntemleriyle iş görülemez mi noktasında şüpheciyim.
SpeedyX in dediği gibi bir şey olurmu olmazmı bunu bilemiyorum ama,
program illa int main(void) fonksiyonunu işletmek zorunda olmalı diye biliyorum.
ne olursa olsun bu işletilip while(1) içerisine girilmek zorunda kalınacaktır sanıyorum.

bir deneyebilirmisiniz derleyiciniz while(1) içerisinden, while(1) dışına yani int main(void) içinde bir yere dallanabiliyor mu?
dallanmıyordur herhalde.
yada bir kesme fonksiyonu içine dallanabiliyormu diyecem ama, bu zaten gereksiz olur herhalde.
kolay gelsin. 

mr.engineer

Amacım yukarıda anlatıldığı gibi iki farklı proje (iki farklı keil projesi) oluşturup ikisini de mikrodenetleyiciye gömmekti. İstediğim zaman birinden diğerine geçeceğim. Dedikleri gibi ROM'u ikiye bölüp her birine bir proje atmak istiyorum. Fakat bir türlü yapamadım.

HAKKITANIR

Alıntı yapılan: mr.engineer - 28 Ocak 2020, 21:11:12Amacım yukarıda anlatıldığı gibi iki farklı proje (iki farklı keil projesi) oluşturup ikisini de mikrodenetleyiciye gömmekti. İstediğim zaman birinden diğerine geçeceğim. Dedikleri gibi ROM'u ikiye bölüp her birine bir proje atmak istiyorum. Fakat bir türlü yapamadım.

yapmaya çalıştığınızın kesin yapılabildiğini bunun mümkün olduğunu söylüyorsunuz anladığım kadarıyla.

bence tek yazılımla bir şekilde olur diye düşünüyorum bu iş. bir programdan komple diğer programa geçilmesindense,
aynı programda farklı özelliklerde çalışma şekli, seçimlik şartları oluştuğunda uygulanabilir diye düşünmüştüm.
umarım yaparsınız istediğinizi bizde olabiliyormuş diye not düşeriz bir yere,
belki bize de lazım olur böyle bir çalışma ileride. sonucu bildirirseniz sevinirim. bizimde bilgimiz olmuş olur.

size iyi çalışmalar dilerim. 

volkanunal

#14
Merhabalar herkese, başlıkta belirtilen konu üzerinde bazı kısımları açıklığa kavuşturmak için bir paylaşım yapmak istiyorum. Hatalı gördüğünüz kısımları belirtirseniz sevinirim.

Şu şekilde bir gidişat yapacağız, 2 farklı projeyi demo bir karta yükleyerek birbiri arasında bir geçiş yapacağız.

Kullandığım kart :NUCLEO-F401RE

Öncelikle kullandığınız işlemcinin "Reference Manual" dosyasını internet üzerinden bulalım. Bu dosyayı bulduktan sonra, "Embedded Flash memory in STM32F401xB/C and STM32F401xD/E" başlığına gidiyoruz.

Burada flash hafızamızın layoutunu görebiliriz ve uygulamaları koyacağımız sektör adreslerini görmekteyiz.

Uygulamada Sector 0'da bir uygulama, Sector 1'de ise diğer uygulamayı barındıracağım ve aralarında geçiş yapacağım.

Öncelikle gelin Sector 0'a yapacağımız uygulamayı hazırlayalım. Öncelikle Sector 1'e atlamak için bir fonksiyon yazalım.

static void JumpFunctionSectorOne(void)
{
	typedef void (*Jump)(void);
	Jump JumpFunc;
	uint32_t JumpAddr = *(uint32_t *)(0x08004000 + 4);
	JumpFunc = (Jump)JumpAddr;
	__set_MSP(*(__IO uint32_t*)0x08004000);
	JumpFunc();
}

Burada
*(uint32_t *)(0x08004000 + 4)
4 ofset vermemizin sebebi zıplayacağımız yerde bulunan vector tablosunda (startup_stm32f4xx dosyasında bulabilirsiniz.). Reset Handler ikinci sırada bulunan bir fonksiyon göstericisi ve word cinsinden adreslendiği için + 4 eklemekteyiz.

Aynı şekilde main stack pointer'i set etmeyi unutmayın, daha sonrasında tanımsız davranışlar yaşamamak için.

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler

Kodu derleyip yüklerken eğer defaultta bulunan sector adresinden başka bir yere yükleme yaparsanız, hem kodun barınacağı adresi hem de vector tablosunun yer alacağı adresi değiştirmeniz gerekmekte. Aksi takdirde jump ettğiniz yerde çakılır uygulamanız, bu oldukça kritik. Biz bu uygulamayı sector 0 yani default alanda barındaracağımız için herhangi bir değişiklik yapmayacağız şu anda. Fakat sector 1 içerisinde çalışacak uygulama için bu değişiklikleri az sonra yapacağız.

Main içerisinde önce uygulamanın başladığını ve bir süre sonra jump edeceğini söylüyoruz.

printf("Cihaz ayakta, Cihaz 0.sektorden basladi");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		printf("\r\n Uygulama Jump Edecek \r\n");
		HAL_Delay(3000);
		JumpFunctionSectorOne();
		printf("\r\n Kod buraya ulasmadi \r\n");
		
    /* USER CODE BEGIN 3 */
  }

Şimdi ise, yazacağımı kodu Sector 1'e koyalım ve Sector 0'a jump yapmasını sağlayalım. Yani bir önce bahsettiğimiz uygulamanın tam tersi. Öncelikle rom adresini değiştirmemiz gerekmekte.
Eğer keil kullanıyorsanız, Option For Target kısmında bulunan IROM1 kısmından bu adresi değişterebilirsiniz. Uygulamayı Sector 1 içerisinde barındaracağım için, reference manual üzerinden oranın adresini aldım ve 0x8004000 şeklinde IROM1 kutucuğunu düzenledim. Tabi bu kadarı yeterli mi? Hayır. Şimdi ise vector tablomuzu düzenlememiz gerekmekte. Bunun için, system_stm32f4xx.c içerisine gidelim

#define VECT_TAB_OFFSET  0x8004000 /*!< Vector Table base offset field. 
                                   This value must be a multiple of 0x200. */

Yukarda görmüş olduğunu kısım ilk uygulamamız da 0x00 olarak tanımlıydı fakat şimdi farklı bir sector de barındırdığımız kodu çalıştıracağımız için buraya offset veriyoruz.

Sıra geldi Sector 0'a zıplamak için yapmamız gerekenlere, bunun için şöyle bir fonksiyon yazalım, ilk uygulamaya benzer şekilde.

static void JumpFunctionSectorZero(void)
{
	typedef void (*Jump)(void);
	Jump JumpFunc;
	uint32_t JumpAddr = *(uint32_t *)(0x08000000 + 4);
	JumpFunc = (Jump)JumpAddr;
	__set_MSP(*(__IO uint32_t*)0x08000000);
	JumpFunc();
}


Şimdi ise main içerisinde uygulamanın sector 1den başladığını ve jump edeceğini belirtelim.

printf("Cihaz ayakta, Cihaz 1.sektorden basladi");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		printf("\r\n Uygulama Jump Edecek \r\n");
		HAL_Delay(3000);
		JumpFunctionSectorZero();
		printf("\r\n Kod buraya ulasmadi \r\n");
		
    /* USER CODE BEGIN 3 */
  }

Şimdi bu kodu da kartımıza yükleyelim, yalnız karta yüklerken flashı temizlemeyin, çünkü sector 0'a yüklemiş olduğumuz bir yazılım var. Eğer temizlerseniz uygulama çalıştıktan bir sonra sector 0'a zıplayacak ve FF olduğu için hardfault'a düşeceksiniz.

İşte bu kadar, iki farklı uygulamayı bu şekilde tek bir kart işlemcide çalıştırabilir ve arasında geçiş yapabilirsiniz.

Eğer üretim kodunuzda böyle yapı barındaracaksınız bazı korumaları almanızda fayda var, başka bir sektöre geçiş yapmadan önce o sektörde bir yazılım olup olmadığını kontrol etmek gibi.