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);
}