Using alternative SPI and USART ports/routing on B-G431B-ESC board

Hi everyone,

I am in the process of attempting to use the SimpleFOC core, with an SSI encoder on the B-G431B-ESC1 board. In order to do this, I want to use an SPI port (master receive only). This functionality I have already tested and confirmed working on a different platform, so it is just a matter of porting to the ESC board, afterwhich I can use it as a generic sensor for commutation feedback, which is where I ran into some issues.

In particular, I am having trouble using alternate functionalities of some pins to get the SPI and UART routed to where I want them. I will address the changes to the board for both of these seperately. The issues persist if the SimpleFOC library is not imported, and hence I am not sure if its directly related to the SimpleFOC libarary. Still, perhaps some people here might have more experience with this, or a clue on where to look next.

Details of setup:

  • BLDC Motor: T-Motor GL100
  • MCU/Motor Driver: B-G431B-ESC1 Discovery kit*
  • Control Library: SimpleFOC
  • Sensor: RLS AksIM-2 64mm SSI 20-bit absolute position sensor
  • Software suite used is visual code with platform.io, installed as per instructions on the SimpleFOC website: PlatformIO | Arduino-FOC

*(For documentation/schematics see bottom of the post)

USART

Normally on the board, USART2 is used to communicate with the ST-Link and outside world. These are hooked to pin 41/42 (PB3/PB4), and routed directly to the output connector J3, and to the ST-Link through R23/24.

The alternate function for PB3/4 is the SPI bus, which I want to use for SSI communication, hence I need to reroute USART to communicate with the outside world.

