cancel
Showing results for 
Search instead for 
Did you mean: 

Sending USART message via interrupt using registers

UMilo.1
Associate III

Hello,

I am working on a college project and struggle with sending a message to the virtual terminal using USART interrupt.

I work with following code:

void UART_Send (uint8_t *buffer){
		USART1->CR1 |= USART_CR1_TXEIE; // Enable TXE Interrupt
		USART1->DR = buffer[0];		// Send data
		}
 
void USART1_IRQHandler(void) {
		// Check TXE flag
	int Tx1_Counter=0;
	if(USART1->SR & USART_SR_TXE) {
			    Tx1_Counter++;
			    if(Tx1_Counter <= strLength(buffer) - 1)
			    {
			    	USART1->DR = buffer[Tx1_Counter] & 0xFF;
		    } else
			    {
	}
			// Transmission completes
			    Tx1_Counter = 0;
			    // Clear the counter
			    // Disable TXE interrupt
			    USART1->CR1 &= ~USART_CR1_TXEIE;
	}
		}

I set up whole init using registers which currently looks like this:

#include <stdio.h>
#include "stm32f103x6.h"
#include "util.h"
 
void USART_Init (USART_TypeDef * USARTx) {
	// Default setting:
	// No hardware flow control, 8 data bits, no parity, 1 start bit and 1 stop bit
	USARTx->CR1 &= ~USART_CR1_UE;  // Disable USART
 
	// Configure word length to 8 bit
	USARTx->CR1 &= ~USART_CR1_M;   // M: 00 = 8 data bits, 01 = 9 data bits, 10 = 7 data bits
	USARTx->CR1 |= (01 << USART_CR1_M_Pos);
	// Configure oversampling mode: Oversampling by 16
	// CORTEX M4 samo - USARTx->CR1 &= ~USART_CR1_OVER8;  // 0 = oversampling by 16, 1 = oversampling by 8
 
 
	// Configure stop bits to 1 stop bit
	//   00: 1 Stop bit;      01: 0.5 Stop bit
	//   10: 2 Stop bits;     11: 1.5 Stop bit
	USARTx->CR2 &= ~USART_CR2_STOP;
 
	// Podesavanje Baudrate - Strana 798 i 799, 27.3.4 Fractional baud rate generation, Tabela 192. na strani 799
	// CSet Baudrate to 9600 using APB frequency
	// Tx/Rx baud = f_CK / (16*USARTDIV),
	// BRR = 0x1D4C za USARTDIV=468,75 za 72MHz, odnosno BRR = 0xEA6, USARTDIV = 234,75 za 36MHZ, Limited to 16 bits
 
	// Default clock je 8MHz, USARTDIV = 8.000.000/(16*9600) = 52,083 ,
	// DIV_Mantissa = 52 = 0x34, DIV_Fraction=0,083 u hex broju = 0,083*16=1,328->DIV_Fraction=0x1
	// -> BRR = 0x341
	USARTx->BRR  = 0x341;
 
	USARTx->CR1  |= (USART_CR1_RE | USART_CR1_TE);  	// Transmitter and Receiver enable
 
	USARTx->CR1  |= USART_CR1_UE; // USART enable
 
}
 
void UART1_GPIO_Init(void) {
 
	// Enable the peripheral clock of GPIO Port A
	RCC->APB2ENR |=   RCC_APB2ENR_IOPAEN;
 
	// ********************** USART 1 ***************************
	// U Reference Manualu STM32F103R6 treba videti na koje pinove GPIO porta se mapira USART1 i u kojem rezimu treba setovati te pinove GPIO porta
	// jer se ukljucuje alternativna funkcija
	// PA9 = USART1_TX, PA10 = USART1_RX (strana 181)
	// Rezim rada GPIO Porta - strana 166 i 167
	// USARTx_TX - Ako radi u Full duplex rezimu podesiti: Alternate function push-pull (The USART_TX pin can also be configured as alternate function open drain.)
	// USARTx_TX - Ako radi u Half duplex synchronous rezimu podesiti: Alternate function push-pull
	// USARTx_RX - Ako radi u Full duplex rezimu, podesiti port kao: Input floating / Input pull-up
	// USARTx_RX - Ako radi u Half duplex synchronous rezimu, onda se ne koristi pin: Not used. Can be used as a general IO
 
	// Prvo podesavamo pin PA9 na koji se povezuje USART1_TX, tako da pin treba da radi u izlaznom rezimu sa alternativnom funkcijom
	GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9); // Obrisati  CNF9[1:0] i MODE9[1:0] - za konfiguraciju 9 pina, porta A
	// Alternativno moze da se koristi GPIOA_CRH &= ~(0xF << 4);
	// Ili GPIOA_CRH &= ~0xF0;
 
	// Prvo se podesava rezim rada pina 9 porta A - ulazni, ili jedan od tri izlazna.
	// Vrednosti za MODE[1:0]: 00: Input mode (reset state), 01: Output mode, max speed 10 MHz.
	// 10: Output mode, max speed 2 MHz, 11: Output mode, max speed 50 MHz.
 
	GPIOA->CRH |= (GPIO_CRH_MODE9_1 | GPIO_CRH_MODE9_0);// Setujemo vrednost 11 za Mode9[1:0] = Output mode, max speed 50 MHz.
	// isto moze i GPIO->CRH |= 0b11 << 4; ili GPIO->CRH |= 0x30;
 
	// Sada se podesava kako da pin radi u izlaznom rezimu.
	// Vrednosti za GPIOx_CNF[1:0]: Ako polje MODE[1:0] nije = 00, odnosno pin je setovan u nekom od izlaznih rezima, onda:
	// 00: General purpose output push-pull, 01: General purpose output Open-drain
	// 10: Alternate function output Push-pull, 11: Alternate function output Open-drain
 
	GPIOA->CRH |= GPIO_CRH_CNF9_1;	// Setuje se vrednost 10 za Mode9[1:0] -> Alternate function push-pull
	// Moze i GPIO->CRH |= 0x80;
 
	// Zatim podesavamo pin PA10 na koji se povezuje USART1_RX, tako da pin treba da radi u ulaznom rezimu sa alternativnom funkcijom
	GPIOA->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10); // Obrisati  CNF9[1:0] i MODE9[1:0] - za konfiguraciju 10 pina, porta A
 
	// Prvo podesavamo rezim rada pina 10, porta A
	// Vrednosti za MODE[1:0]: 00: Input mode (reset state), 01: Output mode, max speed 10 MHz. 10: Output mode, max speed 2 MHz, 11: Output mode, max speed 50 MHz.
	// Mi hocemo da postavimo vrednost 00: Input mode, ali posto smo prethodno obrisali te bitove, ne diramo nista
 
	// Vrednosti za GPIOx_CNF[1:0]: Ako je polje MODE[1:0] = 00, odnosno pin je u input rezimu, onda:
	// 00: Analog mode, 01: Floating input (reset state), 10: Input with pull-up / pull-down, 11: Reserved
 
	GPIOA->CRH |= GPIO_CRH_CNF10_0; // USART radi u full-duplex rezimu, pa setujemo kao Floating input = 01, videti gore ili strana 167
 
}
 
