Page 1 of 1

Trouble using 3 UARTs simultaneous on STM32F405

Posted: Fri Aug 01, 2025 4:27 pm
by metastable
Hey everyone, I've just about ran out of ideas and looked up just about all that I can. I'm using an MKS XDrive Mini motor controller that's using the STM32F405RGT6. I've selected the STM32F405RGTX generic variant in Arduino IDE. I'm using Serial over USB, and I've also added a second Serial2 using

Code: Select all

HardwareSerial Serial2(USART2);
All of that has worked correctly. I'm not attempting to add a third serial. I tried doing

Code: Select all

HardwareSerial Serial4(PC11, PC10);
but I would get a compiler error that Serial4 has already been declared. Digging into that, it seems that Serial4 is defined due to this variant having this

Code: Select all

#ifndef SERIAL_UART_INSTANCE
  #define SERIAL_UART_INSTANCE  4
#endif
which in WSerial.h causes Serial4 to be Serial?

Code: Select all

    #elif SERIAL_UART_INSTANCE == 4
      #define ENABLE_HWSERIAL4
      #if !defined(Serial)
        #define Serial Serial4
        #define serialEvent serialEvent4
      #endif
So I then I tried to do

Code: Select all

HardwareSerial SerialOnStep(PC11, PC10);
but I don't seem to get any data. I've hooked up a logic analyzer on the pins and verified that data is being sent to it PC11, but I can't read it. I'm not sure how the SerialUSB fits into this. I've also tried doing

Code: Select all

HardwareSerial SerialOnStep(PC11_ALT1, PC10_ALT1);
which should be USART3 but I still didn't get anything.

I'm fairly new to all this so if anyone has any other ideas, I'd be very grateful!

This is the schematic of the board in case that's helpful https://github.com/makerbase-mks/ODrive ... ematic.pdf

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sat Aug 02, 2025 4:35 am
by ag123
I think Serial1 .. Serial4 are pre-defined and it is not necessary to re-define them
https://github.com/stm32duino/Arduino_C ... wareserial

for the pins to use look in PeripheralPins.c
https://github.com/stm32duino/Arduino_C ... ins.c#L179

USB Serial is different from those and you can select USB serial to override the default Serial.
so like any of those pre-definied SerialN devices it could simply be

Code: Select all

void setup() {
	Serial1.begin(115200);
	delay(2000); // sleep 2s
	Serial1.println("hello world");
}

#define BUF_LENGTH 32
char *buffer[BUF_LENGTH];
void loop() {
	uint8_t read = 0;
	uint8_t i=0;
	memset(buffer, 0, BUF_LENGTH);
	while(i < BUF_LENGTH) {
		if(Serial1.available()) {
			read = Serial1.read();
			if (read == '\r') break; //break on carriage return
			buffer[i++] = read;
		} else
		delay(1);
	}
	// echo the inputs back to serial terminal
	Serial1.println(buffer);
	delay(100);
}

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sat Aug 02, 2025 5:36 am
by ag123
In addition, for your board, if you want to use usb (e.g. usb serial) you should make a variant to use the HSE crystal, otherwise normally usb may not be stable.

https://github.com/stm32duino/Arduino_C ... 28board%29

as apparently the crystal on your board is 8 Mhz
https://github.com/makerbase-mks/ODrive ... c_v3.6.pdf

you may like to make edits in your local copy of the core variant files generic_clock.c:
https://github.com/stm32duino/Arduino_C ... ic_clock.c

Code: Select all

// RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; // comment this, replace with HSE
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
//  RCC_OscInitStruct.HSIState = RCC_HSI_ON; // comment this replace with HSE
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
//  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; // comment  this
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
//  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; // comment this, replace with HSE
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
//  RCC_OscInitStruct.PLL.PLLM = 8;
/* take HSE = 8 Mhz, feed to PLL = HSE / PLLM = 8 / 4 = 2 Mhz */
  RCC_OscInitStruct.PLL.PLLM = 4;
/* PLL is operated at HSE / PLLM *  PLLN = 8 / 4 * 168 = 336 Mhz */  
  RCC_OscInitStruct.PLL.PLLN = 168;
/* system clock is PLL clock / PLLP =336 / 2 = 168  Mhz */
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
/* PLLQ is used for USB needs to be 48 Mhz 
   USB clock = PLL clock / PLLQ = 336 / 7 = 48 Mhz */
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }
^ for how this works review the ref manual rm0090 chapter 7 Reset and clock control for STM32F405xx/07xx and STM32F415xx/17xx(RCC)
figure 21 clock tree
https://www.st.com/resource/en/referenc ... ronics.pdf

there are some python scripts you can use to play with the clock PLL multiplier configs.
viewtopic.php?t=78

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sat Aug 02, 2025 12:54 pm
by metastable
Thanks for the replies. Could you please explain more what you mean by override with USB serial? Serial over USB is working fine but do you think it could be causing my issue?

Here's what I have for my clock configuration. I took this from the firmware that comes with the board.

Code: Select all

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
    Error_Handler();
  }

    /**Configure the Systick interrupt time 
  */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sat Aug 02, 2025 1:44 pm
by ag123
from the arduino menu, if you select "USB Support - CDC (generic 'Serial' supersede U(S)ART"
USB-Serial become the definiition of Serial

hence when you issue

Code: Select all

void setup() {
	Serial.begin();
}
that goes to USB-Serial. Serial1 .. Serial4 are still there but those are HardwareSerial which goes to the uart.
note that USB Serial is not uart, like the menu titles it is USB CDC (comm device class), it is full speed (12 Mbps) usb that goes to the pc (host) as a virtual comm port, 'baud rates' typically is 1 Mbps or more if you attempt to measure it. but note that usb is polling i.e. the host (your PC) polls the devices including all the hubs and ports and gives them 1 ms each round robin, hence the more devices you connect keyboard, mouse etc the 12 Mbps bandwidth gets multiplexed / divided down further.

