2
\$\begingroup\$

I am working on a project with the STM32F411 microcontroller. It is set in SPI slave mode and receives data without any problems. When I want to send data over the spi peripheral i works sometimes but most of the time the bits I am trying to send a shifted or there is wrong data sent. I have configured the spi peripheral like it was shown in an example in the standard peripheral drivers for this controller and I also don´t have a chip select line because I only have one slave in the system.

Is there something wrong in my setup or do i refill the tx buffer at a wrong moment?

Here is my code:

void spi1_init(){
SPI_InitTypeDef  SPI_InitStruct;

spi1_gpio_init();

// Enable the SPI periph clock
RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1EN, ENABLE);

// Set data size to 8b 
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;

SPI_I2S_DeInit(SPI1);
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // 100MHz / 64 = 1.5625MHz
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStruct.SPI_Mode = SPI_Mode_Slave;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_CRCPolynomial = 7;

// Mode 0 
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;


// Init SPI 
SPI_Init(SPI1, &SPI_InitStruct);


//---------------------------------------------------------------------

spi1_interrupt_init();

//---------------------------------------------------------------------


   // Enable the SPI peripheral 
   SPI_Cmd(SPI1, ENABLE); 

 }


void spi1_gpio_init(void){
GPIO_InitTypeDef GPIO_InitStruct;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}

void spi1_interrupt_init(void){
NVIC_InitTypeDef  NVIC_InitStruct;

// Configure the SPI interrupt priority 
NVIC_InitStruct.NVIC_IRQChannel = SPI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

// Enable the Rx buffer not empty interrupt 
SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);

 // Enable the Tx empty interrupt 
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE);
}


void SPI1_IRQHandler(void)
{  
int8_t CmdReceived;
static uint32_t CmdCount = 0;
static int8_t RecArray[SPI_REC_BYTES];
static uint32_t dataCounter = 0;

    // SPI in Transmitter mode 
  if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_TXE) == SET)
 {
    SPI_I2S_SendData(SPI1, 123);

 }

 // SPI in Slave Receiver mode--------------------------------------- 
  if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) == SET)
 {
 CmdReceived = SPI_ReceiveData(SPI1);

        if(CmdReceived < -100){
            CmdCount = 0;
            RecArray[CmdCount] = CmdReceived;



        } else if(++CmdCount < SPI_REC_BYTES){

            RecArray[CmdCount] = CmdReceived;

            if(CmdCount == (SPI_REC_BYTES-1)){
                callbackfunction(RecArray);
            }
        }


  }


 }
\$\endgroup\$
  • \$\begingroup\$ You are sending the data during an interrupt, right? Are you sure there is no conflict with your SPI functions? In general, I don't think this is a good practice to manage SPI related requests inside interrupts. \$\endgroup\$ – Edesign Aug 17 '17 at 15:03
  • \$\begingroup\$ thank you for replying! yes I am receiving and sending everything in the isr. dma is too complex for my purposes and there is a interrupt for tx buffer empty so why shouldn't it be working with intrreupts? i know dma is better but for simply sending 8 bits this should be enough. How would you program it? Do you have a working example? \$\endgroup\$ – wouser Aug 17 '17 at 15:47
  • \$\begingroup\$ No, I don't have any precise example. But SPI requests are usually quite slow processes for the micro, so I would keep it within the main loop \$\endgroup\$ – Edesign Aug 17 '17 at 16:17
  • \$\begingroup\$ There's no problem in using interrupts for SPI! Of course, DMA will perform better for multi-byte transfers, but interrupts are still much better than polling. What Edesign seems to be referring to is not that it's bad to send/receive bytes via ISR, but the processing once the data has been fully received (callbackfunction in your case) may be better executed asynchronously in the main loop upon being signalled from the ISR that it's time to do so. \$\endgroup\$ – JimmyB Aug 17 '17 at 16:48
  • \$\begingroup\$ Ok I understand what you mean, the callback function is only a short function which is executed after the transmission of two data words. I am generating thos data with a raspberry pi so i know there is a 20ms gap between these pairs of data words so this function is no problem because there is enough time. Mor important is that receiving works but sending is not working and i am testing it without the callback function ever beeing executed so it cant be the problem. I have sent the data byte with value 2 and at the receiver i see that the bits are shifted sometimes left and sometimes right. \$\endgroup\$ – wouser Aug 17 '17 at 19:12
1
\$\begingroup\$
  1. Sending data using interrupts is quite tricky as if you do not have anything to send you should clear TEIE flag (disable the interrupt), and set it when you have something to send. Otherwise the interrupt will be triggered all the time.
  2. You do not set the number of data bits. At can be any value as your struct may contain garbage data.

  3. To send 8 bit of data you need to force the compiler to write byte (not the half word) to the DR register. Many SPL versions had it done wrong. If you write the halfword - half word is stored into the FIFO and you will send two bytes (not one!!). I do not use those silly libraries for such simply peripheral but I advice to step over the send function and see if it written properly

    1506        *(volatile uint8_t *)&SPIx -> DR = command;
    08005a9c:   strb    r1, [r0, #12]
    
\$\endgroup\$
  • \$\begingroup\$ clearing the interrupt flags has no effect, I think they are cleared anyway by executing the isr. I have also tried to force the compiler to write exactly 8Bits like you have shown in your example but the bits are still shifted in the transmission. In the setup i have also this line: SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; which sets the datalength to 8bit anyway. Is there any other way to get the tx line in sync with the clock signal? \$\endgroup\$ – wouser Aug 18 '17 at 8:29
  • \$\begingroup\$ No, you not understand. It is cleared by write to the DR register. It is about disabling this interrupt when not needed. \$\endgroup\$ – P__J__ Aug 18 '17 at 8:31
  • 1
    \$\begingroup\$ There is no TX line only MISO and MOSI \$\endgroup\$ – P__J__ Aug 18 '17 at 8:32
  • \$\begingroup\$ I know there is no Tx line, I meant the MISO line --> Slave sends data to the master and this is transmitting if you are looking from the slave side. Disabling the interrupt when its not needed is not in the code i have sent because for testing purposes I am reading every data packet the master receives. Of course in the final program I am disabling the interrupt when I don´t want to send data but in this testing example I am only sending one byte at the time. \$\endgroup\$ – wouser Aug 18 '17 at 9:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.