Forums

msp432 adc timerA

Started by luca89 3 years ago3 replieslatest reply 3 years ago356 views

Hi all, 

i have a problem with my msp432 (#MSP430). it is connected with a dust sensor and between TIME_ADC_START and TIME_ADC_END i have to sample the signal. But it seems that in this mode adc doesn't work correctly. I post all code below. Several parts of code work for me so don't see them (for example function send, sendallvalues...) because they serve to "speak" with program in python. So what is wrong to use adc? It's better use trigger adc by timerA or keep all inside isr timer (see the commented part in if statement)?

The problem is: adc start to sample when i want but i have doubt that values are wrong. I insert file of values received. 

The Timer ISR takes too long and i will not be able to get the number of samples that you want. For example if i 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 i end up missing samples. 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. So i don't know how solve this.

#include "msp.h"
#include 
#include 
#include 
#include 
// ==========Definizione costanti==========
typedef enum {false=0, true=1} bool;      // definizione tipo bool
// costanti dei tempi di acquisizione
#define STEP10//    10 us     tempo dell'interrupt
#define TIME_ON50//    200 us     led IR on, signal = 0 V
#define TIME_ADC_START    0//    100 us     inizio istante di acquisizione
#define TIME_OFF450//    650 us     led IR off, signal = 3,3 V
#define TIME_ADC_END    1000    //    800 us     fine istante di acquisizione
#define TIME_SAVE1200    //    900 us     controllo fine ciclo
#define TIME_RESET10000    //    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;>
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
    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_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-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);
    NVIC_ISER0 = 1 << ((INT_ADC14 - 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 = 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;
}
[ - ]
Reply by stephanebAugust 3, 2016

I have surrounded your code snippet with <pre> tags to trigger the code highlighter.  

Let me ping @egarante in case he can help you with your problem.  He wrote the msp430 tutorials

[ - ]
Reply by luca89August 3, 2016

I have seen it but i don't know why mw code doesn't work. It's a bit different from that of @egarante :(

[ - ]
Reply by maruthi.hrAugust 3, 2016

Hi,

I could not study your code. However, following are few useful tips when you are working with ADCs in any MCU

1. Know the rate at which you want samples. (In your question, you said that you need to sample at every 5us. Do you really need samples at that fast rate? Most applications don't need. Decide on how many ADC samples you want per second. You need to decide this based on the rate at which your input may change)

2. Most of the MCUs, have different modes in which ADC can operate. 

a) Software Controlled

b) burst mode

If you are using software controlled mode, then you are responsible for starting ADC conversion every time you want a sample. A periodic start of conversion can be easily achieved using a timer. (my suggestion is use a periodic timer interrupt to start conversion and ADC interrupt to read the sample after conversion)

If you are using burst mode, then be sure to initialize ADC as per your needs.


As far as timer ISR taking too much time is concerned, 5us may be too less time for an ISR to complete.