SPI sensor won't work on alternate STM32 SPI pin assignment (SOLVED)

Hi everyone.

I need to use alternate SPI pins for my magnetic sensor, and hitting a problem.

AS5047U SPI sensor won’t work on alternate SPI pin assignments.

I am using STM32F103C8Tx (a.k.a. Blue Pill).

I tried the default pin assignment with the explicit override for the pins and it works.

However, when I try the other two it behaves as if no sensor is connected (reads 0).

Eventually I will need to use two simultaneous SPI sensors.

Two questions:

  1. Any idea why the alternate pin assignments wont work?
  2. Can SimpleFOC read two sensors at the same time? Obviously not exactly at the same concurrent time, but read two sensors connected on two SPI busses? I cannot test this since the alternate pins won’t work now.

Code is attached below. Also a picture.

Thank you all for any help on this.

I am using the following board definition.

https://github.com/stm32duino/BoardManagerFiles/raw/master/package_stmicroelectronics_index.json

#include <SimpleFOC.h>

// NOT WORKING
#define CSN PB12
#define CLK PB13
#define MISOn PB14
#define MOSIn PB15

/*
// WORKING
#define CSN PA4
#define CLK PA5
#define MISOn PA6
#define MOSIn PA7
*/
/*
// NOT WORKING
#define CSN PA15
#define CLK PB3
#define MISOn PB4
#define MOSIn PB5
*/

// Using AS5047U
MagneticSensorSPI spi_sensor = MagneticSensorSPI(CSN, 14, 0x3FFF);

SPIClass SPI_2(MOSIn, MISOn, CLK);

void setup() {

  // init magnetic sensor on SPI bus
  spi_sensor.init(&SPI_2);
  
  // monitoring port
  Serial2.begin(115200);

  // initialise magnetic sensor hardware
  spi_sensor.init();

  Serial2.println("spi_sensor ready");
  _delay(1000);
}

void loop() {
  // display the angle and the angular velocity to the terminal
  Serial2.print(spi_sensor.getAngle());
  Serial2.print("\t");
  Serial2.println(spi_sensor.getVelocity());
  delay(100);
}


I use an alternate SPI with an AS5048A and a STM32L476RG and it works well. Differences with your code are:

  • I use SPI3 (PC12, PC11, PC10) with CSN=PD2
  • I call spi_sensor.init() only once
  • I use @runger’s class MagneticSensorAS5048A instead of MagneticSensorSPI

@quentin

Yes, that was the problem. The second init apparently overrode the first with the defaults.

I wasn’t sure exactly how to call. Great catch!

In case anyone makes the same mistake as mine, here is the working code.

#include <SimpleFOC.h>

#define CSN PB12
#define CLK PB13
#define MISOn PB14
#define MOSIn PB15
/*
#define CSN PA4
#define CLK PA5
#define MISOn PA6
#define MOSIn PA7
*/
/*
#define CSN PA15
#define CLK PB3
#define MISOn PB4
#define MOSIn PB5
*/

// Using AS5047U
MagneticSensorSPI spi_sensor = MagneticSensorSPI(CSN, 14, 0x3FFF);

SPIClass SPI_2(MOSIn, MISOn, CLK);

void setup() {

  // init magnetic sensor on SPI bus
  spi_sensor.init(&SPI_2);
  
  // monitoring port
  Serial2.begin(115200);

  Serial2.println("spi_sensor ready");
  _delay(1000);
}

void loop() {
  // display the angle and the angular velocity to the terminal
  Serial2.print(spi_sensor.getAngle());
  Serial2.print("\t");
  Serial2.println(spi_sensor.getVelocity());
  delay(100);
}
1 Like

In case anyone is interested this is the dual magnetic sensor code example for STM32F103

#include <SimpleFOC.h>

#define CSN2 PB12
#define CLK2 PB13
#define MISO2 PB14
#define MOSI2 PB15

/*
#define CSN1 PA4
#define CLK1 PA5
#define MISO1 PA6
#define MOSI1 PA7
*/

#define CSN1 PA15
#define CLK1 PB3
#define MISO1 PB4
#define MOSI1 PB5


// Using AS5047U
MagneticSensorSPI spi_sensor1 = MagneticSensorSPI(CSN1, 14, 0x3FFF);
MagneticSensorSPI spi_sensor2 = MagneticSensorSPI(CSN2, 14, 0x3FFF);

