Hello!
As this is a bit complicated, I try to make this as concise and systematical as possible; please excuse my lack of full sentences.
EDIT: This is the Tiva C TM4C123G, on the Tiva C launchpad
Concept:
Using the Tiva C as software-programmable USB ADC on four channels.
Method:
- Set CPU clock rate to 100MHz (SysCtlClockGet returns 100000000)
- initialize Timer0 A to exhibit a trigger rate of 50kHz/25kHz/20kHz (note: these are dividers of 500kHz, the physical ADC rate, so they should be attainable!)
- initialize ADC sequencer to provide a four-input ADC sequence, triggered on timer
- Handle ADC results in ADC ISR, push to USB buffer
Problem:
- Works, but:
- sampling rate at what should be a sampling rate of 50kHz is only 0.667*50kHz
- at should-be 25kHz is actually 1.2*25kHz
- at should-be 20kHz is yet different and so on, even for low (sub-10kHz) rates (== big load values for timer load register)
- I verify that neither over- nor underruns in the ADC buffer occur
Code excerpt:
int main()
{
[...]
// Set the clocking to run from the PLL at 100MHz
SysCtlClockSet(SYSCTL_SYSDIV_2 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
// enable the 0. ADC peripheral
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
// enable the GPIO port that hosts the ADC
SysCtlPeripheralEnable(ADC_GPIO_SYSCTL_1);
SysCtlPeripheralEnable(ADC_GPIO_SYSCTL_2);
[...]
//disable the interrupts prior to set up - just a precaution
IntDisable(INT_ADC0SS0);
ADCIntDisable(ADC0_BASE, ADC_SEQUENCER);
// Enable the Timer peripheral
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// Timer should run periodically
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
// set the value that is loaded into the timer everytime it finishes
// it's the number of clock cycles it takes till the timer triggers the ADC
TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/F_SAMPLE);
// enable triggering
TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
// enable the timer
TimerEnable(TIMER0_BASE, TIMER_A);
[...]
// The ADC will follow sampling sequence ADC_SEQUENCER
ADCSequenceConfigure(ADC0_BASE, ADC_SEQUENCER, ADC_TRIGGER_TIMER, 0);
[...]
}
extern void IntADC()
{
ADCIntClear(ADC0_BASE, ADC_SEQUENCER);
[...]
for(int counter = 0; counter < 4; counter++)
{
g_value = HWREG(ADC_REGISTERS + ADC_SSFIFO);
uint8_t *byteptr = (uint8_t *) &g_value;
g_pui8USBTxBuffer[g_write_index++ % BULK_BUFFER_SIZE] = *byteptr;
g_pui8USBTxBuffer[g_write_index++ % BULK_BUFFER_SIZE] = (*(byteptr+1))&0x0F;
}
//underflow?
if(HWREG(ADC0_BASE + ADC_O_USTAT) & 1 << ADC_SEQUENCER)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
HWREG(ADC0_BASE + ADC_O_USTAT) = 1 << ADC_SEQUENCER;
}
//overflow?
if(HWREG(ADC0_BASE + ADC_O_OSTAT) & 1 << ADC_SEQUENCER)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
HWREG(ADC0_BASE + ADC_O_OSTAT) = 1 << ADC_SEQUENCER;
}
g_samples_in_packet_count += 4;
g_request_size -= 1;
}