ADC measures on LCD 1602 with MSP430F5529

Thread Starter

Alejofnb

Joined Dec 24, 2019
20
Hi! I have a problem related to the ADC. My purpose is to take some measures of incident light with 2 LDRs and showing its values with two ADC channels. Also i would like show the value measured on a LCD 1602 to make sure i'm doing it right. My issue is not related to displaying characters as i think, cause i made before an exercise to display an array of characters like the typpical 'Hellow World' and it worked. So my main problem is related to the extra of measuring with the ADC. I have some doubts that i'm doing well the steps for CONSEQ_3 mode in ADC. I paste my code and if anyone see anything wrong, please let me know! Thnks a lot btw.

https://pastebin.com/z0DhFqDc
 

Thread Starter

Alejofnb

Joined Dec 24, 2019
20
Program Listing
C:
#include <msp430.h>
#include <stdint.h>
#include <stdio.h>
// Variables para la LCD:
typedef unsigned char byte;
#define Enable_LCD 0x04
#define iluminacion_LCD 0x08
#define comando_LCD 0x00
#define dato_LCD 0x01
#define LCD_PCF8574_ADDRESS 0x27 // Slave address poniendo A0,A1,A2 = 1
// Variables para el ADC:
volatile uint8_t buffer_lleno = 0;
uint16_t valor_promedio_A0 = 0; // Variable that stores average value of A0
uint16_t valor_promedio_A1 = 0; // Variable that stores average value of A1
uint8_t *PTxData; // Pointer to TX
uint8_t TXByteCtr; // Counter of bytes send
uint16_t volatile contador_milisegundos;
void Inicializacion_Relojes(void)
{
   __bis_SR_register(SCG0); // Disable the FLL control loop
   UCSCTL0 = 0x0000;
   UCSCTL1 = DCORSEL_5;
   UCSCTL2 = FLLD_0 | 487;
   // (N + 1) * (FLLRef/n) = Fdcomsp430
   // (487 + 1) * (32768/1) = 16MHz
   UCSCTL3 = 0; // FLL SELREF = XT1CLK y divisor de FLL = 1 (FLLREFDIV = FLLREFCLK/1)
   UCSCTL4 |= SELA_0 | SELS_4 | SELM_4; // Tomamos ACLK = XT1CLK (Cuarzo externo de 2^15 bits); SMCLK = MCLK = DCOCLKDIV (DCO interno de 16 MHz)
   // UCSCTL5 |= DIVA_0 | DIVS_0;
   __bic_SR_register(SCG0); // Enable the FLL control loop
}

void USCIB1_init()
{
   P4SEL |= BIT2 | BIT1; // P4.1 --> UCB1SDA
   // P4.2 --> UCB1SCL
   UCB1CTL1 |= UCSWRST;
   UCB1CTL0 |= (UCMST | UCMODE_3 | UCSYNC); // UCMST: Master; UCMODE_3: Modulo USCI como I2C; UCSYNC: Modo Sincrono
   UCB1CTL1 |= (UCSSEL_2 | UCSWRST); // UCSSEL_2: SMCLK; UCSWRST: Usci sigue en estado Reset
   UCB1BR0 = 160; // fSCL = SMCLK(16MHz)/160 = 100kHz
   UCB1BR1 = 0;
   UCB1CTL1 &= ~UCSWRST;
   UCB1IE |= UCTXIE;
}

void delay_ms(uint16_t tiempo_milisegundos)
{
   contador_milisegundos = 0;
   TA1CTL |= MC_1; UP MODE
   TA1CCTL0 |= CCIE;
   while(contador_milisegundos < tiempo_milisegundos);
   TA1CTL |= MC_0; // STOP MODE
}

void init_TimerA1_ms(void)
{ // Timer for counting every ms for delay_ms
   TA1CCR0 = 16000-1;
   TA1CTL |= TASSEL_2 | MC_0; // Reloj SMCLK, Frecuencia: 16 MHz. Modo Stop.
}