SPIClass SPI_1(MOSI1, MISO1, CLK1);
SPIClass SPI_2(MOSI2, MISO2, CLK2);

void setup() {

  // init magnetic sensor on SPI bus
  spi_sensor1.init(&SPI_1);
  spi_sensor2.init(&SPI_2);
  
  // monitoring port
  Serial2.begin(115200);

  Serial2.println("Spi sensors ready.");
  _delay(1000);
}

void loop() {
  // display the angle and the angular velocity to the terminal
  Serial2.print("S1\t");
  Serial2.print(spi_sensor1.getAngle());
  Serial2.print("\t");
  Serial2.print(spi_sensor1.getVelocity());
  Serial2.print("\tS2\t");
  Serial2.print("\t");
  Serial2.print(spi_sensor2.getAngle());
  Serial2.print("\t");
  Serial2.println(spi_sensor2.getVelocity());
  delay(100);
}

The “SPI” of “Arduino” is not well written. I once encountered various problems like you, so I had to study the underlying protocol of “SPI” communication. Finally, I wrote a code. These codes do not depend on the “SPI” port of the hardware. I simulated the “SPI” port of the hardware with an ordinary “IO” port.

With it, we can easily configure our own “SPI”. I hope it can help you!

//******************************************************************************************************************//

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __sw_spi_H
#define __sw_spi_H

#include “stm32f3xx_hal.h”

#ifdef __cplusplus
extern “C” {
#endif

//IO Pin F303RE 左上角,8个位置
#define SPI_SCK_PIN GPIO_PIN_5
#define SPI_SCK_GPIO_PORT GPIOC
#define SPI_MISO_PIN GPIO_PIN_6
#define SPI_MISO_GPIO_PORT GPIOC
#define SPI_MOSI_PIN GPIO_PIN_8
#define SPI_MOSI_GPIO_PORT GPIOC
#define SPI_NSS_PIN GPIO_PIN_9
#define SPI_NSS_GPIO_PORT GPIOC

#define SPI_SCK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define SPI_MISO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define SPI_MOSI_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define SPI_NSS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()

#define MOSI_H HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET)
#define MOSI_L HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_RESET)
#define SCK_H HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET)
#define SCK_L HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_RESET)
#define MISO HAL_GPIO_ReadPin(SPI_MISO_GPIO_PORT, SPI_MISO_PIN)
#define NSS_H HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET)
#define NSS_L HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_RESET)

extern void SWSPI_Init(void);
extern uint16_t AS5048_SPI_ReadWrite(uint16_t data);
extern uint16_t DRV83XX_SPI_ReadWrite(uint16_t data);
extern uint16_t spi_exchange(uint16_t x);
extern uint16_t SWSPI_GetAS5048_Raw(void);
extern uint16_t SWSPI_Drv83xx_Write(uint16_t TxData);
extern uint16_t SWSPI_Drv83XX_readwrite(uint16_t TxData);

#ifdef __cplusplus
}
#endif

#endif /*__sw_spi_H */
//*****************************************************************************************************************//
#include “sw_spi.h”
#include “delay.h”

#ifdef __cplusplus
extern “C” {
#endif

extern void SWSPI_Init(void)
{
/##-1- Enable peripherals and GPIO Clocks #################################/
/* Enable GPIO TX/RX clock */
SPI_SCK_GPIO_CLK_ENABLE();
SPI_MISO_GPIO_CLK_ENABLE();
SPI_MOSI_GPIO_CLK_ENABLE();
SPI_NSS_GPIO_CLK_ENABLE();

/##-2- Configure peripheral GPIO ##########################################/
/* SPI SCK GPIO pin configuration */
GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Pin = SPI_SCK_PIN ;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_RESET);

/* SPI MOSI GPIO pin configuration */
GPIO_InitStruct.Pin = SPI_MOSI_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
//GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET);

GPIO_InitStruct.Pin = SPI_NSS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(SPI_NSS_GPIO_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET);

/* SPI MISO GPIO pin configuration */
GPIO_InitStruct.Pin = SPI_MISO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct);

}
/##################################################################/
/* AS5048A SPI mode == CPOL=0,CPHA=1 */
/*1. Before SPI transmission, SCK should be in the low position; */
/*2. Since the data acquisition occurs at the falling edge, we need /
/
to simulate the falling edge and carry out bidirectional data /
/
transmission after the falling edge begins */
/*3. Drv8301, 8305 and 8323 are the same SPI mode */
/4. A complete SPI process of transmitting 16bit needs about /
/
16 * 60 = 960 (NOP). For the convenience of estimation, 960 /
/
“NOPs” are calculated as 1000 “NOPs”, and each NOP of 72m MCU /
/
is about 13.89ns, so the time of one SPI is 14uS. /
/
##################################################################
/
extern uint16_t DRV83XX_SPI_ReadWrite(uint16_t data)
{
uint16_t xx, dt, dts;
dt = 0;
dts = data;

for (xx=0; xx<16; xx++)
{
SCK_H;
HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, dts >> 15);
dts<<=1;
for (volatile int k = 0;k <55;k++) {__NOP();}
SCK_L;

dt<<=1;
dt|= MISO; 
for (volatile int k = 0;k <55;k++) {__NOP();}

}
if(HAL_GPIO_ReadPin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN)==1) {SCK_L;}
return dt;
}

