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-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);
2024-05-16 10:48 PM
What is in your Rx DAM buffer? ("nothing" is not possible).
All 0xFF? then you might have read the empty page from external flash.
If still all 0x00? Maybe an issue with the connections.
Fill the buffer before you read with a pattern and see with which value it was overwritten (and it if it was overwritten).
The scope picture shows something reasonable (reading an "empty"/not programmed location), all 0xFF.
What is "nothing"?
BTW: how is nHOLD connected?
And:
You have pull-ups on your schematics? (100K, not clear to see).
But you enable PULL_DOWN on pin config?
WHY?????
This seems to be wrong: 100K external pull-up and 100K internal pull up (not sure, maybe even a bit stronger) - you "kill" all your logic levels! the chip would see a digital signal right in the middle, e.g. as 1.5V and it violates the CMOS digital logic levels.
Use an analog scope to verify if you have a conflict between external pull-ups and internal pull-downs.
2024-05-17 06:07 AM
This is the RX buffer when the SPI initialization code has set everything to zero and we have hit the TX DMA complete interrupt:
Here is a run when we add a memset to load the RX buffer with 0x55's.
Seems we forgot to put a volatile declaration of the RX buffer so stopping the debugger was giving a false impression. Here is it in the main loop after a proper volatile declaration. Not sure why the first byte is a 0x01 and there are only 14 locations filled:
- how is nHOLD connected? Pulled to 3.3v
- You have pull-ups on your schematics? MISO, MOSI and SCK are pulled down.
So at this point almost half the SPI data is making it into the RX buffer and we are insuring that we stop the debugger out in the main loop.
The RX DMA size we bumped up to match the TX DMA size, the DMA's are configured to be byte aligned, the RX DMA channel is enabled before the TX DMA channel and the logic analyzer shows 32 bytes of data coming out of the EEPROM chip. Does FIFO effect the DMA data?
// 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;
memset((void *)rxSpiBuff, 0x55, sizeof(rxSpiBuff));
// 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+3));
// 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);
2024-05-17 06:15 AM
Just a note, combining debugger accesses to operating peripherals with running code is always risky. Never trust it (unless you actually like chasing squirrels) and create your own instrumentation instead.
2024-05-17 07:34 AM
Back to nothing in the RX DMA buffer again (except the first byte) using the same configuration. Going to have the ST chip solder pins checked.
2024-05-17 08:35 AM
Couple updates:
- The pulldowns are not populated.
- No soldering issues with the ST chip.
- Running it numerous times gives mixed results and maybe 10% of the time all the data is there.
- Because we run four ADC's, a DAC and the SPI bus using DMA's, pushed the SPI DMA channels to very high priority but this made no difference.
- We will go to checking clocking signal levels but the logic analyzer seemed happy with what it had.
2024-05-17 01:33 PM
Some updates
- When on the rare occasion that all the data is received, the transmit complete interrupt will fire.
- There is a little ringing on the signals(as measured from the ST micro input pin), but is this enough to radically effect large portions of RX data?
SCK
MOSI
2024-05-17 04:58 PM
As an experiment, maybe try dropping the SPI clock rate 10x.
2024-05-20 07:05 AM - edited 2024-05-20 07:06 AM
Finally making some progress but this does not look good. Currently there are four DMA channels running: ADC, DAC, SPI RX and SPI TX. The ADC is taking 4 different channels worth of data and the DAC is sending out a 10khz wave. The first test was to change the DMA channel priority to ADC(chan 1) LOW, DAC(chan 4) Medium, SPI TX(chan 2) High and SPI RX(chan 3) Very high. We when we do receive data we get more, but the test is still bad.
We now turned off Timer 2, which is running the DAC to drive the 10khz wave. All the data is now being properly received.
Timer 2 settings with WAVE_10K_FREQ_COUNT = 62.
1/64Mhz = 16nsec
16nsec * 62 = 929nsec
So approximately every 1 usec we push out a new DAC value. Given we can see how the DAC is effecting the SPI RX even when the DAC DMA priority is currently lower, how do we approach this issue? What should the smallest time frame that we can run the DAC and not interfere with the SPI receive?
Thanks
tim_prescaler = 0;
TimOutClock = SystemCoreClock/1;
tim_period = __LL_TIM_CALC_ARR(TimOutClock, tim_prescaler, WAVE_10K_FREQ_COUNT);
TIM_TimeBaseStructure.Prescaler = tim_prescaler;
TIM_TimeBaseStructure.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_TimeBaseStructure.Autoreload = WAVE_10K_FREQ_COUNT;
TIM_TimeBaseStructure.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_TimeBaseStructure.RepetitionCounter = 0;
LL_TIM_Init(TIM2, &TIM_TimeBaseStructure);
2024-05-20 02:00 PM
Couple updates:
- Turned Timer 2 back on to drive the DAC and slowed to SPI bus from 4Mhz(16 Prescale) to 500Khz(128 Prescale). This had little measurable effect for test runs of ten.
- Began increasing the Timer 2 reload value to slow down the DMA transfers to the DAC. Tried 100, 200, 800, 1600 and 3200 with zero for a prescaler value on Timer 2. None of these values seemed to effect the amount of data received for a test runs of ten tests.
- Reached a Timer 2 reload of 6400(again with a zero Prescaler value) and the test would either get all the 32 bytes of data expected or none at all. Similar result with a reload value of 12800, either all the data or none.