USART

La USART es un módulo de comunicación serial asíncrona. Implementa la comunicación full-Duplex (Puede transmitir y Recibir información al mismo tiempo). Por lo tanto, usan dos canales, uno de transmisión Tx y uno de recepción Rx.

NOTA: Los pines deben ser configurados en modo Alterno

La comunicación USART tiene un bit de inicio, 8 bits máximos de comunicación y 1, 1.5 o 2 bits de parada (stop). La STM32401 posee USART1, USART2, y USART6 Pines de USART para la STM32F401RE

USART Tx Rx
USART1 PA9 , PB6 PA10
USART2 PA2 PA3
USART6 PA11, PC6 PA12

Registros USART

Reloj: RCC → APB: Habilitar el reloj de la USART

Baud Rate Register: USARTx_BRR: Configura la velocidad de comunicación del módulo. Se usa la siguiente fórmula:

$$ BRR = \frac{FCLK_{UART}}{Vel.Deseada} \rightarrow \frac{16MHz}{9600bps} = 1666.66 = 1666 \rightarrow 0X682 $$

Velocidad Valor Decimal Código Hexadecimal
1200 13333.33 0X3415
4800 3333.33 0XD05
9600 1666.66 0X682
19200 833.33 0X341
38400 416.66 0X1A0
57600 277.77 0X115
115200 138.88 0X8A

USARTx_CR1 → Se Habilitan los bits 2 (RE) para habilitar recepción, bit 3 (TE) para habilitar Transmisión, bit 5 (RXENEIE) para habilitar interrupción de Recepción y bit 13 (UE) para habilitar el funcionamiento general del puerto USART. En la configuración, se deben habilitar los bits 2, 3 y 5. En el while(1) se habilita el bit 13.

FUNCIONES ALTERNAS A UTILIZAR Se usan AFR[3:0] → 0111 → USART1/2 AF7 1000 → USART6 AF8

Para USART1 en PA9 y PA10 → GPIOA→ AFR[1] |= 0X770;

****Para USART1 en PB6 y PA10 → GPIOA → AFR[1] |= 0X700; ****GPIOB → AFR[0] |= 0X7000000;

****Para USART2 en PA2 y PA3 → GPIOA → AFR[0] |=0X7700;

****Para USART6 en PA11 y PA12 → GPIOA → AFR[1] |= 0X88000;

****Para USART6 en PC6 y PA12 → GPIOC → AFR[0] |= 0X8000000; ****GPIOA → AFR[1] |= 0X80000;

**NOTA: Si se quieren transmitir datos desde la tarjeta hacia el computador usando USART se deberá usar la USART2 sin necesidad de usar los pines PA2 y PA3, solo requiere del cable de datos.

LOS EJEMPLOS A CONTINUACIÓN ENVIAN EL MISMO DATO QUE RECIBEN**

Librería para usar Serial_print() y Serial_write()

Esta librería proporciona funciones para enviar a monitores seriales como el de arduino IDE o CoolTerm, Las funciones tienen un uso similar al del entorno de Arduino.

//Nombre de la ibrería Serial.h

#include "stm32f4xx.h"
#include <stdio.h>
#include <string>

/*Función de configuración para USART con PC
         Pines PA2 y PA3*/
void confi_usart2_PA2_PA3(unsigned int velocidad){ //USART2 PA2 y PA3	
	GPIOA->MODER |= 0XA0;                              //1010 0000 ALTERNATIVO PA2 PA3
	GPIOA->AFR[0] |= 0X7700;                           //Función Alternativa
	RCC ->APB1ENR |= (1UL<<17);                        //Habilitar reloj USART2
	USART2->BRR = (unsigned int)(16000000/velocidad);  //Puerto a 9600bps
	USART2->CR1 |=0X2C;                                //0000 0000 0010 1100 Habilita TX, RX e Interrup RX
	USART2->CR2 = 0;
	USART2->CR3 = 0;
	NVIC_EnableIRQ(USART2_IRQn);                       // Activa vector interrupción 
	USART2->CR1 |= (1UL<<13);                          //Activa el USART bit UE
}

/*Función para enviar caracteres tipo ASCII
    Sin salto de linea*/
		
void Serial_print_siu(const char* cmd) {
    while (*cmd) {
        USART2->DR = *cmd++;
        while (!(USART2->SR & USART_SR_TXE));
    }
}

// Sobrecarga para enteros
void Serial_print(int cmd) {
    char dist[50] = {};
    sprintf(dist, "%d", cmd); // Convierte el entero a cadena y concatena con `val`
    Serial_print_siu(dist);
}

// Sobrecarga para double
void Serial_print(double cmd) {
    char dist[50] = {};
    sprintf(dist, "%.*f", 9, cmd); // Convierte el double a cadena y concatena con `val`
    Serial_print_siu(dist);
}

// Sobrecarga para flotantes
void Serial_print(float cmd) {
    char dist[50] = {};
    sprintf(dist, "%.4f", cmd); // Convierte el flotante a cadena y concatena con `val`
    Serial_print_siu(dist);
}

// Versión original para cadenas
void Serial_print(const char* cmd) {
    char dist[50] = {};
    sprintf(dist, "%s", cmd); // Concatenación con cadena y carácter
    Serial_print_siu(dist);
}

/*Función para enviar caracteres tipo ASCII
    Con salto de linea*/

void Serial_println_siu(const char* cmd) {
    while (*cmd) {
        USART2->DR = *cmd++;
        while (!(USART2->SR & USART_SR_TXE));
    }
		USART2->DR = '\n';
		while (!(USART2->SR & USART_SR_TXE));
}

/*Función para enviar caracteres tipo Byte
		Tiene un argumento:
			1. Valores a enviar puede ser un vector de n valores*/

/*void Serial_write(const uint8_t* data) {
	  uint16_t length = sizeof(data);
    for (uint16_t i = 0; i < length; i++) {
        USART2->DR = data[i]; // Enviar un byte
        while (!(USART2->SR & USART_SR_TXE)); // Esperar a que el registro de datos esté vacío
    }
}*/

void Serial_write(uint8_t data) {
    USART2->DR = data; // Enviar un byte
    while (!(USART2->SR & USART_SR_TXE)); // Esperar a que el registro de datos esté vacío
}

// Sobrecarga para enteros
void Serial_println(int cmd) {
    char dist[50] = {};
    sprintf(dist, "%d", cmd); // Convierte el entero a cadena y concatena con `val`
    Serial_println_siu(dist);
}

// Sobrecarga para double
void Serial_println(double cmd) {
    char dist[50] = {};
    sprintf(dist, "%.*f", 9, cmd); // Convierte el double a cadena y concatena con `val`
    Serial_println_siu(dist);
}

// Sobrecarga para flotantes
void Serial_println(float cmd) {
    char dist[50] = {};
    sprintf(dist, "%.4f", cmd); // Convierte el flotante a cadena y concatena con `val`
    Serial_println_siu(dist);
}

// Versión original para cadenas
void Serial_println(const char* cmd) {
    char dist[50] = {};
    sprintf(dist, "%s", cmd); // Concatenación con cadena y carácter
    Serial_println_siu(dist);
}

char Dato;
extern "C"{
 void USART2_IRQHandler(void){
  Dato=USART2->SR;                        // Limpiar banderas
  Dato=USART2->DR;                        // Lee dato
  while(!(USART2->SR&USART_SR_TXE));      // Espera puerto disponible 
 }
}

Ejemplo usando USART y monitor Serial de arduino

#include "stm32f4xx.h"
#include "Serial.h"
#include <stdio.h>
#include <string>

void Delay_ms(int x){
	for(volatile int i=0;i<x*940; i++){  //Cambiar el 940 por un valor para que de cerca en ms
		asm("nop"); 
	}
}

int main(void){
	RCC->AHB1ENR |= 0X7;      //Activa relojes
	confi_usart2_PA2_PA3(115200);
	float siu = 22.34;
	while(1){
		uint8_t byte[] = {0x41,0x42,0x43}; //Valores: 'A' 'B' 'C'
		Delay_ms(1000);
		Serial_print("Imprimo un entero: "); Serial_println(77);
		Delay_ms(1000);
		Serial_print("Imprimo un double: "); Serial_println(45.1234567890);
		Delay_ms(1000);
		Serial_print("Imprimo un float: "); Serial_println(siu);
		Delay_ms(1000);
		Serial_print("Imprimo un String: "); Serial_println("Siu");
		
		Delay_ms(1000);
		Serial_print("Imprimo un byte: "); 
		for(int i = 0; i<3;i++){
		Serial_write(byte[i]);} 
		Serial_println("");Serial_println("");

	}
	return 0;
}

image.png

Ejemplo usando USART1 PA9 y PA10

#include "stm32f4xx.h"
extern "C"{
 void USART1_IRQHandler(void){
  unsigned char Dato;
  Dato=USART1->SR;                      // Limpiar banderas
  Dato=USART1->DR;                      // Lee dato
  while(!(USART1->SR&USART_SR_TXE));    // Espera puerto disponible 
  USART1->DR=Dato;                      // Transmite dato
 }
}

void confi_usart1_PA9_PA10(unsigned int velocidad){
	//USART1 PA9 y PA10	
	GPIOA->MODER |= 0X280000;    //0010 1000 0000 0000 0000 0000 ALTERNATIVO PA9 PA10
	GPIOA->AFR[1] |= 0X770;      //Función Alternativa
	RCC ->APB2ENR |= (1UL<<4);   //Habilitar reloj USART1
	USART1->BRR = (unsigned int)(16000000/velocidad);       //Puerto a 9600bps
	USART1->CR1 |=0X2C;          //0000 0000 0010 1100 Habilita TX, RX e Interrup RX
	USART1->CR2 = 0;
	USART1->CR3 = 0;
	NVIC_EnableIRQ(USART1_IRQn); // Activa vector interrupción 
	USART1->CR1 |= (1UL<<13);    //Activa el USART bit UE
}

int main(void){
	RCC->AHB1ENR |= 0X7;      //Activa relojes
	confi_usart1_PA9_PA10(9600);
	
	while(1){}
	return 0;
}

Untitled

Ejemplo usando USART1 PB6 y PA10