UART | Interrupt | STM32F103C8T6 - ARM Cortex M3

 Program:

/* STM32 - USART - Asynchronous mode
   LSB first
   Baud Rate = 9600, SysClk = 72 MHz
   Start bit - 8 data bits - 1 Stop bit
*/

#include "stm32f10x.h"
#define baud_9600 0x1D4C              // for SysClk = 72 MHz

void RCC_config(void);
void USART1_IRQHandler(void);

static uint32_t data;

void USART1_IRQHandler()
{
  // Transmission complete
  if((USART1 -> SR & USART_SR_TC)){
    GPIOC -> BSRR = GPIO_BSRR_BR13;      // PC13 LED - ON
  }
  // Received data
  if((USART1 -> SR & USART_SR_RXNE)){
    GPIOC -> BSRR = GPIO_BSRR_BS13;      // PC13 LED - OFF
    data = USART1 -> DR;                  // Read the received data
    // Disable USART if required
  }
}

void RCC_config() // RCC clock configuration
{
  RCC -> CR |= RCC_CR_HSEON;                               // HSE ON
  RCC -> CFGR |= (RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9);    
// Setting PLL - HSE * 9
  RCC -> CFGR |= RCC_CFGR_SW_1;                            // SysClk = PLLCLK
  RCC -> CR |= RCC_CR_PLLON;                                              
  // Turn ON PLL after above PLL configurations, making SysClk = HSE * 9 = 8 * 9 = 72 MHz
RCC -> CFGR |= RCC_CFGR_PPRE1_DIV2;
// APB1 = AHB / 2 = 72 / 2 = 36 MHz (max)
}

int main()
{
  RCC_config();
 
  /* PC13 LED configuration */
  RCC -> APB2ENR |= RCC_APB2ENR_IOPCEN;                  // Enable Port-C Clock
  GPIOC -> CRH |= GPIO_CRH_MODE13_1 | GPIO_CRH_CNF13_0;  // PortC_Pin 13 as output @2MHZ with open-drain
  GPIOC -> BSRR = GPIO_BSRR_BS13;                       // Initialize PC13 LED - OFF
 
  /* USART1 configuration */
  RCC -> APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN;            // Enable UART and Port A clock
  GPIOA -> CRH |= (GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1 | GPIO_CRH_CNF10_1);
// PA9- CNF: 10, MODE: 10; PA10- CNF: 10, MODE: 00
  GPIOA -> CRH &= ((~GPIO_CRH_CNF9_0) & (~GPIO_CRH_CNF10_0));             // Clear default reset value
  USART1 -> BRR = baud_9600;                                              // Baud Rate = 9600
  USART1 -> CR1 |= USART_CR1_UE;                                          
// Enable USART, M = 0 - 8 data bits
  /* USART1 -> CR2 : STOP = 0 -> Indicating 1 stop bit */
  USART1 -> CR1 |= USART_CR1_TCIE;                                        
// Enable Transmission complete interrupt
  USART1 -> CR1 |= USART_CR1_TE;                                          // Sending a Idle line
  /* This should clear TXE and TC flag, after which interrupt is enabled */
  NVIC_EnableIRQ(USART1_IRQn);                                            
// Enable USART1 global interrupt in NVIC                                                              
  USART1 -> DR = 'A';                                                     // Sending data
  USART1 -> CR1 |= USART_CR1_RXNEIE;                                      // Enable Receiver interrupt
  USART1 -> CR1 |= USART_CR1_RE;                                          // Enable Receiver
 
  while(1)
  {
    // Do nothing
  }
  return 0;
}


For more details on RCC  configuration, see previous post.

Baud Rate generation:

Below is the formula to calculate the baud rate:

FCLK = 72 MHz

Lets say, required baud rate = 9600

Tx/ Rx baud  =       FCK
                      ---------------------
                       (16*USARTDIV)

USARTDIV  =      FCK                              =    72000000        =    468.75
                       ---------------------        ---------------- 
                       (16 * baud rate)           (16 * 9600)

USARTDIV is an unsigned fixed point number that is coded on the USART_BRR (baud rate) register, which requires DIV_Fraction and DIV_Mantissa

DIV_Fraction (4 - bit)  = 16 * 0.75  = 12  = 0xC

DIV_Mantissa (12 - bit) = 468 = 0x1D4

So, USART_BRR  = 0x1D4C

Refer reference manual for more details.

Steps:

1. Enable clock for USART1 = 72 MHz
Refer previous post for detailed RCC configuration

2. We are using USART1 and the Tx and Rx pins should be configured based on alternate functionality table, refer datasheet and reference manual






Note: Only USART1 is clocked with PCLK2 (72 MHz max). Other USARTs are clocked with PCLK1 (36 MHz max).

3. Set baud rate in USART_BRR register

4. Enable USART and configure no. of data bits, in this case M = 0 -> 8 data bits

5. Configure no. of stop bits in USART_CR2 register, in this case STOP = 0 -> 1 stop bit

Transmission:

6. Set the TE bit in USART_CR1 to send an idle frame as first transmission

7. Write the data to send in the USART_DR register

TX flag:

After writing data to USART_DR register, TXE bit is set to indicate transmission started
On continuous data transmission, next write to USART_DR  register clears the TXE flag,
else TXE flag stays set even after transmission ends, setting TC flag. Which indicates end of transmission.

TC bit is cleared by a software sequence (a read from the USART_SR register followed by a write to the USART_DR register). The TC bit can also be cleared by writing a '0' to it. This clearing sequence is recommended only for multibuffer communication (i.e., continuous communication).

8. Enable Transmission complete interrupt - TCIE to indicate end of transmission or monitor the TC bit in USART_SR register (polling method).

Note: When using interrupt, enable the specific interrupt in NVIC

Lets turn on the in-built LED connected to PC13 on Transmission complete interrupt.

Receiver:

9. Set the RE bit USART_CR1. This enables the receiver which begins searching for a start bit.

Rx flag:

In single buffer mode, clearing the RXNE bit is performed by a software read to the USART_DR register. The RXNE flag can also be cleared by writing a zero to it. The RXNE bit must be cleared before the end of the reception of the next character to avoid an overrun error

In multibuffer, RXNE is set after every byte received and is cleared by the DMA read to the Data register.

10. Enable Transmission complete interrupt - RXNEIE to to know when data is received or monitor the RXNE bit in USART_SR register (polling method).

USART1_IRQ is a global interrupt, so the same handler / ISR is used for both Tx and Rx interrupt.

Result: Connect the STM32 UART to USB to Serial converter and connect to PC/Laptop and observe the sent data in any serial terminal (realterm), also use send option to send data to STM32

Connection: TX -> RX(PA10), RX -> TX(PA9), VCC -> 3.3V, Gnd -> Gnd

For DMA in STM32, see next post


Comments