I'm trying to transmit a char array using the usart2 configuring the associated registers directly: RCC, GPIO, USART2 and I obtain a bad baud rate when measuring with the oscilloscope (about 8k baud when expecting 9600.)
I'm coding it using atollic True Studio 9.0.1 and a "new embedded C" project selecting the correct MCU, stm32F401RE, and leaving everything but the debug probe as default.
I only have one include: #include "stm32f4xx.h"
My surprise is that when I create the project using stm32CubeMX and generating the minimum code and then substitute the main.c content with my "bare-metal" project code the time base for the uart looks almost perfect on the oscilloscope (9571 bauds).
Isn't it interesting? What may be going on?
This is my code:
Include and main loop:
#include "stm32f4xx.h"
void UART2_Init(void);
void UART2_Test_TX(void);
int main(void)
{
int i = 0;
UART2_Init();
UART2_Test_TX();
while (1)
{
i++;
}
}
A simple test function that sends "U" all the time:
void UART2_Test_TX(void)
{
USART_TypeDef * pUSART2;
pUSART2 = USART2;
char data[] = "U";
while(1)
{
while(!(pUSART2->SR && (1<<7)))// TXE transmit data register empty
{
}
pUSART2->DR = (uint16_t)data[0];
}
}
The initialization function:
void UART2_Init(void)
{
RCC_TypeDef * pRCC;
pRCC = RCC;
GPIO_TypeDef * pGPIOA;
pGPIOA = GPIOA;
USART_TypeDef * pUSART2;
pUSART2 = USART2;
//1. Enable the peripheral clock
/*
* The USART2 is connected to the APB1 bus so we have to check here
* the Reset and Clock Configuration Enable register for APB1APB1_ENR
*
* */
pRCC->APB1ENR |= (1 << 17); // Set the USART2EN bit to enable the clock (RCC_APB1ENR_USART2EN)
//2. Configure the GPIO PINS related to UART TX and RX
/*
*
* To do this we need to find the alternate function of the pins in a reference table. That is located in the Section4, table 8 of the Data sheet:
* USART2_RX *PA3, PD6
* USART2_TX *PA2, PD5
* Also in the user manual of the board (UM1724) the RX and TX pins accessible from the PC are located in port A. We have a winner.
*
* That's good but not enough. PINs MAY HAVE UP TO 16 DIFFERENT FUNCTIONALITIES so we need another table and register to select it (in datasheet table 9)
*
* 2.1 So, enable the RCC clock for GPIOA AHB1
* 2.2 Configure the PINs as alternate function
* 2.3 Configure or not Internal Pull-up resistor
* 2.4 select the alternate function Table 9 of datasheet + GPIOA_AFRL, the low pins registers, from 0 to 7
*
* */
pRCC->AHB1ENR |= 1<<0;//RCC_AHB1ENR_GPIOAEN; // 2.1 Enable the source clock for GPIOA
// configuring pin 2 TX
pGPIOA->MODER &= ~(0b11<<4); // 2.2 Clear previous configuration in PIN2
pGPIOA->MODER |= (0b10<<4); // 2.2 Configure PIN2 as alternate function GPIO_Mode_AF
pGPIOA->AFR[0] &= ~(0b1111<<8); // 2.4 clear the bits in the register ;
pGPIOA->AFR[0] |= (0b0111 <<8); // 2.4 AF7 for TX pin
// FOR SPI, I2C, UART the lines must be held high. So we need pull_up resistors
pGPIOA->PUPDR &= ~(0b11<<4);
pGPIOA->PUPDR |= (0b01 <<4);
// configuring pin 3 RX
pGPIOA->MODER &= ~(0b11<<6); // 2.2 Clear previous configuration in PIN3
pGPIOA->MODER |= (0b10<<6); // 2.2 Configure PIN3 as alternate function GPIO_Mode_AF
pGPIOA->AFR[0] &= ~(0b1111<<12); // 2.4 clear the bits in the register;
pGPIOA->AFR[0] |= (0b0111 <<12); // 2.4 AF7 for RX pin
// FOR SPI, I2C, UART the lines must be held high. So we need pull_up resistors
// pGPIOA->PUPDR &= ~(0b11<<6);
// pGPIOA->PUPDR |= (0b01 <<6);
// Note: I would be more efficient to configure all the pins at the same time but we did this way for clarity
//3. Configure the UART parameters: baudrate, data width, parity, number of stop bits etc
/*
* OVERSAMPLE 16
* Baudrate 115200
*
* OVER8 sampling divider
* 19.3.4 BaudRate = Fck/(16 * USARTDIV) .
* USARTDIV = DIV_Mantisa +(DIV_Fraction/ 8 x (2- OVER8))
* Fck = 16Mhz (default HSI)
*
* data width 8
* parity None
* stopbits 1
* */
// Configuring baudrate: 115200, real baudrate 115107.913669065. Error 0.08%
pUSART2->CR1 &= ~(1<<15); // O: Oversample 16 OK1
pUSART2->BRR &= ~(0xFFFF); // Clear the mantisa and fraction
pUSART2->BRR |= (104<<4); // Mantisa
pUSART2->BRR |= (3<<0); // Fraction
uint32_t cBRR = pUSART2->BRR;
//pUSART2->BRR |= (0x9B); // Mantisa and Fraction as Hex OK1
/*
pUSART2->CR1 &= ~(1<<12); // 8 bits OK1
pUSART2->CR1 &= ~(1<<10); // Parity control disable OK1
pUSART2->CR2 &= ~(0b11<<12); // 1 stop bits OK1
*/
//4. Enable the TX engine of UART2 (do we need RX or we can save power?)
/*
* UE bit USART enable
* TE bit Transmit enable
* TDR Register to output the data
* */
pUSART2->CR1 |= (1<<3); // O: Transmit enable
//5. ENABLE THE USART peripheral Always at the end
/*
* Section 19.3.2
* USART_CR1.UE enable the usart
* USART_CR1.M number of bits 8,9
* USART_CR2 number of stops
* DMA enable...
*
* */
pUSART2->CR1 |= (1<<13); // O: USART enable
// Here is ready
}
SystemClock_Config()
? Show that function and the equivalent function in your True Studio project. – kkrambo