Problem while trying to implement an stm32f103c8 UART driver

Thread Starter

fadi1999

Joined May 29, 2024
2
Hye stackoverflow community I am trying to implement a UART driver but I am facing a problem with these codes and I don't know to fix it, I try to simulate the functionality of this driver in Proteus using virtual terminal but I don't receive the data from the terminal when i simulate the code in proteus, so i don't know eighter the problem is in each function? i wish that i can find someone that can tell me how to fix this code :

1717016463234.png
UART.h :
#ifndef __UART_H__
#define __UART_H__
#include <stdint.h>

#define RCC_BASE 0x40021000

#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011400
#define GPIOD_BASE 0x40011800

// APB2 peripheral clock enable register (RCC_APB2ENR) offset
#define RCC_APB2ENR_OFFSET 0x18

// APB1 peripheral clock enable register (RCC_APB1ENR) offset
#define RCC_APB1ENR_OFFSET 0x1C


// peripheral register offsets
#define GPIO_CRL_OFFSET 0x00
#define GPIO_CRH_OFFSET 0x04
#define GPIO_ODR_OFFSET 0x0C

#define USART1_BASE_ADDR 0x40013800
#define USART2_BASE_ADDR 0x40004400

#define USART_CR1_OFFSET 0x00
#define USART_BRR_OFFSET 0x08
#define USART_DR_OFFSET 0x04
#define USART_SR_OFFSET 0x00

#define SystemCoreClock 168000000

void GPIOx_Clock_enable(uint32_t gpio_base);
void uart_enable_clock(uint32_t uart_base);
void uart_configure_baudrate(uint32_t uart_base, uint32_t baud_rate);
void gpio_set_alternate_function(uint32_t gpio_base, uint8_t pin, uint8_t af_mode);
void uart_configure_transfer_direction(uint32_t uart_base, uint8_t tx_enable, uint8_t rx_enable);
void uart_enable_module(uint32_t uart_base);
void uart_transmit_char(uint32_t uart_base, char ch);
void uart_transmit_string(uint32_t uart_base, const char *str);


#endif

UART.c:

#include "UART.h"


void GPIOx_Clock_enable(uint32_t gpio_base){

volatile uint32_t *RCC_APB2ENR = (volatile uint32_t *)(RCC_BASE + RCC_APB2ENR_OFFSET);

switch (gpio_base) {
case GPIOA_BASE:
// code block
*RCC_APB2ENR |= (1U << 2);
break;
case GPIOB_BASE:
// code block
*RCC_APB2ENR |= (1U << 3);
break;
case GPIOC_BASE:
// code block
*RCC_APB2ENR |= (1U << 4);
break;
case GPIOD_BASE:
// code block
*RCC_APB2ENR |= (1U << 5);
break;
default:
// code block
break;
}
}
void uart_enable_clock(uint32_t uart_base)
{
switch (uart_base) {
case USART1_BASE_ADDR:
// code block
volatile uint32_t *rcc_apb2enr = (volatile uint32_t *)(RCC_BASE + RCC_APB2ENR_OFFSET);
*rcc_apb2enr |= 1U << 14;
break;
case USART2_BASE_ADDR:
// code block
volatile uint32_t *rcc_apb1enr = (volatile uint32_t *)(RCC_BASE + RCC_APB1ENR_OFFSET);
*rcc_apb1enr |= 1U << 17;
break;
default:
// code block
break;
}
}

void uart_configure_baudrate(uint32_t uart_base, uint32_t baud_rate)
{
volatile uint32_t *usart_brr = (volatile uint32_t *)(uart_base + USART_BRR_OFFSET);
//volatile uint32_t *usart_brr = (volatile uint32_t *)(APB1_PERIPHERAL + USART1_PERIPHERAL_OFFSET + USART_BRR_OFFSET);
// Calculate baud rate divisor and set BRR register
*usart_brr = (SystemCoreClock + (baud_rate/2)) / baud_rate;
}

void gpio_set_alternate_function(uint32_t gpio_base, uint8_t pin, uint8_t af_mode)
{
volatile uint32_t *gpio_cr;
uint8_t pin_index = pin % 8;

if (pin < 8) {
gpio_cr = (volatile uint32_t *)(gpio_base + GPIO_CRL_OFFSET);
} else {
gpio_cr = (volatile uint32_t *)(gpio_base + GPIO_CRH_OFFSET);
//pin_index = pin - 8;
}

// Clear pin configuration bits
*gpio_cr &= ~(0xF << (4 * pin_index));

// Set pin mode to alternate function output push-pull
*gpio_cr |= (af_mode << (4 * pin_index));
}

