STM32F0 SPI 8bit communication frame (errata?) - Page 1
Forum section: electronics / general

author: hub.martin
author_rank: Contributor
timestamp: October 03, 2013, 01:49:05 pm
content: Hello,I'm porting NRF24l01+ driver library to STM32F0 Discovery kit. I'm unable to send 8bit SPI data frame. Data size bits in CR2 register are ok and set to 8bit by datasheet but I see 16 clock on line. There's errata on SPI on this chip but I'm not sure if this is the case.See attached picturesThere's also someone who found the same issue.https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2FSTM32F0%20Trying%20to%20use%20SPI2%20in%208-bit%20mode%2C%20but%20each%20transfer%20causes%2016%20clock%20cycles&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=155Thanks for any idea

author: dannyf
author_rank: Super Contributor
timestamp: October 03, 2013, 03:10:38 pm
content: You showed the set-up sequence. What's your transmission sequence?I haven't tried F0 but have used F103, with no problem. The spi peripherals are similar (identical?) so I would expect it to work.

author: AndreasF
author_rank: Frequent Contributor
timestamp: October 03, 2013, 06:42:55 pm
content: Could it be a simple bug in the library? I would check if the registers are actually set as they are supposed to be set.

author: hub.martin
author_rank: Contributor
timestamp: October 03, 2013, 06:53:28 pm
content: QuoteThat was the first thing I've checked. First post: "Data size bits in CR2 register are ok."QuoteI'm at home right now so don't have the code right now with me, but writing is as simple as write data to the SPI1->DR. I use STMF100 and STMF4 and SPI is there ok. But on F0 it has to be crippled a little (because of smaller silicon size and price), also SPI peripheral is able to switch to half-duplex I2S transmission so I guess this is a completely new block from ST.

author: andyturk
author_rank: Frequent Contributor
timestamp: October 05, 2013, 03:08:05 am
content: If you think it's actually running in 16-bit mode, put a known value in for the second byte and see if it shows up on the decode trace. It seems unlikely that they're sending an extra byte of zeros--perhaps there's an off-by-one error in the setup? Are you using DMA?

author: ealex
author_rank: Frequent Contributor
timestamp: October 05, 2013, 06:19:10 am
content: HelloI've had the same problem with the SPI on stm32f0 : the peripheral was configured in 8 bit mode but it would write 16 bits, the first 8 the actual data and the next 8 just 0.I was not using the stm peripheral. library - just the platform header file.In my case the problem was in the SPI typedef in stm32f0...h: the data type that was defined for the SPI data register is 16 bits: the compiler (gcc) will create that operation as a 16 bit write even if i casted all types to uint8My fix was to alter the header file: create a union for the data register: two uint8 over a uint16 type or redefine the data register as uint8 DRH and DRL -> the write operation will explicitly be an 8 bits one and the compiler / peripheral will not get confused: SPI_DR typedef in the code belowinstm32f0xx.hCode:typedef union {uint16_t DR;struct{unsigned char DR0;unsigned char DR1;};}SPI_DR;typedef struct{__IO uint16_t CR1; /*!< SPI Control register 1 (not used in I2S mode), Address offset: 0x00 */uint16_t RESERVED0; /*!< Reserved, 0x02 */__IO uint16_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */uint16_t RESERVED1; /*!< Reserved, 0x06 */__IO uint16_t SR; /*!< SPI Status register, Address offset: 0x08 */uint16_t RESERVED2; /*!< Reserved, 0x0A */__IO SPI_DR DR; /*!< SPI data register, Address offset: 0x0C */uint16_t RESERVED3; /*!< Reserved, 0x0E */__IO uint16_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */uint16_t RESERVED4; /*!< Reserved, 0x12 */__IO uint16_t RXCRCR; /*!< SPI Rx CRC register (not used in I2S mode), Address offset: 0x14 */uint16_t RESERVED5; /*!< Reserved, 0x16 */__IO uint16_t TXCRCR; /*!< SPI Tx CRC register (not used in I2S mode), Address offset: 0x18 */uint16_t RESERVED6; /*!< Reserved, 0x1A */__IO uint16_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */uint16_t RESERVED7; /*!< Reserved, 0x1E */__IO uint16_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */uint16_t RESERVED8; /*!< Reserved, 0x22 */} SPI_TypeDef;

author: dannyf
author_rank: Super Contributor
timestamp: October 05, 2013, 10:40:09 am
content: QuoteEven if the compiler does that, the hardware shouldn't, as you have specified the width to be 8 bits.Puzzling.

