2024-05-16 06:04 AM
STM32G071R8 & M95160-WMN6TP EEPROM
We have the DMA SPI TX direction working but are having issues with the data that is being returned. Technically this is a read from the EEPROM but the command and read address are sent first. The EEPROM responds with the 32 bytes of blank(0xFF). None of the data ends up in the DMA SPI RX buffer. When the DMA TX complete interrupt completes(when it's done transmitting 0x00 so we can receive data), the is nothing in the DMA buffer or even a 0xFF in SPI data register itself.
The SPI is configured for full duplex and we have the DMA TX buffer(35 byte size) send out the read command and then 32 0x00's(three byte command + 32 blanks) so that the clock runs for the incoming data. The RX DMA is set to 32 bytes(3 less bytes.) This was done in the hope we could use the DMA RX transmit complete interrupt to know when the entire read of the data from the EEPROM was done(as suggested by @Nikita91 from this thread: detecting-the-complete-end-of-spi-transfer-in-tx-master-mode.) We are very new to SPI and a lot of data sheet nomenclature is confusing because when you are the master in full duplex mode, you are transmitting data to receive data.
Any help is appreciated.
Test Code
void spi_eeprom_init(void)
{
uint8_t errorLogReset = 0;
uint32_t *addr_p, page1ErrorType, page2ErrorType, pageNumber;
HAL_StatusTypeDef currState;
// Set up the read command to the SPI EEPROM.
txSpiBuff[0] = sEE_CMD_READ;
txSpiBuff[1] = ((HIGH_TEMP_LOCKOUT_ADDRESS & 0xFF00) >> 8);
txSpiBuff[2] = (HIGH_TEMP_LOCKOUT_ADDRESS & 0xFF);
wTransferState = TRANSFER_WAIT;
// Set up the TX SPI DMA
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2, (uint32_t)txSpiBuff,
LL_SPI_DMA_GetRegAddr(SPI1), LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
// Set up the transfer size which is the read command(3 bytes) plus the data we
// want to read.
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, (EEPROM_PAGE_SIZE_IN_BYTES+3));
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, (EEPROM_PAGE_SIZE_IN_BYTES));
// Enable SPI1.
LL_SPI_Enable(SPI1);
// !< Select the EEPROM: Chip Select low
LL_GPIO_ResetOutputPin(GPIOD, SPI1_CS);
// Enable RX and then TX.
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
SPI/DMA setup
void SPI_LowLevel_Init(void)
{
LL_GPIO_InitTypeDef GPIO_InitStructure = {0};
// GPIOA and GPIOC Periph clock's have already been enabled.
/* SPI1 EEPROM Periph clock enable */
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
/* Configure EEprom pins: MISO and MOSI */
GPIO_InitStructure.Pin = SPI1_MISO; // ==> GPIO_PA11
GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_HIGH;
GPIO_InitStructure.Pull = LL_GPIO_PULL_DOWN;
GPIO_InitStructure.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Pin = SPI1_MOSI; // ==> GPIO_PA12
GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_HIGH;
GPIO_InitStructure.Pull = LL_GPIO_PULL_DOWN;
GPIO_InitStructure.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure EEprom pins: SCLK
GPIO_InitStructure.Pin = SPI1_SCLK; // ==> GPIO_PD8
GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_HIGH;
GPIO_InitStructure.Pull = LL_GPIO_PULL_DOWN;
GPIO_InitStructure.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure sEE_CS_PIN pin: sEE Card CS pin */
GPIO_InitStructure.Pin = SPI1_CS; // ==> GPIO_PD9
GPIO_InitStructure.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_HIGH;
GPIO_InitStructure.Pull = LL_GPIO_PULL_DOWN;
LL_GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Insure the EEPROM Chip Select high */
LL_GPIO_SetOutputPin(GPIOD, SPI1_CS);
// GPIO_PinAFConfig parameter #3 possible values.
// This parameter can be one of the following value:
// SCLK PD8 pin 40 @arg USART3_TX, SPI1_SCK/I2S1_CK, LPTIM1_OUT
// MISO PA11 pin 43 @arg SPI1_MISO/I2S1_MCK, USART1_CTS, TIM1_CH4, TIM1_BKIN2, I2C2_SCL, COMP1_OUT
// MOSI PA12 pin 44 @arg SPI1_MOSI/I2S1_SD, USART1_RTS_DE_CK, TIM1_ETR, I2S_CKIN, I2C2_SDA, COMP2_OUT
// PD8(SPI SCLK) --> GPIO_AF_1 for SPI1, Datasheet(table 16) lists AF pin for SPI as: SPI1_SCK/I2S3_CK
LL_GPIO_SetAFPin_0_7(GPIOD, SPI1_SCLK, LL_GPIO_AF_1);
// PA11(SPI MISO) --> GPIO_AF_0 for SPI3, Datasheet(table 13) lists AF pin for SPI as: SPI1_MISO/I2S3ext_SD
LL_GPIO_SetAFPin_0_7(GPIOA, SPI1_MISO, LL_GPIO_AF_0);
// PA12(SPI MOSI) --> GPIO_AF_0 for SPI1, Datasheet(table 13) lists AF pin for SPI as: SPI1_MOSI/I2S3_SD
LL_GPIO_SetAFPin_0_7(GPIOA, SPI1_MOSI, LL_GPIO_AF_0);
// SPI NSS --> GPIO_AF_6 for SPI3, Datasheet lists AF pin for SPI as: SPI1_NSS
//GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_6);
/* SPI Config */
// SPI bus speed should be 64Mhz/16 prescale = 4Mhz
LL_SPI_InitTypeDef SPI_InitStruct = {0};
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI1, &SPI_InitStruct);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_DisableNSSPulseMgt(SPI1);
LL_SPI_SetRxFIFOThreshold(SPI1, LL_SPI_RX_FIFO_TH_QUARTER);
// Configure SPI1 DMA transfer interrupts
// Enable DMA TX Interrupt
LL_SPI_EnableDMAReq_TX(SPI1);
// Enable DMA RX Interrupt
LL_SPI_EnableDMAReq_RX(SPI1);
// DMA channel 2 configuration for Tx of SPI
LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_1, LL_DMAMUX_REQ_SPI1_TX);
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_SPI1_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
// DMA channel 3 configuration for Rx of SPI
LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_2, LL_DMAMUX_REQ_SPI1_RX);
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMAMUX_REQ_SPI1_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);
// Set DMA transfer addresses of source and destination
// Set DMA transfer size
// Enable DMA transfer interruption: transfer complete
// Enable DMA transfer interruption: half transfer
// Enable DMA transfer interruption: transfer error
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2, (uint32_t)txSpiBuff,
LL_SPI_DMA_GetRegAddr(SPI1), LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 1);
// Set DMA transfer addresses of source and destination
// Set DMA transfer size
// Enable DMA transfer interruption: transfer complete
// Enable DMA transfer interruption: half transfer
// Enable DMA transfer interruption: transfer error
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_3, LL_SPI_DMA_GetRegAddr(SPI1),
(uint32_t)rxSpiBuff, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, 1);
/* SPI1 enable */
LL_SPI_Enable(SPI1);
// (5) Enable DMA transfer complete/error interrupts
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_3);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_3);
}
Solved! Go to Solution.
2024-05-22 01:21 PM
Update
We decided to move the DAC output from channel 2 to channel 1. This move involved changing the GPIO pin from PA5(pin 22) to PA4(pin 21) and configuring DAC channel 1 to receive the DMA data instead of DAC channel 2. We used a O'scope to insure the sine wave output was being sent correctly as pin 21 is not connected to anything.
SPI RX DMA data now seems reliable as we have run the test 50 times and all tests pass. This looks like an internal issue between the DAC's channel 2 and SPI RX. When we receive guidance from ST we will update this thread.
2024-06-12 12:05 PM
ST has found our issue. We simply missed the pin assignment. When we read Table 12 of the data sheet, PA4's DAC_OUT1 is listed under the "Additional functions" column right next to the "Alternate functions" column. We configured it as a alternate pin assignment instead of a analog pin assignment as show in some of their Low Level driver example code. The DAC output still works and things are fine until the SPI is run(which is one of PA4's alternate pin configurable s.)
Thanks ST.
Original code
// Configure the PortA(PA) DAC output pin
GPIO_InitStructure.Pin = 0;
GPIO_InitStructure.Pin = DAC_1_OUTPUT;
GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_HIGH;
GPIO_InitStructure.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStructure);
Updated code
// Configure the PortA(PA) DAC output pin
GPIO_InitStructure.Pin = 0;
GPIO_InitStructure.Pin = DAC_1_OUTPUT;
GPIO_InitStructure.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_HIGH;
GPIO_InitStructure.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStructure);