This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Tool/software: Code Composer Studio
Hi,
I am using the MSP430FR5994 launch pad which needs to (1) detect the supply(the capacitor) voltage, and (2) sample audio signal through an audio sensor. Both of them need to use ADC12_B_BASE. I finished the code for them individually and they both work well but once combined together, the one that is executed later does not work well.
I wrote the code for (1)detect the supply voltage based on the OutOfBox_MSP430FR5994, in which there is a FRAMLogMode.c file that has functions detecting and logging the battery voltage into FRAM. This is exactly what I need for (1). My system is powered by a capacitor and I need periodically check the capacitor voltage.
I wrote the code for (2)sample the audio signal based on the BOOSTXL-AUDIO_RecordPlayback_MSP430FR5994. It has a file called audio_collect.c which uses DMA and ADC to sample the audio signal. The ADC is triggered by A0 internally.
In my final project, if I call(1) and then call (2), (1) works well but (2) does not work. (2) gets stuck in the LPM3. My understanding of how (2) works is like this: A0 triggers the ADC internally and ADC performs a sampling action after triggered. When ADC finishes one sampling, DMA directly read the data and save it into FRAM. I used a checkpoint in the DMA interrupt when debugging, the DMA interrupt is not called at all. So, I wonder if the ADC is not triggered by the timer properly.
If I call (2) and then call (1), (2) works well but (1) gets stuck in the LPM3 waiting for the ADC conversion to complete.
I wonder if there is some initialization needed before I call them each time because they both do some setting for ADC12_B_BASE and works well when executed first. The one executed later might be affected by the setting of the first one so it does not work well. But I checked the driverlib, there is no function which clear all the setting of ADC. Any suggestions?
Thanks,
Yubo
This is the initialization code of (1)
void framLog(){ //Initialize the ADC12B Module /* * Base address of ADC12B Module * Use internal ADC12B bit as sample/hold signal to start conversion * USE MODOSC 5MHZ Digital Oscillator as clock source * Use default clock divider/pre-divider of 1 * Use Temperature Sensor and Battery Monitor internal channels */ ADC12_B_initParam initParam = {0}; initParam.sampleHoldSignalSourceSelect = ADC12_B_SAMPLEHOLDSOURCE_SC; initParam.clockSourceSelect = ADC12_B_CLOCKSOURCE_ACLK; initParam.clockSourceDivider = ADC12_B_CLOCKDIVIDER_1; initParam.clockSourcePredivider = ADC12_B_CLOCKPREDIVIDER__1; initParam.internalChannelMap = ADC12_B_TEMPSENSEMAP | ADC12_B_BATTMAP; ADC12_B_init(ADC12_B_BASE, &initParam); //Enable the ADC12B module ADC12_B_enable(ADC12_B_BASE); // Sets up the sampling timer pulse mode ADC12_B_setupSamplingTimer(ADC12_B_BASE, ADC12_B_CYCLEHOLD_128_CYCLES, ADC12_B_CYCLEHOLD_128_CYCLES, ADC12_B_MULTIPLESAMPLESENABLE); // Maps Battery Monitor input channel to Memory 0 and select voltage references /* * Base address of the ADC12B Module * Configure memory buffer 0 * Map input A1 to memory buffer 0 * Vref+ = IntBuffer * Vref- = AVss * Memory buffer 0 is not the end of a sequence */ ADC12_B_configureMemoryParam configureMemoryParam = {0}; configureMemoryParam.memoryBufferControlIndex = ADC12_B_MEMORY_1; configureMemoryParam.inputSourceSelect = ADC12_B_INPUT_BATMAP; configureMemoryParam.refVoltageSourceSelect = ADC12_B_VREFPOS_INTBUF_VREFNEG_VSS; configureMemoryParam.endOfSequence = ADC12_B_ENDOFSEQUENCE; configureMemoryParam.windowComparatorSelect = ADC12_B_WINDOW_COMPARATOR_DISABLE; configureMemoryParam.differentialModeSelect = ADC12_B_DIFFERENTIAL_MODE_DISABLE; ADC12_B_configureMemory(ADC12_B_BASE, &configureMemoryParam); // Clear memory buffer 0 interrupt ADC12_B_clearInterrupt(ADC12_B_BASE, 0, ADC12_B_IFG1 ); // Enable memory buffer 0 interrupt ADC12_B_enableInterrupt(ADC12_B_BASE, ADC12_B_IE1, 0, 0); // Configure internal reference while(Ref_A_isRefGenBusy(REF_A_BASE)); // If ref generator busy, WAIT Ref_A_enableTempSensor(REF_A_BASE); Ref_A_setReferenceVoltage(REF_A_BASE, REF_A_VREF2_0V); Ref_A_enableReferenceVoltage(REF_A_BASE); //Enable/Start sampling and conversion /* * Base address of ADC12B Module * Start the conversion into memory buffer 0 * Use the single-channel, single-conversion mode */ ADC12_B_startConversion(ADC12_B_BASE, ADC12_B_MEMORY_0, ADC12_B_SEQOFCHANNELS); __bis_SR_register(LPM3_bits | GIE); // Wait for conversion to complete __bic_SR_register(GIE); ADC12_B_disable(ADC12_B_BASE); voltageData = ADC12MEM1; }
This is the initialization code of (2):
void Audio_setupCollect(Audio_configParams * audioConfig) { Timer_A_initUpModeParam upConfig = { TIMER_A_CLOCKSOURCE_ACLK, TIMER_A_CLOCKSOURCE_DIVIDER_1, 7,// (__SYSTEM_FREQUENCY_MHZ__ / audioConfig->sampleRate) - 1, TIMER_A_TAIE_INTERRUPT_DISABLE, TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE, TIMER_A_DO_CLEAR, false }; Timer_A_initCompareModeParam compareConfig = { TIMER_A_CAPTURECOMPARE_REGISTER_1, TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE, TIMER_A_OUTPUTMODE_TOGGLE_RESET, 2,//((__SYSTEM_FREQUENCY_MHZ__ / audioConfig->sampleRate) / 2) - 1 }; // Turn on mic power full drive strength and enable mic input pin to ADC MIC_POWER_PORT_OUT |= MIC_POWER_PIN; MIC_POWER_PORT_DIR |= MIC_POWER_PIN; AUDIO_PORT_SEL0 |= MIC_INPUT_PIN; AUDIO_PORT_SEL1 |= MIC_INPUT_PIN; // For safety, protect RMW Cpu instructions DMA_disableTransferDuringReadModifyWrite(); // Initialize the DMA. Using DMA channel 1. dma0Config.channelSelect = DMA_CHANNEL_1; dma0Config.transferModeSelect = DMA_TRANSFER_SINGLE; dma0Config.transferSize = SAMPLES_LENGTH; dma0Config.triggerSourceSelect = DMA_TRIGGERSOURCE_26; dma0Config.transferUnitSelect = DMA_SIZE_SRCWORD_DSTWORD; dma0Config.triggerTypeSelect = DMA_TRIGGER_RISINGEDGE; // Initialize the DMA. Using DMA channel 2. dma1Config.channelSelect = DMA_CHANNEL_2; dma1Config.transferModeSelect = DMA_TRANSFER_SINGLE; dma1Config.transferSize = SAMPLES_LENGTH; dma1Config.triggerSourceSelect = DMA_TRIGGERSOURCE_26; dma1Config.transferUnitSelect = DMA_SIZE_SRCWORD_DSTWORD; dma1Config.triggerTypeSelect = DMA_TRIGGER_RISINGEDGE; DMA_init(&dma0Config); DMA_init(&dma1Config); DMA_setSrcAddress(DMA_CHANNEL_1, (uint32_t) &ADC12MEM0, DMA_DIRECTION_UNCHANGED); DMA_setDstAddress(DMA_CHANNEL_1, (uint32_t) (&dataRecorded1), DMA_DIRECTION_INCREMENT); DMA_setSrcAddress(DMA_CHANNEL_2, (uint32_t) &ADC12MEM0, DMA_DIRECTION_UNCHANGED); DMA_setDstAddress(DMA_CHANNEL_2, (uint32_t) (&dataRecorded2), DMA_DIRECTION_INCREMENT); // Configure ADC ADC12CTL0 &= ~ADC12ENC; // Disable conversions to configure ADC12 // Turn on ADC, sample 32 clock cycles =~ 2us ADC12CTL0 = ADC12ON + ADC12SHT0_3; // Use sample timer, rpt single chan 0, use MODOSC, TA0 timer channel 1 // ADC12SHP: SAMPCON signal is sourced from the sampling timer. // ADC12CONSEQ_2: Repeat-single-channel // ADC12SHS_1: see the device-specific data sheet for source // ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2 + ADC12SHS_1; // Mic input to ADC, (AVCC/AVSS ref), sequence end bit set ADC12MCTL0 = MIC_INPUT_CHAN | ADC12EOS; // Initialize Timer_A channel 1 to be used as ADC12 trigger // Initialize TACCR0 (period register) 8MHz/20kHz = 400 // Simple counter with no interrupt. 0...400 = 400 counts/sample Timer_A_initUpMode(TIMER_A0_BASE, &upConfig); // Initialize TA0CCR1 to generate trigger clock output, reset/set mode Timer_A_initCompareMode(TIMER_A0_BASE, &compareConfig); // Enable ADC to convert when a TA0 edge is generated ADC12CTL0 |= ADC12ENC; // Delay for the microphone to settle __delay_cycles(10000); }
This is the DMA interrupt and ADC interrupt:
//****************************************************************************** // DMA interrupt service routine // every time when the ADC is ready, ADC will trigger the DMA which starts // transferring data from ADC to memory //****************************************************************************** #pragma vector=DMA_VECTOR __interrupt void dmaIsrHandler(void) { switch (__even_in_range(DMAIV, DMAIV_DMA2IFG)) { case DMAIV_DMA0IFG: break; case DMAIV_DMA1IFG: // Disable the dma transfer DMA_disableTransfers(DMA_CHANNEL_1); // Disable DMA channel 1 interrupt DMA_disableInterrupt(DMA_CHANNEL_1); P1OUT &= ~BIT0; // turn off LED //shutdown audio collect Audio_stopCollect(); Audio_shutdownCollect(); // Start Cpu on exit __bic_SR_register_on_exit(LPM3_bits); break; default: break; } } /* ADC12 Interrupt Service Routine*/ #pragma vector = ADC12_VECTOR __interrupt void ADC12_ISR(void) { switch(__even_in_range(ADC12IV,76)) { case ADC12IV_NONE: break; // Vector 0: No interrupt case ADC12IV_ADC12OVIFG: break; // Vector 2: ADC12MEMx Overflow case ADC12IV_ADC12TOVIFG: break; // Vector 4: Conversion time overflow case ADC12IV_ADC12HIIFG: break; // Vector 6: ADC12HI case ADC12IV_ADC12LOIFG: break; // Vector 8: ADC12LO case ADC12IV_ADC12INIFG: break; // Vector 10: ADC12IN case ADC12IV_ADC12IFG0: break; case ADC12IV_ADC12IFG1: // Vector 14: ADC12MEM1 ADC12IFGR0 &= ~ADC12IFG1; // Clear interrupt flag __bic_SR_register_on_exit(LPM3_bits); // Exit active CPU break; case ADC12IV_ADC12IFG2: break; // Vector 16: ADC12MEM2 case ADC12IV_ADC12IFG3: break; // Vector 18: ADC12MEM3 case ADC12IV_ADC12IFG4: break; // Vector 20: ADC12MEM4 case ADC12IV_ADC12IFG5: break; // Vector 22: ADC12MEM5 case ADC12IV_ADC12IFG6: break; // Vector 24: ADC12MEM6 case ADC12IV_ADC12IFG7: break; // Vector 26: ADC12MEM7 case ADC12IV_ADC12IFG8: break; // Vector 28: ADC12MEM8 case ADC12IV_ADC12IFG9: break; // Vector 30: ADC12MEM9 case ADC12IV_ADC12IFG10: break; // Vector 32: ADC12MEM10 case ADC12IV_ADC12IFG11: break; // Vector 34: ADC12MEM11 case ADC12IV_ADC12IFG12: break; // Vector 36: ADC12MEM12 case ADC12IV_ADC12IFG13: break; // Vector 38: ADC12MEM13 case ADC12IV_ADC12IFG14: break; // Vector 40: ADC12MEM14 case ADC12IV_ADC12IFG15: break; // Vector 42: ADC12MEM15 case ADC12IV_ADC12IFG16: break; // Vector 44: ADC12MEM16 case ADC12IV_ADC12IFG17: break; // Vector 46: ADC12MEM17 case ADC12IV_ADC12IFG18: break; // Vector 48: ADC12MEM18 case ADC12IV_ADC12IFG19: break; // Vector 50: ADC12MEM19 case ADC12IV_ADC12IFG20: break; // Vector 52: ADC12MEM20 case ADC12IV_ADC12IFG21: break; // Vector 54: ADC12MEM21 case ADC12IV_ADC12IFG22: break; // Vector 56: ADC12MEM22 case ADC12IV_ADC12IFG23: break; // Vector 58: ADC12MEM23 case ADC12IV_ADC12IFG24: break; // Vector 60: ADC12MEM24 case ADC12IV_ADC12IFG25: break; // Vector 62: ADC12MEM25 case ADC12IV_ADC12IFG26: break; // Vector 64: ADC12MEM26 case ADC12IV_ADC12IFG27: break; // Vector 66: ADC12MEM27 case ADC12IV_ADC12IFG28: break; // Vector 68: ADC12MEM28 case ADC12IV_ADC12IFG29: break; // Vector 70: ADC12MEM29 case ADC12IV_ADC12IFG30: break; // Vector 72: ADC12MEM30 case ADC12IV_ADC12IFG31: break; // Vector 74: ADC12MEM31 case ADC12IV_ADC12RDYIFG: break; // Vector 76: ADC12RDY default: break; } }
Oh, sorry. I copied the wrong code for This is the initialization code of (2). I copied it from the original example code. In my own project, I tailored it and only use one channel and one dataRecorded for DMA. But the ADC setting of my project is the same as the original example code.
Anyway, I put the right initialization code of (2) of my project here:
void Audio_setupCollect(Audio_configParams * audioConfig) { Timer_A_initUpModeParam upConfig = {0}; upConfig.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; upConfig.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1; upConfig.timerPeriod = (__SYSTEM_FREQUENCY_MHZ__ / audioConfig->sampleRate) - 1; upConfig.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE; upConfig.captureCompareInterruptEnable_CCR0_CCIE = TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE; upConfig.timerClear = TIMER_A_DO_CLEAR; upConfig.startTimer = false; Timer_A_initCompareModeParam compareConfig = {0}; compareConfig.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1; compareConfig.compareInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE; compareConfig.compareOutputMode = TIMER_A_OUTPUTMODE_TOGGLE_RESET; compareConfig.compareValue = ((__SYSTEM_FREQUENCY_MHZ__ / audioConfig->sampleRate) / 2) - 1; // Initialize Timer_A channel 1 to be used as ADC12 trigger // Initialize TACCR0 (period register) __SYSTEM_FREQUENCY_MHZ__/sampleRate = NUM // Simple counter with no interrupt. 0...NUM = NUM counts/sample Timer_A_initUpMode(TIMER_A0_BASE, &upConfig); // Initialize TA0CCR1 to generate trigger clock output, reset/set mode Timer_A_initCompareMode(TIMER_A0_BASE, &compareConfig); // Turn on mic power full drive strength and enable mic input pin to ADC MIC_POWER_PORT_OUT |= MIC_POWER_PIN; MIC_POWER_PORT_DIR |= MIC_POWER_PIN; AUDIO_PORT_SEL0 |= MIC_INPUT_PIN; // Enable A/D channel inputs AUDIO_PORT_SEL1 |= MIC_INPUT_PIN; // For safety, protect RMW Cpu instructions DMA_disableTransferDuringReadModifyWrite(); // Initialize the DMA. Using DMA channel 1. dma0Config.channelSelect = DMA_CHANNEL_1; dma0Config.transferModeSelect = DMA_TRANSFER_SINGLE; dma0Config.transferSize = SAMPLES_LENGTH; dma0Config.triggerSourceSelect = DMA_TRIGGERSOURCE_26; dma0Config.transferUnitSelect = DMA_SIZE_SRCWORD_DSTWORD; dma0Config.triggerTypeSelect = DMA_TRIGGER_RISINGEDGE; DMA_init(&dma0Config); DMA_setSrcAddress(DMA_CHANNEL_1, (uint32_t) &ADC12MEM0, DMA_DIRECTION_UNCHANGED); DMA_setDstAddress(DMA_CHANNEL_1, (uint32_t) (&dataRecorded), DMA_DIRECTION_INCREMENT); // Configure ADC ADC12CTL0 &= ~ADC12ENC; // Disable conversions to configure ADC12 // Turn on ADC, sample 32 clock cycles =~ 2us ADC12CTL0 = ADC12ON + ADC12SHT0_3; // Use sample timer, rpt single chan 0, use MODOSC, TA0 timer channel 1 ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2 + ADC12SHS_1; // Mic input to ADC, (AVCC/AVSS ref), sequence end bit set ADC12MCTL0 = MIC_INPUT_CHAN | ADC12EOS; // Enable ADC to convert when a TA0 edge is generated ADC12CTL0 |= ADC12ENC; // Delay for the microphone to settle // ASMP401: the small red audio IC needs 0.2s init time before it starts sensing __delay_cycles(1600000); // 0.2s }
Hi Bruce,
Thanks, I am working on combining the two parts as you suggested.
But I still do not see how the ADC is connected to the voltage. Actually, I do not know for both cases (1) FramLog.c and (2)audio_collect.c how the example codes connect the ADC to the target they want to sample. For (1)FramLog, it wants to sample the VCC voltage but which code line tells the system to sample from the VCC voltage; for (2)audio_collect.c, it wants to sample the audio signal and the external audio_IC's output is connected to P1.3. But I could not find which code line tells the system to sample data from P1.3.
I understand the command "ADC12SHS_x" connects the ADC with a certain timer internally (we need to see the device datasheet). So, I just wander if case (1) and (2) have some similar internal default settings for the relation between ADC and its sampling target too.
Actually this question (which line code tells the system to sample from where) confused me for a long time before. I just ended up using the same pin connection without understanding the code fully. Now that, it seems the time I have to. Could you give me some insights?
Thanks,
Yubo
I just realized "MIC_INPUT_CHAN" is connecting the ADC to the audio_IC's output and "ADC12_B_INPUT_BATMAP" is connecting the ADC to the VCC voltage. And by looking at the #define, MIC_INPUT_CHAN = ADC12INCH_3, ADC12_B_INPUT_BATMAP = ADC12INCH_31.
So, my last question can be converted into this:
it seems the ADC12INCH_3 means the P1.3(to which the audio_IC is connected), and ADC12INCH_31 means the VCC voltage. Where can I find the relations between all those ADC12INCH_x to a specific pin. For example, if I want to use another pin to connect the audio_IC's output, what ADC12INCH_x I should use?
Another question is the use of "ADC12EOS":
ADC12EOS: sequence end bit set. My current understanding is that if I use this command:
ADC12MCTL0 = ADC12INCH_1 | ADC12INCH_2 | ADC12INCH_3 | ADC12INCH_4 | ADC12EOS;
does it mean the ADC will sample from INCH1,2,3,4(in this 1,2,3,4 order) at each time when it is triggered by the internal timer? I can put several INCH_x before ADC12EOS.
Thanks,
Yubo
oh, to sample from more than one pin, the right command should be like this:
ADC12MCTL0 = ADC12INCH_1; ADC12MCTL1 = ADC12INCH_2; ADC12MCTL2 = ADC12INCH_3 ; ADC12MCTL3 = ADC12INCH_4 | ADC12EOS;
Each ADC12MCTLx is refered to one target pin you want to sample. Their ADC results will be saved into corresponding ADC12MEMx. So, I can use DMA to read only the ADC12MEMx I am interested.
It seems much clearer now. :)
Hi Bruce,
I followed your advice and just use the (2) audio_collect.c to sample the voltage too, by change ADC12MCTL0=ADC12_B_INPUT_BATMAP.
The problem I have now is the sampled value. For the (1)FramLog.c, it gives me a direct sample value for the voltage, e.g. the sample value is 3400 for 3.3V, 2200 for 2.0V, etc. The value is not quite accurate but its variation is within my requirement. What setting I should do in (2) auido_collect.c in order to get a very direct sample value for the voltage?
because in FramLog, there are:
// Configure internal reference while(Ref_A_isRefGenBusy(REF_A_BASE)); // If ref generator busy, WAIT Ref_A_enableTempSensor(REF_A_BASE); Ref_A_setReferenceVoltage(REF_A_BASE, REF_A_VREF2_0V); Ref_A_enableReferenceVoltage(REF_A_BASE);
I tried to set the ref in audio_collect.c using the following
// Configure internal reference while(REFCTL0 & REFGENBUSY); // If ref generator busy, WAIT REFCTL0 |= REFVSEL_1 | REFON; // Select internal ref = 2.0V // Internal Reference ON while(!(REFCTL0 & REFGENRDY)); // Wait for reference generator // to settle
But it does not work. I get a sample value=710 for the 3.3V voltage. Actually, even I do not configure internal reference I still get a sample value=710 for the 3.3V voltage.
Thanks,
Yubo
Also, I misread this the first time:
> configureMemoryParam.refVoltageSourceSelect = ADC12_B_VREFPOS_INTBUF_VREFNEG_VSS;
I suspect this is expected for BATMAP; the equivalent is:
> ADC12MCTL1 = ADC12_B_INPUT_BATMAP | ADC12VRSEL_1 | ADC12EOS; // Channel 31 for Vcc, ref=VREF, last conversion in sequence
[Edit: Note: this is the second half of the post. The first half is "above the fold".]
**Attention** This is a public forum