Change Default Serial Port for STM32 Arduino

I have a custom board built with the STM32F405RG and I’m trying to get some debug code sent out to UART4 (Tx=PA0, Rx=PA1). I’ve tried everything I can think of but the only thing that worked was to define a SoftwareSerial but that won’t allow me to easily debug other libraries I’m using.

I created a simple project in STM32CubeIDE and confirmed that I just need to define UART4 and with the hardware connections the same I am getting test messages.

My platform.ini has this:
;[env:stm32f4stamp]
[env:IOTech_STM32F405]
platform = ststm32
;board = genericSTM32F407VGT6
board = IOTech_STM32F405
framework = arduino
upload_protocol = stlink

debug_tool = stlink

;lib_deps = 
;  Adafruit RA8875

In my project directory I have a folder created named “boards” and in that I have a file named IOTech_STM32F405.json which has this:

{
  "build": {
    "core": "stm32",
    "cpu": "cortex-m4",
    "extra_flags": "-DSTM32F405xx",
    "f_cpu": "168000000L",
    "mcu": "stm32f405rgt6",
    "variant": "IOTech_STM32F405"
  },
  "debug": {
    "default_tools": [
      "stlink"
    ],
    "jlink_device": "STM32F405RG",
    "openocd_extra_args": [
      "-c",
      "reset_config none"
    ],
    "openocd_target": "stm32f4x",
    "svd_path": "STM32F40x.svd"
  },
  "frameworks": [
    "arduino",
    "stm32cube"
  ],
  "name": "IOTech Villara Tester",
  "upload": {
    "disable_flushing": false,
    "maximum_ram_size": 196608,
    "maximum_size": 1048576,
    "protocol": "dfu",
    "protocols": [
      "stlink",
      "dfu"
    ],
    "require_upload_port": true,
    "use_1200bps_touch": false,
    "wait_for_upload_port": false
  },
  "url": "www.iotllc.com",
  "vendor": "IO Technologies"
}

Then in this directory, C:\Users\george\AppData\Local\Arduino15\packages\STM32\hardware\stm32\1.7.0\variants\IOTech_STM32F405 I copied Generic_F405RG to a folder named IOTech_STM32F405

In the variant.h file I have this
// UART Definitions
// Define here Serial instance number to map on Serial generic name
#define SERIAL_UART_INSTANCE 4

// Default pin used for 'Serial1' instance
#define PIN_SERIAL_RX           PA1
#define PIN_SERIAL_TX           PA0

In PeripheralPins.c I made this change:

//*** SERIAL ***

