0

I'm trying to learn how to communicate via SPI with STM32 but I've run into some problems.

The first step i took was to implement SPI communications using two arduino unos: the master writes a byte and the slave responds with another byte according to the input (when the master sends anything, triggering the exchange). Everything worked as it should to the best of my knowledge.

The second step is the one I'm stuck at which is to replace the master arduino with a NUCLEO-F767ZI board. I've set up SPI1 using the auto generated code for now, and I've set it up with what I think are the default options. In the main, I send a single byte to the arduino, and this is where the problem starts: By using the serial port in the arduino I can see that it does receive data, but usually bit shifted. So if I send a 16, i usually get a 32 or other power of two in the arduino (see attached image)enter image description here

I'm using clock polarity 0 and clock phase 0 on both microcontrollers (so clock idle on low and sampling on the rising edge). Just to be sure I've tried all possibilities but to no avail, it still doesn't work properly, and I don't really know why. Another thing I considered was that perhaps the clock is running too fast (the peripheral clock is set to 16 MHz), but if I change the prescaler to anything other than SPI_BAUDRATEPRESCALER_2, i stop getting any data on the arduino, only 0s, which I find odd, I would expect it to work just the same if only the master controls the clock.




Just for completeness, here are the spi settings I'm using:

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

The arduino slave code (I've removed the reply part for now):

void setup (void)
{
  pinMode(MISO, OUTPUT);
  Serial.begin(115200);

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPCR |= _BV(SPIE);

 //Check arduino SPI settings, outputs 11000000, so CPHA and CPOL = 0
 Serial.println("SPCR");
 Serial.println(SPCR,BIN);

}  // end of setup

void loop(void){

}

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  byte c = SPDR;
  Serial.println(c);
  } 

}  

STM32 master code (ommiting irrelevant parts):

MX_SPI1_Init();
uint8_t data = 16;

  while (1)
  {
      HAL_SPI_Transmit(&hspi1, &data, 1, 100);
      HAL_Delay(500);
  }

Does anyone have a clue as to why this could be happening? I've found nothing on the prescaler issues and all bitshifting issue posts say that the problem is with clock phase or polarity discrepancies, which I've verified is not. Thanks in advance

7
  • Don't do Serial.println() inside the ISR. Commented Nov 15, 2022 at 5:08
  • According to the Atmega328 datasheet, SPI slave clock should not be faster than 1/4 of CPU clock. So having the clock rate too high is definetely an issue, I would start with very low master clock, like div 64 or even higher. Commented Nov 15, 2022 at 6:08
  • @hcheung I know that's bad practice in general but in this example/test I spaced every write far enough apart for this not to be a problem. Either way I changed it so a flag is raised inside the ISR and the problem persists Commented Nov 15, 2022 at 9:53
  • @Flexz I agree that sounds like that could be it, but like I said if I change the prescaler to anything other than SPI_BAUDRATEPRESCALER_2 I only get 0s in the slave, do you know what could cause that? Commented Nov 15, 2022 at 9:55
  • @HugoPontes there could be a lot of things, actualy. Starting with an incorrect wiring. Look not just on the received values, but also on the timestamps. E.g. there is a missing sample on the screenshot between 42:01 and 42:02 which tells, that the clock is too high. Try also enabling NSS_PULSE or implement software nCS management on the stm32 side to reset slave's logic between the samples. Commented Nov 15, 2022 at 11:10

1 Answer 1

1
  1. SPI clock rate input to Atmega328P should be lower than Fosc/4
  2. SPI requires the use of nCS wire. An SPI slave will reset it's logic on deassertion of nCS, but if nCS is always at a low level - any missed clock pulse would propagate to the next transfer, shifting the data.

Stm32F7 has NSSP: NSS pulse management - the option for issuing CLK pulse between data bytes. To deassert nCS pin between larger transfers SPI shoud either be disabled and then reenabled before the next transfer, or nCS management should be implemented in the software. In a later case make sure to deassert nCS only after all CLK pulses are completed.

  1. Arduino UNO have Atmega powered from 5V, Stm32 is powered from 3V (on some boards it's 3.3V). Logic high input minimum for the Atmega328 is 0.6VCC, 5V * 0.6V = 3V, wich is on the edge. See this question

A scope would help a lot to solve such cases.

Sign up to request clarification or add additional context in comments.

4 Comments

The first two I had already implemented, but your third point motivated me to perform a test: Instead of connecting the 5v pins on both boards, i connected the 3.3V pins...it doesn't work still, but now with all the correct settings (correct phase+low baudrate,etc) I sometimes get the correct data, which doesnt happen if i disconnect it (i only get 0s as I said earlier). I assume that when I do get data the arduino interpreted the 3V as high correctly, and when i dont it doesnt. I don't have a level shifter but ive ordered one and will report back when i get it, thanks!
@HugoPontes that's strage, have you connected GNDs?
Yes I have. But it only works (not well at all but still...) if the 3.3V pins of each board are connected. I'm curious as to what will happen when I use the level shifter, but if it doesn't solve the problem then I really have no idea why connecting the 3.3V pins would work
SOLUTION: Using a level shifter fixed it, it turns out the problem was different logic levels between stm32 and arduino all along. To fix it, I used a level shifter to shift the 3V signal from the STM32 to 5V that the arduino can deal with. It even works with clock divided by 256. @Flexz Thank you so much for you help

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.