Image
29.5.2016 1 Comments

Seriál: Vývoj pre IoT / Aplikácie pre mikrokontroléry ARM Cortex M3 a M4

V minulom čísle sme predstavili produktovú rodinu mikrokontrolérov ARM Cortex a základné princípy architektúry rady M určenej pre nákladovo citlivé aplikácie tak vo veľkosériových produktoch, ako aj v amatérskych, hobby, či študentských projektoch. Ako príklad lacnej, ale pritom výkonnej vývojovej dosky s veľmi nízkou spotrebou sme predstavili kit STM32 Discovery. Vo voľnom pokračovaní ukážeme, ako pre Cortex M3 a M4 vyvíjať aplikácie, v našom prípade bude aplikácia blikať LED diódou, čiže bude využívať vstupno výstupné porty.

Vývojová doska STM32 Discovery. Vpravo je variant STM32L

IO porty

Podľa typu puzdra môže mikrokontrolér STM 32 obsahovať až 80 obojsmerných vstupno-výstupných pinov, ktoré sú zoskupené do piatich 16 pinových  portov označených ako GPIOA, GPIOB, GPIOC, GPIOD a GPIOE. Mikrokontrolér STMF100RB má tri úplné porty po 16 pinov GPIOA, B, C a z portu GPIOD sú k dispozícii tri piny D0, D1 a D2 .   Každý z pinov portu môže byť využitý v troch režimoch

  • GPIO (General Purpose IO)
  • Alternatívna funkcia (USART, CAN, časovače, I2C a SPI) – napríklad pin PA9 môže byť inicializovaný aj ako USART1_TX
  • Vstup externého prerušenia

Alternatívne funkcie pinov procesora STM32F101RB

Aby to bolo ešte zaujímavejšie a flexibilnejšie STM32 umožňuje mapovať alternatívne funkcie na rôzne piny.  Každá z periférií má jedno alebo dve bitové polia, ktoré umožňujú mapovanie k rôznym  kombináciám pinov. Pre túto funkcionalitu odporúčame naštudovať originálnu dokumentáciu.

Pre našu aplikáciu je v inicializácii potrebné nastaviť pin PC8 na ktorom je svietivá dióda LD4 ako výstup. To ale nestačí. Zo schémy systémovej architektúry je zrejmé, že pre fungovanie GPIOA,B,C,D a E je potrebné pripojiť hodinový signál na APB2 zbernicu. Realizuje sa to cez RCC register s názvom APB2ENR. Pre GPIOC musíte nastaviť bit číslo 4  APB2ENR registra.

Schéma systémovej architektúry

V hlavičkovom súbore stm32f10x.h je pre tento účel definovaná bitová maska

#define RCC_APB2ENR_IOPCEN ((uint32_t)0x00000010) /*!< I/O port C clock enable */

 

takže na pripojenie hodín na APB2 použijete príkaz

RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

Teraz môžete inicializovať port GPIOC, pin 8.  Každý GPIO port má niekoľko registrov pre konfiguráciu a ovládanie stavov vstupov a výstupov. Pre konfiguráciu sú najdôležitejšie 32-bitové registre CRL a CRH. CRL register je zodpovedný za konfiguráciu pinov 0-7 a CRH register má na starosti piny 8-15. Pre každý pin GPIO sú teda k dispozícii štyri konfiguračné bity.

Bity konfiguračných registrov pre GPIO porty

V našom príklade budeme konfigurovať pin 8 prostredníctvom registra CRH. Použijeme režim Push-pull a na blikanie LED nám bohato stačí výstupný mód o frekvencii 2 MHz (mode 10 binárne).

GPIOC->CRH   &= 0xFFFFFFF0;  

GPIOC->CRH   |= 0x00000002;   