author: Harvs
author_rank: Super Contributor
timestamp: October 05, 2013, 11:04:58 am
content: Quote from: dannyf on October 05, 2013, 10:40:09 amIt's been a while since I used the F0 so my memory is patchy (and I can't look it up at the moment.) But I recall that when configured in <32bit mode the register operates as a multi-byte buffer. I.e. write a 32bit word to it while it's in 8-bit mode and it will send it as 4 single byte transfers. A 32bit word in 16bit mode will send two 16bit transfers...

author: dannyf
author_rank: Super Contributor
timestamp: October 05, 2013, 11:30:09 am
content: QuoteThose are 16-bit registers so you cannot write 32 bits to them.QuoteIf so, it must be a bug - it defeats the purpose to have the data frame definition.I doubt, however, that's the case - something else is at play here. Does the errata mention it?

author: Harvs
author_rank: Super Contributor
timestamp: October 05, 2013, 12:06:08 pm
content: Quote from: dannyf on October 05, 2013, 11:30:09 amYes you're right, as I said it's been a while, the actual register you write to is 16-bit. However, it's got a 32-bit FIFO behind it as per the attachment. That's what I was thinking of.QuoteThere is a manual, RM0091 Reference manual, that explains all this quite well (along with every other peripheral). In fact there's about three pages on using the FIFO and packing modes in the SPI.

author: dannyf
author_rank: Super Contributor
timestamp: October 05, 2013, 12:19:32 pm
content: QuoteA few pages earlier, it also described how the mcu is to behave with different frame length. That's what you need to read.If the mcu doesn't behave as described in the datasheet, it is a bug.But we have never seen the full code so we will never know.

author: ealex
author_rank: Frequent Contributor
timestamp: October 06, 2013, 04:54:49 am
content: It might be a documentation problem, but the peripheral behaves as Harvs said: in 8 bit mode an 32 or 16 bit access will load 4 or 2 bytes in the internal FIFO -> you will end up with several operation even if you configured it in 8 bit mode.The only way to make is behave as indented is to make an 8 bit write access to the FIFO registers.I've never read RM0091 - discovered the fix after loosing an entire day on the issue ...

author: dannyf
author_rank: Super Contributor
timestamp: October 06, 2013, 11:48:39 am
content: QuoteI don't know how you could have 32-bit access to the internal fifo, given that it is 16-bit.QuoteThat's very convoluted.You could have just written to a uint8_t pointer pointed to SPIx_DR if you are writing 8-bit or less data.

author: hub.martin
author_rank: Contributor
timestamp: October 06, 2013, 07:40:00 pm
content: Hello to all. Thanks for all your code and ideas.It seems that I really have to cast writing value to uint8_t. I've also found function SPI_SendData8(SPI1, data); from the library which does the same.The new code with SPI_SendData8(SPI1, data); really makes just 8 CLK pulses so it was my mistake of not understanding of how FIFO in STM32F works.I do more test tomorrow.Thanks again

author: dannyf
author_rank: Super Contributor
timestamp: October 06, 2013, 07:43:46 pm
content: QuoteThe datasheet is fairly clear about this: writing to a 16-bit SPIx_DR will send out more pulses; typcasting the data to uint8_t before assigning it to SPIx_DR doesn't solve the problem.You have to write to a (uint8_t) pointer pointed to SPIx_DR to limit the pulses. That's effectively what SPI_SendData8() does.

author: hub.martin
author_rank: Contributor
timestamp: October 07, 2013, 01:42:20 pm
content: Perfect, it is working now.This is the right writing sequence for 8 bit writes:Code:uint8_t spiSend(uint8_t data){while((SPI1->SR&SPI_I2S_FLAG_TXE)==RESET);SPI_SendData8(SPI1,data);while((SPI1->SR & SPI_I2S_FLAG_RXNE)==RESET);return SPI_ReceiveData8(SPI1);}Also I had to set RX FIFO level to quarter, so the SPI_I2S_FLAG_RXNE bit is set even when I receive just 8 bitCode:SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF);Thanks to everyone and specially to dannyf.

author: dannyf
author_rank: Super Contributor
timestamp: October 07, 2013, 06:47:40 pm
content: I am glad that it worked out for you.

author: jaakjensen
author_rank: Newbie
timestamp: July 21, 2020, 04:05:43 am
content: Hi Ealex,I just wanted to post on here to thank you very much for your suggestion about turning the DR register into a union. I had been tearing my hair out all night... I was almost at the brink of tears... until I found your advice!I was trying to port over some code to an STM32L432 that I wrote for STM32f405 but the data frame format declarations differ between the two devices. STM32f405 doesn't do the data packing that STM32l432 does.Using the code below and writing to the register using:pSPIx->DR.DR0 = *(pTxBuffer);Did the trick! Thank you so very much. You made my night <3 <3 <3