I’m trying to use the ADC12 with DMA on the MSP430FR5989. I pass in the number of seconds I want to read and store. The function initializes the ADC for a repeat single channel acquisition running from ACLK. The DMA is configured to trigger from the ADC12 and increment the destination address. The DMA interrupt is enabled so after the programmed amount of data is stored, it turns off the ADC12 and DMA.
There is a secondary function that uses the ADC12 in a sequence-of-channels configuration to make 8 consecutive measurements of battery voltage. The 8 samples are then averaged to get an average voltage measurement.
These two functions do not run at the same time. I run one (maybe repeatedly) and then the other, so there is no conflict of resources that can occur.
There are three functions that are relevant: record_amplifier(), stop_asense_capture(), and diagnostics_battery(). The functions are provided below.
The record_amplifier() and stop_asense_capture() functions work as expected if I call the record_amplifier() function right after a boot. Also I can call the record_amplifier() function repeatedly and each time the data in the destination addresses are updated appropriately and the DMA interrupt occurs calling the stop_asense_capture() function which stops the ADC12 and the DMA.
If I call the diagnostics_battery() function, then when I call the record_amplifier() function the ADC12 starts the continuous measurements, but there is no DMA transfer. If I pause the program in the debugger and set the DMALEVEL=1 and then run, then the data is transferred and subsequent calls to record_amplifier() work correctly. But if I call diagnostics_battery() again, then the problem returns – calling record_amplifier() starts the ADC12, but no data is transferred.
What I’ve tried:
1) Stopping the firmware in the debugger and checking that all the registers are set appropriately – they were.
2) Clearing the ADC12MCTLx registers prior to calling record_amplifier() – Didn’t correct it.
3) Setting the ADC12EOS bit in ADC12MCTL0 in the record_amplifier() function. – Didn’t correct it.
4) Setting the DMALEVEL bit in DMA0CTL – This “fixes” the issue but I don’t think this is the preferred configuration for DMA with ADC12.
Have you seen this issue before where the registers are configured correctly, the ADC is running and acquiring data but no transfers are generated?
Do you have any suggestions for correcting the issue.
//Function to initialize ADC12, and DMA for continuous read and store of P9.6 port.
void record_amplifier(uint8_t seconds_to_record)
{
uint16_t samples;
// ASense->P9.6->analog channel 6
P9SEL0 |= (1 << 6); // select A14
P9SEL1 |= (1 << 6); // select A14
P9DIR &= ~(1 << 6); // this is a don't care, it shouldn't matter
// turn off ADC.
ADC12CTL0 &= ~ADC12ENC;
ADC12CTL0 = 0; //Ensure ADC is off, if it's on then REFGENBUSY will always be set.
// Most of the bits in the REFCTL0 register can only be modified when the
// REFGENBUSY bit is 0. So make sure that it is.
while (REFCTL0 & REFGENBUSY);
// Select on 1.2v reference. Turn off temperature sensor to save power.
REFCTL0 = REFTCOFF | REFVSEL_0;
// Turn on Vref (1.2V)
REFCTL0 = REFON;
//We want repeated sample >250 msec, so use ACLCK
//With clock divider=1, SHT = 96, and 15 clock pulses to convert => 111 clock pulses, 3.387 msec, 295 Hz.
// ADC enabled and multi sample mode
ADC12CTL0 = ADC12MSC | ADC12ON | ADC12SHT00 | ADC12SHT02;
// Sampling timer used, ACLK and Repeat Single channel mode
ADC12CTL1 = ADC12SHP | ADC12SSEL0 | ADC12CONSEQ1;
// 12-bit conversion results, Lower Power Mode ??Check to see if LPM is ok
ADC12CTL2 = ADC12RES_2 | ADC12PWRMD_L;
ADC12MCTL0 = ADC12INCH_14 | ADC12VRSEL_1;
ADC12IFGR0 = 0; //Reset any pending interrupts
// Source block address
__data16_write_addr((unsigned short) &DMA0SA,(unsigned long) ADC12MEM0_);
// Destination address
__data16_write_addr((unsigned short) &DMA0DA,(unsigned long) 0x20010);
if(seconds_to_record>25)
{
seconds_to_record = 25; //Cap it at 25 seconds to prevent overflow
}
samples = (uint16_t)(seconds_to_record)*295; //# of seconds * Sample rate
DMA0SZ = samples;
DMACTL0 = DMA0TSEL_26; //ADC12 end of conversion triggers DMA0
//Repeated single transfer, increment destination, enable interrupts
DMA0CTL = DMADT_4 | DMADSTINCR_3 | DMAEN | DMAIE;
// Wait until the reference voltage says it is ready for use.
while (!(REFCTL0 & REFGENRDY));
// Enable sampling and start conversion.
ADC12CTL0 |= ADC12ENC | ADC12SC;
}
//Function to stop ADC12 and DMA
void stop_asense_capture(void)
{
DMA0CTL = 0; // Turn off DMA
// turn off ADC.
ADC12CTL0 &= ~ADC12ENC;
ADC12CTL0 = 0;
// Turn off the reference.
REFCTL0 &= ~REFON;
P9SEL0 &= ~(1 << 6);
P9SEL1 &= ~(1 << 6);
}
//Function to measure a scaled battery voltage on P1.2
#define NSAMPLES 8
int diagnostics_battery(void)
{
uint16_t *adc_mem_reg;
uint16_t *adc_mctl_reg;
uint16_t i;
uint16_t adc_sum;
uint32_t bol_threshold;
uint32_t snapshot;
// VMonitor->P1.2->analog channel 2
P1SEL0 |= (1 << 2); // select A2
P1SEL1 |= (1 << 2); // select A2
P1DIR &= ~(1 << 2); // this is a don't care, it shouldn't matter
// Most of the bits in the REFCTL0 register can only be modified when the
// REFGENBUSY bit is 0. So make sure that it is.
while (REFCTL0 & REFGENBUSY);
// Select on 1.2v reference. Turn off temperature sensor to save power.
REFCTL0 = REFTCOFF | REFVSEL_0;
// Turn on Vref (1.2V)
REFCTL0 = REFON;
// ADC enabled and multi sample mode
ADC12CTL0 = ADC12MSC | ADC12ON | ADC12SHT02;
// Sampling timer used, ACLK and Sequence of channel mode
ADC12CTL1 = ADC12SHP | 0x08 | ADC12CONSEQ0;
// 12-bit conversion results
ADC12CTL2 = ADC12RES_2;
// Select input channel A2 (Vmonitor) and Vref buffered/Vss as refs for
// all samples.
adc_mctl_reg = (uint16_t *)&ADC12MCTL0;
for (i = 0; i < NSAMPLES; i++)
{
// Channel No = Vmonitor
// VR+ = VREF buffered, VR- = AVSS
// ADC12EOS = 0 (not last sample)
adc_mctl_reg[i] = ADC12INCH_2 | ADC12VRSEL_1;
}
// ADC12EOS = 1 (last sample)
adc_mctl_reg[NSAMPLES - 1] |= ADC12EOS;
// Wait until the reference voltage says it is ready for use.
while (!(REFCTL0 & REFGENRDY));
// Enable sampling and start conversion.
ADC12CTL0 |= ADC12ENC | ADC12SC;
// Wait for conversion to complete.
while (ADC12CTL1 & ADC12BUSY);
// turn off ADC.
ADC12CTL0 = 0;
// Turn off the reference.
REFCTL0 &= ~REFON;
// Average the samples.
adc_mem_reg = (uint16_t *)&ADC12MEM0;
adc_sum = 0;
for (i = 0; i < NSAMPLES; i++)
{
adc_sum += adc_mem_reg[i];
}
return 0;
}