Most convenient for this, are pins PB6/7 [p# 44/55] [USART1 TX and RX respectively]. Their default functionality is hall sensor input reading, for which they are directly connected to soldering pads J8 (PB6 → A+/H1 and PB7 → B+/H2). They have a low-pass filter and some clamping, which shouldn’t pose a problem for UART implementation.

Thus to enable the hardware to function properly, R23/24 are desoldered, and A+/B+ of J8 are connected to the ST-link side of these resistors (connecting USART1 with the ST-Link). A+/B+ are routed also to the cable which is used to send/receive commands. The connection of which is verified by using PB6/7 as GPIO and sending out some signals.

I can instantiate a HardwareSerial object in different ways, call the .begin() function with appropriate baudrate (115200), but as soon as I have a .print or .printf in my code it freezes up.
If instead I instantiate the hardwareserial on USART2, it does seem to work properly (to those respective pins).
Normally I expect this behavior if you attempt to instantiate a HardwareSerial object with pins who do not possess USART functionality, however, PB6/7 definitely are able to do this (see CubeMX screenshots below).

SPI/SSI

As discussed above, PB3/4 have an alternative function for SPI (both SPI1 and SPI3). These are wired directly to connector J3, so no additional rewiring is needed. USART2_TX then becomes SPI1/3_SCK and USART2_RX becomes SPI1/3_MISO.

SPI settings are 400khz, mode0, MSB first, but I believe these are not very relevant for the problem at hand.

The port can be instantiated and setup with beginTransaction(…) and begin(), but as soon as any attempt is made to transfer bytes over the port, it freezes.
Here again I expect this is PB3/4 are not capable of SPI functionality, but they definitely are. See the following screenshot of CubeMX:


(Note that SPI1 and SPI3 both can use PB3/4 as SCK/MISO.

I am not sure if this is a hardware or software issue, any feedback on what to try next would be extremely appreciated.


Below I have included a minimum (not) working software example. The board happily blinks its LED, unless one of the “”// IF UNCOMMENTED FREEZES HERE" lines is enabled (which either attempts to use the USART or SPI port).

////// LIBRARIES
#include <Arduino.h>
#include <SPI.h>
#include <SimpleFOC.h> // Not necessary for this code

////// HARDWARE CONNECTIONS
/// SPI/SSI
// SPI port connected to the SSI absolute encoder
#define PIN_SPI_MISO          PB4 // Alternate pin functions, normally PB4 is connected to USART2_TX	
#define PIN_SPI_SCK           PB3 // Alternate pin functions, normally PB3 is connected to USART2_RX
SPIClass SPI_SSI(PNUM_NOT_DEFINED, PB4, PB3); // we skip mosi, only use miso and sclk (uint32_t mosi, uint32_t miso, uint32_t sclk, uint32_t ssel = PNUM_NOT_DEFINED);
// Tried variations:
//SPIClass SPI_SSI(PNUM_NOT_DEFINED, PB_4, PB_3);
//SPIClass SPI_SSI(PNUM_NOT_DEFINED, PB4_ALT1, PB3_ALT1);
//SPIClass SPI_SSI(PNUM_NOT_DEFINED, PB4_ALT2, PB3_ALT2);

// 400khz -> Max ~8k samples/s (max allowed by sensor is 500khz) ; Mode 2 = clock pol 1 CPHA = 0 -> Because of hardware inversion with a not gate, we want clock pol = 0 -> cpol0 & cpha=0 -> mode 0
SPISettings SPI_Bus_Settings(400000, MSBFIRST, SPI_MODE0); // uint32_t clock, BitOrder bitOrder, SPIMode dataMode

/// USART/SERIAL
const uint32_t SERIAL_BAUD = 115200;

// PB7 alternate function is USART1_TX, normally connected to B+/H2 (GPIO) -> Using PB7 as GPIO can see signals at pin
// PB6 alternate function is USART1_RX, normally connected to A+/H1 (GPIO) -> Using PB6 as GPIO can see signals at pin
HardwareSerial serialPort(PB7, PB6); // (PinName _rx, PinName _tx, PinName _rts = NC, PinName _cts = NC);
// Tried variations:
//HardwareSerial serialPort(PB7_ALT2, PB6_ALT2);
//HardwareSerial serialPort(PB7_ALT1, PB6_ALT1); 
//HardwareSerial serialPort(PB_7, PB_6); 
//HardwareSerial serialPort(USART1);



////// FUNCTION DECLARATIONS

////// INITIAL SETUP CODE
void setup() {

    pinMode(LED_RED, OUTPUT); // Set board LED for debugging
    digitalWrite(LED_RED, HIGH);
	
    // Initialize the SSI/SPI port
    SPI_SSI.beginTransaction(SPI_Bus_Settings);
    SPI_SSI.begin();
    
    // Serial port initalization
    serialPort.begin((unsigned long)SERIAL_BAUD);

		// Simplest type of SPI transfer, a single byte
    // uint8_t temp = SPI_SSI.transfer(0xFF); // IF UNCOMMENTED FREEZES HERE
	
    digitalWrite(LED_RED, LOW); // Toggle LED
}

////// MAINLOOP
void loop() {

				// Periodically send a message
				// Confirmed working with HardwareSerial serialPort(USART2) on different USART pins, always freezes with USART1 (PB6/7) in any configurations
       // serialPort.printf("hi\n");  // IF UNCOMMENTED FREEZES HERE
	   
        digitalWrite(LED_RED, !digitalRead(LED_RED)); // Toggle LED, to detect freezing
        //digitalWrite(PB3, !digitalRead(PB3)); 
        delay(100); // Poll at ~10hz
}

In summary, does anyone have experience on getting alternative SPI/USART ports (in particular SPI1 and USART1) working with the G431B-ESC board? Or any other pointers as to what is wrong, or what I might check next?

Thanks a lot in advance.

PS: Apologies for the lack of links and images in the post, apparently new users can only attach a single piece of media and 2 links per post, hence I will add these as a reply.

Additional information

Datasheets/links:

Board schematics for your convenience (from datasheet of b-g431b-esc):


Or high res version available at https://pasteboard.co/GgeIvvqJDBhX.png

Relevant parts of the schematic/modifications are highlighted in green. Red/Yellow dot show the changed routing.

Have you read this?

1 Like

Hi Grizzly,

Thank you very much for your input! I thought I had trawled the web, but this post managed to slip through the nets.

This was exactly what was required to get stuff working, with two additional ‘gotchas’:

  1. You cannot use the SPI port in ‘receive only’ mode, with just MISO and SCLK (you have to define all three pins, MOSI, MISO and SCK), otherwise it will just freeze up as can be seen in ‘SPI.cpp’. MOSI (PB5) is physically connected to GPIO_BEMF, and as such if BEMF isn’t used, it is not a problem to leave this floating about (connection directly to the output is blocked by a reverse biased diode. If you really want to ensure no current flow, set BEMF readout pins low (PA4, PC4, PB11), or desolder/reroute diodes D11,13 and 15 .
  2. The lowest possible SPI clock frequency is ~665kHz (rather, maximum SPI baud prescaler is 256x), which I suppose is just a restriction as we dont have full control of the clocks as we would with bare metal (where we might select a lower CPU clockspeed to compensate). Eventhough this is slightly out of spec, I hope my sensor will just accept it.
    edit: The sensor indeed accepts the clock provided! So no reason to muck about with clockspeeds

For any people in the future who might stumble on the same issue, I mostly followed what @Grizzly has detailed in the post he refers to. I have summarized these points further down below.

Working code example is:

////// LIBRARIES
#include <Arduino.h>
#include <SPI.h>
#include <SimpleFOC.h>

////// HARDWARE CONNECTIONS
// SPI port connected to the SSI absolute encoder
SPIClass SPI_SSI(PB5, PB4, PB3); // mosi, miso and sclk (uint32_t mosi, uint32_t miso, uint32_t sclk, uint32_t ssel = PNUM_NOT_DEFINED);
// 400khz -> Max ~8k samples/s (max allowed by sensor is 500khz) ; Mode 2 = clock pol 1 CPHA = 0 -> Because of hardware inversion with a not gate, we want clock pol = 0 -> cpol0 & cpha=0 -> mode 0
SPISettings SPI_Bus_Settings(400000, MSBFIRST, SPI_MODE0); // uint32_t clock, BitOrder bitOrder, SPIMode dataMode

HardwareSerial serialPort(PB7, PB6); // (PinName _rx, PinName _tx, PinName _rts = NC, PinName _cts = NC);
const uint32_t SERIAL_BAUD = 115200;

////// INITIAL SETUP CODE
void setup() {

    pinMode(LED_RED, OUTPUT); // User LED is PC6
    digitalWrite(LED_RED, HIGH); // Toggle LED

    // Initialize the SSI/SPI port
    SPI_SSI.beginTransaction(SPI_Bus_Settings);
    // In order to prevent current draw, set BEMF pin outputs to low (PA4, PC4, PB11)
    pinMode(PA4, OUTPUT);
    pinMode(PC4, OUTPUT);
    pinMode(PB11, OUTPUT); 
    digitalWrite(PA4, false);
    digitalWrite(PC4, false);
    digitalWrite(PB11, false);

    // Serial port initalization
    serialPort.begin((unsigned long)SERIAL_BAUD);
}

////// MAINLOOP
void loop() {
    uint8_t temp = SPI_SSI.transfer(0xFF); // Write anything to SPI port to test the clock
        serialPort.printf("hi\n"); // Write something to serial port to test

        // Poll at ~10hz so pause for a while
        digitalWrite(LED_RED, !digitalRead(LED_RED)); // Toggle LED
        delay(100);
}

Enabling SPI1 on PB3/4/5 (SCK/MISO/MOSI)

  • Do hardware modifications highlighted in previous post (remove R23/24)
  • In the file “PeripheralPins_B_G431B_ESC1.c” modify the following lines:"

(Line 237 to 272)

//*** SPI ***

#ifdef HAL_SPI_MODULE_ENABLED
WEAK const PinMap PinMap_SPI_MOSI[] = {
  // {PA_7,      SPI1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI1)},
  // {PA_11,     SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
  {PB_5,      SPI1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI1)}, // UNCOMMENTED
  // {PB_5_ALT1, SPI3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_SPI3)},
  // {PB_15,     SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
  {NC,        NP,   0}
};
#endif

#ifdef HAL_SPI_MODULE_ENABLED
WEAK const PinMap PinMap_SPI_MISO[] = {
  // {PA_6,      SPI1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI1)},
  // {PA_10,     SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
   {PB_4,      SPI1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI1)}, // UNCOMMENTED
  // {PB_4_ALT1, SPI3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_SPI3)},
  // {PB_14,     SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
  // {PC_11,     SPI3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_SPI3)},
  {NC,        NP,   0}
};
#endif

#ifdef HAL_SPI_MODULE_ENABLED
WEAK const PinMap PinMap_SPI_SCLK[] = {
  // {PA_5,      SPI1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI1)},
   {PB_3,      SPI1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI1)}, // UNCOMMENTED
  // {PB_3_ALT1, SPI3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_SPI3)},
  // {PB_13,     SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
  // {PC_10,     SPI3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_SPI3)},
  // {PF_1,      SPI2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_SPI2)},
  {NC,        NP,   0}
};
#endif

