Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2014:pixel-light

Řízení pixel light controller kitu

  • Pavel Kostelník
  • xkoste07@stud.feec.vutbr.cz

Zadání

Pomocí mikroprocesoru LPC407x zprostředkujte komunikaci mezi PC, proudovým zdrojem a integrovaným obvodem pixel light controller. Dále implementujte ukázkové funkce pro pixel light controller (wiping blinker, dark zone, …). Využijte plánovač (RTOS).

Úvod

Tento projekt vychází z aktuálně řešené diplomové práce na téma Řízení maticového světlometu s LED diodami. Cílem této práce je úprava programu v mikroprocesoru tak, aby bylo dosaženo větší univerzálnosti, lepšího časování a přidání některých dalších funkcí. Jedná se o úpravu již funkčního programu, který pracuje jako velká nekonečná smyčka, do podoby pro využití operačního systému reálného času (RTOS).

Hardware

Projekt využívá hardwarové prostředky, které byly vytvořeny v rámci diplomové práce. Konkrétně se jedná o takzvaný evaluační kit. Jeho blokové schéma je možné vidět na obrázku:

Výsledná realizace je zachycena na následující fotografii.

Software

Knihovna LPCOpen

Práce stávajícího programu s periferiemi je řešena na úrovni registrů. Přímé použití registrů u procesoru LPC není příliš velký problém, protože příručka (User guide) a datasheet, dodávaný k těmto procesorům, jsou psány vcelku přehledně a srozumitelně, přesto v rámci úprav stávajícího programu bylo uvažováno o použití knihovny LPCOpen, která je oficiálně vydávaná výrobcem mikroprocesoru NXP. Balík LPCopen podporuje několik vývojových prostředí, v rámci toho také mnou použitý Keil uVision. Knihovna pro projekt se skládá ze dvou části – část pro mikroprocesor (lpc_chip) a část pro použitou desku (lpc_board). Každá z obou částí obsahuje vlastní projekt pro Keil uVision, kde jsou různé provázané zdrojové soubory, které obsahují všechny funkce, které knihovna obsahuje. Ty je možné upravit, a po sestavení projektu vznikne soubor *.lib. Oba soubory lpc_chip.lib a lpc_board.lib je třeba přidat do vlastního projektu. Knihovnu pro procesor je možné použít z výchozího stavu, knihovnu pro desku je třeba vždy upravit podle aktuálně použité desky. Větší problém tvoří horší dokumentace k těmto knihovnám a minimum ukázkových projektů, jeden příklad ke každé periferii, s nejistou funkčností. Vzhledem k těmto problémům bylo nakonec od použití knihovny LPCOpen upuštěno, zachováno řešení práce na úrovni registrů a věnování větší pozornosti operačnímu systému reálného času.

Operační systém reálného času RTX

Z důvodu větší univerzálnosti bylo rozhodnuto o použití operačního systému reálného času. Byly uvažovány dva, konkrétně známý FreeRTOS a RTX, dodávaný spolu s vývojovým prostředím Keil uVision 5. Nakonec se jako největší problém ukázal vybraný mikrokontrolér, který není přímo podporovaný systémem FreeRTOS, proto byl vybrán druhý jmenovaný – RTX. Systém RTX je preemptivní operační systém, což znamená, že přepnutí tasků vyvolává samotný systém a nevyžaduje kooperaci s běžícími tasky, které by se musely vzdát běhu. RTX má na stránkách www.keil.com vcelku obsáhlou podporu, skládající se z obecného popisu, popisu všech funkcí a pár ukázkových příkladů. Systém RTX je třeba nastavit v samotném vývojovém prostředí a poté i ve zdrojovém souboru. Nastavení systému se nachází v souboru RTX_Conf_CM.c. Zde je možno například nastavit:

Nastavení systému se nachází v souboru RTX_Conf_CM.c. Zde je možno například nastavit:

  • Maximální počet současně běžících funkcí (12)
  • Velikost zásobníku funkcí (512 B)
  • Kontrola přetečení zásobníku (Ano)
  • Použitý Tick timer (Core SysTick)
  • Konfigurace přepínání (Round-Robin)
  • Chybové funkce

Základní použité funkce systému RTX:

  • os_sys_init() - iniciace operačního systému
  • os_tsk_create() - vytvoření tasku
  • os_tsk_delete() - smazání tasku
  • os_sem_init() – iniciace semaforu
  • os_sem_send() – nastavení semaforu
  • os_sem_wait() – čekání na semafor
  • os_mut_init() – iniciace mutexu
  • os_mut_release() – vzdání se mutexu
  • os_mut_wait() – čekání na volný mutex
  • os_dly_wait() – uspání tasku na daný čas

