2024-07-21 02:43 PM
Hi all! I am trying to communicate with the Nucleo-L4P5ZG via the LPUART1 peripheral over ST-LINK, however it doesn't seem to be transmitting or receiving anything. Following is my system clock initialization code (non-relevant parts cut out):
#pragma once
#include <stm32l4xx.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define SYS_FREQUENCY 16000000
uint32_t SystemCoreClock; // Required by CMSIS
static inline void clock_init(void) {
SCB->CPACR |= 15 << 20; // Enable FPU
RCC->CR |= RCC_CR_HSION; // Set HSI on
while (!(RCC->CR & RCC_CR_HSIRDY)) {}
RCC->CFGR &= ~(RCC_CFGR_SW);
RCC->CFGR |= RCC_CFGR_SW_HSI; // Set HSI as clock source
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) {};
rng_init(); // Initialise random number generator
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable SYSCFG
SystemCoreClock = SYS_FREQUENCY; // Required by CMSIS
}
void LPUART_Init(void) {
// Enable LPUART clock
RCC->APB1ENR2 |= RCC_APB1ENR2_LPUART1EN;
spin(30); // Blocking wait
// Set sysclock as LPUART clock source
RCC->CCIPR |= 0x1 << 10;
// Enable GPIO clock
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOGEN;
spin(5); // Blocking wait
// Configure GPIO pins
GPIOG->MODER &= ~((0x3 << (8 * 2)) | (0x3 << (7 * 2)));
GPIOG->MODER |= (0x2 << (8 * 2) | (0x2 << (7 * 2)));
GPIOG->OTYPER &= ~((0x1 << 8) | (0x1 << 7));
// Set GPIOG pins 7 and 8 speed
GPIOG->OSPEEDR &= ~((0x3 << (8*2)) | (0x3 << (7*2)));
GPIOG->OSPEEDR |= ((0x2 << (8*2)) | (0x2 << (7*2)));
// Set GPIOG pins 7 and 8 to alternate function 8
GPIOG->AFR[0] &= ~((0xF << (7*4)));
GPIOG->AFR[0] |= ((0b1000 << (7*4)));
GPIOG->AFR[1] &= ~((0xF << ((8-8) * 4)));
GPIOG->AFR[1] |= ((0b1000 << ((8-8) *4)));
// Set baud rate
LPUART1->BRR = ((SystemCoreClock) / 9600);
// Enable tx
LPUART1->CR1 |= 0x1 << 3;
// Enable rx
LPUART1->CR1 |= 0x1 << 2;
// Enable LPUART
LPUART1->CR1 |= 0x1;
while ((LPUART1->ISR & USART_ISR_TEACK) == 0);
}
The first function above gets called by the CMSIS startup template, "cmsis_l4/Source/Templates/gcc/startup_stm32l4p5xx.s", which then calls my main():
int main(void) {
// These are defined elsewhere and work fine
gpio_output(LED_PIN);
gpio_input(BTN_PIN);
LPUART_Init();
for (;;) {
gpio_write(LED_PIN, true); // Defined elsewhere, this is reached and works
while(!(LPUART1->ISR & USART_ISR_RXNE_RXFNE)) {};
rxb = LPUART1->RDR;
gpio_write(LED_PIN, false); // Defined elsewhere, this is never reached
while(!(LPUART1->ISR & USART_ISR_TXE_TXFNF)) {};
LPUART1->TDR = rxb;
}
return 0;
}
The first `gpio_write` on line 9 above is reached and activates the on-board LED. I then run `screen -fn /dev/ttyACM0 9600` on the machine connected to the board (which is able to program the board, ruling out the USB) and press some keys. The LED never goes off, which means line 12 is never reached, and, obviously, the byte is never echoed back to the terminal on the machine. Have I done anything obviously wrong? I can connect GDB via openOCD and I see that the registers are set appropriately, so it seems to either be an issue of me not setting the right parameters on initialization or the machine isn't communicating with the board correctly (however it is during programming?). Thanks very much in advance for any assistance!
2024-07-21 03:44 PM - edited 2024-07-21 03:46 PM
BRR seems wrong. See formula in the RM. You're setting it as if there are no fractional bits. Should be 256x what you have.
UE should be set before TE and RE.
Consider setting it up with HAL/CubeMX to see what the proper initialization settings and register values are.
2024-07-21 08:33 PM
For GPIOG you must enable VDDIO via PWR
2024-08-03 02:13 PM
Thank you both @Tesla DeLorean and @TDK, following both of your advice let me to getting some kind of input! My updated LPUART initialization code:
void LPUART_Init(void) {
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
spin(30);
PWR->CR2 |= 0x1 << 9;
spin(30);
// Enable LPUART clock
RCC->APB1ENR2 |= RCC_APB1ENR2_LPUART1EN;
spin(30);
RCC->CCIPR |= 0x1 << 10;
// Initialize GPIO pin
gpio_init(PIN('G', 7), GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, GPIO_PULL_UP, 0b1000);
gpio_init(PIN('G', 8), GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, GPIO_PULL_UP, 0b1000);
// Enable GPIO clock
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOGEN;
spin(30);
// Configure LPUART controller
LPUART1->CR1 &= ~USART_CR1_UE; // Disable LPUART
// Set the baud rate
//uint16_t uartdiv = SystemCoreClock / 9600;
//LPUART1->BRR = uartdiv;
LPUART1->BRR = ((256 * SystemCoreClock) / 9600);
// Leave all interrupts disabled
// Enable LPUART
LPUART1->CR1 |= 0x1;
spin(20);
// Enable tx
LPUART1->CR1 |= 0x1 << 3;
// Enable rx
LPUART1->CR1 |= 0x1 << 2;
while ((LPUART1->ISR & USART_ISR_TEACK) == 0);
}
And the updated code that sends a character (note that I've changed this to send a constant char on button press):
if(gpio_read(BTN_PIN) != 0) {
gpio_write(LED_PIN, true);
LPUART1->TDR = 'h';
while(!(LPUART1->ISR & USART_ISR_TXE_TXFNF)) {};
} else {
gpio_write(LED_PIN, false);
}
Upon starting screen on my computer the same way as before, I get a stream of ">" characters whenever I press the button on the board. I tried changing the char sent by the board to 'r', which changed the character appearing in the terminal to a stream of "�" preceeded by a single "0". I thought this was due to a mismatch between the frames that the tty was expecting and what the board was sending, so I fiddled with stty options by "stty -F /dev/ttyACM0 -cstopb -parenb -clocal cs8". This sets the tty to expect the same as the default LPUART registers - 1 stop bit, 8 data bits, no parity bits. This didn't change anything, however :) I'm left thinking that my baud rate must still not be set correctly, but I cannot see what would be wrong (after correcting the error that @TDK pointed out).