if you are using STM32duino Core the configuration for systick is not necessary and should preferably be commented. systick() is default 1ms and handled in the core for delay() etc.
https://github.com/stm32duino/Arduino_C ... lock.c#L65

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sat Aug 02, 2025 11:37 pm
by metastable
I understand now, thank you! Yes, I have CDC supersede. I looked in WSerial.h to understand a bit more and it looks like that's done by this block

Code: Select all

#if defined (USBCON) && defined(USBD_USE_CDC)
  #ifndef DISABLE_GENERIC_SERIALUSB
    #define ENABLE_SERIALUSB
    #if !defined(Serial)
      #define Serial SerialUSB
      #define serialEvent serialEventUSB
    #endif
  #endif

  #if defined(ENABLE_SERIALUSB)
    #define HAVE_SERIALUSB
  #endif

  extern void serialEventUSB(void) __attribute__((weak));
#endif /* USBCON && USBD_USE_CDC */
Knowing a bit more I've changed my setup code to be

Code: Select all

  Serial4.setRx(PC11);
  Serial4.setTx(PC10);
  Serial4.begin(115200);
but I still don't get anything when doing Serial4.available(). As a sanity check, reduced all my code down and tested on Serial2 like so

Code: Select all

#define SERIAL_RX_BUFFER_SIZE 256

// HardwareSerial Serial4(PC11, PC10);
HardwareSerial Serial2(USART2);

int commandBytesReceived = 0;
uint8_t commandBuffer[256];

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
    Error_Handler();
  }

    /**Configure the Systick interrupt time 
  */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}

void checkForCommands()
{
  Serial.println("Checking for commands");
  int bytesAvailable = Serial2.available();
  if (bytesAvailable > 0)
  {
    Serial2.readBytes(commandBuffer, bytesAvailable);
    for(int i = 0; i < bytesAvailable; i++)
    {
      Serial.print(commandBuffer[i]);
    }
    Serial.println("");
  }
}

void setup()
{
  Serial.begin(115200);

  // Serial4.setRx(PC11);
  // Serial4.setTx(PC10);
  Serial2.begin(115200, SERIAL_8N1);

  delay(1000);
}

void loop()
{
  checkForCommands();

  delay(1000);
}
and I can read the data as expected. I've also tried

Code: Select all

HardwareSerial Serial3(PC11_ALT1, PC10_ALT1);
but that also doesn't work. I finally tried using PA0 and PA1 (UART4) by using Serial4 without any modification and that seems to work as well. So it seems that PC10 and PC11 just don't work on this board? There's an AS5047 also on those pins but its not being used.

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sun Aug 03, 2025 5:04 am
by ag123
I reviewed the codes, and it is quite 'convoluted'
when HardwareSerial initializes
https://github.com/stm32duino/Arduino_C ... l.cpp#L133
it configures the pins according to PeripheralPins.c
https://github.com/stm32duino/Arduino_C ... ins.c#L180
you can try using those pins, or if you like you can redefine them e.g. that PAxx 1st field in your local file. But they need to be valid hardware Rx, Tx pin going to the uart. so check the datasheets and ref manuals.
https://www.st.com/resource/en/datashee ... f405rg.pdf
This isn't the only way, you can also set them using setRx() , setTx() in your sketch before calling SerialN.begin(115200);.
in the same way it must be valid hardware Rx, Tx pins, check datasheet.
-----
that

Code: Select all

  Serial4.setRx(PC11);
  Serial4.setTx(PC10);
  Serial4.begin(115200);
 
should work. in fact it is the same as that in PeripheralPins.c
https://github.com/stm32duino/Arduino_C ... ins.c#L180
uart3 is apparently on PB10, PB11 (not PC10, PC11), you can check in the datasheet table 9 alternate function mapping
(it is also the default mapping in PeripheralPins.c)
https://www.st.com/resource/en/datashee ... f405rg.pdf

I think the

Code: Select all

  Serial4.setRx(PC11);
  Serial4.setTx(PC10);
is after all needed as there are 2 possible uart4 configs (PA0 tx , PA1 rx) or (PC10 tx, PC11 rx)
setting them configures the pinMux when you call SerialN.begin(115200) it looks up PeripheralPins.c and selects the entry from the correspondinig row.

-----
when you call SerialN.begin(115200);, this is what happens.
it is actually HardwareSerial.begin( ... )
https://github.com/stm32duino/Arduino_C ... l.cpp#L390
then HardwareSerial.begin( ... ) in turns calls
https://github.com/stm32duino/Arduino_C ... l.cpp#L449

Code: Select all

  _ready = uart_init(&_serial, (uint32_t)baud, databits, parity, stopbits, _rx_invert, _tx_invert, _data_invert);
that jumps here
https://github.com/stm32duino/Arduino_C ... art.c#L118
^ this code is important as it is where the uart is actually clocked and initialized.
so the initialization is done in SerialN.begin(115200), if you want to change the pins, you need to call setRx() , setTx(), before calling SerialN.begin(115200) say in void setup();

Re: Trouble using 3 UARTs simultaneous on STM32F405

Posted: Sun Aug 03, 2025 6:47 pm
by metastable
Yeah it was really difficult to follow everything myself. After all the testing I did it seems that PC10 and PC11 simply won't work on this board. I'm guessing its due to the AS5047 encoder being connected to those pins as well. I ended up moving to PA0 and PA1 for UART4 and things seem to be working now. Thanks for all of your insight!