#ifdef HAL_UART_MODULE_ENABLED
WEAK const PinMap PinMap_UART_TX[] = {
  {PA_0,  UART4,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_UART4)},
  {PA_2,  USART2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)},
  {PA_9,  USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
  {PB_6,  USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
  {PB_10, USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  {PC_6,  USART6,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_USART6)},
  //{PC_10, UART4,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_UART4)},
  {PC_10, USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  {PC_12, UART5,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_UART5)},
  {NC,    NP,    0}
};
#endif

#ifdef HAL_UART_MODULE_ENABLED
WEAK const PinMap PinMap_UART_RX[] = {
  {PA_1,  UART4,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_UART4)},
  {PA_3,  USART2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)},
  {PA_10, USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
  {PB_7,  USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
  {PB_11, USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  {PC_7,  USART6,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_USART6)},
  //{PC_11, UART4,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_UART4)},
  {PC_11, USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  {PD_2,  UART5,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_UART5)},
  {NC,    NP,    0}
};
#endif

I’ve obviously done something wrong and perhaps there’s a much easier way to change the default serial port but I’ve tried many combinations and can’t seem to get it to work. My board only needs to use SPI3 (but with CS on PC9) and UART4, everything else is just a GPIO.

Thanks in advance for any help pushing me in the right direction!

Then the first question I have to ask is, why not just enable the UART4 peripheral und use the according Serial4 object instead of Serial?

As you can see in the file Arduino_Core_STM32/cores/arduino/WSerial.h at main · stm32duino/Arduino_Core_STM32 · GitHub, the steps to enable this serial are to just define the ENABLE_HWSERIAL4 serial. Then just Serial4.println() stuff.

You can also select which serial the Serial object maps to by controlling the SERIAL_UART_INSTANCE, as you have shown. There you just need to modify this macro and the PIN_SERIAL_RX and PIN_SERIAL_TX macros, but not the definition of the pins in PeripheralPins.c.

But if it’s just for some debug code I’d just add

build_flags = -D ENABLE_HWSERIAL4

and

#define DebugSerial Serial4 

...
void func() { 
   DebugSerial.println("Debug strings");
}

When I add Serial4.begin(9600); I get an error:

.pio/build/IOTech_STM32F405/src/Villara Tester Touch.cpp.o: In function `setup':

Villara Tester Touch.cpp:(.text.setup+0x398): undefined reference to `Serial4’

Even though Intellisense lists it as an available option.

I’d love to at least get Serial4 working but my issue now is also that my SPI LCD screen is not working (works with pure Arduino IDE and a Nucleo-F401RE board). In the Adafruit libraries are some print statements which just use Serial so it would be easiest to just “redefine” Serial to point to UART4

I’ll remove my changes in PeripheralPins.c and test again. How can I check that the code is sending data on UART4 to confirm that my changes are being followed? Since I know that my hardware connections are good (USB to TTL dongle connected to UART4) from the STM32CubeIDE test it seems that my code change is not correct or it’s not looking where I think it should

Not reproducable for me. When I select a board which uses the stm32-core (not the maple core) and do

[env:stm32test]
platform = ststm32
board = black_f407vg
build_flags = -D ENABLE_HWSERIAL4
framework = arduino
upload_protocol = stlink

and

#include <Arduino.h>

#define DebugSerial Serial4

void setup() {
	DebugSerial.begin(115200);
}

void loop() {
   DebugSerial.println("Debug strings");
}

It builds.

RAM:   [          ]   0.8% (used 1008 bytes from 131072 bytes)
Flash: [          ]   2.4% (used 12828 bytes from 524288 bytes)
 [SUCCESS] Took 9.87 seconds 

You seem to have a custom variant though. What’s the exact content of your new variant folder?

I didn’t add this to my platform.ini file I had added a #define ENABLE_HWSERIAL4 in my program.

This compiles so now I have to add some statements and see if I get an output. With the above changes in the variant.h file I should also see data on my UART4 when just using Serial.println("Test"), correct?

I really didn’t need a variant but I wasn’t having any luck. If I just want to use UART4 as the default serial and SPI3 as the default SPI what the best method to do that if I’m using a STM32F405 part?

I just checked and neither Serial or Serial4 are sending anything out. I have Putty open in another window so I can catch all traffic and it doesn’t receive anything.

Have you reverted all changes in the peripheral pins?

Yes, I had only commented out a few lines and those are removed. I also preformed a clean before trying

I just created a new project with just your code and it compiles and programs the board but no messages are written to the serial port (COM9, 115200, 8N1)

Could you try this platformio.ini

[env:stm32test]
platform = ststm32
board = IOTech_STM32F405
framework = arduino
upload_protocol = stlink

Then create a boards/IOTech_STM32F405.json file with

{
  "build": {
    "core": "stm32",
    "cpu": "cortex-m4",
    "extra_flags": "-DSTM32F405xx",
    "f_cpu": "168000000L",
    "mcu": "stm32f405rgt6",
    "variant": "IOTech_STM32F405"
  },
  "debug": {
    "default_tools": [
      "stlink"
    ],
    "jlink_device": "STM32F405RG",
    "openocd_extra_args": [
      "-c",
      "reset_config none"
    ],
    "openocd_target": "stm32f4x",
    "svd_path": "STM32F40x.svd"
  },
  "frameworks": [
    "arduino",
    "stm32cube"
  ],
  "name": "IOTech Villara Tester",
  "upload": {
    "disable_flushing": false,
    "maximum_ram_size": 196608,
    "maximum_size": 1048576,
    "protocol": "dfu",
    "protocols": [
      "stlink",
      "dfu"
    ],
    "require_upload_port": true,
    "use_1200bps_touch": false,
    "wait_for_upload_port": false
  },
  "url": "www.iotllc.com",
  "vendor": "IO Technologies"
}

And then go to C:\Users\george\.platformio\packages\framework-arduinoststm32\variants and create the folder IOTech_STM32F405 and fill it with these files: https://drive.google.com/open?id=1rUolHeoGmxDqjoNLHGVX2ebecBZ3OD6n

And use a src\main.cpp

#include <Arduino.h>

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

void loop() {
   Serial.println("Hello world");
  delay(1000);
}

Is then anything printed at PA1 (TX pin)?

Oh and I made an error in the variant.cpp, I flipped RX and TX. Should be

#define PIN_SERIAL_RX           PA1
#define PIN_SERIAL_TX           PA0

OK, I’m doing this now and will report back soon, thanks for your help. I did try and probe all the other UART Tx pins on the STM32F405 to see if maybe the last code was just sending data to the wrong port but I didn’t find it on any of the 6 UARTs

Unfortunately still nothing running your latest code. I closed Visual Studio and reopened it after I added/changed the files. I have a serial monitor open as well as a logic analyzer and when the Arduino Code setting up a SoftwareSerial mySerial(PA1, PA0); instance I get messages. When I use Visual Studio Code and your program I get nothing

Very very strange… Can you try this code as the src\main.cpp ?

#include <Arduino.h>

UART_HandleTypeDef huart4;

void print_str(const char* str) {
	HAL_UART_Transmit(&huart4, (uint8_t*) str, (unsigned short) strlen(str),
			10000);
}

void setup() {
	GPIO_InitTypeDef GPIO_InitStruct = { 0 };
	__HAL_RCC_UART4_CLK_ENABLE()
	;

	__HAL_RCC_GPIOA_CLK_ENABLE()
	;
	/**UART4 GPIO Configuration
	 PA0-WKUP     ------> UART4_TX
	 PA1     ------> UART4_RX
	 */
	GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOA_CLK_ENABLE()
	;
	huart4.Instance = UART4;
	huart4.Init.BaudRate = 115200;
	huart4.Init.WordLength = UART_WORDLENGTH_8B;
	huart4.Init.StopBits = UART_STOPBITS_1;
	huart4.Init.Parity = UART_PARITY_NONE;
	huart4.Init.Mode = UART_MODE_TX_RX;
	huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart4.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart4) != HAL_OK) {
		Error_Handler();
	}
}

