Author Topic: STM32, SPI With DMA  (Read 21143 times)

0 Members and 1 Guest are viewing this topic.

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 620
  • Country: tn
STM32, SPI With DMA
« on: September 08, 2014, 02:01:28 pm »
Hi everyone,
Again with STM32F, i have to interface an STM32F101 with an STM32L05 via SPI, the STM32L is configured as master , the STM32F1 as slave, using 8 bytes buffering with RX and  TX DMA. The test code is simple, master send those 8 bytes and the slave must respond by sending back those 8 bytes...For master things are good i can receive and send data no problem at all. For the slave well, it receive right data , but it only send them back right at the third read mean :

--First transmission
MOSI : 0xAA 0x01 0x07 0x01 0x01 0x01 0x01 0x01
MISO: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Normal, and as expected

--2nd transmission
MOSI : 0xAA 0x01 0x07 0x01 0x01 0x01 0x01 0x01
MISO: 0x00 0x01 0x07 0x01 0x01 0x01 0x01 0x01

0xAA is missing on the MISO

--3rd transmission
MOSI : 0xAA 0x01 0x07 0x01 0x01 0x01 0x01 0x01
MISO: 0xAA 0x01 0x07 0x01 0x01 0x01 0x01 0x01

Correct data out of the slave !!

i can't see what's seems to be the problem  |O anyone had a similar problem before, please advice.
« Last Edit: September 09, 2014, 07:23:37 am by hamdi.tn »
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 2024
  • Country: nl
Re: STM32, SPI With DMA
« Reply #1 on: September 09, 2014, 03:17:38 am »
Can't say that I've had the same problem, but more of a suggestion. Can't you change the test pattern to something more unique?  That may make it a bit easier to see what bits go where. Purely from the standpoint of debugging with your current data it's a toss up what could be going on. More unique patterns may help you reach that a-hah moment faster.

A predefined random sequence for example would already make things easier.
 

Online Rasz

  • Super Contributor
  • ***
  • Posts: 2441
  • Country: 00
    • My random blog.
Re: STM32, SPI With DMA
« Reply #2 on: September 09, 2014, 07:27:43 am »
buy Salaleaeaea, or chinese clone, or any other logic analyser 0 your life will be so much easier, no more guessing what is happening on the pins
Who logs in to gdm? Not I, said the duck.
My fireplace is on fire, but in all the wrong places.
 

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 620
  • Country: tn
