ST logoV tomto článku si povíme o poměrně důležité věci. Tím je časování a resetování MCU neboť špatně nastavené hodiny mohou způsobit nestabilitu, nefunkčnost některých nebo všech periferií nebo naopak nemusíme dosáhnout největšího výkonu jakým STM32F4 disponuje.

Generování a rozvedení hodinového signálu je u všech synchronních obvodů kritické a to i u našeho MCU. STM32F407VG patří mezi ty výkonnější procesory. Samotné jádro je možno taktovat až na 168MHz (to je maximální frekvence stanovená výrobcem, osobně se mi povedlo jádro taktovat až na cca 224MHz :D, ale byli u toho kompromisy a některé periferie nešlo využít).

 

Hodinový signál

Celé schéma rozvodu hodin je znázorněno v dokumentaci (na Obr.1 je vidět zjednodušené blokové schéma). Můžeme si zvolit zdroj hodinového signálu, od interního RC oscilátoru, přes krystal až po integrované rezonátory. U každého způsobu generace hodin jsou jistá omezení a pravidla. Pro nás je důležitá pouze volba mezi HSI (high speed internal resonator) nebo HSE (high speed external resonator). Na Discovery kitu je k MCU připojen krystal s frekvencí 8MHz.
 

Nástroj nastavení časování
Obr.1: Zjednodušené blokové schéma rozvodu hodinového signálu


Hodinový signál je pak upravován, dělen a násoben, aby každá část MCU dostala jí vyhovující frekvenci. Některé periferie pro svou činnost potřebují přesně danou frekvenci (například modul USB), jiné jsou omezeny jen maximální frekvencí (například časovače).

Možná se ptáte, proč nám v předchozím článku program fungoval a přesto, že jsem časování MCU nějak neřešili. To proto, že v souboru system_STM32F4x.h je základní nastavení a konfigurace (a to nejen hodinového signálu).

Pokud do souboru nahlédneme a podíváme se na tabulku od řádku 43, zjistíme, že soubor počítá s připojeným krystalem na frekvenci 25MHz. Takže do teď jsme programovali MCU s jádrem taktovaným na 54MHz. Tedy výrazně méně, než je jeho maximum.

Nyní tedy upravíme hodnoty v tomto souboru, abychom měli takt jádra opravdu 168MHz. Podle schématu musíme zvolit jako zdroj signálu HSE, což už máme. Dále nastavit správnou hodnotu všem PLL. Jejich nastavení najdeme v souboru system_STM32F4x.h na řádku 147 až 156. Hodnoty nastavíme následovně:
 

/************************* PLL Parameters *************************************/
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M      8
#define PLL_N      336

/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P      2

/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
#define PLL_Q      7


Jelikož jediné v čem byl rozdíl, byla předpokládaná frekvence krystalu a proto stačí změnit jen PPL_M. Pro pohodlné nastavení hodin jak systému, tak i všech periferií, lze použít nástroj od společnosti ST, který i umí vygenerovat už zmiňovaný soubor. Odkaz ke stažení naleznete na konci článku (nástroj je vytvořený v MS Excel a vyžaduje povolená makra).

 

RCC registry

Pokud si najdeme datovou strukturu RCC_TypeDef v soboru stm32f4x.h, zjistíme, že máme k dispozici opravdu velké množství registrů. Některé se týkají obecného nastavení jiné, a ty budeme používat nejčastěji, nastavení hodin pro periferie, konkrétně resetování a povolení. Z důvodu velkého množství registrů, zde popíšu jen ty, co budeme používat. Podrobný popis co který bit nějakého registru dělá, najdete v dokumentaci k MCU.
 

typedef struct
{
  __IO uint32_t CR;            /*!< RCC clock control register,                                  Address offset: 0x00 */
  __IO uint32_t PLLCFGR;       /*!< RCC PLL configuration register,                              Address offset: 0x04 */
  __IO uint32_t CFGR;          /*!< RCC clock configuration register,                            Address offset: 0x08 */
  __IO uint32_t CIR;           /*!< RCC clock interrupt register,                                Address offset: 0x0C */
  __IO uint32_t AHB1RSTR;      /*!< RCC AHB1 peripheral reset register,                          Address offset: 0x10 */
  __IO uint32_t AHB2RSTR;      /*!< RCC AHB2 peripheral reset register,                          Address offset: 0x14 */
  __IO uint32_t AHB3RSTR;      /*!< RCC AHB3 peripheral reset register,                          Address offset: 0x18 */
  uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                                    */
  __IO uint32_t APB1RSTR;      /*!< RCC APB1 peripheral reset register,                          Address offset: 0x20 */
  __IO uint32_t APB2RSTR;      /*!< RCC APB2 peripheral reset register,                          Address offset: 0x24 */
  uint32_t      RESERVED1[2];  /*!< Reserved, 0x28-0x2C                                                               */
  __IO uint32_t AHB1ENR;       /*!< RCC AHB1 peripheral clock register,                          Address offset: 0x30 */
  __IO uint32_t AHB2ENR;       /*!< RCC AHB2 peripheral clock register,                          Address offset: 0x34 */
  __IO uint32_t AHB3ENR;       /*!< RCC AHB3 peripheral clock register,                          Address offset: 0x38 */
  uint32_t      RESERVED2;     /*!< Reserved, 0x3C                                                                    */
  __IO uint32_t APB1ENR;       /*!< RCC APB1 peripheral clock enable register,                   Address offset: 0x40 */
  __IO uint32_t APB2ENR;       /*!< RCC APB2 peripheral clock enable register,                   Address offset: 0x44 */
  uint32_t      RESERVED3[2];  /*!< Reserved, 0x48-0x4C                                                               */
  __IO uint32_t AHB1LPENR;     /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */
  __IO uint32_t AHB2LPENR;     /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */
  __IO uint32_t AHB3LPENR;     /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */
  uint32_t      RESERVED4;     /*!< Reserved, 0x5C                                                                    */
  __IO uint32_t APB1LPENR;     /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */
  __IO uint32_t APB2LPENR;     /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */
  uint32_t      RESERVED5[2];  /*!< Reserved, 0x68-0x6C                                                               */
  __IO uint32_t BDCR;          /*!< RCC Backup domain control register,                          Address offset: 0x70 */
  __IO uint32_t CSR;           /*!< RCC clock control & status register,                         Address offset: 0x74 */
  uint32_t      RESERVED6[2];  /*!< Reserved, 0x78-0x7C                                                               */
  __IO uint32_t SSCGR;         /*!< RCC spread spectrum clock generation register,               Address offset: 0x80 */
  __IO uint32_t PLLI2SCFGR;    /*!< RCC PLLI2S configuration register,                           Address offset: 0x84 */
} RCC_TypeDef;


