Part Number: TM4C123GH6PM
I've spent a lot of time debugging this problem. Let me quickly explain, post source and wait for further help:
I have ADC0 and ADC1 reading samples from pins PE0 and PB5 respectively. When I step through the loop you'll find below, the ADC0 (PE0) result is exactly what I'm expecting, but during the first time through the loop I observe that 4-bits of "underflow" flags are set (ADC0 - USTAT) and all subsequent calls return a result that is approximately 0x1FF too high. This is not double the expected result, but something else.
The following diagram shows that I am expecting 0x234 as a result as hex(int((VOUT/3/3)*4096)) = 0x234. Generally, the first time though the loop, I get 0x22D - 0x236. Subsequent results are between 0x41D and 0x429 (not double the expected result). The voltage appearing at PE0 was verified with an o-scope and is as expected ( ~0.456V) - where the expected result is VOUT = VIN*(R2/(R1+R2)) = 0x4545V.
Here's a simple diagram of the resistor divider going into PE0:
---- VIN (5.0V from USB) ----
R1 = 10K
-------------------------------( VOUT ) PE0
R2 = 1K
---------GND-------------
/************************************/
/* Task */
/************************************/
Void BatteryMonitor(UArg arg0, UArg arg1)
{
uint32_t u32Voltage;
uint32_t u32Light;
Error_Block ebBatMonAccess;
int32_t debug;
// Task Hello
debugPrint("BatteryMonitor Task started\n");
/* Construct a Semaphore object to be use as a resource lock, initial count 1 */
/* Obtain instance handle */
Error_init(&ebBatMonAccess);
semBatMonAccess = Semaphore_create(1, NULL, &ebBatMonAccess);
if ( semBatMonAccess == NULL ) {
System_abort("ERROR: BatteryMonitor: Semaphore creation failed");
}
InitMonitorChannel();
debugPrint("BatteryMonitor Task init done\n");
// task loop
while ( true ) {
//
// Trigger the ADC conversion.
//
ADCProcessorTrigger(ADC0_BASE, 3); // voltage
//
// Wait until the sample sequence has completed
//
while(!ADCIntStatus(ADC0_BASE, 3, false)) {}
//
// Read ADC Value.
//
debug = ADCSequenceDataGet(ADC0_BASE, 3, &u32Voltage);
// From adc.c - ADCIntClear() description
//! \note Because there is a write buffer in the Cortex-M processor, it may
//! take several clock cycles before the interrupt source is actually cleared.
ADCIntClear(ADC0_BASE, 3);
debugPrint("INFO:BatteryMonitor:ADCSequenceDataGet()=%d, u32Voltage = 0x%x\n", debug, u32Voltage);
// mutex-like wait. Don't access if others are accessing
Semaphore_pend(semBatMonAccess, BIOS_WAIT_FOREVER);
adcVoltage = (uint16_t)(u32Voltage & 0xFFFF);
Semaphore_post(semBatMonAccess);
//
// Trigger the ADC conversion.
//
ADCProcessorTrigger(ADC1_BASE, 0); // current
//
// Wait until the sample sequence has completed
//
while(!ADCIntStatus(ADC1_BASE, 0, false)) {}
//
// Read ADC Value.
//
ADCSequenceDataGet(ADC1_BASE, 0, &u32Light);
// From adc.c - ADCIntClear() description
//! \note Because there is a write buffer in the Cortex-M processor, it may
//! take several clock cycles before the interrupt source is actually cleared.
ADCIntClear(ADC1_BASE, 0);
debugPrint("INFO:BatteryMonitor: u32Light = 0x%x\n", u32Light);
// mutex-like wait. Don't access if others are accessing
Semaphore_pend(semBatMonAccess, BIOS_WAIT_FOREVER);
adcLight = (uint16_t)(u32Light & 0xFFFF);
Semaphore_post(semBatMonAccess);
Task_sleep(ADC_SLEEP);
}
}
/*void ADC0_ISR(void)
{
ADC0_ISC_R |= 0x01; //write one to clear the interrupt
}
void ADC1_ISR(void)
{
ADC1_ISC_R |= 0x01; //write one to clear the interrupt
}*/
/************************************/
/* Helper Functions */
/************************************/
static void InitMonitorChannel(void)
{
uint32_t debug;
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); // This is for battery monitoring
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)) {}
ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
GPIOPinTypeGPIOInput(GPIO_PORTE_BASE, GPIO_PIN_0);
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0); // Battery Voltage Monitoring
ADCReferenceSet(ADC0_BASE, ADC_REF_INT);
ADCSequenceDisable(ADC0_BASE, 3); // voltage monitor
ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0); // voltage monitor on sequence 3
ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH3); // voltage monitor
ADCSequenceEnable(ADC0_BASE, 3); // voltage monitor
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1)) {}
ADCClockConfigSet(ADC1_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
GPIOPinTypeGPIOInput(GPIO_PORTB_BASE, GPIO_PIN_5);
GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_5); // Current Monitoring
ADCReferenceSet(ADC1_BASE, ADC_REF_INT);
ADCSequenceDisable(ADC1_BASE, 0); // voltage monitor
ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_PROCESSOR, 0); // current monitor on sequence 4
ADCSequenceStepConfigure(ADC1_BASE, 0, 0, ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH11); // current monitor
ADCSequenceEnable(ADC1_BASE, 0); // current monitor
// debug:
debug = ADCReferenceGet(ADC0_BASE);
switch (debug) {
case ADC_REF_INT:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC0_BASE) = ADC_REF_INT\n");
break;
case ADC_REF_EXT_3V:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC0_BASE) = ADC_REF_EXT_3V\n");
break;
case ADC_REF_EXT_1V:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC0_BASE) = ADC_REF_EXT_1V\n");
break;
default:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC0_BASE) = super ****ed\n");
break;
}
debug = ADCReferenceGet(ADC1_BASE);
switch (debug) {
case ADC_REF_INT:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC1_BASE) = ADC_REF_INT\n");
break;
case ADC_REF_EXT_3V:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC1_BASE) = ADC_REF_EXT_3V\n");
break;
case ADC_REF_EXT_1V:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC1_BASE) = ADC_REF_EXT_1V\n");
break;
default:
debugPrint("DEBUG:BatteryMonitor: ADCReferenceGet(ADC1_BASE) = super ****ed\n");
break;
}
}
