st logoV 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+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).
 

Registry

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).

  • Bit 9 – CTS (tento bit je nastaven vždy, pokud dojde ke změně signálu CTS od druhého zařízení, je třeba jej ručně nulovat)
  • Bit 8 – LBD (týká se LIN)
  • Bit 7 – TXE (tento bit je nastaven jakmile data z DR registru byla přesunuta do odesílacího registru a DR registr je prázdný)
  • Bit 6 – TC (bit je nastaven jakmile je odesílání dokončeno)
  • Bit 5 – RXNE (bit je v log 1 jakmile je přijata zpráva a je možno ji číst z DR registru)
  • Bit 4 – IDLE (bit je nastaven jakmile je zjištěna nečinnost na lince)
  • Bit 3 – ORE (nastaven jakmile přijde zpráva a předchozí ještě nebyla přečtena)
  • Bit 2 – NF (nastaven pokud je zjištěn šum znemožňující komunikaci)
  • Bit 1 – FE (zpráva byla přijata chybně, neodpovídá pozice stop bitu)
  • Bit 0 – PE (neodpovídá paritní bit, pokud je parita povolena)

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).

  • Bit 15:4 – Mantisa (12 bitů)
  • Bit 3:0 – Fraction (4 bity)

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).

  • Bit 15 – OVER8 (počet převzorkování; 1 – 8x; 0 – 16x)
  • Bit 13 – UE (nastavením je povolen provoz periferie USART)
  • Bit 12 – M (délka datového slova; 0 – 8  bitů; 1 – 9 bitů, nutné pokud je využit paritní bit)
  • Bit 11 – WAKE (určení metody probuzení; 0 – pokud je linka v klidu; 1 – pokud je přijata adresa – pouze pro speciální funkce)
  • Bit 10 – PCE (nastavením povolíme HW kontrolu parity, je nutné mít nastaven i bit M)
  • Bit 9 – PS (nastaví parity; 0 – sudá parita; 1 – lichá parita)
  • Bit 8 – PEIE (povolení generování přerušení pokud nastane chyba parity)
  • Bit 7 – TXEIE (povolení generování přerušení pokud dojde k uvolnění DR registru)
  • Bit 6 – TCIE (povolení generování přerušení pokud dojde k odeslání zprávy)
  • Bit 5 – RXNEIE (povolení generování přerušení pokud došlo k příjmu zprávy)
  • Bit 4 – IDLEIE (povolení generování přerušení pokud je linky v klidu)
  • Bit 3 – TE (nastavení povolíme odesílání)
  • Bit 2 – RE (nastavením povolíme příjem)
  • Bit 1 – RWU (nastavení módu; 0 – aktive mode; 1 – mute mode)
  • Bit 0 – SBK (nastavením je odeslán „break“)

USART_CR2 (control register 2)

  • Bit 14 – LINEN (při použití LIN nastavíme tento bit)
  • Bit 13:12 – STOP (nastavení délky stop bitu; 00 – 1 stop; 01 – 0,5 stop; 10 – 2 stop; 11 – 5 stop)
  • Bit 11 – CLKEN (nastavením povolíme generování hodin, při synchroním přenosu)
  • Bit 10 – CPOL (nastavení klidové úrovně hodin)
  • Bit 9 – CPHA (0 – data sou odesílána při prvním pulzu hodit; 1 – data jsou odesíláná při druhém pulzu hodin)
  • Bit 8 – LBCL
  • Bit 6 – LBDIE (povolení generování přeruší při „LIN break“)
  • Bit 5 – LBDL (délka detekce „LIN break“ ; 0 – 10 bitů; 1 – 11 bitů)
  • Bit 3:0 – ADD (adresa uzlu, využito při multiprocesorové komunikaci)

USART_CR3 (control register 3)

  • Bit 11 – ONEBIT (nastavením povolíme jednoduché vzorkování, pak nelze použít detekci šumu)
  • Bit 10 – CTSIE (povolení generování přerušení při CTS=1)
  • Bit 9 – CTSE (povolení hardwarové obsluhy CTS)
  • Bit 8 – RTSE (povolení hardwarové obsluhy RTS)
  • Bit 7 – DMAT (povolení DMA pro vysílání dat)
  • Bit 6 – DMAR (povelení DMA pro příjem dat)
  • Bit 5 – SCEN (povolení modu pro Smartcard)
  • Bit 4 – NACK (povolení Smartcard NACK)
  • Bit 3 – HDSEL (nastavením bude komunikace polo-duplexní)
  • Bit 2 – IRLP (nastavením je IrDA přepnut do nízké spotřeby)
  • Bit 1 – IREN (povolení módu IrDA)
  • Bit 0 – EIE (povolení generování přerušení pokud nastane chyba při přenosu, v případě že DMAR=1)

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.

 

Blikáme diodou podle přijatých znaků

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.
 

Závěr

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  

 
0 # Kitt 2016-08-14 10:15
Škoda že už nejsou další díly, je to skvělý seriál.

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,- :sad: ) 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?
Odpovědět | Odpovědět citací | Citovat
 
 
0 # hexdump 2014-05-19 08:47
Pánové chválím a netrpělivě čekám na další díl.
Odpovědět | Odpovědět citací | Citovat
 
 
+3 # Ondra 2014-03-31 20:45
Skvělý seriál! Sice sem jich už pár pročetl, ale z tohodle toho chápu zatím asi nejvíc :) Snad se dostaneš k dalším dílům.
Odpovědět | Odpovědět citací | Citovat