void uart_configure_transfer_direction(uint32_t uart_base, uint8_t tx_enable, uint8_t rx_enable)
{
volatile uint32_t *usart_cr1 = (volatile uint32_t *)(uart_base + USART_CR1_OFFSET);

// Configure USART TX and RX enable bits
if (tx_enable) {
*usart_cr1 |= (1U << 3); // Set TE (Transmitter Enable) bit
} else {
*usart_cr1 &= ~(1U << 3); // Clear TE bit
}

if (rx_enable) {
*usart_cr1 |= (1U << 2); // Set RE (Receiver Enable) bit
} else {
*usart_cr1 &= ~(1U << 2); // Clear RE bit
}
}

void uart_enable_module(uint32_t uart_base)
{
volatile uint32_t *usart_cr1 = (volatile uint32_t *)(uart_base + USART_CR1_OFFSET);

// Enable USART module by setting UE (USART Enable) bit
*usart_cr1 |= (1U << 13);
}

void uart_transmit_char(uint32_t uart_base, char ch)
{
volatile uint32_t *usart_sr = (volatile uint32_t *)(uart_base + USART_SR_OFFSET);
volatile uint32_t *usart_dr = (volatile uint32_t *)(uart_base + USART_DR_OFFSET);

// Wait until transmit data register is empty
while (!(*usart_sr & (1U << 7)));

// Transmit data by writing to data register
*usart_dr |= (ch & 0xFF);
}

void uart_transmit_string(uint32_t uart_base, const char *str)
{
while (*str != '\0') {
uart_transmit_char(uart_base, *str);
str++;
}
}

main.c:

#include "UART.h"
int main(void)
{

GPIOx_Clock_enable(GPIOA_BASE);
uart_enable_clock(USART1_BASE_ADDR);
// Configure USART1 baud rate to 9600
uart_configure_baudrate(USART1_BASE_ADDR, 9600);

// Configure PA9 (USART1 TX) as alternate function output push-pull
gpio_set_alternate_function(GPIOA_BASE, 9, 0b00000010); // AF mode 0b0010 for USART1 TX

// Configure USART1 for TX and RX (enable both)
uart_configure_transfer_direction(USART1_BASE_ADDR, 1, 0);

// Enable USART1 module
uart_enable_module(USART1_BASE_ADDR);

// Send a string over USART1
uart_transmit_string(USART1_BASE_ADDR, "Hello, UART!\r\n");
while (1)
{
// Main application loop
}

return 0;
}
 

MrChips

Joined Oct 2, 2009
31,067
Your code is too complex to debug. Do simple things, one at a time.
I don’t do Proteus but I can show you how to do it in IAR or in STM32CubeIDE.
 

Thread Starter

fadi1999

Joined May 29, 2024
2
Your code is too complex to debug. Do simple things, one at a time.
I don’t do Proteus but I can show you how to do it in IAR or in STM32CubeIDE.
the code is already commented and divided into sub blocks and sub steps but i think there is an algorithmic step that is missing but i don't know which step i miss.
 

MrChips

Joined Oct 2, 2009
31,067
You have three separate text files shown. It would be better to keep them separate, for example:
C:
#include "UART.h"
int main(void)
{

GPIOx_Clock_enable(GPIOA_BASE);
uart_enable_clock(USART1_BASE_ADDR);
// Configure USART1 baud rate to 9600
uart_configure_baudrate(USART1_BASE_ADDR, 9600);

// Configure PA9 (USART1 TX) as alternate function output push-pull
gpio_set_alternate_function(GPIOA_BASE, 9, 0b00000010); // AF mode 0b0010 for USART1 TX

// Configure USART1 for TX and RX (enable both)
uart_configure_transfer_direction(USART1_BASE_ADDR, 1, 0);

// Enable USART1 module
uart_enable_module(USART1_BASE_ADDR);

// Send a string over USART1
uart_transmit_string(USART1_BASE_ADDR, "Hello, UART!\r\n");
while (1)
{
// Main application loop
}

return 0;
}
Use code tags for better viewing and scrolling like this:
[CODE=C]... your code here ...[/CODE]
 

MrChips

Joined Oct 2, 2009
31,067
What I mean by keeping it simple:

(1) Start by making an LED flash. This would verify that your code for initializing an STM32 peripheral is working.
(2) Send a single character "U" in a loop. If you have an oscilloscope you will be able to confirm the transmission baud.
 

BobTPH

Joined Jun 5, 2013
9,256
(2) Send a single character "U" in a loop. If you have an oscilloscope you will be able to confirm the transmission baud
+1

I always start with that. It will hive you a square wave with a gap for the start and stop bits.

A multimeter will show roughly 1/2 the Vdd in the TX pin, and a frequency of a little less than half the baud rate. So you can debug with just a multimeter.
 
Top