Re: STM32, SPI With DMA
« Reply #3 on: September 09, 2014, 08:18:18 am »
Thanks for your reply  ^-^
I tried different test sequence, always the same missing byte, i checked data in slave Rx buffer once DMA interrupt occur, and it's correct the slave read correct data. I initialised the Slave Rx buffer with that sequence and read it and it's correct even on the first read. Am kind of new with STM32 and i did had a lot of a-hah moment for this application even unthinkable buzzard things for such easy protocol like SPI, for example master read wrong last byte and the problem was MISO input configured at low speed ( used MxCube for automatic configuration code ) , an other example is slave DMA rx interrupt occur only once, and was corrected with DMA mode circular ( i copied the first configuration from SPI DMA example given by ST library  :wtf: and it didn't work )

here the init code for SPI, will be kind if you can help me with this stupid fault , i can't say i truly understand the working mechanism of SPI in STM32 because i don't  :P so SOS :P
Code: [Select]
  #define SPI_SLAVE                    SPI2
  #define SPI_SLAVE_CLK                RCC_APB1Periph_SPI2
  #define SPI_SLAVE_GPIO               GPIOB
  #define SPI_SLAVE_GPIO_CLK           RCC_APB2Periph_GPIOB
  #define SPI_SLAVE_PIN_NSS            GPIO_Pin_12
  #define SPI_SLAVE_PIN_SCK            GPIO_Pin_13
  #define SPI_SLAVE_PIN_MISO           GPIO_Pin_14
  #define SPI_SLAVE_PIN_MOSI           GPIO_Pin_15
  #define SPI_SLAVE_DMA                DMA1
  #define SPI_SLAVE_DMA_CLK            RCC_AHBPeriph_DMA1 
  #define SPI_SLAVE_Rx_DMA_Channel     DMA1_Channel4
  #define SPI_SLAVE_Rx_DMA_FLAG        DMA1_FLAG_TC4
  #define SPI_SLAVE_Tx_DMA_Channel     DMA1_Channel5
  #define SPI_SLAVE_Tx_DMA_FLAG        DMA1_FLAG_TC5 
  #define SPI_SLAVE_DR_Base            0x4000380C

#define RXTXBufferSize 8
uint8_t SPIRx[RXTXBufferSize];
uint8_t SPITx[RXTXBufferSize]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

void SPI_LCD_Init()
{
  SPI_InitTypeDef  SPI_InitStructure;
  DMA_InitTypeDef  DMA_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
 
  RCC_AHBPeriphClockCmd(SPI_SLAVE_DMA_CLK, ENABLE);
  RCC_APB1PeriphClockCmd(SPI_SLAVE_CLK, ENABLE);
 
  GPIO_InitStructure.GPIO_Pin =  SPI_SLAVE_PIN_MISO ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(SPI_SLAVE_GPIO, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = SPI_SLAVE_PIN_SCK  | SPI_SLAVE_PIN_NSS| SPI_SLAVE_PIN_MOSI;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(SPI_SLAVE_GPIO, &GPIO_InitStructure);

  ///DMA RX
  DMA_DeInit(SPI_SLAVE_Rx_DMA_Channel);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPIRx;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize =RXTXBufferSize;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(SPI_SLAVE_Rx_DMA_Channel, &DMA_InitStructure);
  DMA_Cmd(SPI_SLAVE_Rx_DMA_Channel, ENABLE);

  DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);

  //DMA TX
  DMA_DeInit(SPI_SLAVE_Tx_DMA_Channel);
  DMA_StructInit(&DMA_InitStructure);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPITx;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = RXTXBufferSize;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(SPI_SLAVE_Tx_DMA_Channel, &DMA_InitStructure);
  DMA_Cmd(SPI_SLAVE_Tx_DMA_Channel, ENABLE);     
 
  ///SPI
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPI2, &SPI_InitStructure);
 
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 
  SPI_Cmd(SPI_SLAVE, ENABLE);
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx|SPI_I2S_DMAReq_Tx , ENABLE);
   
}

void DMA1_Channel4_IRQHandler()
{
  uint8_t i=0;
 
   if (DMA_GetITStatus(DMA1_IT_TC4))
  {
    for (i=0;i<=7;i++) {SPITx[i]=SPIRx[i];}
    DMA_ClearITPendingBit(DMA1_IT_TC4);
   
  }
}
« Last Edit: September 10, 2014, 04:27:19 pm by hamdi.tn »
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7761
  • Country: nz
Re: STM32, SPI With DMA
« Reply #4 on: September 09, 2014, 08:21:28 am »
I seem to remember a catch with the SPI FIFO buffer and DMA.
Check the datasheet for any special cases where data can be lost. I have a vague memory of something like that.

Also, in your example, is the slave output supposed to represent the previous master output?
So the missing 0xAA is the first ever byte that the slave receives when started?  If so then its probably to do with a bad init somewhere. eg, something isn't setup correctly and so misses the first byte.
« Last Edit: September 09, 2014, 08:29:36 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 620
  • Country: tn
Re: STM32, SPI With DMA
« Reply #5 on: September 09, 2014, 08:24:18 am »
buy Salaleaeaea, or chinese clone, or any other logic analyser 0 your life will be so much easier, no more guessing what is happening on the pins


i do have an oscilloscope and the byte is truly missing on MISO, the rest of the sequence is OK
but yes am thinking about getting a logic analyser, oscilloscope is not that practical in sequence data analysis ;)
 

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 620
  • Country: tn
Re: STM32, SPI With DMA
« Reply #6 on: September 09, 2014, 08:36:55 am »
I seem to remember a catch with the SPI FIFO buffer and DMA.
Check the datasheet for any special cases where data can be lost. I have a vague memory of something like that.

