2018-11-09 04:11 AM
Hi everyone, i'm trying to read some data from an accelerator and for this purpose using SPI. I'm using NUCLEO-F401RE board. So after enabling SPI, clock pin goes HIGH as i want (CPOL=1) but when i send data CLK stays as HIGH until i disable SPI, meaning that it does not clock! What is the possible causes of this problem? What am i doing wrong?
Thanks.
2018-11-09 04:58 AM
>>What am i doing wrong?
Let's not play guessing games, attach a minimal project that illustrates the condition. ZIP to a single file.
2018-11-09 05:37 AM
OK Clive, first is my spi.c file and the second is main. I'm using SPI2 (PB10, PB14, PB15) and PB9 as slave select as GPIO output.
.............................................................111111111111111.................................................................
#include "spi.h"
void SPI2_Config(void)
{
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
GPIOB->MODER |= GPIO_MODER_MODER10_1 | GPIO_MODER_MODER14_1 | GPIO_MODER_MODER15_1;
GPIOB->MODER &= ~(GPIO_MODER_MODER10_0 | GPIO_MODER_MODER14_0 | GPIO_MODER_MODER15_0);
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR14 | GPIO_OSPEEDER_OSPEEDR15;
GPIOB->AFR[1] |= 5UL<<8 | 5UL<<24 | 5UL<<28; //Alternating function 5 for SPI2
//Configure PB9 as output GPIO as NSS//
GPIOB->MODER |= GPIO_MODER_MODER9_0;
GPIOB->MODER &= ~GPIO_MODER_MODER9_1;
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_9;
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;
GPIOB->ODR |= GPIO_ODR_ODR_9;
SPI2->CR1 = 0x0000;
SPI2->CR1 |= SPI_Direction_2Lines_FullDuplex | SPI_CR1_SSM | SPI_BaudRatePrescaler_32 | SPI_CR1_MSTR | SPI_CPOL_High | SPI_CPHA_2Edge;
SPI2->CR2 |= SPI_CR2_SSOE;
for(uint32_t i=0; i<1000000; i++);
}
void SPI2_Enable(void)
{
SPI2->CR1 |= SPI_CR1_SPE;
}
void SPI2_Disable(void)
{
SPI2->CR1 &= ~SPI_CR1_SPE;
}
void SPI2_SendData(uint8_t data)
{
SPI2->DR = data;
}
uint16_t SPI2_ReceiveData(void)
{
return SPI2->DR;
}
..........................................................2222222222222222......................................................................
int main(void)
{
Clock_Config();
SPI2_Config();
GPIOB->ODR &= ~GPIO_ODR_ODR_9;
for(uint32_t i=0; i<1000000; i++);
SPI2_Enable();
for(uint32_t i=0; i<1000000; i++);
SPI2_SendData(0x8F);
while(!((SPI2->SR)&(SPI_SR_TXE)));
while(!((SPI2->SR)&(SPI_SR_RXNE)));
while((SPI2->SR)&(SPI_SR_BSY));
received_data = SPI2_ReceiveData();
SPI2_Disable();
for(uint32_t i=0; i<1000000; i++);
GPIOB->ODR |= GPIO_ODR_ODR_9;
/* Infinite loop */
while (1)
{
}
}
2018-11-09 07:23 AM
Read back the GPIO registers and check if they are set as you think they are. And, if they are not, swap these two statements:
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
to avoid the "some time is needed between enabling (GPIO) clock in RCC until the enabled peripheral gets really available" problem (look into the errata).
JW
2018-11-09 07:19 PM
I would suggest that you enable the SPI at startup and never disable it..
are you sure about this?
SPI2_SendData(0x8F);
while(!((SPI2->SR)&(SPI_SR_TXE)));
while(!((SPI2->SR)&(SPI_SR_RXNE)));
while((SPI2->SR)&(SPI_SR_BSY));
received_data = SPI2_ReceiveData();
how do you know the SPI is not busy before you send the data ?
how do you know the RxData you receive is fresh and not double buffered and old.?
2018-11-10 02:39 AM
Thank you for your answer JW. I checked all registers of the corresponding GPIO's and all of them are as i want them to be. When i checked on oscilloscope after enabling SPI2 clock goes HIGH as i want and when i send data it does not alternate. Also MOSI signal goes HIGH just after sending the data and it stays HIGH too.
2018-11-10 03:12 AM
Thanks TJ, i got your warnings. I add BUSY flag control before SPI2_SendData() function but how can i control double buffering? For 16 clock cycle RX buffer can be full once isn't it?
2018-11-10 05:14 AM
I think you are on the right track but we could be here forever...
Here you can see, we drop the nSS, transfer the address and read a byte, then lift the nSS.
I run the InitSPI once at startup, just to satisfy HAL... ( I dont drop the nSS during initialisation)
void initSPI(void) {
int8_t TxSPIBuffer[1];
int8_t RxSPIBuffer[1];
TxSPIBuffer[0] = 0xFF;
HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)TxSPIBuffer, (uint8_t *)RxSPIBuffer, 1, 10);
}
int8_t readLis3DEByte(char address) {
address &= 0x3F; // 6bit address
address += 0x80; // Read
address += 0x40; // Auto Increment
HAL_GPIO_WritePin(LIS3DE_nSS_GPIO_Port, LIS3DE_nSS_Pin, GPIO_PIN_RESET);
transfer(address);
char dummyByte = 0;
char RxSPI = transfer_receive(dummyByte);
HAL_GPIO_WritePin(LIS3DE_nSS_GPIO_Port, LIS3DE_nSS_Pin, GPIO_PIN_SET);
return RxSPI;
}
void transfer(unsigned short data) { // send only
char RxSPI;
while (!(hspi1.Instance->SR & SPI_FLAG_TXE)) ; //wait until finished sending the last byte
*((__IO uint8_t *)&hspi1.Instance->DR) = data;
RxSPI = hspi1.Instance->DR; // read Rx byte and discard, only clearing the buffer
}
char transfer_receive(unsigned short data) { // send and receive byte
char RxSPI;
while(!(hspi1.Instance->SR & SPI_FLAG_TXE)) // make sure the last byte is gone.
;
while ((hspi1.Instance->SR & SPI_FLAG_RXNE)) // unload any fifo
RxSPI = hspi1.Instance->DR; //empty and dump all fifo bytes
*((__IO uint8_t *)&hspi1.Instance->DR) = data; // force the SPI to transceive 8 bit
while(!(hspi1.Instance->SR & SPI_FLAG_TXE)) // wait to transmitter double buffer to shift into transmitter
;
while ((hspi1.Instance->SR & SPI_FLAG_BSY)) ; // wait for data to shift out of the processor pins
while((hspi1.Instance->SR & SPI_FLAG_RXNE)) // read all the bytes received, only keep the last one
RxSPI = hspi1.Instance->DR; // we only want the last byte
return RxSPI;
}