// Funcion para configurar el ADC:
void ConfiguracionADC(void)
{
   P6SEL |= BIT0 | BIT1; // enable P6.0 y P6.1 as A/D channels
   ADC12CTL0 |= ADC12SHT0_5 | ADC12MSC | ADC12ON; // ADC12ON: enable el ADC; ADC12SHT0_5: 64 sample cycles del S & H
   ADC12CTL1 |= ADC12SSEL_3 | ADC12DIV_7 | ADC12CONSEQ_3 | ADC12SHP; // ADC12SSEL_3: SMCLK; ADC12DIV_7: Preescalador de 8;    ADC12CONSEQ_3: Repeat-Sequence-of-Channels Mode
   ADC12MCTL0 |= ADC12SREF_0 | ADC12INCH_0; // ADC12SREF_0: VR+ = AVcc+ and VR- = AVss ; ADC12INCH_0: Canal A0
   ADC12MCTL1 |= ADC12SREF_0 | ADC12INCH_1 | ADC12EOS; // ADC12INCH_1: Canal A1; ADC12EOS: end of sequence
   ADC12CTL0 |= ADC12ENC; // ADC12ENC: enable conversion
   ADC12IE |= BIT1; // enable ADC12IFG.1
}

void I2C_send(byte addr, byte *buffer, byte numero_datos)
{
   UCB1I2CSA = addr;
   PTxData = buffer;
   TXByteCtr = numero_datos;
   UCB1CTL1 |= (UCTR | UCTXSTT);
   __bis_SR_register(LPM0_bits + GIE);
   __no_operation();
   while(UCB1CTL1 & UCTXSTP);
}

void lcd_send_nibble_cmd(byte dato)
{
   byte buffer[2];
   byte dato_I2C_H;
   dato_I2C_H = dato & 0xF0;
   buffer[0] = dato_I2C_H | iluminacion_LCD | Enable_LCD | comando_LCD;
   buffer[1] = dato_I2C_H | iluminacion_LCD | comando_LCD;
   I2C_send(LCD_PCF8574_ADDRESS, buffer, 2);
}

void lcd_send_byte_data(byte dato)
{
   byte buffer[4];
   byte dato_I2C_H, dato_I2C_L;
   dato_I2C_H = dato & 0xF0;
   dato_I2C_L = (dato << 4) & 0xF0;
   buffer[0] = dato_I2C_H | iluminacion_LCD | Enable_LCD | dato_LCD;
   buffer[1] = dato_I2C_H | iluminacion_LCD | dato_LCD;
   buffer[2] = dato_I2C_L | iluminacion_LCD | Enable_LCD | dato_LCD;
   buffer[3] = dato_I2C_L | iluminacion_LCD | dato_LCD;
   I2C_send(LCD_PCF8574_ADDRESS, buffer, 4);
}

void lcd_send_byte_cmd(byte dato)
{
   byte buffer[4];
   byte dato_I2C_H, dato_I2C_L;
   dato_I2C_H = dato & 0xF0;
   dato_I2C_L = (dato << 4) & 0xF0;
   buffer[0] = dato_I2C_H | iluminacion_LCD | Enable_LCD | comando_LCD;
   buffer[1] = dato_I2C_H | iluminacion_LCD | comando_LCD;
   buffer[2] = dato_I2C_L | iluminacion_LCD | Enable_LCD | comando_LCD;
   buffer[3] = dato_I2C_L | iluminacion_LCD | comando_LCD;
   I2C_send(LCD_PCF8574_ADDRESS, buffer, 4);
}

void init_LCD_PCF8574(void)
{
   delay_ms(20);
   lcd_send_nibble_cmd(0x30);
   delay_ms(5);
   lcd_send_nibble_cmd(0x30);
   delay_ms(1);
   lcd_send_nibble_cmd(0x30);
   delay_ms(5);
   lcd_send_nibble_cmd(0x20); // data bus of 4 bits
   delay_ms(1);
   lcd_send_byte_cmd(0x28);
   delay_ms(1);
   lcd_send_byte_cmd(0x08); // Display off, Cursor off, Blink off
   delay_ms(1);
   lcd_send_byte_cmd(0x01); // Clear Screen, Cursor Home
   delay_ms(2);
   lcd_send_byte_cmd(0x06); // Entry mode set; I/D=1: Incremento;
   delay_ms(10);
   lcd_send_byte_cmd(0x0D); // Display ON, Cursor ON, Cursor Blink
   delay_ms(10);
}

void lcd_setCursor(byte fila, byte columna)
{
   byte address;
   if (fila == 0)
   { // first row
      address = 0;
   }
   else address = 0x40; // Second row
   address |= columna; // Para tener 0x00 + Columna (Fila1)
   // Para tener 0x40 + Columna (Fila2)
   lcd_send_byte_data(0x80 | address); // Set DDRAM Address
   delay_ms(2);
}

