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;
}

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:
I have seen it but i don't know why mw code doesn't work. It's a bit different from that of @egarante :(

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.






