Hi all,
in the code that i post below i want to start adc conversion at TIME_ADC_START (0 or another time) and finish it in TIME_ADC_END. In the isr timer i generate interrupt that turn on and off pin of a external sensor but this is not the problem because it works. My problem is adc: how write in the code, adc start and finish to sampling when i want but i have the doubt that it is not correct.
So is there another mode of using adc? i read in different posts that adc can be triggered by timer but i do it, it doesn't work :(
An important thing: i need funcion sendallvalues(), so if you modify my code keep it because this function sends my array to server. Other function (send_char and received serve to "speak" with python software and command the msp. These work so the problem is only for adc and timer.
#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 5 // 5 us tempo dell'interrupt
#define TIME_ON 50 // 50 us led IR on, signal = 0 V
#define TIME_ADC_START 0 // 0 us inizio istante di acquisizione
#define TIME_OFF 450 // 450 us led IR off, signal = 3,3 V
#define TIME_ADC_END 1000 // 1000 us fine istante di acquisizione
#define TIME_SAVE 1200 // 1200 us controllo fine ciclo
#define TIME_RESET 10000 // 10.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(){
RTCCTL0_H = RTCKEY_H;
RTCCTL0_L |= RTCTEVIE;
RTCCTL0_H = 0;}
void auto_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 | ADC14SHS_1; // 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)
TA0CCR1 = STEP/2;
TA0CCTL1 |= OUTMOD_3;
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===========================
/* it doesn't work!!!!
void ADC14_IRQHandler(void) {
values[index_N] = ADC14MEM0;
index_N++;
if (index_N >= N) {
TA0CCTL1 &= ~OUTMOD_M;
ADC14CTL0 &= ~ADC14ENC;
led_off();
send_all_values();
}
}
*/
// 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 |= ADC14ENC | 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; // 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;
}