AnsweredAssumed Answered

STM32F4 - SPI unidirectional receive only

Question asked by neumann.johannes on Jun 16, 2015
Latest reply on Jun 16, 2015 by neumann.johannes
Hello,

I'm trying to gather 4 bytes of sensor data via SPI using a RX-only protocol (only CS, MISO, SCK). To do so, I would like to use interrupt driven transmissions, as DMA seems overkill for just 4 bytes.
This, in essence, is what I'm running on the STM32F4Disco-Board (IDE is CooCox + gcc):

#define MAIN_SCK_GPIO_PIN         GPIO_Pin_5
#define MAIN_SCK_GPIO_PORT        GPIOA
#define MAIN_SCK_GPIO_CLK         RCC_AHB1Periph_GPIOA
#define MAIN_SCK_SOURCE           GPIO_PinSource5
#define MAIN_SCK_AF                  GPIO_AF_SPI1

#define MAIN_MISO_GPIO_PIN        GPIO_Pin_4
#define MAIN_MISO_GPIO_PORT       GPIOB
#define MAIN_MISO_GPIO_CLK        RCC_AHB1Periph_GPIOB
#define MAIN_MISO_SOURCE          GPIO_PinSource4
#define MAIN_MISO_AF              GPIO_AF_SPI1

#define MAIN_CS_GPIO_PIN          GPIO_Pin_6
#define MAIN_CS_GPIO_PORT         GPIOD
#define MAIN_CS_GPIO_CLK          RCC_AHB1Periph_GPIOD
#define MAIN_CS_SOURCE            GPIO_PinSource6

#define MAIN_SPI                  SPI1
#define MAIN_SPI_CLK              RCC_APB2Periph_SPI1
#define MAIN_SPI_CLK_FN(clk,s)      RCC_APB2PeriphClockCmd(clk, s);
#define MAIN_SPI_IRQ              SPI1_IRQn
#define MAIN_SPI_IRQ_HANDLER       SPI1_IRQHandler
#define MAIN_SPI_IRQ_PREEMPT_PRIO 1


static uint8_t mainRxBuffer[4];
static uint8_t mainRxProgress;


void Init()
{
    // Enable GPIO clock
    RCC_AHB1PeriphClockCmd(MAIN_SCK_GPIO_CLK, ENABLE);
    RCC_AHB1PeriphClockCmd(MAIN_CS_GPIO_CLK, ENABLE);
    RCC_AHB1PeriphClockCmd(MAIN_MISO_GPIO_CLK, ENABLE);

    // Init the GPIOs
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

    // outputs
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Pin = MAIN_CS_GPIO_PIN;
    GPIO_Init(MAIN_CS_GPIO_PORT, &GPIO_InitStructure);

    // hardware driven outputs
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Pin = MAIN_MISO_GPIO_PIN;
    GPIO_Init(MAIN_MISO_GPIO_PORT, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = MAIN_SCK_GPIO_PIN;
    GPIO_Init(MAIN_SCK_GPIO_PORT, &GPIO_InitStructure);

    // connect alternate functions
    GPIO_PinAFConfig(MAIN_MISO_GPIO_PORT, MAIN_MISO_SOURCE, MAIN_MISO_AF);
    GPIO_PinAFConfig(MAIN_SCK_GPIO_PORT, MAIN_SCK_SOURCE, MAIN_SCK_AF);

    // set GPIO to their idle state
    MAIN_CS_GPIO_PORT->BSRRL = MAIN_CS_GPIO_PIN; // high

    // enable clock for SPI
    MAIN_SPI_CLK_FN(MAIN_SPI_CLK, ENABLE);

    /* configure SPI
     * CPOL = 1 --> clock is low when idle
     * CPHA = 1 --> data is sampled at the rising edge
     */
    SPI_InitTypeDef SPI_InitStruct;
    SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Rx;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // clock is high when idle
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // data sampled at rising edge
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal and pull internal NSS high
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // SPI frequency is APB frequency / 2
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(MAIN_SPI, &SPI_InitStruct);

    // configure SPI interrupts
    SPI_I2S_ITConfig(MAIN_SPI, SPI_I2S_IT_RXNE, ENABLE);
    SPI_I2S_ITConfig(MAIN_SPI, SPI_I2S_IT_ERR, ENABLE);

    // enable SPI interrupt handler (one for all SPI interrupts)
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = MAIN_SPI_IRQ;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = MAIN_SPI_IRQ_PREEMPT_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


/**
 * SPI interrupt for main ADC
 */
void MAIN_SPI_IRQ_HANDLER( void )
{
    /* SPI_I2S_IT_TXE: Transmit buffer empty interrupt.
     * SPI_I2S_IT_RXNE: Receive buffer not empty interrupt.
     * SPI_I2S_IT_OVR: Overrun interrupt.
     * SPI_IT_MODF: Mode Fault interrupt.
     * SPI_IT_CRCERR: CRC Error interrupt.
     * SPI_I2S_IT_TIFRFE: Format Error interrupt.
       */
    //ITStatus txe = SPI_I2S_GetITStatus(MAIN_SPI, SPI_I2S_IT_TXE);
    ITStatus rxne = SPI_I2S_GetITStatus(MAIN_SPI, SPI_I2S_IT_RXNE);
    ITStatus ovr = SPI_I2S_GetITStatus(MAIN_SPI, SPI_I2S_IT_OVR);
    //ITStatus modf = SPI_I2S_GetITStatus(MAIN_SPI, SPI_IT_MODF);
    //ITStatus crcerr = SPI_I2S_GetITStatus(MAIN_SPI, SPI_IT_CRCERR);
    //ITStatus tifrfe = SPI_I2S_GetITStatus(MAIN_SPI, SPI_I2S_IT_TIFRFE);

    if (rxne)
    {
        mainRxBuffer[mainRxProgress++] = MAIN_SPI->DR;
        if (mainRxProgress >= 4)
        {
            // Pull CS high to stop SPI package
            MAIN_CS_GPIO_PORT->BSRRL = MAIN_CS_GPIO_PIN;

            mainProcessResult(); // external function
        }
        else if (mainRxProgress == 3)
        {
            // disable the SPI (last byte will be safely received)
            SPI_Cmd(MAIN_SPI, DISABLE);
        }
    }
    if (ovr)
    {
        MAIN_SPI->DR; // according to ref. manual
        MAIN_SPI->SR;
    }

}

void startMeasurement()
{
    mainRxProgress = 0;

    // Pull CS low to start SPI package
    MAIN_CS_GPIO_PORT->BSRRH = MAIN_CS_GPIO_PIN;

    SPI_Cmd(MAIN_SPI, ENABLE); // starts communication
}



Unfortunately there are a bunch of problems for which I can't find a solution using the datasheets and manuals.
Here is what I expect:
after initialization and the start of a transfer, I expect the RXNE-interrupt to trigger 4 times. According to the datasheet, I have to disable the SPI after the reception of the third byte (while the fourth is beeing received) by clearing the SPI enable bit. So after four bytes, the reception should stop. I expect the OVR bit not to be set at any time.
I expect to be able to start a new transmission, after the first transmission is finished.

Here is what happens instead:
1. RXNE-interrupt: RXNE and OVR both set.
2. RXNE-interrupt: RXNE and OVR both set.
3. RXNE-interrupt: RXNE and OVR both set.
4. RXNE-interrupt: RXNE set, OVR reset.
All data received is 0x00 (but I haven't yet checked the wiring)

After the completion of the first transmission, I try to start a new transmission, but no interrupt is ever triggered.

Thank you very much for any hints and help,
Johannes Neumann

Outcomes