Ovládanie pinov GPIO môžete realizovať zápisom hodnoty portom do  ODR (Output Data Register), ktorý modifikuje stavy všetkých pinov, alebo do registrov BSRR (Bit Set / Reset Register), ktorý nastaví výstupný pin na hodnotu 1 či BRR (Bit Reset Register), ktorý nastaví pin do nuly. Tento zápis ovplyvní len vybraný pin. Zvyšný kód pre blikanie LED v slučke je triviálna záležitosť. Pripomíname, že sa jedná o úvodný príklad zameraný na IO porty v ktorom sme úmyselne nepoužili časovač.

volatile uint32_t delay;

      

int main(void)

{

    //hodiny na APB2

      RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

      

    //Inicializacia GPIO8

       GPIOC->CRH   &= 0xFFFFFFF0;  

       GPIOC->CRH   |= 0x00000002;

      

     //hlavna slucka programu

     while (1)

     {

       for(delay = 0; delay < 500000; delay++);

       GPIOC->BSRR = (1 << 8);

       for(delay = 0; delay < 500000; delay++);

       GPIOC->BRR = (1 << 8);

 }

}

 

Nastavenie, nulovanie, či invertovanie hodnôt pinov si môžete taktiež zjednodušiť definovaním funkcií

#define Pin0     ((u16)0x0001) 

#define Pin1     ((u16)0x0002) 

#define Pin2     ((u16)0x0004) 

#define Pin3     ((u16)0x0008) 

#define Pin4     ((u16)0x0010) 

#define Pin5     ((u16)0x0020) 

#define Pin6     ((u16)0x0040) 

#define Pin7     ((u16)0x0080) 

#define Pin8     ((u16)0x0100) 

#define Pin9     ((u16)0x0200) 

#define Pin10    ((u16)0x0400) 

#define Pin11    ((u16)0x0800) 

#define Pin12    ((u16)0x1000) 

#define Pin13    ((u16)0x2000) 

#define Pin14    ((u16)0x4000) 

#define Pin15    ((u16)0x8000) 

#define PinAll   ((u16)0xFFFF) 

 

#define SetPA(Pin)   GPIOA->BSRR = Pin

#define SetPB(Pin)   GPIOB->BSRR = Pin

#define SetPC(Pin)   GPIOC->BSRR = Pin

#define SetPD(Pin)   GPIOD->BSRR = Pin

#define SetPE(Pin)   GPIOE->BSRR = Pin

 

#define ClrPA(Pin)   GPIOA->BRR  = Pin

#define ClrPB(Pin)   GPIOB->BRR  = Pin

#define ClrPC(Pin)   GPIOC->BRR  = Pin

#define ClrPD(Pin)   GPIOD->BRR  = Pin

#define ClrPE(Pin)   GPIOE->BRR  = Pin

 

#define InvPA(Pin)   if((GPIOA->ODR&Pin)==0)  SetPA(Pin); else ClrPA(Pin)

#define InvPB(Pin)   if((GPIOB->ODR&Pin)==0)  SetPB(Pin); else ClrPB(Pin)

#define InvPC(Pin)   if((GPIOC->ODR&Pin)==0)  SetPC(Pin); else ClrPC(Pin)

#define InvPD(Pin)   if((GPIOD->ODR&Pin)==0)  SetPD(Pin); else ClrPD(Pin)

#define InvPE(Pin)   if((GPIOE->ODR&Pin)==0)  SetPE(Pin); else ClrPE(Pin)

 

Určite namietnete, že hlavne nastavenie konfigurácie GPIO portov priamo cez registre CRH, či CRH je pomerne ťažkopádne. Situáciu si môžete zjednodušiť zadefinovaním parametrov a funkcií pre výber správnych konfiguračných pinov v týchto registroch, vrátanie masiek pre ich nulovanie cez operáciu AND, napríklad