Also, in your example, is the slave output supposed to represent the previous master output?
So the missing 0xAA is the first ever byte that the slave receives when started?  If so then its probably to do with a bad init somewhere. eg, something isn't setup correctly and so misses the first byte.

yes 0xAA is the first ever byte that the slave receives.
and yes that's what i think too, i just can't figure out what am doing wrong, been  2 days on this and am stating to have dream about missing 0xAA  :-DD
The application use 11 ADC channel with DMA, I2C connexion , uart connexion for diagnostic purpose and an other uart with dma for RS485 connectivity , SPI for an interface (LCD and some Push Bt) and a couple of generic input / output .. stopped all that , now i just have that SPI init everything else is not used. so if anything is wrong it must be in those lines.
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7761
  • Country: nz
Re: STM32, SPI With DMA
« Reply #7 on: September 09, 2014, 08:56:12 am »
Could be a incorrect initial clock polarity/idle state.
If DMA tries to pull clock low but clock is already low then the receiver would miss it (and the same if it tries to pull high but its already high).
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 2575
  • Country: gb
Re: STM32, SPI With DMA
« Reply #8 on: September 09, 2014, 05:49:14 pm »
You need to enclose code in code blocks, so the forum doesn't interpret your code as formatting code.

e.g. your array indexes aren't visible in your original post since 'i' in square brackets is a formatting code of italics.  With code blocks, why you type is what you get:

Code: [Select]
void DMA1_Channel4_IRQHandler()
{
  uint8_t i=0;
 
   if (DMA_GetITStatus(DMA1_IT_TC4))
  {
    for (i=0;i<=7;i++) {SPITx[i]=SPIRx[i];}
    DMA_ClearITPendingBit(DMA1_IT_TC4);
   
  }
}

Are you performing the write and read as a single operation, i.e. without de-asserting and re-asserting the chip select?  If so you have a race condition here as you have less than a single SPI clock cycle to enter the interrupt handler and copy the contents of one buffer to another.  Chances are the DMA has already loaded the first byte from SPITx[] into the SPI hardware buffer before the copy operation has started.

If you slow the SPI clock rate down and it starts working, then this is likely the problem.
 

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 620
  • Country: tn
Re: STM32, SPI With DMA
« Reply #9 on: September 09, 2014, 11:13:29 pm »
  Chances are the DMA has already loaded the first byte from SPITx[] into the SPI hardware buffer before the copy operation has started.

The master send 8 bytes as a command then pull up the CS, wait a bit of time then pull it down and clock out slave response by sending 8 dummy bytes.
Man that's the only sentence i needed  :-+ coz that's what actually happen , i think since the DMA in circular mode,  it will load SPITx[0] in hardware as soon as it send the last byte before starting the copy operation, i will add a DMA half transfer interrupt to copy  the first byte and test it  ;)

an other thing i didn't understand, the slave sending was a bit unstable and work randomly mean it can stop working by adding any random command in main loop ANYTHING, on scope i can see some little pulses on MISO that correspond what it should send, they are just not high enough to be considered as logic 1. again putting output in high speed mode ( 50Mhz ) solved the problem and i don't understand how this is working  :-//

anyway thanks for the tip i give a test and let you know  :-+
 

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 620
  • Country: tn
Re: STM32, SPI With DMA
« Reply #10 on: September 10, 2014, 04:23:20 pm »
well, update,
By activating a DMA half transfer interrupt and loading only the first received byte in Tx buffer, and copy the rest of Rx buffer in Tx buffer once DMA transfer complete occur. 
Code: [Select]

void DMA1_Channel4_IRQHandler()
{
  uint8_t i=0;
 
  if(DMA_GetITStatus(DMA1_IT_HT4)) { 
  SPITx[0]=SPIRx[0];
  DMA_ClearITPendingBit(DMA1_IT_HT4);
}

if(DMA_GetITStatus(DMA1_IT_TC4)) {
  for (i=0;i<=7;i++) {SPITx[i]=SPIRx[i];}
  DMA_ClearITPendingBit(DMA1_IT_TC4);
}

}

Thanks everyone  ;) Problem solved  :phew:
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf