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.

msp432 adc14 stop single channel repeat mode

hi,

i would want that adc14 of msp432 start to sampling at time = 0 and finish to time 1000 microsecond. This is my part of code interested:

#define STEP			5
#define TIME_ON			50
#define TIME_ADC_START	0
#define TIME_OFF		250
#define TIME_ADC_END	1000
#define ADC_SPENTO		1010
#define TIME_SAVE		1200
#define TIME_RESET		10000

int microsecondi = 0;
int index_N = 0;
uint16_t values[16384];

void IR_off()    {P4OUT    |=  BIT2;}
void IR_on()     {P4OUT    &= ~BIT2;}
void led_on()    {P1OUT    |=  BIT0;}
void led_off()   {P1OUT    &= ~BIT0;}
void TA0_on()    {TA0CCTL0 |=  CCIE;}
void TA0_off()   {TA0CCTL0 &= ~CCIE;}

P6SEL1 |= BIT1;
P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC

//=============ADC14============

ADC14CTL0 &= ~ADC14ENC;                           //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
ADC14CTL0 = ADC14ON | ADC14SHP | ADC14CONSEQ_2;   // Turn on ADC14, set sampling time, repeat single channel
ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results
ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V

// ========== clock a 12 MHz==========

CSKEY = 0x695A;                    // Unlock CS module for register access
CSCTL0 = 0;                        // Reset tuning parameters
CSCTL0 = DCORSEL_3;                // Set DCO to 12MHz (nominal, center of 8-16MHz range)
CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
CSKEY = 0;                         // Lock CS module from unintended accesses


// ==========Configurazione TimerA0==========

NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
TA0CCR0 = STEP;                             // Overflow (step dell'interrupt)
TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)

// Timer A0 interrupt service routine

void TimerA0_0IsrHandler(void) {
    TA0CCTL0 &= ~CCIFG;
    /* STEP = 20 */
	if(TIME_ADC_START < microsecondi <= TIME_ADC_END) {
		ADC14CTL0 |= ADC14ENC | ADC14SC; // Start sampling/conversion; Enable and start conversion.
		send(com_LOG, "...adc...\n");
		while(!(ADC14IFGR0 & BIT0)); // Controlla che non abbia finito
		values[index_N] = ADC14MEM0;
		index_N++;
	}
    switch (microsecondi){
	case TIME_ON:
		IR_on();
		send(com_LOG, "LED_ON\n");
		break;
	case TIME_OFF:
		IR_off();
		send(com_LOG, "LED_OFF\n");
		break;
	case ADC_SPENTO:
		ADC14CTL0 &= ~ADC14CONSEQ_0;
		ADC14CTL0 &= ~ADC14ENC;
		break;
	case TIME_SAVE:
		send(com_LOG, "valori_inviati\n");
		if (index_N >= N){
			index_N = 0;
			microsecondi = 0;
			TA0_off();
			led_off();
			send_all_values();
			}
	default:
		break;
	}
microsecondi += STEP;
   if (microsecondi >= TIME_RESET){
    microsecondi = 0;
  }
}