Nastavení periferií v našem MCU, je rozděleno do skupin:

  • AHB1 USB OTG, Ethernet MAC, DMA2, DMA1, CRC module, GPIOx
  • AHB2USB OTG, Random generátor, Hash module, Cryptographic module, Camera interface
  • AHB3Flexible static memory controller
  • APB1DAC, Power interface, CAN2, CAN1, I2C2, I2C1, UART 2-5, SPI2, SPI3, Watchdog, TIM 2-7 a 12-14
  • APB2TIM 9-11, System configuration controller, SPI1, SDIO, ADC, USART 1 a 6, TIM 8 a 1

Každou periferii před použitím musíme resetovat a poté povolit pro ní hodiny. Proto pro každou skupinu periferií existují registry pro reset AHBxRSTR a APBxRSTR a registry pro povolení hodin AHBxENR a APBxENR (existují i registry pro nastavení jak se má chovat časování pokud je procesor ve sleep módu, jestli má periferie pracovat či také „spát“ a to jsou registry AxBxLPENR).

Pokud bychom chtěli tedy použít například AD převodník, udělali bychom to takhle:
 

/* Reset AD prevodniku */
RCC->APB2RSTR |= RCC_APB2RSTR_ADCRST;
RCC->APB2RSTR &= ~RCC_APB2RSTR_ADCRST;
	
/* Povoleni hodin pro AD prevodnik*/
RCC->APB2ENR = RCC_APB2ENR_ADC1EN;


Registr pro resetování vždy resetuje periferii pokud je v něm zapsána logická jednička (logickou jedničku na správném místě zajišťuje makro RCC_APB2RSTR_ADCRST). Resetuje se, ale pořád, dokud není logická jednička přepsána zpět na nulu. Proto zapíšeme logickou jedničku a ihned ji přepíšeme opět nulou a to tak že znegujeme makro (binární negace) a přes binární součin zapíšeme do registru. Poté povolíme hodiny pro danou periferii zapsáním logické jedničky (opět použijeme makro).

Doporučuji si prohlédnou soubor stm32f4x.h, konkrétně definice maker. Jsou tam pěkně udělané a seskupené do skupin, pro resetování i povolování a ke každé skupině periferií zvlášť.

Samozřejmě by šlo resetovat a povolit hodiny pro AD převodník bez použití maker. Resetování periferie uděláme pomocí 8. bitu v registru APB2RSTR a povolení hodin pak pomocí 8. bitu v registru APB2ENR (pro ADC1).
 

/* Reset AD prevodniku */
RCC->APB2RSTR |= (1<<8);
RCC->APB2RSTR &= ~(1<<8);
	
/* Povoleni hodin pro AD prevodnik*/
RCC->APB2ENR = (1<<8);


Pro lepší pochopení přidám ještě jeden příklad s použitím maker. Budeme chtít použít periferii časovače TIM2. Napíšeme tedy následující kód:
 

/* Reset casovace TIM2 */
RCC->APB1RSTR |= RCC_APB1RSTR_TIM2RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM2RST;
	
/* Povolení hodin pro casovac TIM2 */
RCC->APB1ENR = RCC_APB1ENR_TIM2EN;


Samozřejmě pro správnou funkci je pak třeba ještě časovač nastavit dalšími registry pro daný časovač.

 

Závěr

Dnešní článek je hodně osekaný, chtěl jsem především seznámit s tím jak povolit resetovat a povolit periferii. To bude potřeba do dalších článků, kde budeme zkoušet jednotlivé periferie od časovačů, přes AD převodníky až po DMA. Nastavení okolo RCC je velké množství. Proto na zajímavosti a další možnosti co nám nabízí se podíváme  v některém z dalších článků.


Projekt ke stažení ZDE.


Přidat komentář

Bezpečnostní kód
Obnovit