void loop() {
	print_str("Hello world\r\n");
	delay(1000);
}

Yes, this code works! Now the big question is why does it work and the other methods don’t?!

The Arduino-STM32 code is supposed to do the functionally same thing… Configuring the GPIOs with their correct alternate function and initializing UART4, plus all the needed clocks of course. Why this doesn’t happen correctly is beyond me… I’ll try and check the code again tomorrow…

1 Like

OK, thank you and I look forward to see if you can find what’s wrong. Now “we” just need to find why HAL works and Arduino doesn’t since I need Arduino to debug the other libraries. I now have a feeling that whatever trouble there is with UART might be the same for SPI since that’s what started me on all of this.

1 Like

@maxgerhardt I made some progress but also went backwards too. I went back to using Arduino and thought that perhaps this was a bug with STM32Duino but it turned out that if I used the clock configuration from Adafruit’s STM32F405 Feather board then writing to the default Serial port worked, as well as using Serial4. What’s odd about that is that they have an external crystal on their board and I do not. It doesn’t seem right to me but I can use PLLM = 8 or PLLM =16 and both work in the Arduino “IDE”

//Adafruit Clock
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 CPU, AHB and APB busses clocks
 */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
 RCC_OscInitStruct.PLL.PLLM = 16; //Adafruit uses 8
 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 busses 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();
 }
}

I tried copying over the variant.cpp into the PIO directory but that didn’t work. I’ve gone back to your HAL code and for some reason I can’t get that to work now yet Arduino works as well as code from STM32CubeIDE. I’ve deleted .pio and .vscode directories and restarted VS Code many times. If I hover over the Serial line in the code I can see that it is being mapped to Serial4 so I’m guessing somehow this is all related to clock configuration and something different between PIO and Arduino?