#ifndef _GPIO_CNF

  #define _GPIO_CNF

      

       #define GPIO_ANALOG_IN 0

       #define GPIO_FLOATING_IN 1

       #define GPIO_PULLUPDOWN_IN 2

       #define GPIO_PUSHPULL_OUT 0

       #define GPIO_OPENDRAIN_OUT 1

       #define GPIO_PUSHPULL_AFIO 2

       #define GPIO_OPENDRAIN_AFIO 3

      

       #define GPIO_INPUT 0

       #define GPIO_OUTPUT10MHz 1

       #define GPIO_OUTPUT2MHz 2

       #define GPIO_OUTPUT50MHz 3

      

       #define GPIO_CFG(mod, cfg) ((cfg << 2) | (mod))

       #define PIN_CRL(pin, cfg) (cfg << (pin * 4))

       #define PIN_CRH(pin, cfg) (cfg << ((pin - 8) * 4))

       #define MASK_CRL(pin) ((u32)~(15 << (pin * 4)))

       #define MASK_CRH(pin) ((u32)~(15 << ((pin - 8) * 4)))

#endif

 

Potom pre nastavenie pinu 8 GPIOC použijete výraz, ktorý obsahuje

GPIOC->CRH = (GPIOC->CRH & MASK_CRH(8)) | PIN_CRH(8, GPIO_CFG(GPIO_OUTPUT2MHz, GPIO_PUSHPULL_OUT));

 

Je to univerzálnejšie, no stále musíte určiť, či sa jedná o register CRL pre piny 0-7, či CRH pre piny 8-15, nehovoriac o tom, že by ste takto manuálne museli konfigurovať ďalšie moduly, teda časovače, USART, SPI... Preto v budúcom dieli ukážeme použitie knižníc pre konfiguráciu a obsluhu štandardných periférií. Pre nedočkavých čitateľov – knižnicu nájdete v adresári c:\Keil\ARM\RV31\LIB\ST\STM32F10x_StdPeriph_Driver. (Názov adresára a súborov knižnice závisí od verzie vývojového prostredia)

Aplikácia Blinky

Tento príklad je analógiou aplikácie Hello world, ktorou začíka každý kurz programovania. Cieľom v tomto prípade nie je vypísať text, ale  rozblikať svietiacu diódu na vývojovej doske.  Príklad bude vo vývojovom prostredí Keil μVision  (www.keil.com/arm/mdk.asp) skúšobná verzia, maximálnou veľkosťou generovaného kódu na 32 kb je zdarma. Na vytvorenie nového projektu použite položku menu Project->New μVision Project. V zložke STMicroelectronics vyberte mikrokontrolér, ktorý máte osadený vo vašej doske. Pre vývojovú dosku STM 32 Discovery vyberiete typ  STMF100RB.

Dialóg pre výber procesora je zároveň dobrou príležitosťou na inventúru toho, akými možnosťami príslušný mikrokontrolér disponuje.  STMF100RB je členom produktovej rodiny ARM 32-bit Cortex-M3. Má 128 kB FLASH pamäti a 8kB RAM, ktorý je možné taktovať maximálne na 24 MHz. K dispozícii máte interný 8Mhz RC oscilátor, PLL deličku kmitočtu externého kryštálu (na vývojovej doske STM32 Discovery je osadený kryštál 8 MHz), interný 40kHz oscilátor RTC (Real-Time Clock), NVIC (Nested Interrupt Controller), 7- kanálov DMA, 12-bit 16-kanálový A/D prevodník, dva 12-bitové 2-kanálové D/A prevodníky,  51 rýchlych I/O portov, 2 x SPI, 2 x I2C, 3 x USART, 2 x Watchdog Timer, 6 x 16-bitové časovače a jeden 16-bitový 6-kanálový časovač s rozšírenými možnosťami a podporou PWM. Môžete využiť zavádzanie kódu a ladenie cez rozhrania JTAG a SWD a v prípade potreby môžete procesor prepnúť do  módu pre úsporu napájania.

V prvom projekte ak by sme ho vytvárali úplne od začiatku nie je pre začiatočníka jednoduché pochopiť niektoré súvislosti, preto ako prvý zoznamovací krok s platformou Cortex M3 a M4 otvorte už hotový cvičný projekt Blinky, ktorý nájdete v adresári \Keil\ARM\Boards\ST\STM32-Discovery\Blinky

