Reputation: 23
I am working with STM32 F3 discovery kit and started messing with SPI peripheral. I started with a simple loop-back system: I check the TXFIFOLVL status and if it is not full I send my data to DR register, which then should loop back to my RxBuffer (I read data from DR while RXFIFOLVL is not empty), but I've hit a problem - I don't get anything back on my receiving buffer and I can't seem to see why. I don't use HAL or Standard Peripheral Library, so I configure the SPI and use it via the register values like this:
Header file for SPI code:
#define GPIOA_ENABLE 0b1<<17 // Enable GPIO port A clock in AHBENR register
#define SPI1_CLOCK_ENABLE 0b1<<12 // Enable SPI1 clock in APB2ENR register
#define SPI1_PIN_ALT_FNC 0b1010<<4 // Sets PA5,PA6 & PA7 to Alternative function
#define SPI1_OUTPUT_TYPE ~(0b111<<5) // Sets PA5, PA6 & PA7 to push-pull
#define SPI1_PIN_SPEED 0b1111<<4 // Sets pins from 4 to 7 to work on 50 MHz output speed
#define SPI1_PIN_ALT_FNC_LOW 0b0101<<4 // Sets the Alternative function to AF5 in alternative function low register
#define SPI1_PIN_ALT_FNC_HIGH 0b0101<<4 // Sets the Alternative function to AF5 in alternative function high register
#define SPI1_BAUDRATE_PRESCALER_2 0b000<<3 // F_PCLK/2
#define SPI1_BAUDRATE_PRESCALER_128 0b110<<3 // F_PCLK/128
#define SPI1_MASTER_MODE 0b1<<2 // Sets the SPI1 to master mode
#define SPI1_PERI_ENABLE 0b1<<6 // Enable the SPI peripheral
#define SPI1_SSM_ENABLE 0b1<<9 // Enable SPI software slave management
#define SPI1_SSI_ENABLE 0b1<<8 // SPI1 internal slave select
#define SPI1_NSSP_ENABLE 0b1<<3 // Enable NSS pulse management
#define SPI1_FRXTH_8BIT 0b1<<12 //Set the FIFO reception threshold to 8 bits
#define SPI1_DATA_SIZE 0b0111<<8 // SPI1 DATA size
#define SPI1_TXFIFO_FULL_FLAG 0b11<<11 // SPI1 Tx FIFO transmission flag
#define SPI1_RXFIFO_EMPTY_FLAG 0b00<<9 // SPI1 Rx FIFO reception flag
#include "main.h"
#include "stm32f3xx_hal.h"
void spi_init();
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize);
Code file for SPI code:
#include "SPI_toSD.h"
/* SPI1 configuration
* PA5 - SCK
* PA6 - MISO
* PA7 - MOSI
*/
void spi_init(){
// Start the GPIO and peripheral clocks in Reset and Clock Control register
RCC->AHBENR |= GPIOA_ENABLE;
RCC->APB2ENR |= SPI1_CLOCK_ENABLE;
// Configure the GPIOs for SPI communication
GPIOA->MODER |= SPI1_PIN_ALT_FNC;
GPIOA->OTYPER &= SPI1_OUTPUT_TYPE;
GPIOA->OSPEEDR |= SPI1_PIN_SPEED;
GPIOA->AFR[0] |= SPI1_PIN_ALT_FNC_LOW;
GPIOA->AFR[1] |= SPI1_PIN_ALT_FNC_HIGH;
// Configure the SPI peripheral
SPI1->CR1 |= SPI1_BAUDRATE_PRESCALER_2;
SPI1->CR1 |= SPI1_SSM_ENABLE;
SPI1->CR1 |= SPI1_MASTER_MODE;
SPI1->CR1 |= SPI1_SSI_ENABLE;
SPI1->CR2 |= SPI1_DATA_SIZE;
SPI1->CR2 |= SPI1_FRXTH_8BIT;
SPI1->CR2 |= SPI1_NSSP_ENABLE;
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
}
void spi_WriteRead(uint8_t *rxBuffer, uint8_t *txBuffer, uint8_t bufferSize){
int i;
while((SPI1->SR & 0b11<<11)==SPI1_TXFIFO_FULL_FLAG);
for(i=0;i<bufferSize;i++){
SPI1->DR |= *txBuffer; // send *txBuffer++
txBuffer++;
while((SPI1->SR & 0b11<<9)!=SPI1_RXFIFO_EMPTY_FLAG){
*rxBuffer = SPI1->DR;
rxBuffer++;
}
}
}
In main I simply define my buffers and initialize them like this:
uint8_t rx_buff[SIZE] = {0,0,0,0,0,0,0,0,0,0};
uint8_t tx_buff[SIZE] = {1,2,3,4,5,6,7,8,9,10};
So naturally after my spi_WriteRead() function is called I expect these buffers to have the same values.
I call my spi_init() function and in my while loop I call spi_WriteRead() function:
spi_WriteRead(rx_buff,tx_buff,SIZE);
SIZE is defined in my main.c as:
#define SIZE 10
I use SW4STM32 environment to code and debug so in my debugger I can see all of the register values. My SPI is initialized just as I defined and my data is being sent to TXFIFO buffer, but nothing comes to RXFIFO buffer. If I check SPI SR register I can see that my TXFIFO fills up, but RXFIFO flags say that it is empty.
Does anyone have any clue what I might be doing wrong? Am I grossly misunderstanding something simple about SPI? Thanks for your input!
Upvotes: 2
Views: 979
Reputation: 8860
EDIT:
Take a good look here:
#define SPI1_SSI_ENABLE 0b1<<8
...
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~SPI1_SSI_ENABLE;
Now you'll probably know why #define
macros are generally considered a bad idea. You wouldn't have this problem if you'd use #define
values from stm32f3xxx.h
header, as all values with operations have parentheses there. You don't have them. That's why your code looks like this for the compiler:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= ~0b1<<8;
Which is equivalent to:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= (~0b1)<<8;
And going further:
SPI1->CR1 |= SPI1_PERI_ENABLE;
SPI1->CR1 &= 0xffffff00;
Probably not what you wanted.
You should also know, that if your device is a master, then SSI and SSM bits should both be set. https://stackoverflow.com/a/42169600/157344
ORIGINAL:
Do note, that in these devices when you access SPI1->DR
directly you send/receive TWO bytes at once. That's because this register is defined as uint16_t
and SPI supports so called "Data packing" (search for it in the Reference Manual). If you want to send/receive one byte at a time, then you need to cast the register for write and read like that:
readByte = (volatile uint8_t*)SPI1->DR;
(volatile uint8_t*)SPI1->DR = writeByte;
BTW - why don't you use #defines provided by the CMSIS headers? You wouldn't have to define things like SPI1_MASTER_MODE
...
Upvotes: 2