void lcd_print(char *string, uint8_t fila, uint8_t columna)
{
   uint8_t tamaño;
   lcd_setCursor(fila,columna);
   while(*string != '\0')
   {
      lcd_send_byte_data(*string++); // write the character on the LCD
      tamaño++;
      if(tamaño > (16-columna))
         {
            lcd_send_byte_cmd(0x18); // Display shift to left
            delay_ms(300); // delay to see how characters are shifted
         }
    }
   delay_ms(2);
}

void borrar_pantalla(void)
{
   lcd_send_byte_cmd(0x01); // clear display
   delay_ms(2);
   lcd_send_byte_cmd(0x02); // Return Home
   delay_ms(2);
}

void main(void)
{
   char datos_A0[4], datos_A1[4];
   WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
   Inicializacion_Relojes();
   USCIB1_init();
   init_TimerA1_ms();
   __enable_interrupt();
   init_LCD_PCF8574();
   borrar_pantalla(); // delete anything that could appear in the display, for clearing it
   lcd_print("Hello Worlddddddddddddddd!",0,0); // Message to show that LCD display seems to work fine
   delay_ms(1000);
   borrar_pantalla();
   ConfiguracionADC();
   delay_ms(300); // wait for stabilize voltage reference
   ADC12CTL0 |= ADC12SC; // ADC12SC: begins the conversion
   __bis_SR_register(LPM0_bits + GIE);
   while(1)
   {
      if (buffer_lleno == 1)
      {
         sprintf(datos_A0,"valor ADC_A0=%d",valor_promedio_A0);
         sprintf(datos_A1,"valor ADC_A1=%d",valor_promedio_A1);
         lcd_print(datos_A0,0,4); // In row 0 of the LCD i put the A1 average value
         lcd_print(datos_A1,1,4); // In row 1 of the LCD i put the A1 average value
         valor_promedio_A0 = 0; // Reset value of A0
         valor_promedio_A1 = 0; // Reset value of A1
         buffer_lleno = 0;
         ADC12CTL0 |= ADC12ENC; // enable ADC
      }
   }
}

#pragma vector=TIMER1_A0_VECTOR
__interrupt void timer1_A0_ISR(void)
{
   contador_milisegundos++;
   TA1CCTL0 &= ~CCIFG;
}

#pragma vector = USCI_B1_VECTOR
__interrupt void usci_b1_isr(void)
{
   switch(__even_in_range(UCB1IV,12))
   {
      case 0: break; // Vector 0: No interrupts
      case 2: break; // Vector 2: ALIFG
      case 4: break; // Vector 4: NACKIFG
      case 6: break; // Vector 6: STTIFG
      case 8: break; // Vector 8: STPIFG
      case 10: break; // Vector 10: RXIFG
      case 12: // Vector 12: TXIFG
      if (TXByteCtr) // Check TX byte counter
      {
         UCB1TXBUF = *PTxData++; // Load TX buffer
         TXByteCtr--; // Decrement TX byte counter
      }
      else
      {
         UCB1CTL1 |= UCTXSTP; // I2C stop condition
         UCB1IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
         __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
      }
      default: break;
   }
}

#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
   static unsigned int index = 0;
   switch(__even_in_range(ADC12IV,34))
   {
      case 0: break; // Vector 0: No interrupt
      case 2: break; // Vector 2: ADC overflow
      case 4: break; // Vector 4: ADC timing overflow
      case 6: break;
      case 8: // Vector 8: ADC12IFG1
      valor_promedio_A0 += ADC12MEM0;
      valor_promedio_A1 += ADC12MEM1;
      index++;
      if (index == 8)
      {
         buffer_lleno = 1;
         valor_promedio_A0 >>= 3; // To divide by 8 to take the average value cause i had measured 8 samples.
         valor_promedio_A1 >>= 3;
         index = 0;
         ADC12CTL0 &= ~ADC12ENC; // Deshabilitamos el ADC
      }
      break;
      case 10: break; // Vector 10: ADC12IFG2
      case 12: break; // Vector 12: ADC12IFG3
      case 14: break; // Vector 14: ADC12IFG4
      case 16: break; // Vector 16: ADC12IFG5
      case 18: break; // Vector 18: ADC12IFG6
      case 20: break; // Vector 20: ADC12IFG7
      case 22: break; // Vector 22: ADC12IFG8
      case 24: break; // Vector 24: ADC12IFG9
      case 26: break; // Vector 26: ADC12IFG10
      case 28: break; // Vector 28: ADC12IFG11
      case 30: break; // Vector 30: ADC12IFG12
      case 32: break; // Vector 32: ADC12IFG13
      case 34: break; // Vector 34: ADC12IFG14
      default: break;
      }
}
 