Všetko podstatné s výnimkou inicializácie je sústredené do súboru Blinky.c. V hlavnej slučke while(1)  sa v závislosti od počtu zatlačení tlačidla prepínajú diódy, ktoré blikajú a riadi sa aj ich rozsvecovanie a zhášanie.

int main (void)

{

  LED_Config();

  BTN_Config();                            

 

  SysTick_Config(SystemCoreClock / 100);     //nastavenie casovaca 10 ms

 

  while(1)

  {

    if (BTN_Pressed())

    {

      ledVal += 1;

      if (ledVal > 3) ledVal = 1;

  }

 

    if (ledBlink == 1)

    {

        ledBlink  = 0;

         ledOn    ^= 1;

        if (ledOn == 1) LED_Out (ledVal);       //zapni LED

        else            LED_Out (0);             //vypni led

    }

  }

}

 

To, či má vybraná dióda svietiť je dané obsahom premennej ledBlink. Táto premenná sa nastavuje  v obsluha prerušenia od časovača SysTick. K zmene hodnoty premennej ledBlink dôjde vtedy keď časovač, ktorý je nastavený na 10 ms tiky napočíta v premennej ticks hodnotu definovanú v konštante  LED_BLINK_RATE.

void SysTick_Handler (void)

{

  static uint32_t ticks;

 

  if (ticks++ >= LED_BLINK_RATE)

  {

    ticks    = 0;

    ledBlink = 1;

  }

}

 

Skôr než pristúpime k popisu kódu pre fyzickú obsluhu tlačidiel a svietivých diód pripomenieme ich mapovanie na porty mikrokontroléra

  • Zelená LED LD3  je pripojená na port PC9 (port C, pin 9)
  • Modrá LED LD4 je pripojená na port PC8
  • Tlačidlo B1 je pripojené na port PA0 (pre pokročilejších pripomíname, že môže byť použité ako WAKE-UP)
  • Tlačidlo B2 je pripojené na pin RESET.

 

Pre ovládanie LED diód a snímanie stavu tlačidiel sa využívajú procedúry

 

__INLINE static void LED_Config(void)

{

 

  RCC->APB2ENR |=  1 <<  4;     // Enable GPIOC clock         

  GPIOC->CRH   &= 0xFFFFFF00;   // Configure the GPIO for LEDs

  GPIOC->CRH   |= 0x00000033;   // Configure the GPIO for LEDs

}

 

__INLINE static void LED_On (uint32_t num)

{

  GPIOC->BSRR = led_mask[num];  // Turn On  LED

}

 

__INLINE static void LED_Off (uint32_t num)

{

  GPIOC->BRR  = led_mask[num];  // Turn Off LED

}

 

void LED_Out(uint32_t value) // outputs value to LEDs

