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.