Vlastní realizace softwaru

Program pro mikroprocesor je napsán v jazyce C ve vývojovém prostředí Keil uVision 5. Základem programu je operační systém reálného času RTX, který je pro zjednodušení provozován v konfiguraci Round – Robin, což znamená, že všechny tasky mají stejnou prioritu a k přepínání tasků dochází cyklicky. Perioda přepínání je nastavena na 1 ms.

Znázornění celkové funkce programu je znázorněna na následujícím obrázku.

TASK: init

Po startu programu programu ve funkci main dojde k inicializaci systému RTX, nastavení periferií a povolení přerušení od periferie UART0 při příjmu dat. Následně je spuštěn dočasný inicializační task, ve kterém jsou spuštěny všechny trvalé tasky, inicializovány semafory a mutexy. Po provedení těchto operací se inicializační task sám smaže. Kód tasku je pro ukázku uveden zde:

//------------------------- TASK: init -- (Begin) --------------------------------
// iniciation task, start other task
__task void init (void) 
{
   // create tasks:
      t_blink = os_tsk_create(blink, 0);
      t_uart_tx = os_tsk_create(uart_tx, 0); 
      t_interpreter = os_tsk_create(interpreter, 0);
      t_spi247 = os_tsk_create(spi247, 0);
      t_spi787 = os_tsk_create(spi787, 0);
      t_dc247 = os_tsk_create(dc247, 0);	
 
   // init semaphores:
      os_sem_init (&Semspi247, 0);
      os_sem_init (&Semspi787, 0);
      os_sem_init (&Semdc247, 0);
 
   // init mutexes
      os_mut_init(&Mutspi247);
      os_mut_init(&Mutspi787);
      os_mut_init(&Mutuart_tx);
 
   // delete itself:
      os_tsk_delete_self ();
}
//------------------------- TASK: init -- (End) ----------------------------------

Při obvyklém běhu programu je spuštěno 6 tasků, z nichž dva běží neustále – task blink a interpreter, ostatní trvalé tasky čekají na semafor. Task blink pouze bliká jednou LED diodou na desce s periodou 1 s pro signalizaci běhu programu. Kód tasku je pro ukázku uveden zde:

//-------------------------- TASK: blink -- (Begin) ---------------------------------
// Blinking with LED D5	
__task void blink (void)
{
   while(1)
   {
      LED_off(5);			// turn off LED5
      os_dly_wait (500);              // wait for 500 ms
      LED_on(5);			// turn on LED5
      os_dly_wait (500);              // wait for 500 ms
   }
}
//-------------------------- TASK: blink -- (End) ----------------------------------

TASK: interpreter, spi787, spi247, dc247

Task interpreter přebírá přijatá data po lince UART z počítače a dále je třídí na základě požadované operace, případně danou operaci sám provede. Například pokud je požadována operace komunikace po SPI s obvodem zdroje, task interpreter pošle data do příslušného bufferu a zašle semafor tasku spi787, kterého tím odblokuje, a task spi787 vykoná příkaz, který mu byl předán příslušným bufferem. Po vykonání příkazu task spi787 odešle výsledná data do počítače přes UART linku tím, že přepošle data tasku uart_tx, který se postará o jejich korektní odeslání.

TASK: wb, dz, bl

Dále software obsahuje tři dočasné tasky, které se vytváří a mažou dynamicky v případě, že je daný efekt potřeba. Konkrétně jsou implementovány funkce Wiping Blinker, Dark Zone a Bending Light. Například pokud přijde z počítače povel pro efekt Dark Zone, task interpreter vytvoří nový task dz, který se už sám stará o odesílání příslušných dat do SPI periferie. Task s efektem se maže opět příkazem z počítače. V jednu chvíli je možné mít spuštěných i více kopií stejného tasku, které obsluhují různé obvody integrovaného budiče. Ukázka jednoho tasku je uvedena zde:

//-------------------------- TASK: wb -- (Begin) ----------------------------------
// LED effect - Wiping blinker	
__task void wb (void)
{
   uint8_t target = newtarget;		// target device
   float wb_dt = 0; 			// time difference
 
   while(1)
   {
      wb_dt = (float)(wb_stop[target] - wb_start[target])/12.0;	
 
      if (wb_counter[target] >= wb_per[target])	// counter overflow
         wb_counter[target] = 0;
      else
         wb_counter[target]++;			// increment counter
 
 
      if (wb_counter[target] < wb_start[target])	// all LEDs OFF
      {
         for (i=0; i<12; i++)
         {
            DCs[i] = 1023;
         }
         j=0;
      }
      else if (wb_counter[target] < wb_stop[target])	//all LEDs dimmed
      {	
         for (i=0; i<12; i++)
         {
            if (wb_counter[target] < (wb_start[target] + i*wb_dt))
               DCs[i] = 1023;
            else if (wb_counter[target] < (wb_start[target] + (i+1)*wb_dt))
               DCs[i] = (wb_start[target] + (i+1)*wb_dt - wb_counter[target])/wb_dt*1023;
            else 
               DCs[i] = 0;
         }			
      }
      else	// all LEDs ON
      {
         for (i=0; i<12; i++)
         {
            DCs[i] = 0;
         }
      }
 
      dimming(DCs, LEDcount, ON, OFF, TR, lastLevel[target]);	// ON, OFF, TR values computation using Dimming algorithm
 
      os_mut_wait(&Mutspi247, 0xFFFF);			// wait for mutex
 
      SPI_SendFrame(target + 3, 1, 0xCF, 0, 0, 0);	// send SPI frame to pixel light
 
      while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received 
      SPI_RcvData = (LPC_SSP2->DR)<<16;			// shift received data
      while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received 
      SPI_RcvData += LPC_SSP2->DR;			// sum received data	
 
      SPI_SendFrame(target + 3, 1, 0xCC, ((SPI_RcvData)&(0xFB)), (SPI_RcvData>>8)&0xFF, (SPI_RcvData>>16)&0xFF);	
      while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received
      SPI_RcvData = (LPC_SSP2->DR)<<16;			// shift received data
      while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received
      SPI_RcvData += LPC_SSP2->DR;			// sum received data						
 
      for(i=0; i<12; i++)		// send ON, OFF and TR values to pixel light
      {
         SPI_SendFrame(target + 3, 1, i + 0xC0, ((OFF[i]<<4)+TR[i])&0xFF, ((ON[i]<<6)+(OFF[i]>>4))&0xFF, (ON[i]>>2)&0xFF);  
         while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received
         SPI_RcvData = (LPC_SSP2->DR)<<16;		// shift received data
         while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received
         SPI_RcvData += LPC_SSP2->DR;			// sum received data	
      }
 
      SPI_SendFrame(target + 3, 1, 0xCC, ((SPI_RcvData)&0xFF)|(1<<2), (SPI_RcvData>>8)&0xFF, (SPI_RcvData>>16)&0xFF);	//set MAPENA bit
      while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received
      SPI_RcvData = (LPC_SSP2->DR)<<16;			// shift received data
      while ((LPC_SSP2->SR & 0x4)== 0x0){}		// wait until frame is received
      SPI_RcvData += LPC_SSP2->DR;			// sum received data	
 
      os_mut_release(&Mutspi247);			// release mutex
 
      os_dly_wait(20);
 
   } // end while(1)
}
//--------------------------- TASK: wb -- (End) -----------------------------------

Operační systém reálného času RTX je komplexní a velmi rozsáhlý nástroj, proto bez větších zkušeností je velký problém využít všechny jeho možnosti a vytvořit dobře optimalizovaný program. Taky proto byl kladen důraz na jednoduchost a využití pouze základních možností RTX, aby nedošlo k zamotání do různých funkcí a možností. Mezi další možnosti, které systém RTX podporuje, jsou mailboxy, signály a události, tak by jistě stálo za zvážení, zda by programu pomohly.

Ukázka funkce

Zdrojové soubory

Zdrojová data nejsou z důvodu ochrany duševního vlastnictví společnosti ON Semiconductor zcela zveřejněna. V rámci zdrojových souborů je možné najít nastavení RTX souborem RTX_Conf_CM.c. Dále jsou ve zdrojovém souboru main.c zveřejněny všechny tasky a jejich provázání, pouze pár menších částí bylo vyjmuto, které ale nemají vliv na fungování RTOS. Dále je v přiloženém balíku zveřejněno schéma desky s mikrokontrolérem. mpoa_pixellight.zip

Závěr

Naprogramovaný software je plně funkční, což je možné vidět i na ukázkovém videu níže. Nastavené časové údaje u ukázkových funkcí odpovídají skutečnosti, a proto je možné tvrdit, že časování v RTOS funguje podle požadavků. V softwaru je navíc implementovaná kontrola přetečení zásobníků jednotlivých tasků. Při testování softwaru bylo ověřeno, že aktuálně nastavená hodnota velikosti zásobníků 512 B je dostatečná.

2014/pixel-light.txt · Poslední úprava: 2015/01/19 01:44 autor: Pavel Kostelník