SPI Master dúplex completo utilizando DMA - STM32F105

1

Estoy intentando escribir un controlador SPI para un STM32F105 usando la funcionalidad DMA. Estoy usando la Biblioteca Periférica Estándar de ST. No estoy usando interrupciones. Puedo hablar con el dispositivo de destino si uso rutinas SPI estándar , pero no he descubierto el DMA ... El dispositivo es el Spansion S25FL164K memoria flash IC.

Como prueba, estoy tratando de leer en voz alta los identificadores del dispositivo. Debería enviar un solo byte y luego recibir tres bytes en respuesta. Dado que el STM32 es el SPI maestro, debe proporcionar tres bytes de pulsos de reloj para obtener la respuesta. En general, el dispositivo maestro envía tres bytes ficticios para crear este reloj. Con DMA, le dices cuántos bytes recibir y se supone que debe crear las formas de onda por ti.

Con mi código, el primer byte se transmite, pero no se crean pulsos de reloj para los valores devueltos. Esto implica que los relojes periféricos DMA y SPI se configuran correctamente. Busqué en línea y (sorprendentemente) no puedo encontrar ningún ejemplo, tutorial o notas de aplicaciones que hablen sobre la secuenciación de transmisión y recepción.

Supongo que simplemente me estoy equivocando con la secuenciación.

Para TX, puedo:

  • Habilitar el dispositivo DMA
  • Deshabilitar el dispositivo DMA.
  • Habilite el dispositivo SPI para enviar señales al DMA,
  • Deshabilitar esta señalización
  • Borrar banderas DMA, y
  • Borrar las banderas SPI.

Puedo hacer lo mismo, por separado, para RX.

He intentado diferentes permutaciones para habilitar / deshabilitar estas diferentes funciones. En lugar de intentar enumerarlos a todos aquí, espero que alguien me diga cuál es el método adecuado :) Se agradece cualquier ayuda; Me he estado golpeando la cabeza contra esta.

Aquí está mi configuración de DMA, seguida de una de las iteraciones fallidas de la función de lectura de SPI:

void ConfigureDMA(void)
{
    DMA_InitTypeDef     DMA_InitStructure;

    // Enable DMA1 Peripheral Clock (SPI_DECAWAVE and SPI_BUS)
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // Configure SPI_BUS RX Channel
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // From SPI to memory
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0; // To be set later
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_BufferSize = 1; // To be set later
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);

    // Configure SPI_BUS TX Channel
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // From memory to SPI
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0; // To be set later
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_BufferSize = 1; // To be set later
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);

} // end ConfigureDMA()

En la llamada de función, CommandBuffer y DataBuffer son punteros a matrices. Esta función queda atrapada esperando en la sección "Espere hasta que se reciban los datos", pero incluso si omito esta comprobación, los pulsos de reloj nunca se crearán.

void spiFlashRead(uint8_t CommandLength, const uint8_t *CommandBuffer,
        uint16_t DataLength, uint8_t *DataBuffer)
{
    // Prepare the DMA
    DMA1_Channel5->CNDTR = CommandLength;
    DMA1_Channel5->CMAR = (uint32_t)CommandBuffer;
    DMA1_Channel4->CNDTR = DataLength;
    DMA1_Channel4->CMAR = (uint32_t)DataBuffer;

    // Enable the DMAs - They will await signals from the SPI hardware
    DMA_Cmd(DMA1_Channel5, ENABLE); // TX
    DMA_Cmd(DMA1_Channel4, ENABLE); // RX

    // Activate the Flash CS
    GPIO_ResetBits(SPI_MEM_CS_GPIO, SPI_MEM_CS);

    // Enable the SPI communication to the TX DMA, which will send the command
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);

    // Wait until the command is sent to the DR
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC5));

    // Wait until the transmission is completed
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == RESET);

    // Disable the TX DMA and clear DMA flags
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
    DMA_Cmd(DMA1_Channel5, DISABLE);
    DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_HT4 | DMA1_FLAG_TC4 | DMA1_FLAG_GL5 | DMA1_FLAG_HT5 | DMA1_FLAG_TC5);
    //NOTE: I checked the SPI OVR flag here, and it wasn't set...

    // Enable SPI communication to the RX DMA, which should receive the data
    SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);

    // Wait until the data is received
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));

    // Disable the DMAs
    DMA_Cmd(DMA1_Channel4, DISABLE); // RX
    DMA_Cmd(DMA1_Channel5, DISABLE); // TX

    // Release the Flash CS
    GPIO_SetBits(SPI_MEM_CS_GPIO, SPI_MEM_CS);

} // end spiFlashRead()
    
pregunta bitsmack

2 respuestas

2

SPI no creará las formas de onda por ti. Es dúplex completo y el lado maestro recibe al mismo tiempo que transmite, solo.

El DMA no solicita al receptor que obtenga datos, es al revés: cuando el receptor ha recibido algo, solicita que el DMA lo transfiera.

Es probable que tengas que configurar las matrices de transmisión y recepción en una longitud de 4 e ignorar la primera recibida.

Lo puse a trabajar una vez, siéntete libre de pedir más.

    
respondido por el venny
3

Enfrenté el mismo problema cuando intentaba realizar la transferencia de dúplex completo SPI utilizando el DMA en ambas direcciones en el STM32F103 (Maple Mini).

El búfer de recepción contenía un byte inesperado al principio. Tuve que ignorarlo y aumentar el tamaño del búfer en un byte para que el canal Rx DMA obtenga todos los bytes recibidos. Sin embargo, logré encontrar la solución.

En RM0008 Manual de referencia Encontré la sección 25.3.9 Comunicación SPI utilizando DMA ( direccionamiento de memoria directo) diciendo:

  

En la recepción, se emite una solicitud de DMA cada vez que RXNE se establece en 1. El DMA luego lee   el registro SPI_DR (esto borra la marca RXNE).

Por lo tanto, traté de limpiar el indicador RXNE en el SPI antes de que comenzara la transferencia DMA:

  

El búfer de Rx no está vacío (RXNE)

     

Cuando se establece, este indicador indica que hay datos recibidos válidos en el búfer Rx. Se borra cuando se lee SPI_DR.

Afortunadamente, esto me ayudó y pude obtener datos limpios sin ningún tipo de basura.

    
respondido por el Lubo

Lea otras preguntas en las etiquetas