{

  int i;

 

  for (i = 0; i < LED_NUM; i++)

  {

    if (value & (1<

    {

      LED_On (i);

    } else

    {

      LED_Off(i);

    }

  }

}

 

__INLINE static void BTN_Config(void)

{

  RCC->APB2ENR |=  1 <<  2;      // Enable GPIOA clock         

  GPIOA->CRL   &= 0xFFFFFFF0;    // Configure the GPIO for BTNs

  GPIOA->CRL   |= 0x00000008;    // Configure the GPIO for BTNs

}

 

__INLINE static uint32_t BTN_Get(void)

{

  return (GPIOA->IDR & 0x0001);

}

 

 

uint32_t BTN_Pressed (void) // check if USER1 is pressed (unbounced)

{

  static uint32_t USER1KeyCount = 0, USER1KeyPressed = 0;

 

  if (USER1KeyPressed)

  {

    if ((BTN_Get() == 0 ))

    {                   // Check if USER1 not pressed 

      if (USER1KeyCount < UNBOUNCE_CNT) USER1KeyCount++;

      else {

        USER1KeyPressed = 0;

        USER1KeyCount = 0;   

      }

    }

  }

  else {

    if (!(BTN_Get() == 0 )) 

    {                 // Check if USER1 pressed

      if (USER1KeyCount < UNBOUNCE_CNT) USER1KeyCount++;

      else

      {

        USER1KeyPressed = 1;

        USER1KeyCount = 0;

             return (1);

      }

    }

  }

  return (0);

}

 

Projekt je potrebné preložiť a zostaviť pomocou položky menu Project - Rebuild all target files. Úspešne preložený projekt zavediete do vývojovej dosky pomocou položky menu Flash - Download. O fungovaní programu sa presvedčíte podľa blikanie LED diód. Po opätovnom zatlačení tlačidla USER bude blikať modrá, potom zelená a napokon obidve LED diódy.

Na rozdiel od udalosťami riadených operačných systémov, v ktorých kód reaguje na rôzne udalosti, či už od používateľa, alebo z externého prostredia, kód pre mikrokontrolér sa na najnižšej úrovni (pokiaľ nepoužijete nejaký operačný systém, či knižnice reálneho času) spravidla tvorí tak, že lineárne činnosti sa vykonávajú v slučke, pričom kód slučky je v prípade potreby prerušovaný procedúrami na obsluhu prerušenia, obsluhou časovačov a podobne.

Do súboru main.c vložte najtriviálnejší kód, ktorý v procedúre   int main(void) nebude robiť nič, presnejšie, bude v nekonečnom cykle typu while(1). Tento cyklus tvorí takzvanú hlavnú slučku programu. V našom úvodnom kóde bude zatiaľ hlavná slučka prázdna. Ako naznačuje komentár, pred vstupom do hlavnej slučky sa v reálnom kóde vykoná inicializácia hardvérových komponentov

#include "stm32f10x.h"

 

int main(void)

{

   //inicializačné procedúry

 

   //hlavna slucka programu

     while(1)

     {

 

     }

}

 

Resumé

Popis možností práce s portami a napísanie aplikácie na rozblikanie jedinej LED diódy je dobrým príkladom na vysvetlenie výhod a nevýhod mikrokontrolérových dosiek s operačným systémom, napríklad Raspberry PI s Windows 10 IoT. Tam by vám na rozblikanie LED diódy stačilo niekoľko riadkov kódu. Prečo je to pri priamom programovaní také zložité? Takéto programovanie má však aj svoje výhody. Napríklad spotreba. Na doske aktivujete len tie moduly, ktoré bude aplikácia využívať, čiže inak povedané v príklad Blinky sa do hry vôbec nezapoja komunikačné moduly (UART, USART, SPI, CAN, I2C..) Analógovo digitálne prevodníky, systémy prerušenia a teda nebudú ani spotrebovávať cennú energiu z batérie.

Zobrazit Galériu

Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

ITPro

Red Hat a Veracomp: Open source a cloudy

09.12.2016 12:05

Cieľom ďalšej z radu spoločných akcií firiem Red Hat a Veracomp Slovakia v Bratislave bola prezentácia produktov, služieb a stratégií týkajúcich sa najhorúcejších tém IT podpory biznisu s využitím naj ...

ITPro

Právne okienko

08.12.2016 12:02

1. Ako postupovať, ak obchodník nechce uznať reklamáciu tovaru objednaného z e-shopu? V takomto prípade môžu nastať v zásade dve situácie. Ak zákazník reklamuje tovar do 12 mesiacov od jeho kúpy, mož ...

ITPro

Red Hat Forum 2016: Otvorená digitálna transformácia

09.12.2016 11:51

Podujatím v Prahe pre región CENE (stredná a severná Európa) rezonovala komunitná atmosféra a snaha o spoluprácu podľa pravidla „každý s každým“. IT musí priniesť do digitálneho biznisu novú kultúru a ...

1 Comments

  1. zaujimave reakcia na: Seriál: Vývoj pre IoT / Aplikácie pre mikrokontroléry ARM Cortex M3 a M4
    29.5.2016 22:05
    len vo svete dnesnej komplexnosti mi toto pride strasne low-level. zacinal by som tym ze na zariadeni arm rozchodim armbian resp. nejake ine minimalisticke distro a potom si moze programovat v com chcem.
    Reagovať

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá