32F417 - what could stop this memory-SPI3 DMA transfer running? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: November 02, 2022, 09:45:57 am
content: I have a very intermittent issue, which happens only on one of two "identical" systems.I am using DMA to feed SPI3 TX. Very simple!This is the codeCode:/*** DMA-only version of HAL_SPI_TransmitReceive() but fixed for SPI3 and xx-only options added.** For use where fast transfers are needed, on the limit of SPI3 speed so with zero gaps. This is impossible* to do by polling at 10.5mbps or 21mbps and is probably marginal at 5.25mbps. The 16 bit SPI mode just* manages gap-free with polling but works only with even block sizes, and has the "first byte problem" which* DMA gets around.** ** DMA ONLY SO NO CCM ACCESS SO THE TWO BUFFERS HAVE TO BE "STATIC" **** This function is blocking so the caller can set CS=1 right away (check device data sheet!). A non-blocking* version would make sense only if transmitting only (txonly=true) but the caller would have to tidy up* the DMA and SPI3->CR2.** spi3_set_mode() can still be used to set up the clock speed, phase, etc; this function does nothing* to SPI3 other than to enable/disable DMA. It does however make sense only if SPI3 is a Master.** Two modes, obviously mutually exclusive, for tx-only and rx-only:* If txonly, dumps rx data so you don't need to allocate a buffer for it* If rxonly, transmits all-0x00 so you don't need to feed SPI with some "known garbage"** The rx-only mode is superfluous in most cases but it does avoid shifting non-0x00 data to the device* while we are reading data out of it. With some devices this can matter. The ADS1118 ADC is one such.** Because this function needs to work with KDE_spi3_set_mode(), care is taken to not modify the SPI config.** NULL pointers are allowed if using the txonly or rxonly modes; then the unused one can be NULL.***/bool SPI3_DMA_TransmitReceive(uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, bool txonly, bool rxonly, bool yield){// Check for invalid inputsif ( Size==0 ) return (false);if ( (pTxData==NULL) && !rxonly ) return (false);if ( (pRxData==NULL) && !txonly ) return (false);uint32_t txadd = (uint32_t) pTxData;uint32_t rxadd = (uint32_t) pRxData;static uint8_t txonly_target=0;// must not be in CCMstatic uint8_t rxonly_source=0;// must not be in CCM// DMA ch 1 clock enable already done in b_main.c//RCC->AHB1ENR |= (1u << 21);// DMA1EN=1 - DMA1 clock enable//hang_around_us(1);// give it a chance to wake up// DMA1 Ch 0 Stream 0 is SPI3 RXDMA1_Stream0->CR = 0;// disable DMA so all regs can be writtenDMA1->LIFCR = (0x03d << 0);// clear int flags & transfer complete - 111101 stream 0DMA1_Stream0->NDTR = Size;if (txonly){DMA1_Stream0->M0AR = (uint32_t) &txonly_target;// memory address to dump rx data to}else{DMA1_Stream0->M0AR = rxadd;// memory address in normal mode}DMA1_Stream0->PAR = (uint32_t) &(SPI3->DR);// peripheral addressDMA1_Stream0->FCR = 0;// direct modeif (txonly){DMA1_Stream0->CR = 0 << 25// CHSEL: ch 0| 0 << 23// MBURST: memory burst - single transfer| 0 << 21// PBURST: peripheral burst - single transfer| 3 << 16// PL: highest priority| 0 << 15// PINCOS: no peripheral address increment offset| 0 << 13// MSIZE: memory data size: byte| 0 << 11// PSIZE: peripheral data size: byte| 0 << 10// MINC: memory address increment: 0| 0 << 9// PINC: peripheral address increment: 0| 0 << 8// CIRC: no circular mode| 0 << 6// DIR: peripheral to memory| 0 << 5// PFCTRL: DMA is flow controller| 1 << 0;// EN: enable stream}else{DMA1_Stream0->CR = 0 << 25// CHSEL: ch 0| 0 << 23// MBURST: memory burst - single transfer| 0 << 21// PBURST: peripheral burst - single transfer| 3 << 16// PL: highest priority| 0 << 15// PINCOS: no peripheral address increment offset| 0 << 13// MSIZE: memory data size: byte| 0 << 11// PSIZE: peripheral data size: byte| 1 << 10// MINC: memory address increment: 1| 0 << 9// PINC: peripheral address increment: 0| 0 << 8// CIRC: no circular mode| 0 << 6// DIR: peripheral to memory| 0 << 5// PFCTRL: DMA is flow controller| 1 << 0;// EN: enable stream}// DMA1 Ch 0 Stream 7 is SPI3 TXDMA1_Stream7->CR = 0;// disable DMA so all regs can be writtenDMA1->HIFCR = (0x03d << 22);// clear int flags & transfer complete - 111101 stream 7DMA1_Stream7->NDTR = Size;if (rxonly){DMA1_Stream7->M0AR = (uint32_t) &rxonly_source;// memory address to fetch dummy tx data from}else{DMA1_Stream7->M0AR = txadd;// memory address in normal mode}DMA1_Stream7->PAR = (uint32_t) &(SPI3->DR);// peripheral addressDMA1_Stream7->FCR = 0;// direct modeif (rxonly){DMA1_Stream7->CR = 0 << 25// CHSEL: ch 0| 0 << 23// MBURST: memory burst - single transfer| 0 << 21// PBURST: peripheral burst - single transfer| 0 << 16// PL: priority low| 0 << 15// PINCOS: no peripheral address increment offset| 0 << 13// MSIZE: memory data size: byte| 0 << 11// PSIZE: peripheral data size: byte| 0 << 10// MINC: memory address increment: 0| 0 << 9// PINC: peripheral address increment: 0| 0 << 8// CIRC: no circular mode| 1 << 6// DIR: memory to peripheral| 0 << 5// PFCTRL: DMA is flow controller| 1 << 0;// EN: enable stream}else{DMA1_Stream7->CR = 0 << 25// CHSEL: ch 0| 0 << 23// MBURST: memory burst - single transfer| 0 << 21// PBURST: peripheral burst - single transfer| 0 << 16// PL: priority low| 0 << 15// PINCOS: no peripheral address increment offset| 0 << 13// MSIZE: memory data size: byte| 0 << 11// PSIZE: peripheral data size: byte| 1 << 10// MINC: memory address increment: 1| 0 << 9// PINC: peripheral address increment: 0| 0 << 8// CIRC: no circular mode| 1 << 6// DIR: memory to peripheral| 0 << 5// PFCTRL: DMA is flow controller| 1 << 0;// EN: enable stream}// Config SPI3 to let DMA handle the data. These need to be cleared when transfer complete!// This starts the transferSPI3->CR2 |= 3;// TXDMAEN, RXDMAEN: 11 - both set in one goSPI3->CR1 |= (1<<6);// SPE=1 enable SPI// Wait for DMA to finish. Blocking is necessary to prevent device CS=1 too early.// There could be a timeout here but a failure is impossible short of duff silicon, because// we are a Master and generating the SPI clock.while(true){// Either end-transfer detection method below works but the NDTR one may be more reliable#if 1uint16_t temp1;temp1 = DMA1_Stream0->NDTR;if ( temp1 == 0 ) break;// transfer count = 0#elseuint32_t temp2;temp2 = DMA1->LISR;if ( (temp2 & (1<<5)) !=0 ) break;// TCIF0#endif}SPI3->CR2 &= ~3;// TXDMAEN, RXDMAEN: 00 - both cleared in one go// Clear int pending flags. They get cleared at the top of this function anyway, but...DMA1->LIFCR = (0x03d << 0);// clear int flags & transfer complete - 111101 stream 0DMA1->HIFCR = (0x03d << 22);// clear int flags & transfer complete - 111101 stream 7// Clear any rx data and the overrun flag in case not all received data was readSPI3->CR1 &= ~(1<<6);// SPE=0 disable SPI//hang_around_us(1);SPI3->DR;SPI3->DR;SPI3->SR;return (true);}When it hangs, it hangs in that forever loop near the end. Breaking the code there shows NDTR=6; init value was 7 so it looks like it moved 1 byte. InCode:SPI3->CR2 |= 3;// TXDMAEN, RXDMAEN: 11 - both set in one goSPI3->CR1 |= (1<<6);// SPE=1 enable SPIthe relevant bits are correctly set ie both DMA and SPI are enabled.rxonly=false (txonly mode is used)CR=10001000000CR2=3CR1=1111101100NDTR=6Notably, CR has bit 0 = 0 so1 << 0; // EN: enable streamhas got cleared, but how?What else can clear DMA1_Stream7->CR bit 0???SPI3 access is mutexed. DMA access isn't but nothing else should be using this DMA channel. Is bit 0 of CR somehow shared between multiple DMA "processes"?This code has been running for many months, 24/7, solidly. What has changed? Well, I added an RTOS task which reads an ADS1118 ADC. This could use the above DMA like everything else does (for SPI3) but instead it uses the HAL_SPI_TransmitReceive() function, because the ADS1118 uses the SPI in16 bitmode, and I didn't want to re-hack the DMA code to do that. The ADS1118 code was written and very carefully verified 2 years ago.I am tempted to replace HAL_SPI_TransmitReceive() with a 16 bit DMA version. I also tried to use the above bytewide DMA code but for some reason it didn't work (I didn't forget the fact that in 16 bit mode, SPI transmits the MS byte first).But the basic thing is CR bit 0 getting cleared, before NDTR has reached 0. How is that possible?MasterSPI cannot just be stopped so DMA requests from it cannot just stop.The calling code of that DMA function is a bit of code which feeds a 6 digit LED display controller, hence the 7 bytes getting transmitted. This has been working for years too. It runs at 10Hz. Every few minutes or hours, it gets stuck.FWIW, this is the SPI3 function the ADS1118 uses. It is bloated with loads of junk. We had a thread on this before... it deals with the special condition of transfer count = 1. But I don't think this is the problem.Code:/*** @brief Transmit and Receive an amount of data in blocking mode.* @param hspi pointer to a SPI_HandleTypeDef structure that contains* the configuration information for SPI module.* @param pTxData pointer to transmission data buffer* @param pRxData pointer to reception data buffer* @param Size amount of data to be sent and received* @param Timeout Timeout duration* @retval HAL status*/HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout){uint16_t initial_TxXferCount;uint32_t tmp_mode;HAL_SPI_StateTypeDef tmp_state;uint32_t tickstart;/* Variable used to alternate Rx and Tx during transfer */uint32_t txallowed = 1U;HAL_StatusTypeDef errorcode = HAL_OK;/* Check Direction parameter */assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction));/* Process Locked */__HAL_LOCK(hspi);/* Init tickstart for timeout management*/tickstart = HAL_GetTick();/* Init temporary variables */tmp_state = hspi->State;tmp_mode = hspi->Init.Mode;initial_TxXferCount = Size;if (!((tmp_state == HAL_SPI_STATE_READY) || \((tmp_mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (tmp_state == HAL_SPI_STATE_BUSY_RX)))){errorcode = HAL_BUSY;goto error;}if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)){errorcode = HAL_ERROR;goto error;}/* Don't overwrite in case of HAL_SPI_STATE_BUSY_RX */if (hspi->State != HAL_SPI_STATE_BUSY_RX){hspi->State = HAL_SPI_STATE_BUSY_TX_RX;}/* Set the transaction information */hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->pRxBuffPtr = (uint8_t *)pRxData;hspi->RxXferCount = Size;hspi->RxXferSize = Size;hspi->pTxBuffPtr = (uint8_t *)pTxData;hspi->TxXferCount = Size;hspi->TxXferSize = Size;/*Init field not used in handle to zero */hspi->RxISR = NULL;hspi->TxISR = NULL;#if (USE_SPI_CRC != 0U)/* Reset CRC Calculation */if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE){SPI_RESET_CRC(hspi);}#endif /* USE_SPI_CRC *//* Check if the SPI is already enabled */if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}/* Transmit and Receive data in 16 Bit mode */if (hspi->Init.DataSize == SPI_DATASIZE_16BIT){if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U)){hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint16_t);hspi->TxXferCount--;}while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)){/* Check TXE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U)){hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint16_t);hspi->TxXferCount--;/* Next Data is a reception (Rx). Tx not allowed */txallowed = 0U;#if (USE_SPI_CRC != 0U)/* Enable CRC Transmission */if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)){SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);}#endif /* USE_SPI_CRC */}/* Check RXNE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U)){*((uint16_t *)hspi->pRxBuffPtr) = (uint16_t)hspi->Instance->DR;hspi->pRxBuffPtr += sizeof(uint16_t);hspi->RxXferCount--;/* Next Data is a Transmission (Tx). Tx is allowed */txallowed = 1U;}if (((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)){errorcode = HAL_TIMEOUT;goto error;}}}/* Transmit and Receive data in 8 Bit mode */else{if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U)){*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint8_t);hspi->TxXferCount--;}while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)){/* Check TXE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U)){*(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr++;hspi->TxXferCount--;/* Next Data is a reception (Rx). Tx not allowed */txallowed = 0U;#if (USE_SPI_CRC != 0U)/* Enable CRC Transmission */if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)){SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);}#endif /* USE_SPI_CRC */}/* Wait until RXNE flag is reset */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U)){(*(uint8_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;hspi->pRxBuffPtr++;hspi->RxXferCount--;/* Next Data is a Transmission (Tx). Tx is allowed */txallowed = 1U;}if ((((HAL_GetTick() - tickstart) >= Timeout) && ((Timeout != HAL_MAX_DELAY))) || (Timeout == 0U)){errorcode = HAL_TIMEOUT;goto error;}}}#if (USE_SPI_CRC != 0U)/* Read CRC from DR to close CRC calculation process */if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE){/* Wait until TXE flag */if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, SET, Timeout, tickstart) != HAL_OK){/* Error on the CRC reception */SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC);errorcode = HAL_TIMEOUT;goto error;}/* Read CRC */READ_REG(hspi->Instance->DR);}/* Check if CRC error occurred */if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)){SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC);/* Clear CRC Flag */__HAL_SPI_CLEAR_CRCERRFLAG(hspi);errorcode = HAL_ERROR;}#endif /* USE_SPI_CRC *//* Check the end of the transaction */if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK){errorcode = HAL_ERROR;hspi->ErrorCode = HAL_SPI_ERROR_FLAG;goto error;}/* Clear overrun flag in 2 Lines communication mode because received is not read */if (hspi->Init.Direction == SPI_DIRECTION_2LINES){__HAL_SPI_CLEAR_OVRFLAG(hspi);}error :hspi->State = HAL_SPI_STATE_READY;__HAL_UNLOCK(hspi);return errorcode;}

author: eutectique
author_rank: Frequent Contributor
timestamp: November 02, 2022, 11:42:13 am
content: As I get it, you are using HAL and non-HAL versions to access the same SPI resource. HAL version has__HAL_LOCK(hspi), while non-HAL doesn't.Could it be possible that two processes access SPI3 concurrently?The difference between two "identical" systems could be slight timing variations.

author: peter-h
author_rank: Super Contributor
timestamp: November 02, 2022, 12:16:13 pm
content: The HAL_LOCK feature doesn't actually do anything useful. It is brain-dead code. One has to do that sort of thing properly i.e. with mutexes, and that is done.SPI3 is shared among a number of devices, and is mutexed. But this is nothing new; this system (shared SPI3) has been running for ages. I even have different initialisation possible for each device (it keeps track of the "current device" and re-initialises if changed).This issue is more weird, because we have the DMA enable bit getting cleared before NDTR reached 0, which should be impossible if feeding aMasterSPI channel. If you use DMA to move RAM -> SPI, the SPI does the DMA request for each transfer (8 or 16 bits, as configured), and an SPI cannot possibly (??) not do that transfer all the time it is enabled... It is like setting up a DMA-fed UART. It should just keep going until the DMA transfer count = 0.I am wondering whether my understanding of the 32F4 DMA controller is incomplete and there are actuallysharedregisters. I know streams cannot be shared unless you do mutual-exclusive coding of some sort (they cannot run concurrently; a "stream" is just a system of multiple "data request" connections to the same DMA controller). This is my current DMA usageStream 6 is not actually used but since I am feeding a 32 bit value to DAC1, this comes out of both DAC1 & DAC2. I don't think this is relevant.FWIW, because the ADS1118 only ever gets 16 bits and concurrently returns 16 bits, I stripped down the HAL SPI function to just thisCode:// Version of HAL_SPI_TransmitReceive but dedicated to ADS1118 usage.// Transmits one 16 bit word and receives one 16 bit word back.// Created to avoid the bloated HAL_SPI_TransmitReceive() function but still supporting 16 bit mode.// SPI3 must already be initialised appropriately e.g. clock speed, 16 bit mode.void SPI3_TransmitReceive_16(uint16_t ValueIn, uint16_t * ValueOut){// enable SPI3SPI3->CR1 |= SPI_CR1_SPE;// Transmit the 16 bit wordSPI3->DR = (uint32_t) ValueIn;// Wait until a 16 bit word comes back. This cannot hang if the SPI is a Master. You always get back what you sent.while (true){if ( (SPI3->SR & 1) != 0 )// if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) etc{*ValueOut = (uint16_t) SPI3->DR;break;}}// Clear any rx data and the overrun flag in case not all received data was read.// A small delay is needed because the very last transfer may not propagate to DR.hang_around_us(1);SPI3->DR;SPI3->DR;SPI3->SR;}The code is running fine but it's been only an hour or two.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 02, 2022, 02:20:35 pm
content: I recall a bug when using more than 2 dmas concurrently, not sure what models were affected.

author: peter-h
author_rank: Super Contributor
timestamp: November 02, 2022, 02:29:01 pm
content: I vaguely recall that also, but I am using only DMA1. DMA2 is not currently used.But streams 0 3 4 5 and 7 on channel 0 are active potentially concurrently as highlighted in yellow above.Page 16 herehttps://www.st.com/content/ccc/resource/technical/document/errata_sheet/0a/98/58/84/86/b6/47/a2/DM00037591.pdf/files/DM00037591.pdf/jcr:content/translations/en.DM00037591.pdfNo idea how to create a shirt clickable link.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 02, 2022, 02:39:17 pm
content: Page 16hereJust:Code:Page 16 [url=url_link]hereCode:Page 16 [url=https://www.st.com/content/ccc/resource/technical/document/errata_sheet/0a/98/58/84/86/b6/47/a2/DM00037591.pdf/files/DM00037591.pdf/jcr:content/translations/en.DM00037591.pdf]here

author: peter-h
author_rank: Super Contributor
timestamp: November 02, 2022, 02:47:28 pm
content: Page 16hereGot it, but how weird.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 02, 2022, 02:57:39 pm
content: A lot of forums are migrating to Markdown syntax.I got angry at first (Why reinvent the wheel, need to relearn everything again, etc) but I must admit I ended liking it.

author: eutectique
author_rank: Frequent Contributor
timestamp: November 02, 2022, 03:38:24 pm
content: Quote from: peter-h on November 02, 2022, 02:47:28 pmAnd evenbetter, directing straight to the chapter in question.

author: voltsandjolts
author_rank: Unknown
timestamp: November 02, 2022, 04:04:47 pm
content: Quote from: eutectique on November 02, 2022, 03:38:24 pmCool, how did you generate that link?

author: eutectique
author_rank: Frequent Contributor
timestamp: November 02, 2022, 04:44:33 pm
content: Quote from: voltsandjolts on November 02, 2022, 04:04:47 pmFollowed the original link --> scrolled to page 2 "Contents" --> right-clicked on "2.1.10 DMA2 ..." --> "Copy link" in the context menu --> pasted it here.Apparently, this will work only if the link in the table of contents exists.

author: peter-h
author_rank: Super Contributor
timestamp: November 02, 2022, 06:37:08 pm
content: Any input on the topic?

author: mikerj
author_rank: Super Contributor
timestamp: November 02, 2022, 08:28:54 pm
content: What are you seeing in the DMA status register when this fails? From the reference manual:If the TEIFx or the FEIFx flag is set due to incompatibility between burst size and FIFO threshold level, the faulty stream is automatically disabled through a hardware clear of its EN bit in the corresponding stream configuration register (DMA_SxCR).

author: peter-h
author_rank: Super Contributor
timestamp: November 02, 2022, 08:47:54 pm
content: That's really interesting - a method whereby the DMA could clear the EN bit.The wording in the RM isIf the TEIFx or the FEIFx flag is set due to incompatibility between burst size and FIFOthreshold level, the faulty stream is automatically disabled through a hardware clear of itsEN bit in the corresponding stream configuration register (DMA_SxCR).I haveCode:DMA1_Stream7->CR = 0 << 25// CHSEL: ch 0| 0 << 23// MBURST: memory burst - single transfer| 0 << 21// PBURST: peripheral burst - single transfer| 0 << 16// PL: priority low| 0 << 15// PINCOS: no peripheral address increment offset| 0 << 13// MSIZE: memory data size: byte| 0 << 11// PSIZE: peripheral data size: byte| 1 << 10// MINC: memory address increment: 1| 0 << 9// PINC: peripheral address increment: 0| 0 << 8// CIRC: no circular mode| 1 << 6// DIR: memory to peripheral| 0 << 5// PFCTRL: DMA is flow controller| 1 << 0;// EN: enable streamand this works, so how can there be an error sometimes?The EN bit must have been set initially for NDTR to go down from 7 to 6.Unfortunately, having rewritten the huge chunk of ST HAL SPI code to just the bare minimum (further above) I have not been able to make it go wrong. The interface to this DAC is just single 16 bit words so I have the very minimal function now.Nothing else should be using the same stream.Also in the RM:In direct mode (when the DMDIS value in the DMA_SxFCR register is ‘0’), the thresholdlevel of the FIFO is not used: after each single data transfer from the peripheral to the FIFO,the corresponding data are immediately drained and stored into the destination.and I am using direct modeCode:DMA1_Stream7->PAR = (uint32_t) &(SPI3->DR);// peripheral addressDMA1_Stream7->FCR = 0;// direct mode

author: wek
author_rank: Frequent Contributor
timestamp: November 02, 2022, 11:06:06 pm
content: You haven't answered this:Quote from: mikerj on November 02, 2022, 08:28:54 pmAlso, what's in the memory address register of said stream at that moment?JW

author: peter-h
author_rank: Super Contributor
timestamp: November 03, 2022, 07:40:23 am
content: After removing HAL_SPI_TransmitReceive (ADS1118 code; uses 16 bit SPI mode andpolleddata transfer) the code runs 100% reliably, so to make it fail I put in that crappy code back, and got this from breaking the hanging code. This break is inDMAcode feeding a STLED316 chip which is fed with 7 bytes and whose NDTR was stuck at 6 as mentioned previously.Hard to see how the polled HAL_SPI_TransmitReceive, feeding a single 16 bit word to SPI3, can bugger up the DMA channel feeding seven bytes to another chip, with the SPI activity being mutexed, but here we are... Maybe HAL_SPI_TransmitReceive is doing some funny SPI config which causes the SPI to do just 1 DMA request?HAL_SPI_TransmitReceive is a load of junk and I have gradually been binning these HAL functions over the last 2+ years. I moved all instances of HAL_SPI_TransmitReceive to using the above-postedDMAcode which runs solidly, with about 10 different devices. But for the ADS1118 I am using 16 bit SPI mode so I am not currently using DMA and just use a simple polled functionCode:// Version of HAL_SPI_TransmitReceive but dedicated to ADS1118 usage.// Transmits one 16 bit word and receives one 16 bit word back.// Created to avoid the bloated HAL_SPI_TransmitReceive() function but still supporting 16 bit mode.// SPI3 must already be initialised appropriately e.g. clock speed, 16 bit mode.void SPI3_TransmitReceive_16(uint16_t ValueIn, uint16_t * ValueOut){// enable SPI3SPI3->CR1 |= SPI_CR1_SPE;// Transmit the 16 bit wordSPI3->DR = (uint32_t) ValueIn;// Wait until RXNE=1 and get the 16 bit value outwhile ( (SPI3->SR & 1) == 0 ) {}*ValueOut = (uint16_t) SPI3->DR;// Clear any rx data and the overrun flag in case not all received data was read.// A small delay is needed because the very last transfer may not propagate to DR.hang_around_us(1);SPI3->DR;SPI3->DR;SPI3->SR;}Anyway, the registers:M0AR = 0x20007401 (not in CCM; looks right)PAR = 0x40003c0c (SPI3 DR)FCR = 100000CR = 110000000000000001NDTR = 6 (initial load value = 7 so DMA did start and moved just 1 byte)LIFCR = HIFCR = 0No idea how to find any SR; there is no DMA1_Stream0->SR

author: mikerj
author_rank: Super Contributor
timestamp: November 03, 2022, 12:58:36 pm
content: Quote from: peter-h on November 03, 2022, 07:40:23 amDMA_LISR or DMA_HISR holds the TEIFx and FEIFx flags, depending on the stream in use.

author: wek
author_rank: Frequent Contributor
timestamp: November 03, 2022, 01:17:30 pm
content: OK so is the stream enabled or disabled?You are using two DMA streams so post content of registers for both. And the SPI registers, too. Screenshots from the IDE are OK. Also, what do you observe on the signals?Quote from: mikerj... content of these (both) too.JW

author: peter-h
author_rank: Super Contributor
timestamp: November 03, 2022, 04:20:06 pm
content: I can't get why NDTR is not the same for the two streams.I haven't worked out how to make Cube show all values in hex or binary.QuoteFeeding the SPI is obviously on-chip. I could monitor the ADS1118 and STLED316 signals but that takes a lot of setting up and it is much much faster to sidestep this problem by not using HAL_SPI_TransmitReceive().If anyone can see any hints in the config, I will happily try a few things. Thank you!My money is on HAL_SPI_TransmitReceive() having some side effect, because I stopped using it about a year ago (switched all SPI stuff to DMA) and this problem turned up only when I dug up the ADS1118 code after not using it for a couple of years, and this code uses HAL_SPI_TransmitReceive() (which nothing else uses) to send and receive a single 16 bit word.Someone else did the ADS1118 code (he used the HAL funcs for most things) and it took him a good number of days to get the ADS1118 to work. It is an unusual device. I tried just now to use my DMA code for it but failed and decided to do something more useful, then replaced HAL_SPI_TransmitReceive() with my trivial 16 bit polled SPI function (above).

author: wek
author_rank: Frequent Contributor
timestamp: November 03, 2022, 04:28:25 pm
content: OMG how can you set values display to decimal? Do you expect us to convert? OK now I see you've already wrote about it... still, how can you work with it like that? Went through those numbers and can't see anything obviously wrong, sorry.JW

author: peter-h
author_rank: Super Contributor
timestamp: November 03, 2022, 04:35:33 pm
content: I have to click on them one at a time to see other formats.The others are "mouseover" popups and I can't see where the config for the number formats is. Sorry!

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 03, 2022, 04:47:13 pm
content: Use SFR view, you'll see the bitfields organization, with names and descriptions.

author: peter-h
author_rank: Super Contributor
timestamp: November 03, 2022, 05:07:02 pm
content: OK; just found that.

author: wek
author_rank: Frequent Contributor
timestamp: November 03, 2022, 07:04:08 pm
content: So, if you use SPI3 also elsewhere, after you've finished with it and before setting it up for DMA, do you disable it by clearing SPI3_CR1.SPE?JW

author: peter-h
author_rank: Super Contributor
timestamp: November 03, 2022, 07:30:54 pm
content: This is the latter part of my DMA code, and SPI gets disabled via CR1 and CR2Code:// Config SPI3 to let DMA handle the data. These need to be cleared when transfer complete!// This starts the transferSPI3->CR2 |= 3;// TXDMAEN, RXDMAEN: 11 - both set in one goSPI3->CR1 |= (1<<6);// SPE=1 enable SPI// Wait for DMA to finish. Blocking is necessary to prevent device CS=1 too early.// There could be a timeout here but a failure is impossible short of duff silicon, because// we are a Master and generating the SPI clock.while(true){// Either end-transfer detection method below works but the NDTR one may be more reliable#if 1uint16_t temp1;temp1 = DMA1_Stream0->NDTR;if ( temp1 == 0 ) break;// transfer count = 0#elseuint32_t temp2;temp2 = DMA1->LISR;if ( (temp2 & (1<<5)) !=0 ) break;// TCIF0#endifif (yield) osDelay(1);// release to RTOS (see notes in comments)}SPI3->CR2 &= ~3;// TXDMAEN, RXDMAEN: 00 - both cleared in one go// Clear int pending flags. They get cleared at the top of this function anyway, but...DMA1->LIFCR = (0x03d << 0);// clear int flags & transfer complete - 111101 stream 0DMA1->HIFCR = (0x03d << 22);// clear int flags & transfer complete - 111101 stream 7// Clear any rx data and the overrun flag in case not all received data was readSPI3->CR1 &= ~(1<<6);// SPE=0 disable SPI//hang_around_us(1);SPI3->DR;SPI3->DR;SPI3->SR;return (true);}However AFAICS HAL_SPI_TransmitReceive() does not disable SPI, does it? And neither does my short function, so I need to do that as shown:Code:// Version of M_HAL_SPI2_TransmitReceive but dedicated to ADS1118 usage.// Transmits one 16 bit word and receives one 16 bit word back.// Created to avoid the bloated HAL_SPI_TransmitReceive() function but still supporting 16 bit mode.// SPI3 must already be initialised appropriately e.g. clock speed, 16 bit mode.void SPI3_TransmitReceive_16(uint16_t ValueIn, uint16_t * ValueOut){// enable SPI3SPI3->CR1 |= SPI_CR1_SPE;// Transmit the 16 bit wordSPI3->DR = (uint32_t) ValueIn;// Wait until RXNE=1 and get the 16 bit value outwhile ( (SPI3->SR & 1) == 0 ) {}*ValueOut = (uint16_t) SPI3->DR;// Clear any rx data and the overrun flag in case not all received data was read.// A small delay is needed because the very last transfer may not propagate to DR.hang_around_us(1);SPI3->DR;SPI3->DR;SPI3->SR;SPI3->CR1 &= ~SPI_CR1_SPE; // disable SPI3}And my DMA-SPI function does not disable SPI3 prior to loading up the DMA. Probably a good idea. But can one config an SPI channel with it disabled? I know one can't do so with the clock disabled.

author: wek
author_rank: Frequent Contributor
timestamp: November 03, 2022, 07:49:01 pm
content: Yes, you normally configure SPI with SPE being zero. The clock for SPI as a module comes from RCC so it has to be enabled there, but it probably is all the time.SPE enables SPI's internal state machine, i.e. it starts to transmit, receive etc. At SPE down the internal counters are reset. If you change e.g. CPOL/CPHA or number of bits while SPE is not clear, the results may be surprising (guess how do I know).JW

author: peter-h
author_rank: Super Contributor
timestamp: November 03, 2022, 09:46:10 pm
content: I wonder if anyone has been able to find anything in those register bits. Sorry I couldn't get them in binaryAll I can think of is that HAL SPI function exits with SPIenabled, and I wasn't disabling SPI prior to using SPI. However the function whichinitialises (configures)SPI (for each different device, if there has been a change) does disable SPI and leaves it disabled:Code:*/void KDE_spi3_set_mode(uint8_t mode){// SPI configurationswitch (mode) {case KDE_SPI_MODE_STLED316:// options *** below work but should notm_spi.Init.Direction = SPI_DIRECTION_2LINES;m_spi.Init.DataSize = SPI_DATASIZE_8BIT;//m_spi.Init.CLKPolarity = SPI_POLARITY_HIGH;// ****m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;// ***//m_spi.Init.CLKPhase = SPI_PHASE_2EDGE;// ****m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;// ***m_spi.Init.NSS = SPI_NSS_SOFT;m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;// 650kHz (max 1MHz)m_spi.Init.FirstBit = SPI_FIRSTBIT_LSB;// LSB first - unusualm_spi.Init.CRCPolynomial = 7;m_spi.Init.Mode = SPI_MODE_MASTER;m_spi.Instance = STLED316S_SPI;break;case KDE_SPI_MODE_ADS1118:m_spi.Init.Direction = SPI_DIRECTION_2LINES;m_spi.Init.DataSize = SPI_DATASIZE_16BIT;m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;// correct per data sheetm_spi.Init.CLKPhase = SPI_PHASE_2EDGE;// likewise - sample on -ve ck edgem_spi.Init.NSS = SPI_NSS_SOFT;m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;// 2.6MHz (max 4MHz for ADS1118)m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;m_spi.Init.CRCPolynomial = 7;m_spi.Init.Mode = SPI_MODE_MASTER;m_spi.Instance = ADS1118_SPI;break;case KDE_SPI_MODE_MCP3550:m_spi.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;m_spi.Init.DataSize = SPI_DATASIZE_8BIT;m_spi.Init.CLKPolarity = SPI_POLARITY_HIGH;//m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;// ***m_spi.Init.CLKPhase = SPI_PHASE_2EDGE;//m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;// ***m_spi.Init.NSS = SPI_NSS_SOFT;m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;// 2.6MHz (max 5MHz for MCP3550)m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;m_spi.Init.CRCPolynomial = 7;m_spi.Init.Mode = SPI_MODE_MASTER;m_spi.Instance = MCP3550_SPI;break;case KDE_SPI_MODE_HI3593:m_spi.Init.Direction = SPI_DIRECTION_2LINES;m_spi.Init.DataSize = SPI_DATASIZE_8BIT;m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;// This is actually correct per data sheetm_spi.Init.CLKPhase = SPI_PHASE_1EDGE;// and so is thism_spi.Init.NSS = SPI_NSS_SOFT;m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;// 5.2MHz (max 10MHz for HI3593)//m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;// 10.5MHz (max 10MHz for HI3593)m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;m_spi.Init.CRCPolynomial = 7;m_spi.Init.Mode = SPI_MODE_MASTER;m_spi.Instance = HI3593_SPI;break;case KDE_SPI_MODE_NEO_M9N:m_spi.Init.Direction = SPI_DIRECTION_2LINES;m_spi.Init.DataSize = SPI_DATASIZE_8BIT;m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;m_spi.Init.NSS = SPI_NSS_SOFT;m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;// 64: 650kHz (max 1MHz for 125kbyte/sec max data rate)m_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;m_spi.Init.CRCPolynomial = 7;m_spi.Init.Mode = SPI_MODE_MASTER;m_spi.Instance = NEO_M9N_SPI;break;case KDE_SPI_MODE_LY68L6400:m_spi.Init.Direction = SPI_DIRECTION_2LINES;m_spi.Init.DataSize = SPI_DATASIZE_8BIT;m_spi.Init.CLKPolarity = SPI_POLARITY_LOW;m_spi.Init.CLKPhase = SPI_PHASE_1EDGE;m_spi.Init.NSS = SPI_NSS_SOFT;m_spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;// 2: 21MHz - max possible on KDEm_spi.Init.FirstBit = SPI_FIRSTBIT_MSB;m_spi.Init.CRCPolynomial = 7;m_spi.Init.Mode = SPI_MODE_MASTER;m_spi.Instance = LY68L6400_SPI;break;default:break;}HAL_SPI_Init(&m_spi);}Code:/*** @brief Initialize the SPI according to the specified parameters* in the SPI_InitTypeDef and initialize the associated handle.* @param hspi pointer to a SPI_HandleTypeDef structure that contains* the configuration information for SPI module.* @retval HAL status*/HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi){/* Check the SPI handle allocation */if (hspi == NULL){return HAL_ERROR;}hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;if (hspi->State == HAL_SPI_STATE_RESET){/* Init the low level hardware : GPIO, CLOCK, NVIC... */HAL_SPI_MspInit(hspi);}/* Disable the selected SPI peripheral */__HAL_SPI_DISABLE(hspi);/*----------------------- SPIx CR1 & CR2 Configuration ---------------------*//* Configure : SPI Mode, Communication Mode, Data size, Clock polarity and phase, NSS management,Communication speed, First bit and CRC calculation state */WRITE_REG(hspi->Instance->CR1, (hspi->Init.Mode | hspi->Init.Direction | hspi->Init.DataSize |hspi->Init.CLKPolarity | hspi->Init.CLKPhase | (hspi->Init.NSS & SPI_CR1_SSM) |hspi->Init.BaudRatePrescaler | hspi->Init.FirstBit | hspi->Init.CRCCalculation));/* Configure : NSS management, TI Mode */WRITE_REG(hspi->Instance->CR2, (((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE) | hspi->Init.TIMode));#if defined(SPI_I2SCFGR_I2SMOD)/* Activate the SPI mode (Make sure that I2SMOD bit in I2SCFGR register is reset) */CLEAR_BIT(hspi->Instance->I2SCFGR, SPI_I2SCFGR_I2SMOD);#endif /* SPI_I2SCFGR_I2SMOD */hspi->ErrorCode = HAL_SPI_ERROR_NONE;return HAL_OK;}I think it is right to always leave SPI disabled. And obviously it needs to be enabled before any usage. And it should be disabled before anyconfigurationchange. I am doing all that now.