void UART1_Init(void) {
	// Enable the clock of USART 1
	RCC->APB2ENR |= RCC_APB2ENR_USART1EN;  // Enable USART 1 clock
 
	UART1_GPIO_Init();
	USART_Init(USART1);
 
	//NVIC_SetPriority(USART1_IRQn, 0);			// Set Priority to 1
	//NVIC_EnableIRQ(USART1_IRQn);				// Enable interrupt of USART1 peripheral
}

Sorry if Serbian comments make the code hard to read.

The whole project is done in Proteus 8.12 and Eclipse. I am working with STM32F103R6.

The main problem is how to call the interrupt and how to pass buffer in the interrupt handler.

Any help would be much appreciated!

1 ACCEPTED SOLUTION

Accepted Solutions
gbm
Lead III

Now the ISR:

void USART1_IRQHandler(void) 
{
    if(USART1->CR1 & USART1->SR & USART_SR_TXE) {
        uint8_t c = *sendbuf++;
        if (*sendbuf == 0)
            USART1->CR1 &= ~USART_CR1_TXEIE;
    	USART1->DR = c;
    }
}

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

View solution in original post

9 REPLIES 9
gbm
Lead III

Let's start with USART_Init:

#define BAUD 9600u
#define UARTCLK 72000000u
 
void USART_Init (USART_TypeDef * USARTx) {
USARTx->BRR = (UARTCLK + BAUD / 2) / BAUD;
	USARTx->CR1  = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE; 
}

After initializing UART, enable its interrupt with NVIC_EnableIRQ(USARTx_IRQn);

Now let's deal with UART_Send:

static const uint8_t *sendbuf;
 
void UART_Send (const uint8_t *buffer){
    sendbuf = buffer;
    USART1->CR1 |= USART_CR1_TXEIE; // Enable TXE Interrupt
}
 

t.b.c. ;)

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
UMilo.1
Associate III

Thanks!

I enabled the interrupt and adjusted the functions. But I still can't write the message on the virtual terminal, do you have any tips for the Handler function?

UMilo.1
Associate III

Further investigating: my code seems to stop working after USART1->CR1 |= USART_CR1_TXEIE; command. Not sure why...

But IRQHandler works as respected when I try to call it explicitly from UART_Send function

gbm
Lead III

Now the ISR:

void USART1_IRQHandler(void) 
{
    if(USART1->CR1 & USART1->SR & USART_SR_TXE) {
        uint8_t c = *sendbuf++;
        if (*sendbuf == 0)
            USART1->CR1 &= ~USART_CR1_TXEIE;
    	USART1->DR = c;
    }
}

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

If the TXE is flagging, and you don't service it with data or disable the interrupt, it will keep re-entering, and no foreground execution will occur.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thank you!

UMilo.1
Associate III

Thanks, this really helped!

This solved the issue. I added

 delay(300)

after

uint8_t c = *sendbuf++;

in order to get the whole string on a virtual display. I am getting a weird symbol at the start of the display, its symbol "<"

Not sure why it's showing and how to get rid of that.

gbm
Lead III

Adding delay() did not solve any issue. It only masked the problem. Before sending the next string you must make sure that the previous send operation has completed. This can be implemented in many ways, for example by checking if the TXEIE bit in CR1 is 0.

The extra character surely comes from your program. Search for the mistake in your code.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
UMilo.1
Associate III

Thanks, I solved all the issues. Thank you again for helping me!