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()