Dear Ti support,
We found a potential bug / undocumented behaviour in the ADC module in connection with the DMA module.
We have found a workaround that works for us, but hopefully you can review it or give us alternative.
First, let me explain our setup:
We are using 21 adc channel from the TMS.
17 Channel are in group 1 and will be triggered by the SW. The channels are triggered by a Task (SafeRTOS) using the function adc_TMS570LS_getADCValue (see below)
4 channel are in group 2 and are triggered as a group conversion by RTI_COMP_0.
Every group conversion should trigger a DMA request, which then copies the group buffer into RAM.
The DMA is configured as follows:
- 8 elements per frame ( = ADC group 2 fifo size)
- Element size 32bit
- Frame Transfer
The RTI_COMP_0 timer is also used as the systick interrupt for the OS. (1000hz frequency)
We have noticed, that after an undefined but certain time the G2_Thr is decreasing slowly from 8(=FifoSize = ElementCount) to 0.
When G2_Thr reaches 0, the DMA request is triggered more frequently and the values read by DMA are invalid.
We have understood that the G2_Thr decreases if we are start the conversion and it increases again, if it is not read.
We expect the following behavior:
- Every 1ms the ADC conversions are triggered
- 4 valid ADC values are written into the group2 buffer
- DMA is triggered after successfully writing the 4 ADC values into the group buffer
- DMA copies the whole group buffer (8 elements) into the configured RAM section.
- So every 1ms we should see 4 new adc values in the RAM
In our system we also use CAN-nodes. If we increase the bus load on CAN, we noticed that the value of G2_Thr decreases faster.
At the same time, the adc channel triggered by SW times out more often (check adc_TMS570LS_getADCValue).
To us it appears there are two issues:
- Collisions between manual ADC conversions triggered by the Software and automatic conversions triggered directly via RTI can happen
- Is there any Documentation on that collisions can happen? If so, how to prevent collisions?
- After a collision happens, DMA is requested too quickly
- We expect a DMA request to happen only when 4 valid ADC values have been written
In identifying the problem, we developed a workaround that works for us, but we need you to verify it.
- we set the HW-trigger source to NHET-14
- so that the trigger timing is shifted and independent of the RTI
- We check G2_Thr in our SW and if it falls below 5, then:
- Reset Fifo: adc[0]->GxFIFORESETCR[2] = 1;
- Clear Conversion Endflag and Threshold IRQ Flag, adc[0]->GxINTFLG[2] = 9;
- Set Threshold Counter to 8; adc[0]->GxINTCR[2] = 8;
- Set G2_Blocks to 8: adc[0]->G2DMACR= 0x0008000D;
- Enable Group1 Conversion End Interrupt Enable
- So the SW-triggered ADC conversions do not timeout.
Here are our code functions:
ADC_STATUS_t adc_TMS570LS_getADCValue(const ADC_SignalConfig* signal, uint16_t* adcValue)
{
ADC_STATUS_t adcStatus = ADC_FAILRUE;
ADC_DATA_t convData[22] = { 0 };
uint8_t adcPortIndex = (signal->port == ADC_PORTA) ? 0 : 1;
adc_TMS570LS_startConversion(signal); /* Aquire Conversion Data */
if((adcReg[adcPortIndex]->GxSEL[signal->group] != 0U))
{
uint16_t timeout = 0xffff;
// wait for completion
while(!adc_TMS570LS_isConversionComplete(signal) && timeout)
{
timeout--;
}
if(timeout != 0u)
{
adc_TMS570LS_getData(signal, convData);
*adcValue = convData[0].value;
adcStatus = ADC_SUCCESS;
}
else
{
adcStatus = ADC_TIMEOUT;
}
}
return adcStatus;
}
adc_TMS570LS_getData:
static uint32_t adc_TMS570LS_getData(const ADC_SignalConfig* signal, ADC_DATA_t* data)
{
uint32_t count = UINT32_MAX;
uint8_t adcPortIndex = (signal->port == ADC_PORTA) ? 0 : 1;
uint32_t buf;
uint32_t intcr_reg = adcReg[adcPortIndex]->GxINTCR[signal->group]; // polyspace RTE:NIV [Justified]
ADC_DATA_t* ptr = data;
uint32_t count = (intcr_reg >= 256U) ? FiFoSize[adcPortIndex] : (FiFoSize[adcPortIndex] – (intcr_reg & 0xFFU));
if((adcReg[(uint8_t)signal->port - 1u]->OPMODECR & 0x80000000U) > 0) // if ADC configured for 12-Bit resolution
{
/** - Get conversion data and channel/pin id */
for(uint32_t i = 0U; i < count; i++)
{
buf = adcReg[adcPortIndex]->GxBUF[signal->group].BUF0; // polyspace RTE:NIV [Justified]
ptr->value = (uint16)(buf & 0xFFFU);
ptr->id = (uint32)((buf >> 16U) & 0x1FU);
ptr++;
}
}
adcReg[adcPortIndex]->GxINTFLG[signal->group] = 9U;
return count;
}
Best Regards,
Thorben