MrChips

Joined Oct 2, 2009
30,710
Firstly, a few comments:

1) Why such long delays (2ms) for the LCD? You can reduce the delay to μs. What is your MCU clock frequency?
You can also use the LCD controller BUSY signal.

2) Why put delay_ms( ) after every LCD function call? Put the delay in the function itself.

3) Why all the empty case statements? Why not use the default handler?

As for ADC programming, I suggest that you test one thing at a time.

1) Start with just one channel.
2) Try one channel, single conversion.
3) Then try one channel, continuous conversion.
4) Do you need evenly spaced samples and therefore using the timer to trigger ADC conversion?
5) If you need to use ADC interrupts then test that the interrupt is working without worrying about the actual ADC value.

In summary, take one step at a time.
 

Thread Starter

Alejofnb

Joined Dec 24, 2019
20
Firstly, a few comments:

1) Why such long delays (2ms) for the LCD? You can reduce the delay to μs. What is your MCU clock frequency?
You can also use the LCD controller BUSY signal.

2) Why put delay_ms( ) after every LCD function call? Put the delay in the function itself.

3) Why all the empty case statements? Why not use the default handler?

As for ADC programming, I suggest that you test one thing at a time.

1) Start with just one channel.
2) Try one channel, single conversion.
3) Then try one channel, continuous conversion.
4) Do you need evenly spaced samples and therefore using the timer to trigger ADC conversion?
5) If you need to use ADC interrupts then test that the interrupt is working without worrying about the actual ADC value.

In summary, take one step at a time.
I have made a program just focusing on the measures of the ADC. But i don't understand why this isn't going and gives nothing.. took a look on Users Guide and Resource explorer of TI website wwhere one can find basic examples but nothing. Any suggestion? Thanks btw

C:
#include <msp430.h>
#include <stdint.h>
#include <stdio.h>

volatile uint8_t buffer_lleno = 0;
volatile uint8_t end_conversion = 1;
uint16_t valor_promedio_A0 = 0; // Variable que guarda el valor promedio de A0
uint16_t valor_promedio_A1 = 0; // Variable que guarda el valor promedio de A1

void Inicializacion_Relojes(void){

    __bis_SR_register(SCG0); // Disable the FLL control loop
    UCSCTL0 = 0x0000; // Ponemos el DCOx y MODx al minimo posible
    UCSCTL1 = DCORSEL_5; // Seleccionamos un rango de operación del DCO range de 16MHz
    UCSCTL2 = FLLD_0 | 487;  // Poniendo FLLD_0 hacemos tomamos como 1 el divisor de la frecuencia de entrada del cristal de cuarzo y 487 es el valor multiplicador de FLLN
                             // (N + 1) * (FLLRef/n) = Fdco
                             // (487 + 1) * (32768/1) = 16MHz
    UCSCTL3 = 0; // FLL SELREF = XT1CLK y divisor de FLL = 1 (FLLREFDIV = FLLREFCLK/1)
    UCSCTL4 |= SELA_0 | SELS_4 | SELM_4;  // Tomamos ACLK = XT1CLK (Cuarzo externo de 2^15 bits); SMCLK = MCLK = DCOCLKDIV (DCO interno de 16 MHz)
    // UCSCTL5 |= DIVA_0 | DIVS_0; // Divisor para SMCLK = f(SMCLK)/1; ACLK = f(ACLK)/1 --- NO ES NECESARIA PORQUE SON LOS VALORES POR DEFECTO
    __bic_SR_register(SCG0); // Enable the FLL control loop
}

// Funcion para configurar el ADC:

void ConfiguracionADC(void){
    P6SEL |= BIT0 | BIT1; // Habilitamos P6.0 y P6.1 como canales de entrada del A/D
    ADC12CTL0 |= ADC12SHT0_5 | ADC12ON; // ADC12ON: Activamos el ADC;  ADC12SHT0_5: 64 ciclos de muestreo del S & H
    ADC12CTL1 |= ADC12SSEL_3 | ADC12DIV_7 | ADC12CONSEQ_3 | ADC12SHP; // ADC12SSEL_3: SMCLK; ADC12DIV_7: Preescalador de 8; ADC12CONSEQ_3: Multiples muestras en multiples canales
    ADC12MCTL0 |= ADC12SREF_0 | ADC12INCH_0; // ADC12SREF_0: VR+ = AVcc+ and VR- = AVss ; ADC12INCH_0: Canal A0
    ADC12MCTL1 |= ADC12SREF_0 | ADC12INCH_1 | ADC12EOS; // ADC12INCH_1: Canal A1; ADC12EOS: Fin Secuencia canales
    ADC12CTL0 |= ADC12ENC; // ADC12ENC: Habilitamos conversión;
    ADC12IE |= BIT1; // Habilitamos ADC12IFG.1
}

