cancel
Showing results for 
Search instead for 
Did you mean: 

I can't make bare metal LPUART work in L496

henry_kay
Associate

Hello all,

 

I've recently started experimenting with bare metal programming in L496, I made LED and push button I/Os working, interrupts etc, but I cannot figure out the exact steps to make LPUART1 work and I cannot find any references/examples of LPUART initialization in L4. This is my code:

 

RCC -> APB1ENR1 |= (RCC_APB1ENR1_PWREN);

// Enable GPIOG clock
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOGEN;

// Enable LPUART1 clock
RCC -> APB1ENR2 |= RCC_APB1ENR2_LPUART1EN;

// Configure PG7 and PG8 as Alternate function
GPIOG->MODER &= ~(GPIO_MODER_MODE7_Msk | GPIO_MODER_MODE8_Msk);
GPIOG->MODER |= (0x02 <<GPIO_MODER_MODE7_Pos) | (0x02 << GPIO_MODER_MODE8_Pos);

// 1000: AF8
GPIOG->AFR[0] &= ~(0xFFFFFFFF);
GPIOG->AFR[1] &= ~(0xFFFFFFFF);

GPIOG->AFR[0] |= (0x8 << GPIO_AFRL_AFSEL7_Pos);
GPIOG->AFR[1] |= (0x8 << GPIO_AFRH_AFSEL8_Pos);

// 11: Very high speed
GPIOG->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED7_Msk);
GPIOG->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED8_Msk);
GPIOG->OSPEEDR |= (0x03 << GPIO_OSPEEDR_OSPEED7_Pos);
GPIOG->OSPEEDR |= (0x03 << GPIO_OSPEEDR_OSPEED8_Pos);

// 8-bit, 1 start, 1 stop, CTS/RTS disabled
LPUART1->CR1 = 0x00000000;
LPUART1->CR2 = 0x00000000;
LPUART1->CR3 = 0x00000000;

// Select PCLK1 (APB1) as clock source
// PCLK -> 48 MHz
RCC->CCIPR &= ~(RCC_CCIPR_LPUART1SEL_Msk);




// LPUART1->BRR = 833;
LPUART1->BRR = 0x2B672;

// Enable Transmitter and Receiver and USART
LPUART1->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;

 

 

and in main:

 

for(i=0; i<500000; i++);
	
LPUART1->TDR = 'e';

 

I have even tried setting up LPUART with LL (it works) and then copy pasting the same register values as an experiment, but with no success:

 

 

// clock config
RCC->PLLCFGR = 0x1000;
RCC->PLLSAI1CFGR = 0x1000;
RCC->PLLSAI2CFGR = 0x1000;
RCC->ICSCR = 0x408b0088;

RCC->CR = 0x63;
while (!(RCC->CR & RCC_CR_PLLRDY)) {};

RCC->CFGR = 0x0;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL)) {};

//Clear LPUART registers
LPUART1->CR1 = 0x0;
LPUART1->CR2 = 0x0;
LPUART1->CR3 = 0x0;
LPUART1->BRR = 0x0;
LPUART1->ISR = 0xc0;
LPUART1->RDR = 0x0;
LPUART1->TDR = 0x0;

RCC->APB1ENR1 = 0x100;
	
RCC->APB2ENR = 0x1;
while (!(RCC->APB2ENR & RCC_APB2ENR_SYSCFGEN)) {};

RCC -> APB1ENR1 = 0x10000400;
while (!(RCC->APB1ENR1 & RCC_APB1ENR1_PWREN)) {};
	
RCC -> APB1ENR2 = 0x1;
while (!(RCC->APB1ENR2 & RCC_APB1ENR2_LPUART1EN)) {};

RCC -> AHB2ENR = 0x40;
while (!(RCC->AHB2ENR & RCC_AHB2ENR_GPIOGEN)) {};

RCC->CCIPR = 0x0;
PWR->CR1 = 0x200;
PWR->CR2 = 0x200;

GPIOG->OSPEEDR = 0x3c000;
GPIOG->AFR[0] = 0x80000000;
GPIOG->AFR[1] = 0x8;
GPIOG->MODER = 0xffffffff;

LPUART1->CR1 = 0xc;
//This register can only be written when the LPUART is disabled (UE=0).
LPUART1->BRR = 0x22b9;
LPUART1->ISR = 0xc0;
LPUART1->CR1 = 0xd;
LPUART1->ISR = 0x6000d0;

 

 

Any idea what is the problem? I would greatly appreciate your help!

6 REPLIES 6
gbm
Lead III

MCU programming is not magic, so don't use magic instructions and magic numbers.

MODER fields should be set to 10 binary (AF).

To program UART (after enabling it in RCC and selecting the clock source in RCC->CCIPR), just set the BRR:

LPUART1->BRR = (UART_CLK_FREQ + baud / 2) / baud;

Then set the CR1:

LPUART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;

To send data:

while (~LPUART1->ISR & USART_ISR_TXE) ;

LPUART1->TDR = 'x';

 

 

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

The while loop has two flaws - it uses the binary NOT operator instead of the logical NOT and it is missing parentheses, both of which break the logic. A correct loop is like this:

while (!(LPUART1->ISR & USART_ISR_TXE));

 

Really?... Check it then. :)

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

Ahh, sorry. Of course, the lack of parentheses makes it correct. Anyway, I would avoid using such version as it can add an additional instruction for the bitwise NOT. The logical NOT doesn't need an additional instruction, because the compiler can just adjust the branch or conditional instructions accordingly.

https://godbolt.org/z/zYrYP7W5j

The bitwise version is the same, except for an additional MVNS instruction at line 27. Though, turning on any level of optimization, makes both versions equal.

Shirley.Ye
ST Employee

you can check the cube libary, there is example code in NUCLEO-L496ZG\Examples\LPUART

For a bare metal development the LL examples are closer than the HAL ones:

https://github.com/STMicroelectronics/STM32CubeL4/tree/master/Projects/NUCLEO-L496ZG/Examples_LL/LPUART