Home Programování C/C++ (MCU) STM32F4 Discovery - Tutorial (08) Universal synchronous asynchronous receiver transmitter - USART
V tomto článku si ukážeme jak jednoduše přenášet data mezi dvěma zařízeními, MCU s MCU nebo MCU s PC, a to pomocí sériového přenosu dat – periferií USART.
Dnes již každý MCU obsahuje minimálně jednu periferii UART nebo USART. Jedná se o univerzální periferii pro sériový přenos dat a to v asynchronní nebo synchronní variantě. Nás v tomto článku bude zajímat známý asynchronní přenos dat, také jako RS-232, s jehož pomocí lze snadno propojit dvě zařízení (například dvě MCU nebo MCU-PC atd.). Zatímco každý MCU již dnes má periferii pro sériový přenos, bohužel počítače již sériový port často postrádají a u notebooků již zcela vymizel. To nám ale nebrání, neboť lze použít převodníky USB->RS-232.
Teorii kolem RS-232 a protokolu zde nebude rozebírat, ta je natolik „profláklá“, že je k nalezení na internetu bez problému. Dále taky nebudu popisovat jak použít převodník USB->RS-232 nebo jak jej získat (koupit nebo vyrobit ze součástek, pro začínající doporučuji koupit). Ti co mají ještě PC s „COM“ portem, jsou na tom nejlépe (ale pozor na napětí!).
Pouze připomenu věc pro ty méně znalé a profesionálové mi odpustí následující část. V době svého vzniku se počítalo s většími vzdálenostmi přenosu dat a proto standart RS-232 definuje napětí na datových vodičích v úrovních od -25V do +25V (-25V je log 1 a 25V je log 0). Klasické stolní počítače pak využívali úrovně -12V až +12V (u notebooku se to může různit). Z toho důvodu nelze přímo propojit MCU se stolním počítačem, protože například STM32F4xx má vstupní piny maximálně 5V tolerantní a větší napětí by jej poškodilo. Existují obvody, který tento problém řeší a převádějí jednotlivé napěťové úrovně (například známý MAX232). Znalejší elektronice si jistě poradí i jiným způsobem. Při použití převodníku USB->RS232 již tento problém není většinou potřeba řešit, protože napětí na těchto převodnících se pohybuje od 0V do 5V. Tento odstavec je napsán jen jako upozornění, aby nedošlo ke zničení MCU.
Periferie USART v STM32F4xx nabízí spoustu možností, komunikace s IrDA, Smartcard … My si ale povíme pouze o zcela běžné asynchronním přenosu. K dispozici máme celkem 6 modulů USART (USART 4 a 5 jsou mírně ochuzeny o rozšířené funkce).
USART_SR (status register)
V tomto registru najdeme příznaky přenosu a chyb (některé bity nejsou třeba používat u obyčejného přenosu).
USART_DR (data register)
Přes tento registr dochází k výměně dat. Lze zapisovat nebo číst pouze spodních 8 bitů. Pokud je do něj zapsáno, jsou data předána k odeslání. Naopak pokud je z něj čteno, čteme přijatý byte pokud je RXNE = 1. Tedy pokud do něj zapíšeme, ty samá data z něj nemusíme přečíst.
USART_BRR (baud rate register)
V tomto registru nastavujeme jakou rychlostí bude komunikováno. V podstatě se jedná o nastavení děliče frekvence, kterým je periferie taktována (Výpočet hodnot níže).
USART_CR1 (control register 1)
Využito pouze spodních 16 bitů (některé bity nejsou třeba používat u obyčejného přenosu).
USART_CR2 (control register 2)
USART_CR3 (control register 3)
USART_GTPR (Gaurd time and prescaler register)
Je využito jen spodních 16 bitů. Registr se týká nastavení časování a dělení hodin pro Smartcard a IrDA.
Nyní tedy přejdeme k samotnému kódu. Budeme měnit stav LED diod podle přijatých znaků. Například pokud přijde znak ‘G’, změní se stav na zelené diodě atd. Část kódu k nastavení vývodů na diody zde nebudu popisovat, protože to již důvěrně známe a vrhneme se rovnou na kód spojený s USARTem.
// Povoleni hodin RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; // Resetovani GPIOC RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIOCRST; RCC->AHB1RSTR &= ~RCC_AHB1RSTR_GPIOCRST; // Nastaveni GPIOC GPIOC->MODER |= (GPIO_MODER_MODER6_1 | // Alternativni funkce na PC6 a PC7 GPIO_MODER_MODER7_1); GPIOC->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6_1 | // Rychlost PC6 a PC7 - 50MHz GPIO_OSPEEDER_OSPEEDR7_1); GPIOC->AFR[0] |= 0x88000000; // Alternativni funkce 8 na pinech PC6 a PC7
Nejprve je třeba nastavit vývody pro samotný USART. Pro USART6 lze použít například PC6 (TX) a PC7 (RX). Proto povolíme hodiny pro GPIOC a dále nastavíme piny 6 a 7 na alternativní funkci číslo 8 (podle tabulky z dokumentace). Dále nastavíme USART.
// Povoleni hodin RCC->APB2ENR |= RCC_APB2ENR_USART6EN; // Resetovani USART6 RCC->APB2RSTR |= RCC_APB2RSTR_USART6RST; RCC->APB2RSTR &= ~RCC_APB2RSTR_USART6RST; // Nastaveni USART6 USART6->CR1 |= (USART_CR1_UE | // Povoleni USART USART_CR1_TE | // Povoleni vysilani USART_CR1_RE | // Povoleni prijmu USART_CR1_RXNEIE); // Povoleni generovani preruseni pri prijmu
Začínáme povolením hodin periferii a jejím resetováním. Dále v registru USART_CR1 povolíme samotný USART, povolíme vysílání zpráv, povolíme příjem zpráv a povolíme generování přerušení pokud je přijata nová zpráva. Dále je třeba nastavit komunikační rychlost. Zvolíme například 9600 Baud.
USART6->BRR = 0x0000222E; // Fraction - 14, Mantisa - 546 // Rychlost prenosu 9600 Baud
Vycházíme ze vzorce uvedeném v dokumentaci:
Baud = (fck) / (8 * (2 - OVER8) * USARTDIV)
Kde Baud je komunikační rychlost, fck je taktovací frekvence (USART6 je připojen na APB2 a při maximálním kmitočtu je na kmitočtu 84 MHz), OVER8 je bit v registru USART_CR1, v našem případě je v log 0, a USARTDIV je konstanta pro nastaveni děličky hodinového kmitočtu. Tedy jediné co neznáme je USARTDIV a proto vzorec upravíme a USARTDIV spočítáme:
USARTDIV = (fck) / (Baud * 8 * (2 - OVER8))
USARTDIV = (84e6) / (9600 * 8 * (2 - 0)) = 546,875
Z USARTDIV následně již můžeme získat hodnoty Mantisa a Fraction do registru USART_BRR a to následovně rozdělením na celé a desetiné číslo:
Mantisa = 546
Fraction = 8 * (2 - OVER8) * 0,875 = 16 * 0,875 = 14
Nezapomene povolit přerušení i v samotném řadiči přerušení.
// Nastaveni preruseni NVIC->ISER[USART6_IRQn >> 0x05] |= (0x01 << (USART6_IRQn & 0x1F));
Rutina pro jeho obsluhu obsahuje vyhodnocení přečtené zprávy.
void USART6_IRQHandler(void) { uint8_t data; // Pokud jsou prijata data a nenastala chyba if((USART6->SR & USART_SR_RXNE) && !(USART6->SR & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE))) { data = USART6->DR; // Precteme prijata data (1 byte) // Podle prijateho bytu, zmen stav prislusne LED if((data == 'B') || (data=='b')) GPIOD->ODR ^= GPIO_ODR_ODR_15; else if((data == 'G') || (data=='g')) GPIOD->ODR ^= GPIO_ODR_ODR_12; else if((data == 'R') || (data=='r')) GPIOD->ODR ^= GPIO_ODR_ODR_14; else if((data == 'O') || (data=='o')) GPIOD->ODR ^= GPIO_ODR_ODR_13; // Prijaty byte odesleme jakmile je DR volny while(!(USART6->SR & USART_SR_TXE)); USART6->DR = data; } return; }
Nejprve se ujistíme, že došlo opravdu k přijetí nové zprávy (USART_SR_RXNE) a zda nenastala chyba indikovaná stavovými bity z registru USART_SR (USART_SR_PE, USART_SR_FE, USART_SR_NE, USART_SR_ORE). Dále přečteme přijatý byte a uložíme do proměnné data. Jednoduchou podmínkou vyhodnotíme, jaký znak byl přijat. Pokud se jedná o B, G, R nebo O, změníme stav na příslušné diodě. Jako bonus pak ty samá data odešleme zpět. Nejprve počkáme, dokud není registr DR prázdný a poté do něj zapíšeme data. Ihned po zapsání jsou přesunuta k odeslání a odeslána.
Jak je vidět tak samotná práce s periferií USART je poměrně jednoduchá. Na závěr přikládám celý kód programu.
#include <stm32f4xx.h> void USART6_IRQHandler(void) { uint8_t data; // Pokud jsou prijata data a nenastala chyba if((USART6->SR & USART_SR_RXNE) && !(USART6->SR & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE))) { data = USART6->DR; // Precteme prijata data (1 byte) // Podle prijateho bytu, zmen stav prislusne LED if((data == 'B') || (data=='b')) GPIOD->ODR ^= GPIO_ODR_ODR_15; else if((data == 'G') || (data=='g')) GPIOD->ODR ^= GPIO_ODR_ODR_12; else if((data == 'R') || (data=='r')) GPIOD->ODR ^= GPIO_ODR_ODR_14; else if((data == 'O') || (data=='o')) GPIOD->ODR ^= GPIO_ODR_ODR_13; // Prijaty byte odesleme jakmile je DR volny while(!(USART6->SR & USART_SR_TXE)); USART6->DR = data; } return; } void GPIOC_conf(void) { // Povoleni hodin RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; // Resetovani GPIOC RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIOCRST; RCC->AHB1RSTR &= ~RCC_AHB1RSTR_GPIOCRST; // Nastaveni GPIOC GPIOC->MODER |= (GPIO_MODER_MODER6_1 | // Alternativni funkce na PC6 a PC7 GPIO_MODER_MODER7_1); GPIOC->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6_1 | // Rychlost PC6 a PC7 - 50MHz GPIO_OSPEEDER_OSPEEDR7_1); GPIOC->AFR[0] |= 0x88000000; // Alternativni funkce 8 na pinech PC6 a PC7 return; } void GPIOD_conf(void) { // Povoleni hodin pro GPIOD RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Reset portu GPID RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIODRST; RCC->AHB1RSTR &= ~(RCC_AHB1RSTR_GPIODRST); // Nastaveni modu GPIOD->MODER |= (GPIO_MODER_MODER12_0 | //nastaveni pinu 12, 13, 14 a 15 jako výstupní GPIO_MODER_MODER13_0 | GPIO_MODER_MODER14_0 | GPIO_MODER_MODER15_0); return; } void USART6_conf(void) { // Povoleni hodin RCC->APB2ENR |= RCC_APB2ENR_USART6EN; // Resetovani USART6 RCC->APB2RSTR |= RCC_APB2RSTR_USART6RST; RCC->APB2RSTR &= ~RCC_APB2RSTR_USART6RST; // Nastaveni USART6 USART6->CR1 |= (USART_CR1_UE | // Povoleni USART USART_CR1_TE | // Povoleni vysilani USART_CR1_RE | // Povoleni prijmu USART_CR1_RXNEIE); // Povoleni generovani preruseni pri prijmu USART6->BRR = 0x0000222E; // Fraction - 14, Mantisa - 546 // Rychlost prenosu 9600 Baud // Nastaveni preruseni NVIC->ISER[USART6_IRQn >> 0x05] |= (0x01 << (USART6_IRQn & 0x1F)); return; } int main(void) { GPIOC_conf(); GPIOD_conf(); USART6_conf(); while(1); }
Pokud k otestování použijete PC, ověřte si správné zapojení datových vodičů TX a RX. V PC pak lze použít k jednoduchému odesílání znaků z klávesnice například program Putty což je jednoduchý terminál. Jinak pomocí USARTu lze odesílat obecně libovolná data, nemusí to být jen znaky.
V tomto článku jsme si tedy ukázali jak používat periferii USART. Příště se podíváme na složitější periferii na přenos dat a tou bude periferie bxCAN pro komunikaci na CAN zběrnici.
Projekt ke stažení ZDE.
Komentáře
Nicméně chtěl bych se zeptat: V současnosti již není k sehnání STM32F4 Discovery kit za rozumných 350,- Kč, ale pod novým objednacím kódem STM32F407G-DISC 1 (a za zhruba 800,- ) se dá sehnat kit, který označují jako nástupce. Je to úplně stejný kit a rozdíl je pouze v ceně, nebo jsou nějaké rozdíly i v hardware?