2020-08-18 03:33 PM
Hi,
I am using 2 DMA transfers (DMA2) in the following configuration. The basic project is generated with cubeMX. Mostly clocking and USB parts are done in cubeMX, everything else is done "by hand".
The first DMA2 transfer uses stream0 and is transferring data from SPI to my buffer and that is done 8 times for a 16 bit value. This transfer is repeated every time new data is available. You can also look at this as one 128 bit data burst with clock period of 4 MHz. The burst period is about 64 us so there is about 50% dead time once the data transfer is completed (32 us to transfer the data and 32 us when no transfer is made). This part is working fine and as expected so no problem here.
The second DMA2 transfer uses stream1 in memory to memory mode. It is triggered when the first DMA transfer is complete inside stream0 interrupt handler. In addition I use an external trigger (command string sent over USB) to set a flag. Only when the flag is set the DMA transfer is actually enabled and in turn started from the stream0 interrupt handler (transfer complete) by enabling the stream1 DMA.
Once the second (strream1) DMA transfer is done the DMA is disabled in the stream1 interrupt handler (transfer complete).
Looking at the array values when debugging or if I send them over USB the second array has data shifted by 1 bit to left.
For example in [0] of the first array (stream0) the data is 0x100C which is the expected value. And in [0] of the second array (stream1) the data is 0x2019. This also means that the bit 0 is actually bit 15 from [1].
What is going on here and how can I solve the problem?
I also noticed that if I disable the first DMA transfer (stream0) once the transfer is done (inside stream0 interrupt handler) the data is also shifted left for 1 bit.
Again I am confused what is going on here.
This is the initialisation for the DMA2 stream0 part:
DMA2_Stream0->CR |= (4UL << 25)|(3UL << 16)|(1UL << 13)|(1UL << 11)|(1UL << 10)|(1UL << 8)|(1UL << 4);
DMA2_Stream0->NDTR = 8;
DMA2_Stream0->PAR = (uint32_t)(&(SPI4->DR));
DMA2_Stream0->M0AR = (uint32_t)adcData;
DMA2->LIFCR |= DMA_LIFCR_CTCIF0;
NVIC_SetPriority(DMA2_Stream0_IRQn, 0);
NVIC_ClearPendingIRQ(DMA2_Stream0_IRQn);
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
DMA2_Stream0->CR |= (1UL << 0);
This is the initialisation for the DMA2 stream1 part:
DMA2_Stream1->CR |= (3UL << 16)|(1UL << 13)|(1UL << 11)|(1UL << 10)|(1UL << 9)|(1UL << 8)|(2UL << 6)|(1UL << 4);
DMA2_Stream1->NDTR = 8;
DMA2_Stream1->PAR = (uint32_t)adcData;
DMA2_Stream1->M0AR = (uint32_t)adcDataBuffered;
DMA2->LIFCR |= DMA_LIFCR_CTCIF1;
NVIC_SetPriority(DMA2_Stream1_IRQn, 0);
NVIC_ClearPendingIRQ(DMA2_Stream1_IRQn);
NVIC_EnableIRQ(DMA2_Stream1_IRQn);
And I enable/disable the DMA2 stream1 inside the interrupt handlers like:
DMA2_Stream1->CR |= (1UL << 0);
DMA2_Stream1->CR &= ~(1UL << 0);
Hopefully someone will help me resolve both problems.
2020-08-18 06:17 PM
You're probably getting an extra clock at the beginning due to the CLK line cycling during initialization and/or incorrect clock settings. Ensure your CPHA and CPOL values match what is expected. Ensure the CLK link is initialized (So SPI enabled) prior to pulling CS low.
A logic analyzer would show you exactly what's going on with the line.
2020-08-19 01:33 AM
I have the LA connected and that is not the problem. As I already wrote the SPI->memory transfer is working fine except when I disable the DMA.
Your reply doesn't really take in to account the memory to memory transfer bit shift which is the important problem I am trying to solve. If I am also able to solve the problem when disabling the stream0 transfer that would be helpful but in general stream0 is always running.
Here are the interrupt handlers:
void EXTI1_IRQHandler(void){
//SPI4->DR;
SPI4->CR1 = 0x0641; //Clear SSI; = is faster than |=
EXTI->PR = (1UL << 1);
NVIC_ClearPendingIRQ(EXTI1_IRQn);
}
void DMA2_Stream0_IRQHandler(void){
DMA2->LIFCR |= DMA_LIFCR_CTCIF0;
SPI4->CR1 |= (1UL << 8);
spiDummy = SPI4->DR;
spiDummy = SPI4->DR;
if(measure == 1){
DMA2_Stream1->CR |= (1UL << 0);
}
NVIC_ClearPendingIRQ(DMA2_Stream0_IRQn);
}
void DMA2_Stream1_IRQHandler(void){
measure = 2;
DMA2_Stream1->CR &= ~(1UL << 0);
DMA2->LIFCR |= DMA_LIFCR_CTCIF1;
NVIC_ClearPendingIRQ(DMA2_Stream1_IRQn);
}
EXTI1 is triggered with external DRDY signal to signal that data transfer will begin. This is all ok and timings are ok.
DMA2 stream0 is triggered once the DMA transfer has completed transferring 8x 16 bit values from SPI DR register to my buffer. This is also working fine and the values in the array are ok. Except when I disable the DMA inside this interrupt handler. Then all of the data is also shifted for 1 bit to the left. I would be happy to solve this but in general stream0 is always running. The measure variable is the flag I set to start the stream1 transfer.
Looking at the LA this part is executed a few cycles after the last bit has been received with SPI.
DMA2 stream1 is triggered once the DMA transfer has completed transferring 8x 16 bit values from my buffer to another buffer. The data in the destination buffer is shifted for 1 bit while the data in the source buffer is ok. This DMA stream is configured in memory to memory mode.
Looking at LA this part is executed a few cycles after the DMA stream0 transfer has completed.
If I send the data from the first buffer over USB (used in stream0) the data is also ok (unless I disable the DMA). Hovewer I can't send the data in real time so I only send every 500th measurement.
In some weird situation it could happen that the data I see during debugging and what I send it over USB for the first buffer is ok and all other measurements are also shifted and I just can't see that. That would explain why the second buffer has the data shifted and it is just a coincidence that I never write the unshifted data I see in the second buffer. But I think something else is going on here.
2020-08-19 02:03 AM
Or you sample on the wrong edge.
2020-08-19 02:22 AM
SPI should be set up correctly. CPOL = 0 and CPHA = 1.
This is the timing diagram of the ADC that is sending the data to the MCU (AD7768-4)
2020-08-19 06:28 AM
> I have the LA connected and that is not the problem.
Okay, show it. Prove the data on the SPI line is correct.
2020-08-19 08:36 AM
Well here you go. I expect that you now have a solution for my problem.
The PULSE signal is there so that I can visualise when the SPI has been enabled (using SSI) and when the DMA transfer has been completed. The first pulse is sent after the SPI has been enabled so that I don't introduce additional delay.
And here are the values:
The second buffer is empty as I didn't sent a measure command yet. Indices 0, 2, 4, 6 are the ones holding header data that is constant. And only the 8 MSB are the header data. Everything else is the ADC measurement data. So if I would combine [0] and [1] that would give me 0x100CF0A3 and 0x10 is the header indicating channel (0) and some other status bits and the 0x0C40A3 is the 24 bit measurement data. Same goes for 2,3 and 4,5 and 6,7 (this is a 4 channel ADC).
Once I send the measure command over USB this is the result:
Look only at the header data as everything else is ADC data and is not the same in the pictures as the DMA2 stream0 is running and I can't do 3 printscreens in the same time. You can see that it is shifted for 1 bit.
Now if I would also stop stream0 the top buffer would have the same value as the bottom one in the picture (so also shifted).
2020-08-19 10:32 AM
I found out that when I send my trigger over USB that introduces a delay somewhere that causes my EXTI interrupt handler to execute slower (too late). That causes the 1 bit shift. But If I sent every 500th (or 200th, 100th,... doesn't matter matter) measurement automatically there is no such delay.
And how it should be:
What could be the cause of this?
I did set my EXTI1 to highest priority from the program
NVIC_SetPriority(EXTI1_IRQn, 0);
And this is how it looks like in cubeMX for other periphery:
I am also using TCM flash interface with ART accelerator and instruction prefetch enabled.
2020-08-19 11:58 AM
> Well here you go. I expect that you now have a solution for my problem.
This sounds a little aggressive, I don't know if that was your intent. It turns out the problem was in fact due to a delay on the line and missing the first clock cycle. All the evidence suggested that, so that is why I asked you to prove it's not the case (which it was).
> What could be the cause of this?
Interrupts are not instant. On the M7 core, this is 12 cycles plus some jitter. More if you use the FPU. Even for the highest priority interrupt, it could take tens of cycles before it starts executing code within the ISR. How long is 12+ system ticks in comparison to your ADC clock rate?
2020-08-19 12:30 PM
But the problem is not the SPI. If I analyse the data with LA the data is still the same. The problem is the added delay before the EXTI1 interrupt handler executes so the SPI is enabled too late.
Without the delay (without the measure command being sent over USB) it takes about 40 clock cycles (MCU is clocked at 216 MHz) which is perfectly fine and fast enough.
And if I send the measure command over USB that is increased to about 65 clock cycles. So something is preventing the interrupt from being executed.
The SPI is clocked at 4.096 MHz so you can easily calculate the time from the images. And the EXTI is triggered on the rising edge of the DRDY signal.
I will have to check the FPU. I don't see the settings in cubeMX and there is nothing in the RM. There is information about this in the programming manual but I will need some time to understand it. However it looks like the FPU should be disabled by default, unless the cubeMX generated code enables it.
I also use strcmp() function to compare the received command. I will check if that may be the cause and do the compare character by character manually.