extern uint16_t AS5048_SPI_ReadWrite(uint16_t data)
{

uint16_t xx, dt, dts;
dt = 0;
dts = data;

for (xx=0; xx<16; xx++)
{
SCK_H;
for (volatile int k = 0;k <30;k++) {__NOP();}
SCK_L;

HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, dts >> 15);
dts<<=1;
dt<<=1;
dt|= MISO; 

for (volatile int k = 0;k <25;k++) {__NOP();}

}
return dt;
}

extern uint16_t spi_exchange(uint16_t x) {
uint16_t rx;
rx = DRV83XX_SPI_ReadWrite(x);
return rx;
}

extern uint16_t SWSPI_GetAS5048_Raw(void){
uint16_t DeRxBuffer,DeTxBuffer;
DeTxBuffer = 0b0100000000000000 |0x3fff;

NSS_L;
for (volatile int k = 0;k <75;k++) {__NOP();} //delay_us(2);
AS5048_SPI_ReadWrite(DeTxBuffer);
for (volatile int k = 0;k <75;k++) {__NOP();} //delay_us(2);
NSS_H;

//delay_us(5);
for (volatile int k = 0;k <400;k++) {__NOP();}
DeTxBuffer =0;

NSS_L;
for (volatile int k = 0;k <75;k++) {__NOP();} //delay_us(2);
DeRxBuffer = AS5048_SPI_ReadWrite(DeTxBuffer);
for (volatile int k = 0;k <75;k++) {__NOP();} //delay_us(2);
NSS_H;
return DeRxBuffer & ~0XC000 ;
}

extern uint16_t SWSPI_Drv83xx_Write(uint16_t TxData)
{
uint16_t DeRxBuffer,DeTxBuffer;

DeTxBuffer = TxData;
DeRxBuffer = 0;

NSS_L;
for (volatile int k = 0;k <150;k++) {__NOP();} //delay_us(2);
DeRxBuffer = DRV83XX_SPI_ReadWrite(DeTxBuffer);
for (volatile int k = 0;k <150;k++) {__NOP();} //delay_us(2);
NSS_H;

//delay_us(10);
for (volatile int k = 0;k <720;k++) {__NOP();}

return DeRxBuffer;
}

extern uint16_t SWSPI_Drv83XX_readwrite(uint16_t TxData)
{
uint16_t DeRxBuffer,DeTxBuffer;
DeTxBuffer = TxData;
DeRxBuffer = 0;

NSS_L;
DRV83XX_SPI_ReadWrite(DeTxBuffer);
NSS_H;

//delay_us(10);
for (volatile int k = 0;k <720;k++) {__NOP();}

NSS_L;
DeRxBuffer = DRV83XX_SPI_ReadWrite(DeTxBuffer);
NSS_H;
return DeRxBuffer;
}

#ifdef __cplusplus
}
#endif

//**********************************************************************************************//

Just out of curiosity, why not just use a chip select pin to switch devices the way it’s meant to be used? Or doesn’t one of the devices use chip select?

@Roiki , my original problem was using the second spi bus due to pin conflict. the two simultaneous spi bus connections was for me to test the code working correctly. otherwise yeah you are correct i could hook up 10 devices and turn on the spi sensor / device you want for the moment, transmit, then switch to another, ad nauseum. in stm32 f103 the spi bus takes the analog pins which i need.