SPI esclavo dúplex completo, DMA e interrupciones en STM32F103

4

Estoy buscando un poco de ayuda con SPI y DMA en una placa STM32F103C8.

Lo tengo funcionando como esclavo SPI usando interrupciones y tengo problemas para hacer que DMA funcione. Mi objetivo es obtener transacciones de dúplex completo de 16 bits, con una interrupción DMA que se genera en la recepción de datos. Lo que pienso detrás de esto es si relleno el búfer tx y habilito DMA para tx y rx, luego, cuando la selección de esclavos sea baja (configurada como hardware esclavo), los datos de rx se recibirán cuando el maestro elimine los datos tx. Una vez hecho esto, se producirá la interrupción de rx, puedo verificar los indicadores, etc. para tx en la interrupción DMA y volver a habilitar DMA para que se repita.

Logré que DMA y SPI funcionaran para recibir datos, pero no puedo hacer que transmita nada. Estoy mirando en un analizador lógico pero todo está tranquilo en el frente de SDO. He intentado llorar, jurar y tomar cerveza, pero ninguno parece funcionar. Estoy bastante convencido de que mi problema está en la configuración de mi DMA. ¿Alguien puede echar un vistazo a mi código para ver si estoy en el camino correcto?

Configurar y habilitar:

// Setup Code
GPIO_InitTypeDef    GPIO_InitStructure;
SPI_InitTypeDef     SPI_InitStructure;
NVIC_InitTypeDef    NVIC_InitStructure;
DMA_InitTypeDef     DMA_InitStructure;

volatile uint16_t   dummyRX;
volatile uint16_t   dummyTX;

// Host SPI Interface
RCC_APB2PeriphClockCmd(SPI_GPIO_CLK | SPI_CLK, ENABLE);         // Enable the clock for SPI1

// Configure GPIO for SPI slave: SCK, MOSI, SS as inputs
GPIO_InitStructure.GPIO_Pin = MOSI_PIN | CS_PIN | SCK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           // Configure SCK and MOSI pins as Input Floating 
GPIO_Init(SCK_PORT, &GPIO_InitStructure);

// Configure GPIO for SPI slave: MISO as AF output
GPIO_InitStructure.GPIO_Pin = MISO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(MISO_PORT, &GPIO_InitStructure);



SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;          // CHECK! pg682 - Should be full duplex as we need to xmit all F's for 485 drivers when recieving 
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                     // SCK idle high
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                    // second transition -> SCK Idle high => capture on rising edge of clock
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
//SPI_BaudRatePrescaler
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
//SPI_CRCPolynomial                  
SPI_Init(SPI1, &SPI_InitStructure);




//Enable DMA1 channel IRQ Channel 
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


DMA_DeInit(DMA1_Channel2);
DMA_DeInit(DMA1_Channel3); 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SPI_DR_Base;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;


DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&dummyRX;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_Init(DMA1_Channel2, &DMA_InitStructure);


DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&dummyTX;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);



dummyTX = 0xAFFA;       // Some data to send
dummyRX = 0x0000;



// Start the devices
DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);                 // Enable DMA1 Channel Transfer Complete interrupt

SPI_Cmd(SPI1, ENABLE);                                          // Enable SPI device
SPI_CalculateCRC(SPI1, DISABLE);

SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);

DMA_Cmd(DMA1_Channel3, ENABLE);
DMA_Cmd(DMA1_Channel2, ENABLE);                                 // Enable DMA channels

DMA rx (canal 2) interrupción

    //Test on DMA1 Channel1 Transfer Complete interrupt
if(DMA_GetITStatus(DMA1_IT_TC2)) {
    //Clear DMA1 Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits
    DMA_ClearITPendingBit(DMA1_IT_GL2);

    LED_1_PORT->ODR ^= LED_1_PIN;                       // Toggle test LED


    while (DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET) {}
    // wait for tx to complete - page 692
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) {}
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) {}

    DMA_ClearFlag(DMA1_FLAG_GL3);                       // Clear the global flag

    spi_buffer_rx = dummyRX;

    // Disable DMA
    DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, DISABLE);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
    DMA_Cmd(DMA1_Channel2, DISABLE);
    DMA_Cmd(DMA1_Channel3, DISABLE);

    while (DMA1_Channel2->CCR & DMA_CCR2_EN);           // wait until DMA is actually off
    while (DMA1_Channel3->CCR & DMA_CCR3_EN);   


    DMA_SetCurrDataCounter(DMA1_Channel2, 1);           // Set the number of transfers
    DMA_SetCurrDataCounter(DMA1_Channel3, 1);

    DMA_ClearITPendingBit(DMA1_IT_GL2);     // clear again
    DMA_ClearFlag(DMA1_FLAG_GL3);           // Clear the global flag

    // Start DMA controller again
    DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
    DMA_Cmd(DMA1_Channel2, ENABLE);
    DMA_Cmd(DMA1_Channel3, ENABLE);

    }
    
pregunta law

1 respuesta

6

Debajo de un código que funciona para mí. La asignación de canales DMA es crítica. DMA1 canal 4 para solicitud de SPI2 RX y DMA1 canal 5 para solicitud de SPI2 TX. Ver aquí, página 147:

  

enlace

uint16_t SPIReceivedValue[2];
uint16_t SPITransmittedValue[2] = {0xFF00,0x00FF};

void SPI_Slave_and_DMA_Configuration(void)
{
    SPI_InitTypeDef SPI_InitStructure; //Variable used to setup the SPI
    DMA_InitTypeDef DMA_InitStructure; //Variable used to setup the DMA


    //--Enable the SPI2 periph
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

    // Reset SPI Interface
    SPI_I2S_DeInit(SPI2);

    //== SPI2 configuration
    SPI_StructInit(&SPI_InitStructure);
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &SPI_InitStructure);


    //--Enable DMA1 clock--
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    //==Configure DMA1 - Channel4== (SPI -> memory)
    DMA_DeInit(DMA1_Channel4); //Set DMA registers to default values
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPIReceivedValue[0]; //Variable to which received data will be stored
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 2; //Buffer size
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure); //Initialise the DMA
    DMA_Cmd(DMA1_Channel4, ENABLE); //Enable the DMA1 - Channel4         

    //==Configure DMA1 - Channel5== (memory -> SPI)
    DMA_DeInit(DMA1_Channel5); //Set DMA registers to default values
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPITransmittedValue[0]; //Variable from which data will be transmitted
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 2; //Buffer size
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure); //Initialise the DMA
    DMA_Cmd(DMA1_Channel5, ENABLE); //Enable the DMA1 - Channel5         

    // Enable SPI2
    SPI_Cmd(SPI2, ENABLE);

    // Enable the SPI2 RX & TX DMA requests
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
}
    
respondido por el user40226

Lea otras preguntas en las etiquetas