void main(void){
    WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer
    Inicializacion_Relojes();
    ConfiguracionADC();
    ADC12CTL0 |= ADC12SC;
    __bis_SR_register(LPM0_bits | GIE);
    while(1){
        if (buffer_lleno == 1){
            valor_promedio_A0 = 0; // Reseteamos el valor de A0
            valor_promedio_A1 = 0; // Reseteamos el valor de A1
            buffer_lleno = 0;
            ADC12CTL0 |= ADC12ENC;
        }
    }
}

#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
    static unsigned int index = 0;
    switch(__even_in_range(ADC12IV,34))
    {
    case 0: break; // Vector 0: No interrupt
    case 2: break; // Vector 2: ADC overflow
    case 4: break; // Vector 4: ADC timing overflow
    case 6: break;
    case 8: // Vector 8: ADC12IFG1
        valor_promedio_A0 += ADC12MEM0;
        valor_promedio_A1 += ADC12MEM1;
        index++;
        if (index == 8){
            valor_promedio_A0 >>= 3; // Para dividir entre 8 y obtener el valor promedio
            valor_promedio_A1 >>= 3;
            buffer_lleno = 1;
            index = 0;
            ADC12CTL0 &= ~ADC12ENC;
        }
        break;
    case 10: break; // Vector 10: ADC12IFG2
    case 12: break; // Vector 12: ADC12IFG3
    case 14: break; // Vector 14: ADC12IFG4
    case 16: break; // Vector 16: ADC12IFG5
    case 18: break; // Vector 18: ADC12IFG6
    case 20: break; // Vector 20: ADC12IFG7
    case 22: break; // Vector 22: ADC12IFG8
    case 24: break; // Vector 24: ADC12IFG9
    case 26: break; // Vector 26: ADC12IFG10
    case 28: break; // Vector 28: ADC12IFG11
    case 30: break; // Vector 30: ADC12IFG12
    case 32: break; // Vector 32: ADC12IFG13
    case 34: break; // Vector 34: ADC12IFG14
    default: break;
    }
}
Moderators note : used code tags to preserve spaces.
 
Last edited by a moderator:

mckenney

Joined Nov 10, 2018
125
Code:
   ADC12CTL0 |= ADC12SHT0_5 | ADC12ON; // ADC12ON: Activamos el ADC;  ADC12SHT0_5: 64 ciclos de muestreo del S & H
You need MSC=1 (with CONSEQ=3) to keep the sequence/repeat going. [Ref User Guide (SLAU208Q) Fig 28-10]
Code:
   ADC12CTL0 |= ADC12SHT0_5 | ADC12MSC | ADC12ON; // ADC12ON: Activamos el ADC;  ADC12SHT0_5: 64 ciclos de muestreo del S & H
Setting ENC=1 in the loop may or may not be enough to keep the ADC going. If it has stopped by then, you need to set ADC12SC=1 as well. I suggest you wait for it to stop, then restart it.
 

Thread Starter

Alejofnb

Joined Dec 24, 2019
20
You need MSC=1 (with CONSEQ=3) to keep the sequence/repeat going. [Ref User Guide (SLAU208Q) Fig 28-10]
Code:
   ADC12CTL0 |= ADC12SHT0_5 | ADC12MSC | ADC12ON; // ADC12ON: Activamos el ADC;  ADC12SHT0_5: 64 ciclos de muestreo del S & H
Setting ENC=1 in the loop may or may not be enough to keep the ADC going. If it has stopped by then, you need to set ADC12SC=1 as well. I suggest you wait for it to stop, then restart it.
Both things you say now i've fixed minutes after posting my code, so the measures by ADC seems like had fixed but i have one last problem now which is that after finishing the 8 measures it doesn't go to main and restart the variables valores_promedio. and go back to ISR to start over. Any idea why this could happen? Thanks
 
Top