2022-02-16 11:20 AM
Hello,
I have a project on a Nucleo board that samples the ADC and typecasts the result.
The typecasted value and the original value are sent via UART and virtual com to my PC.
When I put this code in a single for-loop, I can receive both the raw and converted values as can be seen by the overlapping first red and green graph in the attachment. I know they have some strange scale and offset that I think is caused by converting to bytes and sending the stream to my PC that reinterprets the bytes stream.
However, as soon as I split the for-loop in two, as seen in the code-snipped below, the scaled and offset data gets corrupted while, to my surprise, the raw ADC samples are still shown correctly. This can be seen in the second red and green graph where I think that the green line represents the scaled_and_offset_buffer samples. What puzzels me the most is that the red graph is not corrupted in the second case
What could cause this behavior?
Thanks in advance.
Ruud
Code snippet below. I included the complete project in the attachment.
...
typedef struct
{
uint8_t header[2];
uint8_t size;
int32_t adc_buffer_element;
int32_t scale_and_offset_buffer_element;
}UartFrame;
...
int Main_Update() //Continiously called from main while loop
{
while(!adc_transfer_complete_flags);
adc_transfer_complete_flags = 0;
/* Scale and Offset converted ADC samples */
for(uint32_t n=0; n<ADC_BUFFER_SIZE; n++)
{
scale_and_offset_buffer[n] = adc_buffer_ptr[n];
//scale_and_offset_buffer[n] <<= ADC_BIT_SHIFTS;
//scale_and_offset_buffer[n] += ADC_OFFSET;
/* works fine in a single for-loop */
//frame.adc_buffer_element = adc_buffer_ptr[n];
//frame.scale_and_offset_buffer_element = scale_and_offset_buffer[n];
//HAL_UART_Transmit(&hlpuart1, (uint8_t*) &frame, sizeof(frame), HAL_MAX_DELAY);
}
//arm_mult_q31(scale_and_offset_buffer, nco_cos_out, mult0_out, ADC_BUFFER_SIZE);
//arm_mult_q31(scale_and_offset_buffer, nco_sin_out, mult1_out, ADC_BUFFER_SIZE);
/* scale and offset buffer gets corrupted when for-loop is split */
for(uint32_t n=0; n<ADC_BUFFER_SIZE; n++)
{
frame.adc_buffer_element = adc_buffer_ptr[n];
frame.scale_and_offset_buffer_element = scale_and_offset_buffer[n];
HAL_UART_Transmit(&hlpuart1, (uint8_t*) &frame, sizeof(frame), HAL_MAX_DELAY);
}
return 0;
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
adc_transfer_complete_flags |= ADC_FULL_TRANSFER_COMPLETE;
adc_buffer_ptr = &adc_buffer[ADC_BUFFER_SIZE];
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
adc_transfer_complete_flags |= ADC_HALF_TRANSFER_COMPLETE;
adc_buffer_ptr = &adc_buffer[0];
}
2022-02-16 11:30 AM
Are variables volatile where they need to be? Likely to alter how the optimization works, and what's removed from the loop.
The ADC buffer presumably remains active, changing the underlying buffer content and pointers around.
The UART is probably slower than the ADC acquisition.
Perhaps have some method of queuing the data, or moving it to a non-active buffer, and don't overwrite it until the loop code releases it to be refilled.
2022-02-16 01:10 PM
Hello @Tesla DeLorean,
Thanks for the response.
-switched off compiler optimization
-the scale_and_offset_buffer, the adc_buffer, and the frame struct are defined as volatile
-double buffered scale_and_offset in the same way as the adc_buffer
-sampling frequency was lowered from 200kHz to 50kHz.
-lowering the sampling frequency does have an effect, but the green waveform is still not an exact copy of the red one.
problem is still there :)
Furthermore, I'm not sure if overwriting data is the problem. In that scenario, I would also expect case 1 (single for-loop) to fail and in case 2 (split for-loop) I would expect problems with both the red and green graph because green is copied one-to-one from red, but the red one does not show any problems in both cases.
Any suggestions on how to debug these kind of problems?
2022-02-16 02:07 PM
>>Any suggestions on how to debug these kind of problems?
Look at the code the compiler generates. Single-stepping isn't an option because the data is dynamic, relentless, and at speeds beyond human reaction time.
Create an output buffer rather than send the data piece meal via a blocking UART transmit function. This way at least a whole frame is going to be consistent and not timing dependent, math would at least be less timing critical.
If you staged the data into a holding buffer in the interrupt, and only cleared the flagging variable once you've *finished* with the data you'd be able to eliminate possibilities rather than being unsure.
If you think corruption is occurring on the buffer, perhaps checksum on either side of other code you think might be effecting them. Perhaps create safety zones between the buffers in memory, and fill with a pattern you can check later.
DMA buffers should be considered volatile, as they change outside program flow.
On CM7 processors watch for cache coherency issues.
2022-02-17 04:48 AM
Amazing... that you get any "valid" data in any case.
With the ADC running at 50 kHz, but sending each sample in real-time with your 7-byte struct (by the way, I had some problems on 32-bit CPUs with these odd struct sizes) means you need to send
7 x 50 kHz x 10 bits (assuming start & stop) = 3.5 Mbit / s
Hmm, good luck with that!
So I guess no matter how you do it, if in 1 or 2 loops, you will not get any data without missing samples.
You're doing some kind of "random downsampling".
So what you should do, as Mr. TDL already mentioned, fill a BIG buffer with ADC samples, then stop sampling, then send data, when that is done, restart the ADC.
With much lower sampling rates you could send some real-time data, even more buffers wouldn't help, the UART is too slow, you're sending too much data, but well, do the math...