I don't know how stop conversion and sampling of adc14 because with this step it samples to 1200microsecond and doesn't stop at 1000

  • Dear Luca,
    I am looking into an example that takes advantace of the timer triggering the ADC and utilizing the ADC interrupt instead of polling the IFG within the timer ISR. I think this will help greatly with the timing. Also, I noticed that the timer is in 'up' mode and uses a value of '5' in the CCR0. Please be advised that the timer period is actually 5+1 because of the extra clock cycle to count from 5 to 0. So the actual timer period is 6 SMCLK cycles instead of 5.

    I hope to get back to you within the next day with an example.

    Best Regards,
    Chris
  • oh wow i didn't know that!! So you think is better using adc14isrInterrupt? and if i make that, how i controll the sampling of adc? Here i have a frequency of 1/5us but using adc interrupt? Then how can i solve the problem of step (ccr0) = 5? i have to change time setting previuous step? for example TIME_ON = 45? thanks and sorry for my bad english
  • Luca,

    Please find the attached logic photo and example code.  I had to increase the clock frequency to 24Mhz to prevent any missing conversions.  There were instances where the time spent in the ISR was too long and consequently either did not support the 1/5us sampling rate or in the case of using the ADC interrupt, multiple interrupts would occur and the previous sample was overwritten before it could be read.  

    I removed the send commands in the timer ISR and this might become an issue when you re-apply them because of the amount of time. You can further increase the MCLK frequency to 48Mhz and make the appropriate changes to the Vcore and waitstates per the example provided (see also datasheet and code example for 48Mhz operation).  In the code I put in a couple of traps so that you would be able to tell fairly easily if there was a timing issue and you are missing samples.

    I hope that helps.

    Chris

    msp432p401_adc14_custom_02.c
    /* --COPYRIGHT--,BSD_EX
     * Copyright (c) 2013, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *******************************************************************************
     *
     *                       MSP432 CODE EXAMPLE DISCLAIMER
     *
     * MSP432 code examples are self-contained low-level programs that typically
     * demonstrate a single peripheral function or device feature in a highly
     * concise manner. For this the code may rely on the device's power-on default
     * register values and settings such as the clock configuration and care must
     * be taken when combining code from several examples to avoid potential side
     * effects. Also see http://www.ti.com/tool/mspdriverlib for an API functional
     * library & https://dev.ti.com/pinmux/ for a GUI approach to peripheral configuration.
     *
     * --/COPYRIGHT--*/
    //******************************************************************************
    //******************************************************************************
    //  MSP432P401 Demo - ADC14, Sample A14, AVcc Ref, Trigger ADC from TA0.1
    //
    //   Description: Trigger ADC conversion of A14 200 times at 5us rate.  A trap
    //                is created in the event of a system error.  This could be
    //                the result in an error setting PMM core voltage or an ISR
    //                taking too long and the 5us sampling rate is not satisfied.
    //                For example at 12Mhz the ADC is triggered again while still
    //                in the ADC ISR, this is further complicated when the timer
    //                and adc isr are coincident.  Caution should be taken when
    //                re-inserting send commands in timer ISR as this may
    //                interfere with timing.
    //
    //
    //                MSP432p401rpz
    //             -----------------
    //         /|\|              XIN|-
    //          | |                 |
    //          --|RST          XOUT|-
    //            |                 |
    //        >---|P6.1/A14     P1.0|-->
    //            |       P2.4/TA0.1|-->Output to Logic Analyzer
    //            |             P2.0|-->TIME_ON (Output to Logic Analyzer)
    //            |             P2.1|-->TIME_OFF (Output to Logic Analyzer)
    //            |             P2.2|-->ADC_SPENTO (Output to Logic Analyzer)
    //
    //   Built with Code Composer Studio V6.1.3
    //******************************************************************************
    #include "msp.h"
    #include "stdint.h"
    
    #define TA0CLK_MHZ		1
    
    #define STEP            5*TA0CLK_MHZ
    #define TIME_ON         50*TA0CLK_MHZ
    #define TIME_ADC_START  0*TA0CLK_MHZ
    #define TIME_OFF        250*TA0CLK_MHZ
    #define TIME_ADC_END    1000*TA0CLK_MHZ
    #define ADC_SPENTO      1010*TA0CLK_MHZ
    #define TIME_SAVE       1200*TA0CLK_MHZ
    #define TIME_RESET      10000*TA0CLK_MHZ
    
    #define SAMPLE_SIZE		TIME_ADC_END/STEP
    int microsecondi = 0;
    int index_N = 0;
    uint16_t values[SAMPLE_SIZE];
    void IR_off()    {P4->OUT    |=  BIT2;}
    void IR_on()     {P4->OUT    &= ~BIT2;}
    void led_on()    {P1->OUT    |=  BIT0;}
    void led_off()   {P1->OUT    &= ~BIT0;}
    void TA0_on()    {TIMER_A0->CCTL[0] |=  CCIE;}
    void TA0_off()   {TIMER_A0->CCTL[0] &= ~CCIE;}  //enable interrupt - does not stop timer
    
    void error(void);
    
    int main(void)
    {
    	// volatile unsigned int i;
    	volatile uint16_t i;
    	uint32_t currentPowerState;
        WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD;                 // Stop WDT
    
        // GPIO Setup
        P1->OUT &= ~BIT0;                           // Clear LED to start
        P1->DIR |= BIT0;                            // Set P1.0/LED to output
    
        P2->DIR |= BIT4;                            // Output TA0.1 on p2.4
        P2->SEL0 |= BIT4;
        P2->SEL1 &= ~BIT4;
    
        P2->OUT &= ~(BIT0+BIT1+BIT2);                           // Clear LED to start
        P2->DIR |= (BIT0+BIT1+BIT2);                            // Set P1.0/LED to output
    
    //    P4->DIR |= BIT3;
    //    P4->SEL0 |= BIT3;                  // MCLK
    //    P4->SEL1 &= ~BIT3;
    
        __enable_interrupt();
        // Enable ADC interrupt in NVIC module
        P6->SEL1 |= BIT1;
        P6->SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC
    
        /*
         * Vcore and Wait state taken from example msp432p401_cs_03
         * 24Mhz Mclk and 0 wait states, per table 5.8 in datasheet
         */
    
        /* Step 1: Transition to VCORE Level 1: AM0_LDO --> AM1_LDO */
    
        /* Get current power state, if it's not AM0_LDO, error out */
        currentPowerState = PCM->CTL0 & PCM_CTL0_CPM_MASK;
        if (currentPowerState != PCM_CTL0_CPM_0)
            error();
        while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
        PCM->CTL0 = PCM_CTL0_KEY_VAL | PCM_CTL0_AMR_1;
        while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
        if (PCM->IFG & PCM_IFG_AM_INVALID_TR_IFG)
            error();                            // Error if transition was not successful
        if ((PCM->CTL0 & PCM_CTL0_CPM_MASK) != PCM_CTL0_CPM_1)
            error();                            // Error if device is not in AM1_LDO mode
    
        /* Step 2: Configure Flash wait-state to 0 for both banks 0 & 1 */
        FLCTL->BANK0_RDCTL = FLCTL->BANK0_RDCTL & (~FLCTL_BANK0_RDCTL_WAIT_MASK) | FLCTL_BANK0_RDCTL_WAIT_0;
        FLCTL->BANK1_RDCTL  = FLCTL->BANK0_RDCTL & (~FLCTL_BANK1_RDCTL_WAIT_MASK) | FLCTL_BANK1_RDCTL_WAIT_0;
    
        // ========== clock a 12 MHz==========
        CS->KEY = CS_KEY_VAL;                                    // Unlock CS module for register access
        CS->CTL0 = 0;
        CS->CTL0 = CS_CTL0_DCORSEL_4;        // Set DCO to 24MHz
    //    CS->CTL0 = CS_CTL0_DCORSEL_3;        // Set DCO to 12MHz (nominal, center of 8-16MHz range)
        CS->CTL1 = CS_CTL1_SELA__REFOCLK | CS_CTL1_SELS__DCOCLK | CS_CTL1_SELM__DCOCLK; // Select ACLK = REFO, SMCLK = MCLK = DCO
        CS->CLKEN |= CS_CLKEN_MODOSC_EN;
        CS->KEY = 0;                         // Lock CS module from unintended accesses
    
        //=============ADC14============
        ADC14->CTL0 &= ~ADC14_CTL0_ENC;                           //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
        ADC14->CTL0 = ADC14_CTL0_ON | ADC14_CTL0_SHP | ADC14_CTL0_CONSEQ_2 |
        		ADC14_CTL0_SHS_1;   // Turn on ADC14, set sampling time, repeat single channel, trigger TA0.1
        // Setting the SHP bit means that the time is determined by the settings in ADC14SHT0x where the default of �0� is four clock cycles.
        ADC14->CTL1 = ADC14_CTL1_RES__14BIT;                      // Use sampling timer, 14-bit conversion results
        ADC14->MCTL[0] = ADC14_MCTLN_VRSEL_0 | ADC14_MCTLN_INCH_14;         // A14 ADC input select; Vref=AVCC=3.3V
        // with a setting of 14, the conversion takes 16 clocks, the default ADC clock is MODCLOCK, ~25Mhz
        // 20 clocks at 25Mhz -> 800ns
        ADC14->CLRIFGR0 |= ADC14_CLRIFGR0_CLRIFG0;        // clear IFG
        ADC14->IER0 |= ADC14_IER0_IE0;                    // Enable ADC conv complete interrupt
        ADC14->CTL0 |= ADC14_CTL0_ENC;
    
        // ==========Configurazione TimerA0, CCR0 and CCR1 ==========
        TIMER_A0->CCR[0] = STEP-1;                        // Overflow (step dell'interrupt)
        TIMER_A0->CCR[1] = STEP/2;
        TIMER_A0->CCTL[1] |= TIMER_A_CCTLN_OUTMOD_3;      // Output TA0.1 to set on CCR1(30) and reset CCR0(59),
        TIMER_A0->CTL = TIMER_A_CTL_SSEL__SMCLK | TIMER_A_CTL_ID__8;  // SMCLK, UP mode, Divisione per 8 (24 MHz / 8 = 3 MHz)
    //    TIMER_A0->CTL = TIMER_A_CTL_SSEL__SMCLK | TIMER_A_CTL_ID__4;  // SMCLK, UP mode, Divisione per 8 (24 MHz / 8 = 3 MHz)
        TIMER_A0->EX0 |= TIMER_A_EX0_IDEX__3;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)
        TIMER_A0->CCTL[0] &= ~TIMER_A_CCTLN_CCIFG;        // clear IFG
        TIMER_A0->CCTL[0] |= TIMER_A_CCTLN_CCIE;          // enable ta0.0 interrupt
    
        SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;              // Enable sleep on exit from ISR
        __enable_interrupt();
        NVIC->ISER[0] = 1 << ((TA0_0_IRQn) & 31);
        NVIC->ISER[0] |= 1 << ((ADC14_IRQn) & 31);
    
        TIMER_A0->CTL |= TIMER_A_CTL_MC__UP;
    
        while (1)
        {
          __sleep();
          __no_operation();                     // For debugger
        }
    }
    
    void error(void)
    {
        while (1);
    }
    
    // ADC14 interrupt service routine
    void ADC14_IRQHandler(void)
    {
    	values[index_N] = ADC14->MEM[0];
    	index_N++;
        if (index_N >= SAMPLE_SIZE)
        {
            TIMER_A0->CCTL[1] &= ~TIMER_A_CCTLN_OUTMOD_MASK;      // Clear output mode, stop timer trigger to ADC
            ADC14->CTL0 &= ~ADC14_CTL0_ENC;
            led_off();
            //index_N = 0;
            //microsecondi = 0;
            //TA0_off();
        }
        //P1->OUT ^= BIT0;                           // Clear LED to start
    }
    
    void TimerA0_0IsrHandler(void)
    {
    	TIMER_A0->CCTL[0] &= ~TIMER_A_CCTLN_CCIFG;
        /* STEP = 20 */
    //    if(TIME_ADC_START < microsecondi <= TIME_ADC_END) {
    //        P1->OUT |= BIT0;                           //
    //        ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC; // Start sampling/conversion; Enable and start conversion.
    //        //send(com_LOG, "...adc...\n");
    //        while(!(ADC14->IFGR0 & BIT0)); // Controlla che non abbia finito
    //        values[index_N] = ADC14->MEM[0];
    //        index_N++;
    //        P1->OUT &= ~BIT0;                           //
    //    }
        switch (microsecondi)
        {
        case TIME_ON:
            IR_on();
            P2->OUT |= BIT0;                           //
            //send(com_LOG, "LED_ON\n");
            break;
        case TIME_OFF:
            IR_off();
            P2->OUT |= BIT1;                           //
            //send(com_LOG, "LED_OFF\n");
            break;
        case ADC_SPENTO:
            P2->OUT |= BIT2;                           //
            //ADC14->CTL0 &= ~ADC14CONSEQ_0;           // this instruction has no effect, '0'
            ADC14->CTL0 &= ~ADC14_CTL0_ENC;
            break;
        case TIME_SAVE:
            //send(com_LOG, "valori_inviati\n");
        	P2->OUT &= ~(BIT0+BIT1+BIT2);
        	TIMER_A0->CTL &= ~TIMER_A_CTL_MC_MASK;  // stop timer
        	if(index_N != SAMPLE_SIZE)
        	{
        		error();
        	}
        default:
            break;
        }
        microsecondi += STEP;
        if (microsecondi >= TIME_RESET)
        {
            microsecondi = 0;
            while(1);  // trap
        }
    }
    

  • i don't understand a thing. If a want change the time of ADC trig? for example start to sampling at another time (from 200 to 1ms) because time on is changed to 400 and time off to 600. the code helps me but it is written in a different form from mine. the funcion send_all_values() is for send values to server so i need it because after a cycle it starts another with same time.
    i don't understand the time*TA0CLK_MHZ.
    i don't understand this void TA0_off() {TIMER_A0->CCTL[0] &= ~CCIE;} //enable interrupt - does not stop timer

    sorry for questions, if you want we can write on facebook
  • i used 12Mhz because, if it is correct, the serial comunication need this.
  • Luca,
    Let me try and address each of your questions:
    (1) I have increased the DCO to 24Mhz which is configured to source both MCLK and SMCLK. You can divide SMCLK in the Clock System Module (CSCTL1 Register, Field DIVS) to maintain the SMCLK frequency you want and still have MCLK at 24Mhz so that the ISRs execute in a timely fashion.
    (2) The TA0CLK_MHZ was intended to scale the timer values based upon the timer clock selected. In your case it is simply 12/12, so that 1 timer clock is 1us. If the timer clock was 12Mhz (no divider) then the timer multiplier would be 12. Then the STEP value would be 60 and at 12Mhz the step would still be 5us. The intent of the definition is to maintain the 5us step regardless of the timer clock frequency.
    (3) The function TA0_off() is a function that I do not understand. By clearing the CCIE, the timer is not off, only the interrupt is disabled. I was concerned that you thought this was actually stopping the timer.
    (4) In the application that I provided the enable convert, ENC, is set immediately (line 171). Similar to what you did previously, when microsecondi reaches the desired value you can set ENC. There are a couple of different ways that you could approach this, I think the key is to minimize the amount of time in the ISRs.
    (5) The form of the code is CMSIS as opposed to the classic MSP430 style.

    Hope that helps,
    Chris
  • Hi,

    sorry for late. I think it's better post al code so you can have a complete idea of my work. Msp is connected with a dust sensor of sharp. This sensor has IR led that i can turn on or off when i want (it's that i make in Time_ON and OFF). In this period i have to read the analogi signal (A14) during turn on of IR that indicates the presence of dust in the air. Because i'm doing different experiment of period of IR_ON i want read the signal in different times ok? So maybe start adc at 0 and finish at 1ms or change and start adc at 200us because IR turns on later and finish adc read before (for example 800us). The ir can be turned on for a time i want. (200us, 400us etc...).

    Now the code is connected with a program in Python that starts the program manually or automatically. This part of code works for me and i have no problems. It is base on exchange of letters that define command. In msp i have the function sendallvalues that send values of array to my server. To make this i have to have a serial connection with (i think) 24Mhz of clock. For this my question about DCO. 

    Now with the log i noted that timerA counts perfectly the time with step dedicated and not Step+1 as you think. So why make step-1? (see image below).

    I don't understand the two values of ccr0 and ccr1. Why use thes and not one? 

    I need the function sendAllValues so at TIME_save this is called.When the array is full index_N = 0 but you have commented this row. Where i make the call of function and control of index array?

    With the code below i note that time is perfect but maybe the adc (as you said me) don't works correctly in ISRtimer for the high frequency. So what i want is only have a correct utilization of adc triggered by ISR timer that dictate the steps of code. 

    RTC is used if it works in automatically mode. This works for me. 

    Only thing i want is that adc start and finish his operation in time i want and fills array to send in serial comunication. 

    Sorry for lot of messages and thanks for help!!!! :) :) :)

    code.c
    #include "msp.h"
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    
    // ==========Definizione costanti==========
    
    typedef enum {false=0, true=1} bool;      // definizione tipo bool
    
    // costanti dei tempi di acquisizione
    #define STEP			10		//	10 us	 tempo dell'interrupt
    #define TIME_ON			200		//	200 us	 led IR on, signal = 0 V
    #define TIME_ADC_START	100		//	100 us	 inizio istante di acquisizione
    #define TIME_OFF		650		//	650 us	 led IR off, signal = 3,3 V
    #define TIME_ADC_END	800		//	800 us	 fine istante di acquisizione
    #define TIME_SAVE		1200	//	900 us	 controllo fine ciclo
    #define TIME_RESET		10000	//	1.000 us periodo del segnale generato
    
    // costanti comandi protocollo trasmissione seriale
    #define com_PING  'P'         // ping
    #define com_T     'T'         // parametro T (periodo in minuti)
    #define com_N     'N'         // parametro N (numero campioni per periodo)
    #define com_UTC   'U'         // sincronizzazione data/ora
    #define com_MODE  'A'         // 0 (off) - 1 (auto) - 2 (manual)
    #define com_START 'S'         // inizio acquisizione degli N valori
    #define com_RESET 'R'         // provoca reset
    
    #define com_VALUE 'D'         // invio di N valori
    #define com_END   'E'         // fine invio valori
    #define com_LOG   'L'         // stringa di log
    
    
    // ==========Dichiarazione variabili globali==========
    
    // parametri modificabili via seriale a run-time
    int T =   5;  // min      // Periodo globale di acquisizione               [N/100/60  < T  < 32767]
    int N = 100;              // Numero di campioni memorizzati per periodo    [    1     < N  < 16384]
    
    // variabili tempi
    int microsecondi = 0;      // contatore di microsecondi del timer A0        [0 - 10.000]
    int minuti = 0;            // contatore di minuti del Real Time Clock       [0 - T]
    
    // indici contatori array
    int index_N = 0;           // contatore campioni                            [0 - N]
    int index_Singola = 0;
    
    // variabili acquisizione
    uint16_t values[16384];    // array che contiene tutti gli N valori di un periodo
    uint16_t values_Singola[50];
    
    char micro[8];
    
    // variabili ricezione seriale
    char RXData[32];           // buffer che contiene la stringa ricevuta
    char RXByte;               // ultimo byte ricevuto
    int  index_RX;             // indice del ciclo che riempie il buffer RXData
    int indice;
    int somma = 0;
    int media = 0;
    
    
    
    // ==========Funzioni in/out==========
    
    void IR_off()    {P4OUT    |=  BIT2;}
    void IR_on()     {P4OUT    &= ~BIT2;}
    //void fan_on()  {P4OUT    |=  BIT4;}
    //void fan_off() {P4OUT    &= ~BIT4;}
    void led_on()    {P1OUT    |=  BIT0;}
    void led_off()   {P1OUT    &= ~BIT0;}
    void TA0_on()    {TA0CCTL0 |=  CCIE;}
    void TA0_off()   {TA0CCTL0 &= ~CCIE;}
    void auto_on(){
    	//fan_on();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L |= RTCTEVIE;
    	RTCCTL0_H = 0;}
    void auto_off(){
    	//fan_off();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIE;
    	RTCCTL0_H = 0;}
    
    // ===========================Funzioni Real Time Clock===========================
    
    void get_now(char *now){
    	// legge i registri del 'Real Time Clock' e scrive la data in now
    	sprintf(now     , "%04x-",  RTCYEAR);
    	sprintf(now +  5, "%02x-",  RTCMON);
    	sprintf(now +  8, "%02x ",  RTCDAY);
    	sprintf(now + 11, "%02x:",  RTCHOUR);
    	sprintf(now + 14, "%02x:",  RTCMIN);
    	sprintf(now + 17, "%02x\n", RTCSEC);
    }
    
    void set_rtc(char *date){
    	// riceve una data in formato stringa 'YYYY-MM-DD hh:mm:ss' e scrive i registri del 'Real Time Clock'
    	RTCCTL0_H = RTCKEY_H;                       //   Unlock RTC key protected registers
    	RTCYEAR = strtol(&date[ 0], NULL, 16);
    	RTCMON  = strtol(&date[ 5], NULL, 16);
    	RTCDAY  = strtol(&date[ 8], NULL, 16);
    	RTCHOUR = strtol(&date[11], NULL, 16);
    	RTCMIN  = strtol(&date[14], NULL, 16);
    	RTCSEC  = strtol(&date[17], NULL, 16);
        RTCCTL0_H = 0;                              //   Lock RTC key protected registers
    }
    
    
    
    // ===========================Funzioni Protocollo Seriale===========================
    
    void send_char(char c){                       // invia un carattere sulla seriale
    	while(!(UCA0IFG&UCTXIFG)); //finchè c' da trasmettere
    	UCA0TXBUF = c; //riempie il buffer di trasmissione
    }
    
    int send(char command, char *text){
    	send_char(command);                       // invio comando
    	int i=0;
    	while (text[i] != 10){				      // finchè è diverso da 'a capo \n'
    		send_char(text[i]);
    		i++;
    	}                                         // invio testo
    	send_char(10);                            // invio '\n'
    	return i+2;                               // restituisce il numero di caratteri inviati
    }
    
    void send_all_values(){
    	int i, i_N;
    	int n=0;
    	int samples_per_row = 20; //sono il numero di campioni su gni riga stampati su monitor (10000/20=500 righe di campioni adc)
    	char TXData[32];
    	char UTC[32];
    	get_now(UTC);
    	for (i_N=0; i_N<N; i_N++){
    		if (i_N == n){
    			n += samples_per_row;
    			send_char(com_VALUE);
    			for(i = 0; i < 19; i++) //19 lunghezza timestamp
    				send_char(UTC[i]);
    			send_char(';');
    		}
    		sprintf(TXData, "%d,", values[i_N]);
    		for(i = 0; i < strlen(TXData); i++)
    			send_char(TXData[i]);
    		if (i_N == n-1 || i_N == N-1){
    			send_char(10);
    		}
    	}
    	send(com_END, "E\n");
    }
    
    
    void received(){                              // elabora i dati ricevuti dalla seriale
    	char TXData[32];
    	int mode;
    	switch(RXData[0]){
    		case (int)com_PING:
    			send(com_PING, "ok\n");
    			break;
    		case (int)com_T:
    			T = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", T);
    			send(com_T, TXData);
    			break;
    		case (int)com_N:
    			N = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", N);
    			send(com_N, TXData);
    			break;
    		case (int)com_UTC:
    			set_rtc(&RXData[1]);
    			get_now(TXData);
    			send(com_UTC, TXData);
    			break;
    		case (int)com_MODE:
    			mode = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", mode);
    			send(com_MODE, TXData);
    			if (mode == 1) {
    				auto_on();
    			}
    			else {
    				auto_off();
    			}
    			break;
    		case (int)com_START:
    			microsecondi = 0;
    			TA0_on();
    			led_on();
    			send(com_START, "\n");
    			break;
    		case (int)com_RESET:
    			send(com_LOG, "ricevuto comando reset\n");
    			RSTCTL_RESET_REQ |= (0x6900 | RSTCTL_RESET_REQ_HARD_REQ);
    		default:
    			send(com_LOG, "comando sconosciuto\n");
    			break;
    	}
    }
    
    // ===========================Funzioni adc===========================
    int main(void)
    {
    	// ==========Configurazioni di base==========
    	WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    
    	P1DIR |= BIT0;
    	P1OUT |= BIT0;                     // Set 1.0 as digital out (led rosso a bordo)
    
    	P4DIR |= BIT2;
    	P4OUT |= BIT2;              // Set 4.2 as digital out (IR)
    
    	SCB_SCR |= SCB_SCR_SLEEPONEXIT;    // Enable sleep on exit from ISR
    	__enable_interrupt();
    
    
    	// ==========Configurazione ADC 14 bit==========
    	P6SEL1 |= BIT1;
    	P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC
    
    	ADC14CTL0 &= ~ADC14ENC;							  //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
    	ADC14CTL0 = ADC14ON | ADC14SHP | ADC14CONSEQ_2;   // Turn on ADC14, set sampling time, repeat single channel
    	//ADC14_CTL0_SHS_1 why 1??
    	ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results
    	ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V
    	//ADC14IER0 |= ADC14IE0;						//????????????????????????
    	ADC14CTL0 |= ADC14ENC;                          // ADC enable
    
    
    	// ==========Configurazione clock a 12 MHz==========
    	CSKEY = 0x695A;                    // Unlock CS module for register access
    	CSCTL0 = 0;                        // Reset tuning parameters
    	CSCTL0 = DCORSEL_3;                // Set DCO to 12MHz (nominal, center of 8-16MHz range)
    	// Here have i change DCO and SMCLK?????
    	CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
    	CSKEY = 0;                         // Lock CS module from unintended accesses
    
    
    	// ==========Configurazione TimerA0==========
    	NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
    	TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
    	TA0CCR0 = STEP;                             // Overflow (step dell'interrupt)
    	TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
    	TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)
    
    
    	// ==========Configurazione Seriale==========
    	P1SEL0 |= BIT2 | BIT3;                       // set 2-UART pin as second function
    	NVIC_ISER0 = 1 << ((INT_EUSCIA0 - 16) & 31); // Enable eUSCIA0 interrupt in NVIC module
    
    	// Configure UART
    	UCA0CTLW0 |= UCSWRST;
    	UCA0CTLW0 |= UCSSEL__SMCLK;                  // Put eUSCI in reset
    	    /* Baud Rate calculation
    	     * 12.000.000/(16*9.600) = 78.125
    	     * Fractional portion = 0.125
    	     * User's Guide Table 21-4: UCBRSx = 0x10
    	     * UCBRFx = int ( (78.125-78)*16) = 2
    	     */
    	UCA0BR0 = 78;                                 // 12.000.000 / 16 / 9.600 = 78,125
    	UCA0BR1 = 0x00;
    	UCA0MCTLW = 0x1000 | UCOS16 | 0x0020;
    	UCA0CTLW0 &= ~UCSWRST;                        // Initialize eUSCI
    	UCA0IE |= UCRXIE;                             // Enable USCI_A0 RX interrupt
    
    
    	// ==========Configurazione Real Time Clock==========
        RTCCTL0_H = RTCKEY_H;                    // Unlock RTC key protected registers
        RTCCTL0_L &= ~RTCTEVIE;                  // Interrupt disable
        RTCCTL0_L &= ~(RTCTEVIFG);
        RTCCTL1 = RTCBCD | RTCHOLD;              // RTCTEVx -> 00b = Minute changed event
        // RTC enable, BCD mode, RTC hold
        // enable RTC read ready interrupt
        // enable RTC time event interrupt
        RTCCTL1 &= ~(RTCHOLD);                    // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock the RTC registers
    
        NVIC_ISER0 = 1 << ((INT_RTC_C - 16) & 31);
    
    	// ==========Inizializzazione==========
        //fan_off();
        led_off();							// Led msp spento (si accende solo alle acquisizioni)
        IR_off();							// Led IR spento
        set_rtc("2000-01-01 00:00:00");		// Setto rtc
    
    	__sleep();
    	//while(1);
    }
    
    
    // ===========================Interrupt Service Routines===========================
    void ADC14_IRQHandler(void)
    {
    	// What i keep here and what i keep in time_save???
    }
    
    // Timer A0 interrupt service routine
    void TimerA0_0IsrHandler(void) {
        TA0CCTL0 &= ~CCIFG;
        /* STEP = 20; Tempo di campionamento compreso tra adc_start, adc_end; Riempio array e invio su seriale */
    	if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) {
    		ADC14CTL0 |= ADC14SC; // Start sampling/conversion; Enable and start conversion.
    		//sprintf(micro, "%07d\n", microsecondi);	// Converto da intero a stringa dentro variabile micro
    		//send(com_LOG, micro);						// ---------->USED TO CONTROL TIMERA AND FREQUENCY
    		while(!(ADC14IFGR0 & BIT0));				// Controlla che non abbia finito
    		values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    		index_N++;									// Incrementa indice array
    	}
        switch (microsecondi){
    		case TIME_ON:	// Accendo IR
    			IR_on();
    			break;
    		case TIME_OFF:	// Spengo IR
    			IR_off();
    			break;
    		case TIME_SAVE:	// Invio valori
    			if (index_N >= N){
    				index_N = 0; //Need it
    				microsecondi = 0;
    				TA0_off();
    				led_off();
    				send_all_values(); //Need it
    			}
    		default:
    			break;
    		}
        microsecondi += STEP;	// Interrupt ogni STEP
        if (microsecondi >= TIME_RESET){
        	microsecondi = 0;
        }
    }
    
    
    // UART interrupt service routine (Seriale)
    void eUSCIA0IsrHandler(void) {
    	/* Eseguita quando viene ricevuto un byte sulla seriale.
    	 * Se il byte e' 10 (\n) chiama received che elabora il messaggio ricevuto
    	 */
        if (UCA0IFG & UCRXIFG) {
    		while(!(UCA0IFG & UCTXIFG));
    		RXByte = UCA0RXBUF;
    		RXData[index_RX++] = RXByte;
    		if(RXByte==10) {
    		  index_RX=0;
    		  received();
    		}
        }
    }
    
    // RTC interrupt service routine (essendo in real time VA IN MINUTI vedi configurazione SOPRA rtc)
    void RtcIsrHandler(void) {
        if (RTCCTL0 & RTCTEVIFG) {
        	// Evento abilitato in modalita' automatica, accade ogni minuto
        	if (minuti == 0){
        		TA0_on();
    			led_on();
        	}
    		// Se T e' maggiore di 5 minuti, spengo la ventola e la riaccendo un minuto prima di ricominciare
    		/*if (T > 5){
    			if (minuti == 4){
    				//fan_off();
    			}
    			if (minuti >= T-1){
    				//fan_on();
    			}
    		}*/
    		minuti++;
    		if (minuti >= T) {
    			minuti = 0;
    			microsecondi = 0;
    		}
    	}
        // Chiudo l'interrupt flag
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIFG;
    	RTCCTL0_H = 0;
    }
    

  • i have posted below my complete code. When you can please read it and if you want modify it. Thanks for help :)
  • luca_code_01.c
    #include "msp.h"
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    
    // ==========Definizione costanti==========
    
    typedef enum {false=0, true=1} bool;      // definizione tipo bool
    
    // costanti dei tempi di acquisizione
    #define STEP			10		//	10 us	 tempo dell'interrupt
    #define TIME_ON			200		//	200 us	 led IR on, signal = 0 V
    #define TIME_ADC_START	100		//	100 us	 inizio istante di acquisizione
    #define TIME_OFF		650		//	650 us	 led IR off, signal = 3,3 V
    #define TIME_ADC_END	800		//	800 us	 fine istante di acquisizione
    #define TIME_SAVE		1200	//	900 us	 controllo fine ciclo
    #define TIME_RESET		10000	//	1.000 us periodo del segnale generato
    
    // costanti comandi protocollo trasmissione seriale
    #define com_PING  'P'         // ping
    #define com_T     'T'         // parametro T (periodo in minuti)
    #define com_N     'N'         // parametro N (numero campioni per periodo)
    #define com_UTC   'U'         // sincronizzazione data/ora
    #define com_MODE  'A'         // 0 (off) - 1 (auto) - 2 (manual)
    #define com_START 'S'         // inizio acquisizione degli N valori
    #define com_RESET 'R'         // provoca reset
    
    #define com_VALUE 'D'         // invio di N valori
    #define com_END   'E'         // fine invio valori
    #define com_LOG   'L'         // stringa di log
    
    
    // ==========Dichiarazione variabili globali==========
    
    // parametri modificabili via seriale a run-time
    int T =   5;  // min      // Periodo globale di acquisizione               [N/100/60  < T  < 32767]
    int N = 100;              // Numero di campioni memorizzati per periodo    [    1     < N  < 16384]
    
    // variabili tempi
    int microsecondi = 0;      // contatore di microsecondi del timer A0        [0 - 10.000]
    int minuti = 0;            // contatore di minuti del Real Time Clock       [0 - T]
    
    // indici contatori array
    int index_N = 0;           // contatore campioni                            [0 - N]
    int index_Singola = 0;
    
    // variabili acquisizione
    uint16_t values[16384];    // array che contiene tutti gli N valori di un periodo
    uint16_t values_Singola[50];
    
    char micro[8];
    
    // variabili ricezione seriale
    char RXData[32];           // buffer che contiene la stringa ricevuta
    char RXByte;               // ultimo byte ricevuto
    int  index_RX;             // indice del ciclo che riempie il buffer RXData
    int indice;
    int somma = 0;
    int media = 0;
    
    
    
    // ==========Funzioni in/out==========
    
    void IR_off()    {P4OUT    |=  BIT2;}
    void IR_on()     {P4OUT    &= ~BIT2;}
    //void fan_on()  {P4OUT    |=  BIT4;}
    //void fan_off() {P4OUT    &= ~BIT4;}
    void led_on()    {P1OUT    |=  BIT0;}
    void led_off()   {P1OUT    &= ~BIT0;}
    void TA0_on()    {TA0CCTL0 |=  CCIE;}
    void TA0_off()   {TA0CCTL0 &= ~CCIE;}
    void auto_on(){
    	//fan_on();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L |= RTCTEVIE;
    	RTCCTL0_H = 0;}
    void auto_off(){
    	//fan_off();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIE;
    	RTCCTL0_H = 0;}
    
    // ===========================Funzioni Real Time Clock===========================
    
    void get_now(char *now){
    	// legge i registri del 'Real Time Clock' e scrive la data in now
    	sprintf(now     , "%04x-",  RTCYEAR);
    	sprintf(now +  5, "%02x-",  RTCMON);
    	sprintf(now +  8, "%02x ",  RTCDAY);
    	sprintf(now + 11, "%02x:",  RTCHOUR);
    	sprintf(now + 14, "%02x:",  RTCMIN);
    	sprintf(now + 17, "%02x\n", RTCSEC);
    }
    
    void set_rtc(char *date){
    	// riceve una data in formato stringa 'YYYY-MM-DD hh:mm:ss' e scrive i registri del 'Real Time Clock'
    	RTCCTL0_H = RTCKEY_H;                       //   Unlock RTC key protected registers
    	RTCYEAR = strtol(&date[ 0], NULL, 16);
    	RTCMON  = strtol(&date[ 5], NULL, 16);
    	RTCDAY  = strtol(&date[ 8], NULL, 16);
    	RTCHOUR = strtol(&date[11], NULL, 16);
    	RTCMIN  = strtol(&date[14], NULL, 16);
    	RTCSEC  = strtol(&date[17], NULL, 16);
        RTCCTL0_H = 0;                              //   Lock RTC key protected registers
    }
    
    
    
    // ===========================Funzioni Protocollo Seriale===========================
    
    void send_char(char c){                       // invia un carattere sulla seriale
    	while(!(UCA0IFG&UCTXIFG)); //finchè c' da trasmettere
    	UCA0TXBUF = c; //riempie il buffer di trasmissione
    }
    
    int send(char command, char *text){
    	send_char(command);                       // invio comando
    	int i=0;
    	while (text[i] != 10){				      // finchè è diverso da 'a capo \n'
    		send_char(text[i]);
    		i++;
    	}                                         // invio testo
    	send_char(10);                            // invio '\n'
    	return i+2;                               // restituisce il numero di caratteri inviati
    }
    
    void send_all_values(){
    	int i, i_N;
    	int n=0;
    	int samples_per_row = 20; //sono il numero di campioni su gni riga stampati su monitor (10000/20=500 righe di campioni adc)
    	char TXData[32];
    	char UTC[32];
    	get_now(UTC);
    	for (i_N=0; i_N<N; i_N++){
    		if (i_N == n){
    			n += samples_per_row;
    			send_char(com_VALUE);
    			for(i = 0; i < 19; i++) //19 lunghezza timestamp
    				send_char(UTC[i]);
    			send_char(';');
    		}
    		sprintf(TXData, "%d,", values[i_N]);
    		for(i = 0; i < strlen(TXData); i++)
    			send_char(TXData[i]);
    		if (i_N == n-1 || i_N == N-1){
    			send_char(10);
    		}
    	}
    	send(com_END, "E\n");
    }
    
    
    void received(){                              // elabora i dati ricevuti dalla seriale
    	char TXData[32];
    	int mode;
    	switch(RXData[0]){
    		case (int)com_PING:
    			send(com_PING, "ok\n");
    			break;
    		case (int)com_T:
    			T = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", T);
    			send(com_T, TXData);
    			break;
    		case (int)com_N:
    			N = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", N);
    			send(com_N, TXData);
    			break;
    		case (int)com_UTC:
    			set_rtc(&RXData[1]);
    			get_now(TXData);
    			send(com_UTC, TXData);
    			break;
    		case (int)com_MODE:
    			mode = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", mode);
    			send(com_MODE, TXData);
    			if (mode == 1) {
    				auto_on();
    			}
    			else {
    				auto_off();
    			}
    			break;
    		case (int)com_START:
    			microsecondi = 0;
    			TA0_on();
    			led_on();
    			send(com_START, "\n");
    			break;
    		case (int)com_RESET:
    			send(com_LOG, "ricevuto comando reset\n");
    			RSTCTL_RESET_REQ |= (0x6900 | RSTCTL_RESET_REQ_HARD_REQ);
    		default:
    			send(com_LOG, "comando sconosciuto\n");
    			break;
    	}
    }
    
    // ===========================Funzioni adc===========================
    int main(void)
    {
    	// ==========Configurazioni di base==========
    	WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    
    	P1DIR |= BIT0;
    	P1OUT |= BIT0;                     // Set 1.0 as digital out (led rosso a bordo)
    
    	P4DIR |= BIT2;
    	P4OUT |= BIT2;              // Set 4.2 as digital out (IR)
    
    	SCB_SCR |= SCB_SCR_SLEEPONEXIT;    // Enable sleep on exit from ISR
    	__enable_interrupt();
    
    
    	// ==========Configurazione ADC 14 bit==========
    	P6SEL1 |= BIT1;
    	P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC
    
    	ADC14CTL0 &= ~ADC14ENC;							  //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
    	ADC14CTL0 = ADC14ON | ADC14SHP | ADC14CONSEQ_2;   // Turn on ADC14, set sampling time, repeat single channel
    	//ADC14_CTL0_SHS_1 why 1?? this sets the trigger for the ADC, 0 -> ADC14SC , 1-> TimerA0CCR1 see datasheet Table 6-51 ADC14 Trigger Signal Connections
    	ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results
    	ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V
    	ADC14IER0 |= ADC14IE0;						//???????????????????????? - see next line
        //ADC14->IER0 |= ADC14_IER0_IE0;          // Enable ADC conv complete interrupt, Section 20.3.9 of SLAU356 ADC14IER0 Register
    	ADC14CTL0 |= ADC14ENC;                          // ADC enable
    
    
    	// ==========Configurazione clock a 12 MHz==========
    	CSKEY = 0x695A;                    // Unlock CS module for register access
    	CSCTL0 = 0;                        // Reset tuning parameters
    	CSCTL0 = DCORSEL_3;                // Set DCO to 12MHz (nominal, center of 8-16MHz range)
    	// Here have i change DCO and SMCLK?????
    	CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
    	CSKEY = 0;                         // Lock CS module from unintended accesses
    
    
    	// ==========Configurazione TimerA0==========
    	NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
    	TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
    	TA0CCR0 = STEP;                             // Overflow (step dell'interrupt)
    	TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
    	TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)
    
    
    	// ==========Configurazione Seriale==========
    	P1SEL0 |= BIT2 | BIT3;                       // set 2-UART pin as second function
    	NVIC_ISER0 = 1 << ((INT_EUSCIA0 - 16) & 31); // Enable eUSCIA0 interrupt in NVIC module
    
    	// Configure UART
    	UCA0CTLW0 |= UCSWRST;
    	UCA0CTLW0 |= UCSSEL__SMCLK;                  // Put eUSCI in reset
    	    /* Baud Rate calculation
    	     * 12.000.000/(16*9.600) = 78.125
    	     * Fractional portion = 0.125
    	     * User's Guide Table 21-4: UCBRSx = 0x10
    	     * UCBRFx = int ( (78.125-78)*16) = 2
    	     */
    	UCA0BR0 = 78;                                 // 12.000.000 / 16 / 9.600 = 78,125
    	UCA0BR1 = 0x00;
    	UCA0MCTLW = 0x1000 | UCOS16 | 0x0020;
    	UCA0CTLW0 &= ~UCSWRST;                        // Initialize eUSCI
    	UCA0IE |= UCRXIE;                             // Enable USCI_A0 RX interrupt
    
    
    	// ==========Configurazione Real Time Clock==========
        RTCCTL0_H = RTCKEY_H;                    // Unlock RTC key protected registers
        RTCCTL0_L &= ~RTCTEVIE;                  // Interrupt disable
        RTCCTL0_L &= ~(RTCTEVIFG);
        RTCCTL1 = RTCBCD | RTCHOLD;              // RTCTEVx -> 00b = Minute changed event
        // RTC enable, BCD mode, RTC hold
        // enable RTC read ready interrupt
        // enable RTC time event interrupt
        RTCCTL1 &= ~(RTCHOLD);                    // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock the RTC registers
    
        NVIC_ISER0 = 1 << ((INT_RTC_C - 16) & 31);
    
        // Enable ADC interrupt in NVIC module
        NVIC->ISER[0] |= 1 << ((ADC14_IRQn) & 31);
    
    	// ==========Inizializzazione==========
        //fan_off();
        led_off();							// Led msp spento (si accende solo alle acquisizioni)
        IR_off();							// Led IR spento
        set_rtc("2000-01-01 00:00:00");		// Setto rtc
    
    	__sleep();
    	//while(1);
    }
    
    
    // ===========================Interrupt Service Routines===========================
    void ADC14_IRQHandler(void)
    {
    	// What i keep here and what i keep in time_save???
    	values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    	index_N++;
    }
    
    // Timer A0 interrupt service routine
    void TimerA0_0IsrHandler(void) {
        TA0CCTL0 &= ~CCIFG;
        /* STEP = 20; Tempo di campionamento compreso tra adc_start, adc_end; Riempio array e invio su seriale */
    	if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) {
    		ADC14CTL0 |= ADC14SC; // Start sampling/conversion; Enable and start conversion.
    		//sprintf(micro, "%07d\n", microsecondi);	// Converto da intero a stringa dentro variabile micro
    		//send(com_LOG, micro);						// ---------->USED TO CONTROL TIMERA AND FREQUENCY
    //		while(!(ADC14IFGR0 & BIT0));				// Controlla che non abbia finito
    //		values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    //		index_N++;									// Incrementa indice array
    	}
        switch (microsecondi){
    		case TIME_ON:	// Accendo IR
    			IR_on();
    			break;
    		case TIME_OFF:	// Spengo IR
    			IR_off();
    			break;
    		case TIME_SAVE:	// Invio valori
    			if (index_N >= N){
    				index_N = 0; //Need it
    				microsecondi = 0;
    				TA0_off();
    				led_off();
    				send_all_values(); //Need it
    			}
    		default:
    			break;
    		}
        microsecondi += STEP;	// Interrupt ogni STEP
        if (microsecondi >= TIME_RESET){
        	microsecondi = 0;
        }
    }
    
    
    // UART interrupt service routine (Seriale)
    void eUSCIA0IsrHandler(void) {
    	/* Eseguita quando viene ricevuto un byte sulla seriale.
    	 * Se il byte e' 10 (\n) chiama received che elabora il messaggio ricevuto
    	 */
        if (UCA0IFG & UCRXIFG) {
    		while(!(UCA0IFG & UCTXIFG));
    		RXByte = UCA0RXBUF;
    		RXData[index_RX++] = RXByte;
    		if(RXByte==10) {
    		  index_RX=0;
    		  received();
    		}
        }
    }
    
    // RTC interrupt service routine (essendo in real time VA IN MINUTI vedi configurazione SOPRA rtc)
    void RtcIsrHandler(void) {
        if (RTCCTL0 & RTCTEVIFG) {
        	// Evento abilitato in modalita' automatica, accade ogni minuto
        	if (minuti == 0){
        		TA0_on();
    			led_on();
        	}
    		// Se T e' maggiore di 5 minuti, spengo la ventola e la riaccendo un minuto prima di ricominciare
    		/*if (T > 5){
    			if (minuti == 4){
    				//fan_off();
    			}
    			if (minuti >= T-1){
    				//fan_on();
    			}
    		}*/
    		minuti++;
    		if (minuti >= T) {
    			minuti = 0;
    			microsecondi = 0;
    		}
    	}
        // Chiudo l'interrupt flag
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIFG;
    	RTCCTL0_H = 0;
    }
    
    Luca,

    I have simply modified the code so that the ADC ISR handles the moving of data and so that you can remove the IFG check in the Timer ISR.  Hopefully this will help speed up the ISR handling.  I have inserted some answers to questions in your code, but let me know if you have any additional questions.

    Regards,

    Chris

  • Hi Chris,

    you are helping me a lot. My questions are:

    1)ADC14_CTL0_SHS_1: ok, so if adc start when i want i don't need it or shs_0 is the default values? Because in your code is not setted.

    2)ADC14CTL0 |= ADC14ENC: this enables adc conversion. I keep so or i keep togheter ADC14CTL0 |= ADC14SC?

    3)Why do you tell me "Hopefully this will help speed up the ISR handling?". It's not good as thing?

    4)TA0CCR0 = STEP: i have to keep so or write step-1?

    5)NVIC_ISER0 = 1 << ((INT_ADC14 - 16) & 31) --> i need this?

    6)ADC14CTL0 &= ~ADC14ENC; when index_N>=N i haven't to set this to stop conversion?

    If i have more questions i will write you soon.

    Luca

  • Luca,
    (1) The SHS bit field in ADC14 control register 0 (ADC14CTL0) controlls the trigger source for the ADC14. If this field is '0' then the ADC is triggered from the SC bit which is controlled in firmware.
    (2) You do not have to keep the ENC and SC in the same instruction. These can be in the same instruction but the ENC bit is not automatically cleared like the SC bit so it is not necessary to keep setting the ENC bit. Please see www.ti.com/.../slau356d.pdf .
    (3) The Timer ISR takes too long and you will not be able to get the number of samples that you want. For example if you want to sample at 5us rate for 1ms you should get 200 samples. With the 'if' statement that checks the variable microsecond the Timer ISR prevents service of the ADC14 interrupt and you end up missing samples. In the previous code I sent I put in error checks to help identify when this happens. My understanding of the original problem was that you were seeing 1.2ms instead of 1ms. This is because the time keeping mechanism microsecond+=STEP is dependent upon servicing the timer ISR at each step (5us) but in reality because the ISR takes so long the actual time for each interrupt service is longer.
    (4) Yes. Please see www.ti.com/.../slau356d.pdf .
    (5) Yes, if you want to service the ADC14 interrupt. I see several places in the code where you use '=' instead of '|=' when writing to NVIC_ISER0 please ensure that you are not overwriting your previous instructions. '|=' is a logical 'OR' while '&=' is a logical 'AND'. Typically |= is used to set bits while '&= ~' is used to clear bits, the '~' is the logical inverse so ~0x01 would be 0xFE.
    (6) Yes the idea is to stop the ADC after the correct number of conversions has been recorded.

    Regards,
    Chris
  • With  the code you have sent me i note that adc doesn't work good because the values are very low. First with my code i had accettable values. So what is wrong? 

    When i use NVIC_ISER0 for example is use this code because a lot of example are written so. I think it is correct. 

    Is the timerA setted correctly? I don't know what is wrong. It seems that in this mode adc works worse :(

  • Luca,
    You are correct and I was incorrect about the NVIC_ISER0. Writing a '0' has no effect so you can write to each bit location independently with a simple '=' instruction. www.ti.com/.../slau356d.pdf . Is it just the conversion that is low or are there several? I do not currently have the ability to test the non-CMSIS code so I was not able to test. I should have cleared the IFG before enabling the interrupt to ensure that the code does not immediately go to the ISR before a conversion actually happens.

    Chris
  • I post here two example of csv file that i receive from conversion. The first is with my old code (all adc starting and array is in isr timer) and the second is your code.

    Also if i make TIME_SAVE longer to give time at adc to finish sampling or conversion, it doens't change. 

    Files.zip

  • Maybe the problem can be the setting of SHP bit that is determined by adc14sht0x (clock cycles)??? 

    the strange thing is that if i keep adc14 sampling into isr timer, it takes values. If i use isrADC interrupt the values are very low and wrong, i'm sure. So i don't know what do. Sorry for the long post, i understand if you want to go on holiday ahah

    In a precedent post you write me: "

    ADC14->MCTL[0] = ADC14_MCTLN_VRSEL_0 | ADC14_MCTLN_INCH_14; // A14 ADC input select; Vref=AVCC=3.3V
    // with a setting of 14, the conversion takes 16 clocks, the default ADC clock is MODCLOCK, ~25Mhz
    // 20 clocks at 25Mhz -> 800ns

    ".

    Another thing: i have tried to set DCO at 24MHZ because you wrote me "I had to increase the clock frequency to 24Mhz to prevent any missing conversions.  There were instances where the time spent in the ISR was too long and consequently either did not support the 1/5us sampling rate or in the case of using the ADC interrupt, multiple interrupts would occur and the previous sample was overwritten before it could be read. " But for serial comunication i need 12MHZ. I tried to use divs field (divs_1) to divide 2. But it doesn't work. 

    Maybe is this? CS->CLKEN |= CS_CLKEN_MODOSC_EN;

    code2.txt
    #include "msp.h"
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    
    // ==========Definizione costanti==========
    
    typedef enum {false=0, true=1} bool;      // definizione tipo bool
    
    // costanti dei tempi di acquisizione
    #define STEP			10		//	10 us	 tempo dell'interrupt
    #define TIME_ON			50		//	200 us	 led IR on, signal = 0 V
    #define TIME_ADC_START	0		//	100 us	 inizio istante di acquisizione
    #define TIME_OFF		450		//	650 us	 led IR off, signal = 3,3 V
    #define TIME_ADC_END	1000	//	800 us	 fine istante di acquisizione
    #define TIME_SAVE		1200	//	900 us	 controllo fine ciclo
    #define TIME_RESET		10000	//	1.000 us periodo del segnale generato
    
    // costanti comandi protocollo trasmissione seriale
    #define com_PING  'P'         // ping
    #define com_T     'T'         // parametro T (periodo in minuti)
    #define com_N     'N'         // parametro N (numero campioni per periodo)
    #define com_UTC   'U'         // sincronizzazione data/ora
    #define com_MODE  'A'         // 0 (off) - 1 (auto) - 2 (manual)
    #define com_START 'S'         // inizio acquisizione degli N valori
    #define com_RESET 'R'         // provoca reset
    
    #define com_VALUE 'D'         // invio di N valori
    #define com_END   'E'         // fine invio valori
    #define com_LOG   'L'         // stringa di log
    
    
    // ==========Dichiarazione variabili globali==========
    
    // parametri modificabili via seriale a run-time
    int T =   5;  // min      // Periodo globale di acquisizione               [N/100/60  < T  < 32767]
    int N = 100;              // Numero di campioni memorizzati per periodo    [    1     < N  < 16384]
    
    // variabili tempi
    int microsecondi = 0;      // contatore di microsecondi del timer A0        [0 - 10.000]
    int minuti = 0;            // contatore di minuti del Real Time Clock       [0 - T]
    
    // indici contatori array
    int index_N = 0;           // contatore campioni                            [0 - N]
    int index_Singola = 0;
    
    // variabili acquisizione
    uint16_t values[16384];    // array che contiene tutti gli N valori di un periodo
    uint16_t values_Singola[50];
    
    char micro[8];
    
    // variabili ricezione seriale
    char RXData[32];           // buffer che contiene la stringa ricevuta
    char RXByte;               // ultimo byte ricevuto
    int  index_RX;             // indice del ciclo che riempie il buffer RXData
    int indice;
    int somma = 0;
    int media = 0;
    
    
    
    // ==========Funzioni in/out==========
    
    void IR_off()    {P4OUT    |=  BIT2;}
    void IR_on()     {P4OUT    &= ~BIT2;}
    //void fan_on()  {P4OUT    |=  BIT4;}
    //void fan_off() {P4OUT    &= ~BIT4;}
    void led_on()    {P1OUT    |=  BIT0;}
    void led_off()   {P1OUT    &= ~BIT0;}
    void TA0_on()    {TA0CCTL0 |=  CCIE;}
    void TA0_off()   {TA0CCTL0 &= ~CCIE;}
    void auto_on(){
    	//fan_on();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L |= RTCTEVIE;
    	RTCCTL0_H = 0;}
    void auto_off(){
    	//fan_off();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIE;
    	RTCCTL0_H = 0;}
    
    // ===========================Funzioni Real Time Clock===========================
    
    void get_now(char *now){
    	// legge i registri del 'Real Time Clock' e scrive la data in now
    	sprintf(now     , "%04x-",  RTCYEAR);
    	sprintf(now +  5, "%02x-",  RTCMON);
    	sprintf(now +  8, "%02x ",  RTCDAY);
    	sprintf(now + 11, "%02x:",  RTCHOUR);
    	sprintf(now + 14, "%02x:",  RTCMIN);
    	sprintf(now + 17, "%02x\n", RTCSEC);
    }
    
    void set_rtc(char *date){
    	// riceve una data in formato stringa 'YYYY-MM-DD hh:mm:ss' e scrive i registri del 'Real Time Clock'
    	RTCCTL0_H = RTCKEY_H;                       //   Unlock RTC key protected registers
    	RTCYEAR = strtol(&date[ 0], NULL, 16);
    	RTCMON  = strtol(&date[ 5], NULL, 16);
    	RTCDAY  = strtol(&date[ 8], NULL, 16);
    	RTCHOUR = strtol(&date[11], NULL, 16);
    	RTCMIN  = strtol(&date[14], NULL, 16);
    	RTCSEC  = strtol(&date[17], NULL, 16);
        RTCCTL0_H = 0;                              //   Lock RTC key protected registers
    }
    
    
    
    // ===========================Funzioni Protocollo Seriale===========================
    
    void send_char(char c){                       // invia un carattere sulla seriale
    	while(!(UCA0IFG&UCTXIFG)); //finchè c' da trasmettere
    	UCA0TXBUF = c; //riempie il buffer di trasmissione
    }
    
    int send(char command, char *text){
    	send_char(command);                       // invio comando
    	int i=0;
    	while (text[i] != 10){				      // finchè è diverso da 'a capo \n'
    		send_char(text[i]);
    		i++;
    	}                                         // invio testo
    	send_char(10);                            // invio '\n'
    	return i+2;                               // restituisce il numero di caratteri inviati
    }
    
    void send_all_values(){
    	int i, i_N;
    	int n=0;
    	int samples_per_row = 20; //sono il numero di campioni su gni riga stampati su monitor (10000/20=500 righe di campioni adc)
    	char TXData[32];
    	char UTC[32];
    	get_now(UTC);
    	for (i_N=0; i_N<N; i_N++){
    		if (i_N == n){
    			n += samples_per_row;
    			send_char(com_VALUE);
    			for(i = 0; i < 19; i++) //19 lunghezza timestamp
    				send_char(UTC[i]);
    			send_char(';');
    		}
    		sprintf(TXData, "%d,", values[i_N]);
    		for(i = 0; i < strlen(TXData); i++)
    			send_char(TXData[i]);
    		if (i_N == n-1 || i_N == N-1){
    			send_char(10);
    		}
    	}
    	send(com_END, "E\n");
    }
    
    
    void received(){                              // elabora i dati ricevuti dalla seriale
    	char TXData[32];
    	int mode;
    	switch(RXData[0]){
    		case (int)com_PING:
    			send(com_PING, "ok\n");
    			break;
    		case (int)com_T:
    			T = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", T);
    			send(com_T, TXData);
    			break;
    		case (int)com_N:
    			N = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", N);
    			send(com_N, TXData);
    			break;
    		case (int)com_UTC:
    			set_rtc(&RXData[1]);
    			get_now(TXData);
    			send(com_UTC, TXData);
    			break;
    		case (int)com_MODE:
    			mode = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", mode);
    			send(com_MODE, TXData);
    			if (mode == 1) {
    				auto_on();
    			}
    			else {
    				auto_off();
    			}
    			break;
    		case (int)com_START:
    			microsecondi = 0;
    			TA0_on();
    			led_on();
    			send(com_START, "\n");
    			break;
    		case (int)com_RESET:
    			send(com_LOG, "ricevuto comando reset\n");
    			RSTCTL_RESET_REQ |= (0x6900 | RSTCTL_RESET_REQ_HARD_REQ);
    		default:
    			send(com_LOG, "comando sconosciuto\n");
    			break;
    	}
    }
    
    // ===========================Funzioni adc===========================
    int main(void)
    {
    	// ==========Configurazioni di base==========
    	WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    
    	P1DIR |= BIT0;
    	P1OUT |= BIT0;                     // Set 1.0 as digital out (led rosso a bordo)
    
    	P4DIR |= BIT2;
    	P4OUT |= BIT2;              // Set 4.2 as digital out (IR)
    
    	SCB_SCR |= SCB_SCR_SLEEPONEXIT;    // Enable sleep on exit from ISR
    	__enable_interrupt();
    
    
    	// ==========Configurazione ADC 14 bit==========
    	P6SEL1 |= BIT1;
    	P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC
    
    	NVIC_ISER0 = 1 << ((INT_ADC14 - 16) & 31);
    
    	ADC14CTL0 &= ~ADC14ENC;							  //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
    	ADC14CTL0 = ADC14ON | ADC14SHP | ADC14CONSEQ_2;   // Turn on ADC14, set sampling time, repeat single channel
    	ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results
    	ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V
    	//ADC14CLRIFGR0 |= ADC14IFG0;
    	ADC14IER0 |= ADC14IE0;
    	ADC14CTL0 |= ADC14ENC;                          // ADC enable
    
    
    	// ==========Configurazione clock a 12 MHz==========
    	CSKEY = 0x695A;                    // Unlock CS module for register access
    	CSCTL0 = 0;                        // Reset tuning parameters
    	CSCTL0 = DCORSEL_4;                // Set DCO to 12MHz (nominal, center of 8-16MHz range)
    	CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
    	CSCTL1 = DIVS_1;
    	CSKEY = 0;                         // Lock CS module from unintended accesses
    
    
    	// ==========Configurazione TimerA0==========
    	NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
    	TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
    	TA0CCR0 = STEP-1;                             // Overflow (step dell'interrupt)
    	TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
    	TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)
    
    
    	// ==========Configurazione Seriale==========
    	P1SEL0 |= BIT2 | BIT3;                       // set 2-UART pin as second function
    	NVIC_ISER0 = 1 << ((INT_EUSCIA0 - 16) & 31); // Enable eUSCIA0 interrupt in NVIC module
    
    	// Configure UART
    	UCA0CTLW0 |= UCSWRST;
    	UCA0CTLW0 |= UCSSEL__SMCLK;                  // Put eUSCI in reset
    	    /* Baud Rate calculation
    	     * 12.000.000/(16*9.600) = 78.125
    	     * Fractional portion = 0.125
    	     * User's Guide Table 21-4: UCBRSx = 0x10
    	     * UCBRFx = int ( (78.125-78)*16) = 2
    	     */
    	UCA0BR0 = 78;                                 // 12.000.000 / 16 / 9.600 = 78,125
    	UCA0BR1 = 0x00;
    	UCA0MCTLW = 0x1000 | UCOS16 | 0x0020;
    	UCA0CTLW0 &= ~UCSWRST;                        // Initialize eUSCI
    	UCA0IE |= UCRXIE;                             // Enable USCI_A0 RX interrupt
    
    
    	// ==========Configurazione Real Time Clock==========
        RTCCTL0_H = RTCKEY_H;                    // Unlock RTC key protected registers
        RTCCTL0_L &= ~RTCTEVIE;                  // Interrupt disable
        RTCCTL0_L &= ~(RTCTEVIFG);
        RTCCTL1 = RTCBCD | RTCHOLD;              // RTCTEVx -> 00b = Minute changed event
        // RTC enable, BCD mode, RTC hold
        // enable RTC read ready interrupt
        // enable RTC time event interrupt
        RTCCTL1 &= ~(RTCHOLD);                    // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock the RTC registers
    
        NVIC_ISER0 = 1 << ((INT_RTC_C - 16) & 31);
    
    	// ==========Inizializzazione==========
        //fan_off();
        led_off();							// Led msp spento (si accende solo alle acquisizioni)
        IR_off();							// Led IR spento
        set_rtc("2000-01-01 00:00:00");		// Setto rtc
    
    	__sleep();
    	//while(1);
    }
    
    
    // ===========================Interrupt Service Routines===========================
    void ADC14_IRQHandler(void) {
    	if (ADC14IFGR0 & ADC14IFG0)
    	{
    		values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    		index_N++;
    	}
    }
    
    
    // Timer A0 interrupt service routine
    void TimerA0_0IsrHandler(void) {
        TA0CCTL0 &= ~CCIFG;
        /* STEP = 20; Tempo di campionamento compreso tra adc_start, adc_end; Riempio array e invio su seriale */
    	if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) {
    		ADC14CTL0 |= ADC14SC; // Start sampling/conversion; Enable and start conversion.
    		//sprintf(micro, "%07d\n", microsecondi);	// Converto da intero a stringa dentro variabile micro
    		//send(com_LOG, micro);						// ---------->USED TO CONTROL TIMERA AND FREQUENCY
    		//while(!(ADC14IFGR0 & BIT0));				// Controlla che non abbia finito
    		//values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    		//index_N++;									// Incrementa indice array
    	}
        switch (microsecondi){
    		case TIME_ON:	// Accendo IR
    			IR_on();
    			//send(com_LOG, "IR_ON\n");
    			break;
    		case TIME_OFF:	// Spengo IR
    			IR_off();
    			//send(com_LOG, "IR_OFF\n");
    			break;
    		case TIME_SAVE:	// Invio valori
    			/*
    			QUI DENTRO FACCIO IL CALCOLO MEDIA DELL'ARRAY2 per metterlo in quello principale
    			ovvero la values[index_N] = "media calcolata da values[index_M]"
    			*/
    				/*for(indice = 0; indice < index_Singola; indice ++) {
    					somma += values_Singola[indice];
    				}
    				media = somma/index_Singola;
    				values[index_N] = media;
    				index_N++;
    				media = 0;
    				somma = 0;
    				index_Singola = 0;*/
    				if (index_N >= N){
    					index_N = 0;
    					microsecondi = 0;
    					TA0_off();
    					led_off();
    					send_all_values();
    				}
    		default:
    			break;
    		}
        microsecondi += STEP;	// Interrupt ogni STEP
        if (microsecondi >= TIME_RESET){
        	microsecondi = 0;
        }
    }
    
    
    // UART interrupt service routine (Seriale)
    void eUSCIA0IsrHandler(void) {
    	/* Eseguita quando viene ricevuto un byte sulla seriale.
    	 * Se il byte e' 10 (\n) chiama received che elabora il messaggio ricevuto
    	 */
        if (UCA0IFG & UCRXIFG) {
    		while(!(UCA0IFG & UCTXIFG));
    		RXByte = UCA0RXBUF;
    		RXData[index_RX++] = RXByte;
    		if(RXByte==10) {
    		  index_RX=0;
    		  received();
    		}
        }
    }
    
    // RTC interrupt service routine (essendo in real time VA IN MINUTI vedi configurazione SOPRA rtc)
    void RtcIsrHandler(void) {
        if (RTCCTL0 & RTCTEVIFG) {
        	// Evento abilitato in modalita' automatica, accade ogni minuto
        	if (minuti == 0){
        		TA0_on();
    			led_on();
        	}
    		// Se T e' maggiore di 5 minuti, spengo la ventola e la riaccendo un minuto prima di ricominciare
    		/*if (T > 5){
    			if (minuti == 4){
    				//fan_off();
    			}
    			if (minuti >= T-1){
    				//fan_on();
    			}
    		}*/
    		minuti++;
    		if (minuti >= T) {
    			minuti = 0;
    			microsecondi = 0;
    		}
    	}
        // Chiudo l'interrupt flag
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIFG;
    	RTCCTL0_H = 0;
    }
    

    I'm trying to find the problem. It must be possible resolve it *-*

  • Hi Luca,

    Chris is out of office for a few days, so I'll do my best to help out.

    Let's start with your second question, since it's related to the DCO. Like Chris mentioned, we want to increase the DCO to 24 MHz. Then, we can divide it by two to provide SMCLK with 12 MHz (to preserve your existing TimerA0 and UART configurations for 12 MHz). For now, let's focus on getting this set up properly, then we can come back to the ADC-related discussion.

    For this, I modified the 'msp432p401_cs_03.c' code example to set DCO at 24 MHz, then divide it by 2 for MCLK at 12 MHz, and output MCLK to P4.3 to be easily measured by an oscilloscope. Can you check this? Here's my code below. Since we're increasing the DCO frequency above 16 MHz, we need to transition to VCORE Level 1, as shown in Section 5.8 in the datasheet.

    /* --COPYRIGHT--,BSD_EX
     * Copyright (c) 2013, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *******************************************************************************
     *
     *                       MSP432 CODE EXAMPLE DISCLAIMER
     *
     * MSP432 code examples are self-contained low-level programs that typically
     * demonstrate a single peripheral function or device feature in a highly
     * concise manner. For this the code may rely on the device's power-on default
     * register values and settings such as the clock configuration and care must
     * be taken when combining code from several examples to avoid potential side
     * effects. Also see www.ti.com/.../mspdriverlib for an API functional
     * library & https://dev.ti.com/pinmux/ for a GUI approach to peripheral configuration.
     *
     * --/COPYRIGHT--*/
    //******************************************************************************
    //   MSP432P401 Demo - Device configuration for operation @ DCO = 24MHz and MCLK = 12MHz
    //
    //   Description: Proper device configuration to enable operation at DCO=24MHz
    //   including:
    //   1. Configure VCORE level to 1
    //   2. Configure flash wait-state to 0
    //   3. Configure DCO frequency to 24MHz
    //   4. Ensure MCLK is sourced by DCO and divided by 2
    //
    //   After configuration is complete, MCLK is output to port pin P4.3.
    //
    //                MSP432p401rpz
    //             -----------------
    //         /|\|                 |
    //          | |                 |
    //          --|RST              |
    //            |             P4.3|----> MCLK
    //            |                 |
    //
    //   Dung Dang
    //   Texas Instruments Inc.
    //   October 2015 (updated) | November 2013 (created)
    //   Built with Code Composer Studio V6.0
    //******************************************************************************
    #include "msp.h"
    #include "stdint.h"
    
    
    void error(void);
    
    int main(void)
    {
        uint32_t currentPowerState;
        WDTCTL = WDTPW | WDTHOLD;               // Stop WDT
    
    
        /* NOTE: This example assumes the default power state is AM0_LDO.
         * Refer to  MSP4322001_pcm_0x code examples for more complete PCM operations
         * to exercise various power state transitions between active modes.
         */
    
        /* Step 1: Transition to VCORE Level 1: AM0_LDO --> AM1_LDO */
    
        /* Get current power state, if it's not AM0_LDO, error out */
        currentPowerState = PCM->CTL0 & PCM_CTL0_CPM_MASK;
        if (currentPowerState != PCM_CTL0_CPM_0)
            error();
    
        while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
        PCM->CTL0 = PCM_CTL0_KEY_VAL | PCM_CTL0_AMR_1;
        while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
        if (PCM->IFG & PCM_IFG_AM_INVALID_TR_IFG)
            error();                            // Error if transition was not successful
        if ((PCM->CTL0 & PCM_CTL0_CPM_MASK) != PCM_CTL0_CPM_1)
            error();                            // Error if device is not in AM1_LDO mode
    
        /* Step 2: Configure Flash wait-state to 0 for both banks 0 & 1 */
        FLCTL->BANK0_RDCTL = FLCTL->BANK0_RDCTL & (~FLCTL_BANK0_RDCTL_WAIT_MASK) | FLCTL_BANK0_RDCTL_WAIT_0;
        FLCTL->BANK1_RDCTL  = FLCTL->BANK0_RDCTL & (~FLCTL_BANK1_RDCTL_WAIT_MASK) | FLCTL_BANK1_RDCTL_WAIT_0;
    
        /* Step 3: Configure DCO to 24MHz, ensure MCLK uses DCO as source */
        CS->KEY = CS_KEY_VAL ;                        // Unlock CS module for register access
        CS->CTL0 = 0;                                 // Reset tuning parameters
        CS->CTL0 = CS_CTL0_DCORSEL_4;                 // Set DCO to 24MHz
        /* Select MCLK = DCO divided by 2 */
        CS->CTL1 = CS->CTL1 & ~(CS_CTL1_SELM_MASK | CS_CTL1_DIVM_MASK); // Clear existing registers
        CS->CTL1 = CS->CTL1 | (CS_CTL1_SELM_3 | CS_CTL1_DIVM_1);        // For MCLK, select DCO as source then divide by 2
        CS->KEY = 0;                             // Lock CS module from unintended accesses
    
        /* Step 4: Output MCLK to port pin to demonstrate 12MHz operation */
        P4DIR |= BIT3;
        P4SEL0 |=BIT3;                         // Output MCLK
        P4SEL1 &= ~(BIT3);
    
        /* Go to sleep */
        __sleep();
        __no_operation();                      // For debugger
    }
    
    void error(void)
    {
        volatile uint32_t i;
        P1DIR |= BIT0;
        while (1)
        {
            P1OUT ^= BIT0;
            for(i=0;i<20000;i++);       // Blink LED forever
        }
    }
    

    Since we want to use SMCLK for TimerA0 and UART, we just need to change a few lines of code in main(), as shown below. Moving forward, I'd recommend that you update your code accordingly before we continue tackling the ADC. This should allow your serial communication to work now.

    int main(void)
    {
        uint32_t currentPowerState;
        WDTCTL = WDTPW | WDTHOLD;               // Stop WDT
    
    
        /* NOTE: This example assumes the default power state is AM0_LDO.
         * Refer to  MSP4322001_pcm_0x code examples for more complete PCM operations
         * to exercise various power state transitions between active modes.
         */
    
        /* Step 1: Transition to VCORE Level 1: AM0_LDO --> AM1_LDO */
    
        /* Get current power state, if it's not AM0_LDO, error out */
        currentPowerState = PCM->CTL0 & PCM_CTL0_CPM_MASK;
        if (currentPowerState != PCM_CTL0_CPM_0)
            error();
    
        while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
        PCM->CTL0 = PCM_CTL0_KEY_VAL | PCM_CTL0_AMR_1;
        while ((PCM->CTL1 & PCM_CTL1_PMR_BUSY));
        if (PCM->IFG & PCM_IFG_AM_INVALID_TR_IFG)
            error();                            // Error if transition was not successful
        if ((PCM->CTL0 & PCM_CTL0_CPM_MASK) != PCM_CTL0_CPM_1)
            error();                            // Error if device is not in AM1_LDO mode
    
        /* Step 2: Configure Flash wait-state to 0 for both banks 0 & 1 */
        FLCTL->BANK0_RDCTL = FLCTL->BANK0_RDCTL & (~FLCTL_BANK0_RDCTL_WAIT_MASK) | FLCTL_BANK0_RDCTL_WAIT_0;
        FLCTL->BANK1_RDCTL  = FLCTL->BANK0_RDCTL & (~FLCTL_BANK1_RDCTL_WAIT_MASK) | FLCTL_BANK1_RDCTL_WAIT_0;
    
        /* Step 3: Configure DCO to 24MHz, ensure SMCLK uses DCO as source */
        CS->KEY = CS_KEY_VAL ;                        // Unlock CS module for register access
        CS->CTL0 = 0;                                 // Reset tuning parameters
        CS->CTL0 = CS_CTL0_DCORSEL_4;                 // Set DCO to 24MHz
        /* Select SMCLK = DCO divided by 2 */
        CS->CTL1 = CS->CTL1 & ~(CS_CTL1_SELS_MASK | CS_CTL1_DIVS_MASK); // Clear existing registers
        CS->CTL1 = CS->CTL1 | (CS_CTL1_SELS_3 | CS_CTL1_DIVS_1);        // For SMCLK, select DCO as source then divide by 2
        CS->KEY = 0;                             // Lock CS module from unintended accesses
    
        /* Go to sleep */
        __sleep();
        __no_operation();                      // For debugger
    }

    Please let me know if you were able to measure 12 MHz at P4.3 and then if your serial communication works.

    Regards,

    James

    MSP Customer Applications

  • Hi James,

    I'll try it now :) I modify only this part without see the adc part so i'm sure that serial communication works. One part for time. When i have done i'll write here. One question: in my python program i set serial communication with 9600baud. Is it ok with this change also? Do you need python program to make test? 

    Thanks for all!!!

    Luca

  • It works!!! Ok the problem about SMCLK = 12Mhz and DCO = 24Mhz resolved!! 

    Now we can pass to ADC problem i think :) If you need this is code loaded on msp now. If you see, i have void ADC14_IRQHandler(void) for use interrupt but first i used the if statement. (if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) ) with all part of sampling and memorization of value from buffer to array. What is correct or better? I'll wait your idea ;)

    6136.code2.txt
    #include "msp.h"
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    
    // ==========Definizione costanti==========
    
    typedef enum {false=0, true=1} bool;      // definizione tipo bool
    
    // costanti dei tempi di acquisizione
    #define STEP			10		//	10 us	 tempo dell'interrupt
    #define TIME_ON			50		//	200 us	 led IR on, signal = 0 V
    #define TIME_ADC_START	0		//	100 us	 inizio istante di acquisizione
    #define TIME_OFF		450		//	650 us	 led IR off, signal = 3,3 V
    #define TIME_ADC_END	1000	//	800 us	 fine istante di acquisizione
    #define TIME_SAVE		1200	//	900 us	 controllo fine ciclo
    #define TIME_RESET		10000	//	1.000 us periodo del segnale generato
    
    // costanti comandi protocollo trasmissione seriale
    #define com_PING  'P'         // ping
    #define com_T     'T'         // parametro T (periodo in minuti)
    #define com_N     'N'         // parametro N (numero campioni per periodo)
    #define com_UTC   'U'         // sincronizzazione data/ora
    #define com_MODE  'A'         // 0 (off) - 1 (auto) - 2 (manual)
    #define com_START 'S'         // inizio acquisizione degli N valori
    #define com_RESET 'R'         // provoca reset
    
    #define com_VALUE 'D'         // invio di N valori
    #define com_END   'E'         // fine invio valori
    #define com_LOG   'L'         // stringa di log
    
    
    // ==========Dichiarazione variabili globali==========
    
    // parametri modificabili via seriale a run-time
    int T =   5;  // min      // Periodo globale di acquisizione               [N/100/60  < T  < 32767]
    int N = 100;              // Numero di campioni memorizzati per periodo    [    1     < N  < 16384]
    
    // variabili tempi
    int microsecondi = 0;      // contatore di microsecondi del timer A0        [0 - 10.000]
    int minuti = 0;            // contatore di minuti del Real Time Clock       [0 - T]
    
    // indici contatori array
    int index_N = 0;           // contatore campioni                            [0 - N]
    int index_Singola = 0;
    
    // variabili acquisizione
    uint16_t values[16384];    // array che contiene tutti gli N valori di un periodo
    uint16_t values_Singola[50];
    
    char micro[8];
    
    // variabili ricezione seriale
    char RXData[32];           // buffer che contiene la stringa ricevuta
    char RXByte;               // ultimo byte ricevuto
    int  index_RX;             // indice del ciclo che riempie il buffer RXData
    int indice;
    int somma = 0;
    int media = 0;
    
    
    
    // ==========Funzioni in/out==========
    
    void IR_off()    {P4OUT    |=  BIT2;}
    void IR_on()     {P4OUT    &= ~BIT2;}
    //void fan_on()  {P4OUT    |=  BIT4;}
    //void fan_off() {P4OUT    &= ~BIT4;}
    void led_on()    {P1OUT    |=  BIT0;}
    void led_off()   {P1OUT    &= ~BIT0;}
    void TA0_on()    {TA0CCTL0 |=  CCIE;}
    void TA0_off()   {TA0CCTL0 &= ~CCIE;}
    void auto_on(){
    	//fan_on();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L |= RTCTEVIE;
    	RTCCTL0_H = 0;}
    void auto_off(){
    	//fan_off();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIE;
    	RTCCTL0_H = 0;}
    
    // ===========================Funzioni Real Time Clock===========================
    
    void get_now(char *now){
    	// legge i registri del 'Real Time Clock' e scrive la data in now
    	sprintf(now     , "%04x-",  RTCYEAR);
    	sprintf(now +  5, "%02x-",  RTCMON);
    	sprintf(now +  8, "%02x ",  RTCDAY);
    	sprintf(now + 11, "%02x:",  RTCHOUR);
    	sprintf(now + 14, "%02x:",  RTCMIN);
    	sprintf(now + 17, "%02x\n", RTCSEC);
    }
    
    void set_rtc(char *date){
    	// riceve una data in formato stringa 'YYYY-MM-DD hh:mm:ss' e scrive i registri del 'Real Time Clock'
    	RTCCTL0_H = RTCKEY_H;                       //   Unlock RTC key protected registers
    	RTCYEAR = strtol(&date[ 0], NULL, 16);
    	RTCMON  = strtol(&date[ 5], NULL, 16);
    	RTCDAY  = strtol(&date[ 8], NULL, 16);
    	RTCHOUR = strtol(&date[11], NULL, 16);
    	RTCMIN  = strtol(&date[14], NULL, 16);
    	RTCSEC  = strtol(&date[17], NULL, 16);
        RTCCTL0_H = 0;                              //   Lock RTC key protected registers
    }
    
    
    
    // ===========================Funzioni Protocollo Seriale===========================
    
    void send_char(char c){                       // invia un carattere sulla seriale
    	while(!(UCA0IFG&UCTXIFG)); //finchè c' da trasmettere
    	UCA0TXBUF = c; //riempie il buffer di trasmissione
    }
    
    int send(char command, char *text){
    	send_char(command);                       // invio comando
    	int i=0;
    	while (text[i] != 10){				      // finchè è diverso da 'a capo \n'
    		send_char(text[i]);
    		i++;
    	}                                         // invio testo
    	send_char(10);                            // invio '\n'
    	return i+2;                               // restituisce il numero di caratteri inviati
    }
    
    void send_all_values(){
    	int i, i_N;
    	int n=0;
    	int samples_per_row = 20; //sono il numero di campioni su gni riga stampati su monitor (10000/20=500 righe di campioni adc)
    	char TXData[32];
    	char UTC[32];
    	get_now(UTC);
    	for (i_N=0; i_N<N; i_N++){
    		if (i_N == n){
    			n += samples_per_row;
    			send_char(com_VALUE);
    			for(i = 0; i < 19; i++) //19 lunghezza timestamp
    				send_char(UTC[i]);
    			send_char(';');
    		}
    		sprintf(TXData, "%d,", values[i_N]);
    		for(i = 0; i < strlen(TXData); i++)
    			send_char(TXData[i]);
    		if (i_N == n-1 || i_N == N-1){
    			send_char(10);
    		}
    	}
    	send(com_END, "E\n");
    }
    
    
    void received(){                              // elabora i dati ricevuti dalla seriale
    	char TXData[32];
    	int mode;
    	switch(RXData[0]){
    		case (int)com_PING:
    			send(com_PING, "ok\n");
    			break;
    		case (int)com_T:
    			T = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", T);
    			send(com_T, TXData);
    			break;
    		case (int)com_N:
    			N = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", N);
    			send(com_N, TXData);
    			break;
    		case (int)com_UTC:
    			set_rtc(&RXData[1]);
    			get_now(TXData);
    			send(com_UTC, TXData);
    			break;
    		case (int)com_MODE:
    			mode = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", mode);
    			send(com_MODE, TXData);
    			if (mode == 1) {
    				auto_on();
    			}
    			else {
    				auto_off();
    			}
    			break;
    		case (int)com_START:
    			microsecondi = 0;
    			TA0_on();
    			led_on();
    			send(com_START, "\n");
    			break;
    		case (int)com_RESET:
    			send(com_LOG, "ricevuto comando reset\n");
    			RSTCTL_RESET_REQ |= (0x6900 | RSTCTL_RESET_REQ_HARD_REQ);
    		default:
    			send(com_LOG, "comando sconosciuto\n");
    			break;
    	}
    }
    
    // ===========================Funzioni adc===========================
    int main(void)
    {
    	// ==========Configurazioni di base==========
    	uint32_t currentPowerState;
    	WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    
    	/* Step 1: Transition to VCORE Level 1: AM0_LDO --> AM1_LDO */
    	currentPowerState = PCMCTL0 & CPM_M;
    	if (currentPowerState != CPM_0)
    		error();
    
    	while ((PCMCTL1 & PMR_BUSY));
    	PCMCTL0 = PCM_CTL_KEY_VAL | AMR_1;
    	while ((PCMCTL1 & PMR_BUSY));
    	if (PCMIFG & AM_INVALID_TR_IFG)
    		error();                            // Error if transition was not successful
    	if ((PCMCTL0 & CPM_M) != CPM_1)
    		error();                            // Error if device is not in AM1_LDO mode
    
    	/* Step 2: Configure Flash wait-state to 2 for both banks 0 & 1 */
    	FLCTL_BANK0_RDCTL = FLCTL_BANK0_RDCTL & ~FLCTL_BANK0_RDCTL_WAIT_M | FLCTL_BANK0_RDCTL_WAIT_0;
    	FLCTL_BANK1_RDCTL = FLCTL_BANK0_RDCTL & ~FLCTL_BANK1_RDCTL_WAIT_M | FLCTL_BANK1_RDCTL_WAIT_0;
    
    	P1DIR |= BIT0;
    	P1OUT |= BIT0;                     // Set 1.0 as digital out (led rosso a bordo)
    
    	P4DIR |= BIT2;
    	P4OUT |= BIT2;              // Set 4.2 as digital out (IR)
    
    	SCB_SCR |= SCB_SCR_SLEEPONEXIT;    // Enable sleep on exit from ISR
    	__enable_interrupt();
    
    
    	// ==========Configurazione ADC 14 bit==========
    	P6SEL1 |= BIT1;
    	P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC
    
    	NVIC_ISER0 = 1 << ((INT_ADC14 - 16) & 31);
    
    	ADC14CTL0 &= ~ADC14ENC;							  //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
    	ADC14CTL0 = ADC14ON | ADC14SHP | ADC14CONSEQ_2;   // Turn on ADC14, set sampling time, repeat single channel
    	ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results
    	ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V
    	//ADC14CLRIFGR0 |= ADC14IFG0;
    	ADC14IER0 |= ADC14IE0;
    	ADC14CTL0 |= ADC14ENC;                          // ADC enable
    
    
    	// ==========Configurazione clock a 12 MHz==========
    	CSKEY = 0x695A;                    // Unlock CS module for register access
    	CSCTL0 = 0;                        // Reset tuning parameters
    	CSCTL0 = DCORSEL_4;                // Set DCO to 12MHz (nominal, center of 8-16MHz range)
    	CSCTL1 = CSCTL1 & ~(SELS_M | DIVS_M); // Clear existing registers
    	CSCTL1 = CSCTL1 | (SELS_3 | DIVS_1); // For SMCLK, select DCO as source then divide by 2
    	//CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
    	CSKEY = 0;                         // Lock CS module from unintended accesses
    
    	/* Step 4: Output MCLK to port pin to demonstrate 12MHz operation */
    	P4DIR |= BIT3;
    	P4SEL0 |=BIT3;
    	P4SEL1 &= ~(BIT3);
    
    
    	// ==========Configurazione TimerA0==========
    	NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
    	TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
    	TA0CCR0 = STEP-1;                             // Overflow (step dell'interrupt)
    	TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
    	TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)
    
    
    	// ==========Configurazione Seriale==========
    	P1SEL0 |= BIT2 | BIT3;                       // set 2-UART pin as second function
    	NVIC_ISER0 = 1 << ((INT_EUSCIA0 - 16) & 31); // Enable eUSCIA0 interrupt in NVIC module
    
    	// Configure UART
    	UCA0CTLW0 |= UCSWRST;
    	UCA0CTLW0 |= UCSSEL__SMCLK;                  // Put eUSCI in reset
    	    /* Baud Rate calculation
    	     * 12.000.000/(16*9.600) = 78.125
    	     * Fractional portion = 0.125
    	     * User's Guide Table 21-4: UCBRSx = 0x10
    	     * UCBRFx = int ( (78.125-78)*16) = 2
    	     */
    	UCA0BR0 = 78;                                 // 12.000.000 / 16 / 9.600 = 78,125
    	UCA0BR1 = 0x00;
    	UCA0MCTLW = 0x1000 | UCOS16 | 0x0020;
    	UCA0CTLW0 &= ~UCSWRST;                        // Initialize eUSCI
    	UCA0IE |= UCRXIE;                             // Enable USCI_A0 RX interrupt
    
    
    	// ==========Configurazione Real Time Clock==========
        RTCCTL0_H = RTCKEY_H;                    // Unlock RTC key protected registers
        RTCCTL0_L &= ~RTCTEVIE;                  // Interrupt disable
        RTCCTL0_L &= ~(RTCTEVIFG);
        RTCCTL1 = RTCBCD | RTCHOLD;              // RTCTEVx -> 00b = Minute changed event
        // RTC enable, BCD mode, RTC hold
        // enable RTC read ready interrupt
        // enable RTC time event interrupt
        RTCCTL1 &= ~(RTCHOLD);                    // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock the RTC registers
    
        NVIC_ISER0 = 1 << ((INT_RTC_C - 16) & 31);
    
    	// ==========Inizializzazione==========
        //fan_off();
        led_off();							// Led msp spento (si accende solo alle acquisizioni)
        IR_off();							// Led IR spento
        set_rtc("2000-01-01 00:00:00");		// Setto rtc
    
    	__sleep();
    	//while(1);
    }
    
    void error(void)
    {
        volatile uint32_t z;
        P1DIR |= BIT0;
        while (1)
        {
            P1OUT ^= BIT0;
            for(z=0;z<20000;z++);       // Blink LED forever
        }
    }
    
    
    // ===========================Interrupt Service Routines===========================
    void ADC14_IRQHandler(void) {
    	if (ADC14IFGR0 & ADC14IFG0)
    	{
    		values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    		index_N++;
    	}
    }
    
    
    // Timer A0 interrupt service routine
    void TimerA0_0IsrHandler(void) {
        TA0CCTL0 &= ~CCIFG;
        /* STEP = 20; Tempo di campionamento compreso tra adc_start, adc_end; Riempio array e invio su seriale */
    	if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) {
    		ADC14CTL0 |= ADC14SC; // Start sampling/conversion; Enable and start conversion.
    		//sprintf(micro, "%07d\n", microsecondi);	// Converto da intero a stringa dentro variabile micro
    		//send(com_LOG, micro);						// ---------->USED TO CONTROL TIMERA AND FREQUENCY
    		//values[index_N] = ADC14MEM0;				// FIRST METHOD
    		//index_N++;
    	}
        switch (microsecondi){
    		case TIME_ON:	// Accendo IR
    			IR_on();
    			//send(com_LOG, "IR_ON\n");
    			break;
    		case TIME_OFF:	// Spengo IR
    			IR_off();
    			//send(com_LOG, "IR_OFF\n");
    			break;
    		case TIME_SAVE:	// Invio valori
    				if (index_N >= N){
    					index_N = 0;
    					microsecondi = 0;
    					TA0_off();
    					led_off();
    					send_all_values();
    				}
    		default:
    			break;
    		}
        microsecondi += STEP;	// Interrupt ogni STEP
        if (microsecondi >= TIME_RESET){
        	microsecondi = 0;
        }
    }
    
    
    // UART interrupt service routine (Seriale)
    void eUSCIA0IsrHandler(void) {
    	/* Eseguita quando viene ricevuto un byte sulla seriale.
    	 * Se il byte e' 10 (\n) chiama received che elabora il messaggio ricevuto
    	 */
        if (UCA0IFG & UCRXIFG) {
    		while(!(UCA0IFG & UCTXIFG));
    		RXByte = UCA0RXBUF;
    		RXData[index_RX++] = RXByte;
    		if(RXByte==10) {
    		  index_RX=0;
    		  received();
    		}
        }
    }
    
    // RTC interrupt service routine (essendo in real time VA IN MINUTI vedi configurazione SOPRA rtc)
    void RtcIsrHandler(void) {
        if (RTCCTL0 & RTCTEVIFG) {
        	// Evento abilitato in modalita' automatica, accade ogni minuto
        	if (minuti == 0){
        		TA0_on();
    			led_on();
        	}
    		minuti++;
    		if (minuti >= T) {
    			minuti = 0;
    			microsecondi = 0;
    		}
    	}
        // Chiudo l'interrupt flag
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIFG;
    	RTCCTL0_H = 0;
    }
    

    My idea is start and end adc sampling and conversion in ADC_START and ADC_END. With if statement it works (i see it with log that send me the time), but it seems that there is no time for complete conversion or sampling. I note this because the values of adc are very low when it's near IR_OFF while it should be costant. (IR is a led of dust sensor). Maybe i don't use correct method. When you can, explain me. The frequency of adc sampling has to be 1/STEP where step can be 10us,5us... For now assume that STEP=5us. (in the code is set to 10, sorry)

    Maybe i have to set adc14ssel to use correct clock of adc? I'll wait your answer :)

    Thanks,

    Luca

  • hi, when you are free we can see the adc part and solve it :) i think i am near to solve the problem but i need your help :)
    See my precedent answer to have idea with my code and if there is solution setting adc14ssel or adc14msc bit. Then another question: why we choose 24mhz? if step was higher 12mhz was ok?
    thanks
  • Hi Luca,

    I'm sorry I haven't had a chance to dig into the ADC part yet, but I'll plan on looking at it tomorrow. Thanks for your patience.

    Regards,

    James
    MSP Customer Applications
  • Ok don't worry, i wait you :)

    Meantime i send you my updated code.

    Using SMCLK as adc14 clock (12Mhz), a resolution of 14bit (so 16 clock cycles for conversion ), a step of 10us:

    • i have set ADC14SHT0_4 (64 clock cycles for sample and hold) --> 64+16/12 = 6.67us (it is < 10us so it should be good).
    • If i use STEP = 5us i think to use ADC14SHT0_x higher or use DCO clock (24Mhz).

    question:

    1. i use conseq_2 (single channel repeat sequence). But if i set adc14msc bit my program doesn't work. Why?
    2. Is it better use smclk or dco clock as source for adc?
    3. have i to set ADC14CLRIFGR0 |= ADC14IFG0;?
    4. tell me if the code is set right (ADC14_IRQHandler, TimerA0_0IsrHandler etc... i remember that i need the function sendallvalues to send values to server).
    5. If set step = 5 i have 200samples. step=10 i have 100samples... (is it possible use step = 2us?)

    What to expect: adc_start at time 0 (or other but first of IR_ON) -- IR_on -- IR-off -- adc_end. I expect a kind of gaussian or rectangular wave given by ir (it is a led od sharp dust sensor). 

    5554.code2.txt
    #include "msp.h"
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    
    // ==========Definizione costanti==========
    
    typedef enum {false=0, true=1} bool;      // definizione tipo bool
    
    // costanti dei tempi di acquisizione
    #define STEP			10		//	10 us	 tempo dell'interrupt
    #define TIME_ON			50		//	200 us	 led IR on, signal = 0 V
    #define TIME_ADC_START	0		//	100 us	 inizio istante di acquisizione
    #define TIME_OFF		600		//	650 us	 led IR off, signal = 3,3 V
    #define TIME_ADC_END	1000	//	800 us	 fine istante di acquisizione
    #define TIME_SAVE		1200	//	900 us	 controllo fine ciclo
    #define TIME_RESET		10000	//	1.000 us periodo del segnale generato
    
    // costanti comandi protocollo trasmissione seriale
    #define com_PING  'P'         // ping
    #define com_T     'T'         // parametro T (periodo in minuti)
    #define com_N     'N'         // parametro N (numero campioni per periodo)
    #define com_UTC   'U'         // sincronizzazione data/ora
    #define com_MODE  'A'         // 0 (off) - 1 (auto) - 2 (manual)
    #define com_START 'S'         // inizio acquisizione degli N valori
    #define com_RESET 'R'         // provoca reset
    
    #define com_VALUE 'D'         // invio di N valori
    #define com_END   'E'         // fine invio valori
    #define com_LOG   'L'         // stringa di log
    
    
    // ==========Dichiarazione variabili globali==========
    
    // parametri modificabili via seriale a run-time
    int T =   5;  // min      // Periodo globale di acquisizione               [N/100/60  < T  < 32767]
    int N = 100;              // Numero di campioni memorizzati per periodo    [    1     < N  < 16384]
    
    // variabili tempi
    int microsecondi = 0;      // contatore di microsecondi del timer A0        [0 - 10.000]
    int minuti = 0;            // contatore di minuti del Real Time Clock       [0 - T]
    
    // indici contatori array
    int index_N = 0;           // contatore campioni                            [0 - N]
    
    // variabili acquisizione
    uint16_t values[16384];    // array che contiene tutti gli N valori di un periodo
    
    char micro[8];
    
    // variabili ricezione seriale
    char RXData[32];           // buffer che contiene la stringa ricevuta
    char RXByte;               // ultimo byte ricevuto
    int  index_RX;             // indice del ciclo che riempie il buffer RXData
    
    
    
    // ==========Funzioni in/out==========
    
    void IR_off()    {P4OUT    |=  BIT2;}
    void IR_on()     {P4OUT    &= ~BIT2;}
    void led_on()    {P1OUT    |=  BIT0;}
    void led_off()   {P1OUT    &= ~BIT0;}
    void TA0_on()    {TA0CCTL0 |=  CCIE;}
    void TA0_off()   {TA0CCTL0 &= ~CCIE;}
    void auto_on(){
    	//fan_on();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L |= RTCTEVIE;
    	RTCCTL0_H = 0;}
    void auto_off(){
    	//fan_off();
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIE;
    	RTCCTL0_H = 0;}
    
    // ===========================Funzioni Real Time Clock===========================
    
    void get_now(char *now){
    	// legge i registri del 'Real Time Clock' e scrive la data in now
    	sprintf(now     , "%04x-",  RTCYEAR);
    	sprintf(now +  5, "%02x-",  RTCMON);
    	sprintf(now +  8, "%02x ",  RTCDAY);
    	sprintf(now + 11, "%02x:",  RTCHOUR);
    	sprintf(now + 14, "%02x:",  RTCMIN);
    	sprintf(now + 17, "%02x\n", RTCSEC);
    }
    
    void set_rtc(char *date){
    	// riceve una data in formato stringa 'YYYY-MM-DD hh:mm:ss' e scrive i registri del 'Real Time Clock'
    	RTCCTL0_H = RTCKEY_H;                       //   Unlock RTC key protected registers
    	RTCYEAR = strtol(&date[ 0], NULL, 16);
    	RTCMON  = strtol(&date[ 5], NULL, 16);
    	RTCDAY  = strtol(&date[ 8], NULL, 16);
    	RTCHOUR = strtol(&date[11], NULL, 16);
    	RTCMIN  = strtol(&date[14], NULL, 16);
    	RTCSEC  = strtol(&date[17], NULL, 16);
        RTCCTL0_H = 0;                              //   Lock RTC key protected registers
    }
    
    
    
    // ===========================Funzioni Protocollo Seriale===========================
    
    void send_char(char c){                       // invia un carattere sulla seriale
    	while(!(UCA0IFG&UCTXIFG)); //finchè c' da trasmettere
    	UCA0TXBUF = c; //riempie il buffer di trasmissione
    }
    
    int send(char command, char *text){
    	send_char(command);                       // invio comando
    	int i=0;
    	while (text[i] != 10){				      // finchè è diverso da 'a capo \n'
    		send_char(text[i]);
    		i++;
    	}                                         // invio testo
    	send_char(10);                            // invio '\n'
    	return i+2;                               // restituisce il numero di caratteri inviati
    }
    
    void send_all_values(){
    	int i, i_N;
    	int n=0;
    	int samples_per_row = 20; //sono il numero di campioni su gni riga stampati su monitor (10000/20=500 righe di campioni adc)
    	char TXData[32];
    	char UTC[32];
    	get_now(UTC);
    	for (i_N=0; i_N<N; i_N++){
    		if (i_N == n){
    			n += samples_per_row;
    			send_char(com_VALUE);
    			for(i = 0; i < 19; i++) //19 lunghezza timestamp
    				send_char(UTC[i]);
    			send_char(';');
    		}
    		sprintf(TXData, "%d,", values[i_N]);
    		for(i = 0; i < strlen(TXData); i++)
    			send_char(TXData[i]);
    		if (i_N == n-1 || i_N == N-1){
    			send_char(10);
    		}
    	}
    	send(com_END, "E\n");
    }
    
    
    void received(){                              // elabora i dati ricevuti dalla seriale
    	char TXData[32];
    	int mode;
    	switch(RXData[0]){
    		case (int)com_PING:
    			send(com_PING, "ok\n");
    			break;
    		case (int)com_T:
    			T = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", T);
    			send(com_T, TXData);
    			break;
    		case (int)com_N:
    			N = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", N);
    			send(com_N, TXData);
    			break;
    		case (int)com_UTC:
    			set_rtc(&RXData[1]);
    			get_now(TXData);
    			send(com_UTC, TXData);
    			break;
    		case (int)com_MODE:
    			mode = atoi(&RXData[1]);
    			sprintf(TXData, "%d\n", mode);
    			send(com_MODE, TXData);
    			if (mode == 1) {
    				auto_on();
    			}
    			else {
    				auto_off();
    			}
    			break;
    		case (int)com_START:
    			microsecondi = 0;
    			TA0_on();
    			led_on();
    			send(com_START, "\n");
    			break;
    		case (int)com_RESET:
    			send(com_LOG, "ricevuto comando reset\n");
    			RSTCTL_RESET_REQ |= (0x6900 | RSTCTL_RESET_REQ_HARD_REQ);
    		default:
    			send(com_LOG, "comando sconosciuto\n");
    			break;
    	}
    }
    
    void error(void)
    {
        volatile uint32_t z;
        P1DIR |= BIT0;
        while (1)
        {
            P1OUT ^= BIT0;
            for(z=0;z<20000;z++);       // Blink LED forever
        }
    }
    
    // ===========================Funzioni adc===========================
    int main(void)
    {
    	// ==========Configurazioni di base==========
    	uint32_t currentPowerState;
    	WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    
    	/* Step 1: Transition to VCORE Level 1: AM0_LDO --> AM1_LDO */
    	currentPowerState = PCMCTL0 & CPM_M;
    	if (currentPowerState != CPM_0)
    		error();
    
    	while ((PCMCTL1 & PMR_BUSY));
    	PCMCTL0 = PCM_CTL_KEY_VAL | AMR_1;
    	while ((PCMCTL1 & PMR_BUSY));
    	if (PCMIFG & AM_INVALID_TR_IFG)
    		error();                            // Error if transition was not successful
    	if ((PCMCTL0 & CPM_M) != CPM_1)
    		error();                            // Error if device is not in AM1_LDO mode
    
    	/* Step 2: Configure Flash wait-state to 2 for both banks 0 & 1 */
    	FLCTL_BANK0_RDCTL = FLCTL_BANK0_RDCTL & ~FLCTL_BANK0_RDCTL_WAIT_M | FLCTL_BANK0_RDCTL_WAIT_0;
    	FLCTL_BANK1_RDCTL = FLCTL_BANK0_RDCTL & ~FLCTL_BANK1_RDCTL_WAIT_M | FLCTL_BANK1_RDCTL_WAIT_0;
    
    	P1DIR |= BIT0;
    	P1OUT |= BIT0;                     // Set 1.0 as digital out (led rosso a bordo)
    
    	P4DIR |= BIT2;
    	P4OUT |= BIT2;              // Set 4.2 as digital out (IR)
    
    	SCB_SCR |= SCB_SCR_SLEEPONEXIT;    // Enable sleep on exit from ISR
    	__enable_interrupt();
    
    
    	// ==========Configurazione ADC 14 bit==========
    	P6SEL1 |= BIT1;
    	P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC
    
    	NVIC_ISER0 = 1 << ((INT_ADC14 - 16) & 31);
    
    	ADC14CTL0 &= ~ADC14ENC;							  //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
    	ADC14CTL0 = ADC14ON | ADC14SHT0_4 | ADC14CONSEQ_2 | ADC14SSEL_4 | ADC14SHP | ADC14SHS_0;   // Turn on ADC14, set sampling time, repeat single channel
    	ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results (16 clock cycles)
    	ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V
    	//ADC14CLRIFGR0 |= ADC14IFG0;
    	ADC14IER0 |= ADC14IE0;
    	ADC14CTL0 |= ADC14ENC;                          // ADC enable
    
    
    	// ==========Configurazione clock a 24 MHz==========
    	CSKEY = 0x695A;                    // Unlock CS module for register access
    	CSCTL0 = 0;                        // Reset tuning parameters
    	CSCTL0 = DCORSEL_4;                // Set DCO to 24MHz
    	CSCTL1 = CSCTL1 & ~(SELS_M | DIVS_M); // Clear existing registers
    	CSCTL1 = CSCTL1 | (SELS_3 | DIVS_1); // For SMCLK, select DCO as source then divide by 2 (SMCLK = 12Mhz)
    	//CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
    	CSKEY = 0;                         // Lock CS module from unintended accesses
    
    	/* Step 4: Output MCLK to port pin to demonstrate 12MHz operation */
    	P4DIR |= BIT3;
    	P4SEL0 |=BIT3;
    	P4SEL1 &= ~(BIT3);
    
    
    	// ==========Configurazione TimerA0==========
    	NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
    	TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
    	TA0CCR0 = STEP-1;                           // Overflow (step dell'interrupt)
    	TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
    	TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)
    
    
    	// ==========Configurazione Seriale==========
    	P1SEL0 |= BIT2 | BIT3;                       // set 2-UART pin as second function
    	NVIC_ISER0 = 1 << ((INT_EUSCIA0 - 16) & 31); // Enable eUSCIA0 interrupt in NVIC module
    
    	// Configure UART
    	UCA0CTLW0 |= UCSWRST;
    	UCA0CTLW0 |= UCSSEL__SMCLK;                  // Put eUSCI in reset
    	    /* Baud Rate calculation
    	     * 12.000.000/(16*9.600) = 78.125
    	     * Fractional portion = 0.125
    	     * User's Guide Table 21-4: UCBRSx = 0x10
    	     * UCBRFx = int ( (78.125-78)*16) = 2
    	     */
    	UCA0BR0 = 78;                                 // 12.000.000 / 16 / 9.600 = 78,125
    	UCA0BR1 = 0x00;
    	UCA0MCTLW = 0x1000 | UCOS16 | 0x0020;
    	UCA0CTLW0 &= ~UCSWRST;                        // Initialize eUSCI
    	UCA0IE |= UCRXIE;                             // Enable USCI_A0 RX interrupt
    
    
    	// ==========Configurazione Real Time Clock==========
        RTCCTL0_H = RTCKEY_H;                    // Unlock RTC key protected registers
        RTCCTL0_L &= ~RTCTEVIE;                  // Interrupt disable
        RTCCTL0_L &= ~(RTCTEVIFG);
        RTCCTL1 = RTCBCD | RTCHOLD;              // RTCTEVx -> 00b = Minute changed event
        // RTC enable, BCD mode, RTC hold
        // enable RTC read ready interrupt
        // enable RTC time event interrupt
        RTCCTL1 &= ~(RTCHOLD);                    // Start RTC calendar mode
        RTCCTL0_H = 0;                            // Lock the RTC registers
    
        NVIC_ISER0 = 1 << ((INT_RTC_C - 16) & 31);
    
    	// ==========Inizializzazione==========
        //fan_off();
        led_off();							// Led msp spento (si accende solo alle acquisizioni)
        IR_off();							// Led IR spento
        set_rtc("2000-01-01 00:00:00");		// Setto rtc
    
    	__sleep();
    	//while(1);
    }
    
    // ===========================Interrupt Service Routines===========================
    void ADC14_IRQHandler(void) {
    		values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    		index_N++;
    }
    
    
    // Timer A0 interrupt service routine
    void TimerA0_0IsrHandler(void) {
        TA0CCTL0 &= ~CCIFG;
        /* STEP = 10; Tempo di campionamento compreso tra adc_start, adc_end; Riempio array e invio su seriale */
    	if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) {
    		ADC14CTL0 |= ADC14SC; // Start sampling/conversion; Enable and start conversion.
    		//sprintf(micro, "%07d\n", microsecondi);	// Converto da intero a stringa dentro variabile micro
    		//send(com_LOG, micro);						// ---------->USED TO CONTROL TIMERA AND FREQUENCY
    		//while(!(ADC14IFGR0 & BIT0));				// Controlla che non abbia finito
    		//values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
    		//index_N++;									// Incrementa indice array
    	}
        switch (microsecondi){
    		case TIME_ON:	// Accendo IR
    			IR_on();
    			//send(com_LOG, "IR_ON\n");
    			break;
    		case TIME_OFF:	// Spengo IR
    			IR_off();
    			//send(com_LOG, "IR_OFF\n");
    			break;
    		case TIME_SAVE:	// Invio valori
    				if (index_N >= N){
    					index_N = 0;
    					microsecondi = 0;
    					TA0_off();
    					led_off();
    					send_all_values();
    				}
    		default:
    			break;
    		}
        microsecondi += STEP;	// Interrupt ogni STEP
        if (microsecondi >= TIME_RESET){
        	microsecondi = 0;
        }
    }
    
    
    // UART interrupt service routine (Seriale)
    void eUSCIA0IsrHandler(void) {
    	/* Eseguita quando viene ricevuto un byte sulla seriale.
    	 * Se il byte e' 10 (\n) chiama received che elabora il messaggio ricevuto
    	 */
        if (UCA0IFG & UCRXIFG) {
    		while(!(UCA0IFG & UCTXIFG));
    		RXByte = UCA0RXBUF;
    		RXData[index_RX++] = RXByte;
    		if(RXByte==10) {
    		  index_RX=0;
    		  received();
    		}
        }
    }
    
    // RTC interrupt service routine (essendo in real time VA IN MINUTI vedi configurazione SOPRA rtc)
    void RtcIsrHandler(void) {
        if (RTCCTL0 & RTCTEVIFG) {
        	// Evento abilitato in modalita' automatica, accade ogni minuto
        	if (minuti == 0){
        		TA0_on();
    			led_on();
        	}
    		minuti++;
    		if (minuti >= T) {
    			minuti = 0;
    			microsecondi = 0;
    		}
    	}
        // Chiudo l'interrupt flag
    	RTCCTL0_H = RTCKEY_H;
    	RTCCTL0_L &= ~RTCTEVIFG;
    	RTCCTL0_H = 0;
    }
    

    When you can see the code. Thanks for all

    Luca

  • Hi Luca,

    I'm doing my best to understand what you are trying to do here, so let me step back and talk through your application flow at a higher level.

    In your initial post, you mentioned that you want the ADC to sample from TIME_START_ADC = 0 microseconds to TIME_STOP_ADC = 1000 microseconds, which we'll call TIME_SAMPLING. For now, let's assume that the number of samples within this time frame doesn't matter. Next, it seems like your STEP is the highest resolution interval (or segment) of TIME_SAMPLING. Like you point out, this gives you 200 STEP if STEP equals 5 microseconds. <---- This makes sense so far.

    For your ADC input, which is the IR sensor, I'd suggest that this gets turned on before you start sampling and turned off after you stop sampling. This way, it's output is stable when you're measuring it - let's assume it stays on all the time.

    Now, what is the smallest possible STEP size (remember we're still ignoring the number of samples inside each STEP for now)? For a single ADC sample, there is sampling time and conversion time. The minimum sampling time for your design needs to be calculated using Equation 9 on page 668 of the Technical Reference Manual (TRM). This time depends on the source resistance (Rs) of the IR sensor, the external parasitic capacitance (Cpext) of the IR sensor and PCB, the internal parasitic capacitance (Cpint), the input capacitance (Ci), and the internal mux-on input resistance (Ri), which are all highlighted in Figure 20-5 in the TRM. You can find Ri and Cpint on the bottom of page 64 in the datasheet.

    For sake of discussion, let's use the calculated minimum sample time equal to 3.28 microseconds. For the conversion time, the 12 MHz SMCLK is sourcing ADC14CLK, so 16 cycles at (1 / 12 MHz) equals approximately 1.33 microseconds. Total time is 4.61 microseconds, which is less than STEP at 5 microseconds. Thus, we could do one sample every one STEP.

    Thus, you could use TimerA to count for 5 microseconds, then increment a variable. In your timer ISR, check when the variable is 0 and start the ADC. When the variable reaches 200, stop the ADC, which can be done two ways (as discussed in Section 20.2.8.6 in the TRM). First, ADC14ENC can be reset, which stops the ADC at the end of the current conversion. Second, reset ADC14ENC and set CONSEQx to 0 to stop conversion immediately. Here, you could choose to stop the conversion immediately when the variable reaches 200. Again, we aren't worrying about the number of samples, but this implementation would ensure the ADC is sampling only between 0 and 1000 microseconds.

    Depending on your calculated minimum sample time, you can choose to have STEP equal to one sample or increase it to include several samples - it's your choice.

    Now, let me try to address your questions:

    Luca Palombella said:
    1. i use conseq_2 (single channel repeat sequence). But if i set adc14msc bit my program doesn't work. Why?

    You're correctly using CONSEQx = 2 for repeat-single-channel mode. ADC14MSC should be set, which selects Pulse Sample Mode and uses the sampling timer. After looking at your code, you need to also set the ADC14SHT0x and ADC14SHT1x bits in ADC14CTL0. Please read Section 20.2.6.2 in the TRM and refer to the 'msp432p401_adc14_06' code example. NOTE: Like Chris already mentioned, it looks like you're still using "=" instead of "|=" for some registers, and this could cause some problems for your ADC configuration. Also, I see that you're calling ADC14CTL0 |= ADC14SC each time you enter your timer ISR - this isn't necessary in Pulse Sample Mode. Please see how this mode allows you to continue to sample, convert, store, then back up to sample as shown in the state diagram in Figure 20-8 in the TRM.

    Luca Palombella said:
    2. Is it better use smclk or dco clock as source for adc?

    My suggestion would be to use the lowest possible clock frequency that meets your requirements to achieve the lowest possible power. Since the minimum sample time is dependent on several parameters mentioned above, the conversion time would be faster with a faster SMCLK. If you use the Pulse Sample Mode and use TimerA at the beginning and end of 1000 microseconds, you may be able to go back to using the 12 MHz DCO configuration. Chris correctly recommended that you increase the DCO frequency because the ISR was taking too long in that case.

    Luca Palombella said:
    3. have i to set ADC14CLRIFGR0 |= ADC14IFG0;?

    If you're using Pulse Sample Mode, no. You just need to enable the interrupt flag like in the example code mentioned above. The interrupt flags are cleared in the ADC ISR.

    Luca Palombella said:
    4. tell me if the code is set right (ADC14_IRQHandler, TimerA0_0IsrHandler etc... i remember that i need the function sendallvalues to send values to server).

    If you use 24 MHz DCO configuration, you may be able to do this in the TimerA ISR.

    Luca Palombella said:
    5. If set step = 5 i have 200samples. step=10 i have 100samples... (is it possible use step = 2us?)

    See discussion above about calculating minimum sample time.

    Luca Palombella said:
    What to expect: adc_start at time 0 (or other but first of IR_ON) -- IR_on -- IR-off -- adc_end. I expect a kind of gaussian or rectangular wave given by ir (it is a led od sharp dust sensor). 

    If you aren't sure of the IR output voltage, I'd use a known voltage input to help determine if your ADC readings are correct or not. Otherwise, you don't know if the ADC is wrong or if your input voltage is right.

    msp432p401_adc14_06.c
    /* --COPYRIGHT--,BSD_EX
     * Copyright (c) 2013, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *******************************************************************************
     *
     *                       MSP432 CODE EXAMPLE DISCLAIMER
     *
     * MSP432 code examples are self-contained low-level programs that typically
     * demonstrate a single peripheral function or device feature in a highly
     * concise manner. For this the code may rely on the device's power-on default
     * register values and settings such as the clock configuration and care must
     * be taken when combining code from several examples to avoid potential side
     * effects. Also see http://www.ti.com/tool/mspdriverlib for an API functional
     * library & https://dev.ti.com/pinmux/ for a GUI approach to peripheral configuration.
     *
     * --/COPYRIGHT--*/
    //******************************************************************************
    //  MSP432P401 Demo - ADC14, Repeated Sequence of Conversions
    //
    //  Description: This example shows how to perform a repeated sequence of
    //  conversions using "repeat sequence-of-channels" mode.  AVcc is used for the
    //  reference and repeated sequence of conversions is performed on Channels A0,
    //  A1, A2, and A3. Each conversion result is stored in ADC14->MEM[0], ADC14->MEM[1],
    //  ADC14->MEM[2], and ADC14->MEM[3] respectively. After each sequence, the 4 conversion
    //  results are moved to A0results[], A1results[], A2results[], and A3results[].
    //  Test by applying voltages to channels A0 - A3. Open a watch window in
    //  debugger and view the results. Set Breakpoint1 in the index increment line
    //  to see the array values change sequentially and Breakpoint2 to see the entire
    //  array of conversion results in A0results[], A1results[], A2results[], and
    //  A3results[]for the specified Num_of_Results.
    //
    //  Note that a sequence has no restrictions on which channels are converted.
    //  For example, a valid sequence could be A0, A3, A2, A4, A2, A1, A0, and A7.
    //  See the User's Guide for instructions on using the ADC14.
    //
    //               MSP432P401
    //             -----------------
    //         /|\|                 |
    //          | |                 |
    //          --|RST              |
    //            |                 |
    //    Vin0 -->|P5.5/A0          |
    //    Vin1 -->|P5.4/A1          |
    //    Vin2 -->|P5.3/A2          |
    //    Vin3 -->|P5.2/A3          |
    //            |                 |
    //
    //   Wei Zhao
    //   Texas Instruments Inc.
    //   October 2015 (updated) | June 2014 (created)
    //   Built with Code Composer Studio V6.0
    //******************************************************************************
    #include "msp.h"
    #include <stdint.h>
    
    #define   Num_of_Results   8
    
    volatile uint16_t A0results[Num_of_Results];
    volatile uint16_t A1results[Num_of_Results];
    volatile uint16_t A2results[Num_of_Results];
    volatile uint16_t A3results[Num_of_Results];
    static uint8_t index;
    
    int main(void)
    {
      WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
      // Configure GPIO
      P5SEL1 |= BIT5 | BIT4 | BIT3 |BIT2;       // Enable A/D channel A0-A3
      P5SEL0 |= BIT5 | BIT4 | BIT3 |BIT2;
    
      __enable_interrupt();
      NVIC->ISER[0] = 1 << ((ADC14_IRQn) & 31);// Enable ADC interrupt in NVIC module
    
      ADC14->CTL0 = ADC14_CTL0_ON | ADC14_CTL0_MSC | ADC14_CTL0_SHT0__192 | ADC14_CTL0_SHP | ADC14_CTL0_CONSEQ_3; // Turn on ADC14, extend sampling time
                                                                                  // to avoid overflow of results
    
      ADC14->MCTL[0] = ADC14_MCTLN_INCH_0;                 // ref+=AVcc, channel = A0
      ADC14->MCTL[1] = ADC14_MCTLN_INCH_1;                 // ref+=AVcc, channel = A1
      ADC14->MCTL[2] = ADC14_MCTLN_INCH_2;                 // ref+=AVcc, channel = A2
      ADC14->MCTL[3] = ADC14_MCTLN_INCH_3+ADC14_MCTLN_EOS;        // ref+=AVcc, channel = A3, end seq.
      ADC14->IER0 = ADC14_IER0_IE3;                     // Enable ADC14IFG.3
    
      SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;             // Wake up on exit from ISR
      
      while(1)
      {
        ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC;        // Start conversion-software trigger
        __sleep();
        __no_operation();                       // For debugger 
      }
      
    }
    
    // ADC14 interrupt service routine
    void ADC14_IRQHandler(void) 
    {
        if (ADC14->IFGR0 & ADC14_IFGR0_IFG3)
        {
            A0results[index] = ADC14->MEM[0];       // Move A0 results, IFG is cleared
            A1results[index] = ADC14->MEM[1];       // Move A1 results, IFG is cleared
            A2results[index] = ADC14->MEM[2];       // Move A2 results, IFG is cleared
            A3results[index] = ADC14->MEM[3];       // Move A3 results, IFG is cleared
            index = (index + 1) & 0x7;          // Increment results index, modulo
            __no_operation();                   //Set Breakpoint1 here
        }
    
    }
    

    Regards,

    James

    MSP Customer Applications

  • Hi James,

    i would like to thank you for your patience and kindness. If you go on holiday in Italy i'll offer you a coffee :)

    So i have understand all. But i have some questions again to complete the discussion:

    • "After looking at your code, you need to also set the ADC14SHT0x and ADC14SHT1x bits in ADC14CTL0": if you see code i have set this (only adc14sht0x because i use Mem0).
    • I have set ADC14SHT0_4 (64 clock cycles for sample and hold) --> 64+16/12 = 6.67us (it is < 10us so it should be good). and if i use step = 5us i set ADC14SHT0_x higher or use adc14clk source different (dco for example).
    • "it looks like you're still using "=" instead of "|=" for some registers": in the example you have posted (msp432_06.c) i use the same method ("=" or "|=") so what is wrong?
    • "Also, I see that you're calling ADC14CTL0 |= ADC14SC each time you enter your timer ISR - this isn't necessary in Pulse Sample Mode": i use this method to be sure that adc start conversion only from adc_start to adc_end (time_sampling). I think it is like "It remains activated during" and not like "active it every step...". Is it correct? Another way to make it is adding a case into switch/case (case adc_start --> adc14sc... and case adc_end reset adc14enc..). What is better? You tell me to reset adc14enc when my variable is 200 (index_N). So i make this into if statement (index_N > N=200). 
      • case TIME_SAVE: // Send values
        if (index_N >= N){ ..... } here i control the variable = 200 (or 100 if step=10). So in this point (time_save is 1200us, 200 us after the adc_end (1000), the adc should have finished the conversion.)

    • I start adc conversion before and after IR so i can see the values increment, keep a "costant" value and decrement when IR is turned off. I note that with adc_irqHandler, i don't know why, IR_on and IR_off happens two time (i remember what i want: adc -> irOn -> irOff -> adc_end -> send values while with this method i have adc -> irOn -> irOff -> irOn -> irOff -> adc.....).

    Im' sorry if this discussion is too long. Thanks again for help.

    Luca

  • Hi Luca,

    Luca Palombella said:
    "After looking at your code, you need to also set the ADC14SHT0x and ADC14SHT1x bits in ADC14CTL0": if you see code i have set this (only adc14sht0x because i use Mem0).

    You're right. In general, using ADC14SHT0x and/or ADC14SHT1x depends on how many input channels are required and which input channel is selected. As shown in the 'msp432p401x_adc14_06.c' code example, the ADC input channel (e.g. A4) is typically routed to the related memory buffer (e.g. MEM4). In your case, you're using A14, so if you'd use MEM14, ADC14SHT1x would need to be used. Your configuration is fine here.

    Luca Palombella said:
    I have set ADC14SHT0_4 (64 clock cycles for sample and hold) --> 64+16/12 = 6.67us (it is < 10us so it should be good). and if i use step = 5us i set ADC14SHT0_x higher or use adc14clk source different (dco for example).

    The sample time equation should be used to calculate your minimum sample time, which can be used to specify your step size/time. For a longer sample time, increase ADC14SHT0_x. Since you're using ADC14SHT0_4 (which is the minimum clock cycles), the only way to shorten the sample time would be to increase the frequency of the ADC14CLK source.

    Luca Palombella said:
    "it looks like you're still using "=" instead of "|=" for some registers": in the example you have posted (msp432_06.c) i use the same method ("=" or "|=") so what is wrong?

    This is just some general coding advice. In the code example, when "=" is used with the same register consecutively, you have to be very careful and include any important previous register fields, as highlighted below. Thus, using bit-wise OR "|=" instead, you don't have to carry over these important register fields to the next assignment.

    Luca Palombella said:
    "Also, I see that you're calling ADC14CTL0 |= ADC14SC each time you enter your timer ISR - this isn't necessary in Pulse Sample Mode": i use this method to be sure that adc start conversion only from adc_start to adc_end (time_sampling). I think it is like "It remains activated during" and not like "active it every step...". Is it correct? Another way to make it is adding a case into switch/case (case adc_start --> adc14sc... and case adc_end reset adc14enc..). What is better? You tell me to reset adc14enc when my variable is 200 (index_N). So i make this into if statement (index_N > N=200). 
    • case TIME_SAVE: // Send values
      if (index_N >= N){ ..... } here i control the variable = 200 (or 100 if step=10). So in this point (time_save is 1200us, 200 us after the adc_end (1000), the adc should have finished the conversion.)

    In the code example, the ADC14_CTL0_MSC bit is set, which allows multiple sample and conversion to be performed automatically. If you don't use the MSC bit, you may have to manually trigger the next sample. Take a look at Figure 20-7 in the TRM to know how the ADC operates when CONSEQx = 2. As you can see, after the conversion has been completed and stored in memory buffer, sampling occurs automatically (when ADC14MSC = 1 and ADC14SHP = 1 and ADC14EOS.x = 0).

    Luca Palombella said:
    I start adc conversion before and after IR so i can see the values increment, keep a "costant" value and decrement when IR is turned off.

    This should work fine and makes sense.

    Luca Palombella said:
    I note that with adc_irqHandler, i don't know why, IR_on and IR_off happens two time (i remember what i want: adc -> irOn -> irOff -> adc_end -> send values while with this method i have adc -> irOn -> irOff -> irOn -> irOff -> adc.....).

    If your Timer interrupt is set up to trigger faster than your ADC interrupt, I could see where the Timer interrupt could happen twice before the ADC interrupt.

    Regards,

    James

    MSP Customer Applications

  • Ok i understand. But i don't know how set the ADC14CTL0 |= ADC14SC and be sure that timer interrupt is not faster adc interrupt.

    1. ADC14CTL0 |= ADC14SC: ok if i set MSC bit i have multiple conversion automatically. But in my code how i set it? the adc14sc goes in switch case (so i'm sure that it is actived only one time?) Then to stop the conversion i use another case to reset adc14enc? And in the adc14 settings bit is set adc14msc, shp =1 and don't set adc14eos right?
    2. timer interrupt and adc interrupt: how can i be sure that timing is the same and i haven't repeated cycles of IR or other? i know the only method posted at prior post (SampleHold clock cycles + Conversion clock cycles --> in my case ADC14SHT0_4 is 64 clock cycles for SampleHold and Conversion (Manual) is always 16 clock cycles (14 bit resolution). If i use 12Mhz i have 64+16/12 = 6.67 microseconds that is less than 10 microseconds. What is wrong?

    these are the last two problems than i finish :) 

    Luca

**Attention** This is a public forum