In the file “variant_B_G431B_ESC1.h” modify the following lines (or define them at the top of your own source file):

(Line 177 to 185)

#ifndef PIN_SPI_MOSI
  #define PIN_SPI_MOSI          PB5 // From PNUM_NOT_DEFINED
#endif
#ifndef PIN_SPI_MISO
  #define PIN_SPI_MISO          PB4 // From PNUM_NOT_DEFINED
#endif
#ifndef PIN_SPI_SCK
  #define PIN_SPI_SCK           PB3 // From PNUM_NOT_DEFINED
#endif

Enabling and changing USART1 to default (instead of default USART2)

  • Do hardware modifications highlighted in previous post (R23/24)

  • In the file “PeripheralPins_B_G431B_ESC1.c” modify the following lines:

(Line 179 - 212)

//*** UART ***

#ifdef HAL_UART_MODULE_ENABLED
WEAK const PinMap PinMap_UART_TX[] = {
  //  {PA_2,      LPUART1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_LPUART1)},
  //  {PA_2_ALT1, 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)},
  //  {PA_14,     USART2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)},
  //{PB_3,      USART2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)}, // COMMENTED
    {PB_6,      USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)}, // UNCOMMENTED
  //  {PB_9,      USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  //  {PB_10,     USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  //  {PB_11,     LPUART1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_LPUART1)},
  //  {PC_4,      USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
  //  {PC_10,     USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  //  {NC,        NP,      0}
};
#endif

#ifdef HAL_UART_MODULE_ENABLED
WEAK const PinMap PinMap_UART_RX[] = {
  //  {PA_3,      LPUART1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF12_LPUART1)},
  //  {PA_3_ALT1, 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)},
  //  {PA_15,     USART2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)},
  //{PB_4,          USART2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)}, // COMMENTED
    {PB_7,      USART1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)}, // UNCOMMENTED
  //  {PB_8,      USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  //  {PB_10,     LPUART1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_LPUART1)},
  //  {PB_11,     USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  //  {PC_11,     USART3,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
  {NC,        NP,      0}
};
#endif
  • In the file “variant_B_G431B_ESC1.h” the following changes:

(Line 204 to 214)

// UART Definitions
#define SERIAL_UART_INSTANCE    1 //Connected to ST-Link, changed from 2

// Default pin used for 'Serial' instance (ex: ST-Link)
// Mandatory for Firmata
#ifndef PIN_SERIAL_RX
  #define PIN_SERIAL_RX         PB7 // Changed from PB4
#endif
#ifndef PIN_SERIAL_TX
  #define PIN_SERIAL_TX         PB6 // Changed from PB3
#endif

Many thanks again @Grizzly for your help.

1 Like