This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/MSP430F5529: Control of LED and LDR

Part Number: MSP430F5529

Tool/software: Code Composer Studio

Hi, i've made two voltage dividers supplied by an external voltage of 3.3V and made of a LDR connected in series to a resistor. I have also used LEDs to show the change of the internal resistor of the LDR. My purpose is, by changing the light applied to LDRs, change the PWM duty cycle that goes to the LEDs associated to each LDR with timers. So, for example, if i have a situation of absence of light two LEDs (one for each LDR) will light so much to compensate this absence of light. 

I paste my code here. If anyone see any problem, i would be grateful for any suggestion. Thanks btw!

To read it in a better way with identations and all that stuff i paste also the same code with pastebin here: https://pastebin.com/htjSHBX9

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

uint16_t adc_A0[8] = {0}; // Array para to store a total of 8 values initialized to zero for A0
uint16_t adc_A1[8] = {0}; // Array para to store a total of 8 values initialized to zero for A1
uint8_t buffer_lleno = 0;
volatile uint8_t end_conversion = 1; // variable to determine wether the conversion has finished or not yet
uint16_t valor_promedio_A0 = 0; // Variable that stores the average value of A0 channel
uint16_t valor_promedio_A1 = 0; // Variable that stores the average value of A1 channel

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
}

void Inicializacion_Leds(void){
P1SEL |= (BIT2 | BIT3); // alternative function for leds
P1DIR |= (BIT2 | BIT3); // LEDs as outputs
}

void TimerA0_PWM(void){
TA0CCR0 = 2000-1; // Tperíodo del PWM: 2000 milisegundos
TA0CCTL1 |= OUTMOD_3; // Modo CCR1: Set/Reset.
TA0CCR1 = 0; // Duty Cycle inicial: 0%
TA0CCTL2 |= OUTMOD_3; // Modo CCR1: Set/Reset.
TA0CCR2 = 0; // Duty Cycle inicial: 0%
TA0CTL |= TASSEL_2 | ID_3 | MC_1 | TACLR; // Reloj SMCLK, Frecuencia: 16 MHz. Modo Up. Preescalador: 8
}

// Funcion para configurar el ADC:

void ConfiguracionADC(void){
P6SEL |= BIT0 | BIT1; // to use P6.0 y P6.1 as entries of channel A0 and A1
ADC12CTL0 |= ADC12SHT0_5 | ADC12MSC | 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 samples in multiple channels
ADC12MCTL0 |= ADC12SREF_0 | ADC12INCH_0; // ADC12SREF_0: VR+ = AVcc+ and VR- = AVss ; ADC12INCH_0: Channel A0
ADC12MCTL1 |= ADC12SREF_0 | ADC12INCH_1 | ADC12EOS; // ADC12INCH_1: Channel A1; ADC12EOS: end of sequence
__delay_cycles(30); // wait for stabilize of voltage reference
ADC12CTL0 |= ADC12ENC; // ADC12ENC: enable conversion
ADC12IE |= BIT1; // enable ADC12IFG.1
}

void main(void){
uint8_t cont; // Variable to iterate in the array
uint16_t value1,value2;
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
Inicializacion_Relojes();
Inicializacion_Leds();
TimerA0_PWM();
ConfiguracionADC();
__enable_interrupt();
while(1){
if(end_conversion == 1){ // if the conversion has finished
ADC12CTL0 |= ADC12SC; // ADC12SC: you can start over another conversion
end_conversion = 0;
}
if (buffer_lleno == 1){
for(cont=0; cont<8; cont++){
valor_promedio_A0 += adc_A0[cont];
valor_promedio_A1 += adc_A1[cont];
}
valor_promedio_A0 >>= 3; // to divide by 8 (3 bits shift to the right)
valor_promedio_A1 >>= 3;
value1 = (TA0CCR0*(valor_promedio_A0))/(4096);
TA0CCR1 = value1;
value2 = (TA0CCR0*(valor_promedio_A1))/(4096);
TA0CCR2 = value2;
valor_promedio_A0 = 0; // Reset value of A0
valor_promedio_A1 = 0; // Reset value of A0
buffer_lleno = 0;
end_conversion = 1;
}
}
}

#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR (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
adc_A0[index] = ADC12MEM0;
adc_A1[index] = ADC12MEM1;
index++;
if (index == 8){
buffer_lleno = 1;
index = 0;
ADC12CTL0 &= ~ADC12SC;
end_conversion = 0;
}
else{
end_conversion = 1;
}

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

  • What does it do now?

    --------------

    > while(!(ADC12IFG & BIT1)); // wait for the data to be ready

    This will probably spin forever, since the ISR will clear the IFG before you see it. One solution is to declare buffer_lleno "volatile" and change this to:

    >while (buffer_lleno == 0);    // wait for the data to be ready

    --------------

    > value1 = (TA0CCR0*(valor_promedio_A0))/(4095);

    TA0CCR0 is about 2^11, and valor_promedio_A0 can be up to 2^12, so there's a pretty good chance that the multiplication will overflow (2^23). Also, the division really should be /4096, since that's the "width" of the range. Try:

    > value1 = (TA0CCR0*((unsigned long)valor_promedio_A0))/(4096);

  • Thanks for help. I paste the new code with the changes you told me. The problem is that i analyze the program step by step and seems like it goes well but when it computes the first time of average values in a good way, then it goes directly to while(buffer_lleno ==0) and stay there like forerver so it cannot computes more values..

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

    uint16_t adc_A0[8] = {0}; // Array para guardar 8 valores de A0 inicializados a cero
    uint16_t adc_A1[8] = {0}; // Array para guardar 8 valores de A1 inicializados a cero
    volatile uint8_t buffer_lleno = 0;
    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
    }

    void Inicializacion_Leds(void){
    P1SEL |= (BIT2 | BIT3); // Pines GPIO
    P1DIR |= (BIT2 | BIT3); // LEDs como salidas
    P1OUT &= ~(BIT2 | BIT3); // LEDs apagados
    }

    void TimerA0_PWM(void){
    TA0CCR0 = 2000-1; // Tperíodo del PWM: 2000 milisegundos
    TA0CCTL1 |= OUTMOD_3; // Modo CCR1: Set/Reset.
    TA0CCR1 = 0; // Duty Cycle inicial: 0%
    TA0CCTL2 |= OUTMOD_3; // Modo CCR1: Set/Reset.
    TA0CCR2 = 0; // Duty Cycle inicial: 0%
    TA0CTL |= TASSEL_2 | ID_3 | MC_1 | TACLR; // Reloj SMCLK, Frecuencia: 16 MHz. Modo Up. Preescalador: 8
    }

    // 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 | ADC12MSC | 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
    __delay_cycles(30); // Espera a que acabe se ajuste la referencia de voltaje
    ADC12CTL0 |= ADC12ENC; // ADC12ENC: Habilitamos conversión;
    ADC12IE |= BIT1; // Habilitamos ADC12IFG.1
    }

    void main(void){
    uint8_t cont; // Variable que itera sobre las posiciones del array
    uint16_t value1,value2;
    WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
    Inicializacion_Relojes();
    Inicializacion_Leds();
    TimerA0_PWM();
    ConfiguracionADC();
    __enable_interrupt(); // Habilitamos interrupciones generales
    while(1){
    ADC12CTL0 |= ADC12SC; // ADC12SC: Empieza conversión
    while(buffer_lleno == 0); // Esperamos hasta que esté listo el dato
    if (buffer_lleno == 1){
    for(cont=0; cont<8; cont++){
    valor_promedio_A0 += adc_A0[cont];
    valor_promedio_A1 += adc_A1[cont];
    }
    valor_promedio_A0 >>= 3; // Para dividir entre 8 y obtener el valor promedio
    valor_promedio_A1 >>= 3;
    value1 = (TA0CCR0*((unsigned long)valor_promedio_A0))/(4096);
    TA0CCR1 = value1;
    value2 = (TA0CCR0*((unsigned long)valor_promedio_A1))/(4096);
    TA0CCR2 = value2;
    valor_promedio_A0 = 0; // Reseteamos el valor de A0
    valor_promedio_A1 = 0; // Reseteamos el valor de A1
    buffer_lleno = 0;
    }
    }
    }

    #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
    adc_A0[index] = ADC12MEM0;
    adc_A1[index] = ADC12MEM1;
    index++;
    if (index == 8){
    buffer_lleno = 1;
    index = 0;
    ADC12CTL0 &= ~ADC12ENC;
    }
    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;
    }
    }

  • One possibility is that the ADC is taking longer to stop than we think; I haven't done the arithmetic, but I see a number of dividers in use and with CONSEQ=3 it has to finish the entire sequence. [Ref User Guide (SLAU208Q) Sec 28.2.7.6]. Try waiting for it to stop before restarting it. The sequence should probably be something like:

    > while (ADC12CTL1 & ADCBUSY) /*EMPTY*/; // Wait for sequence to finish

    > index = 0;   // restart at the beginning

    > buffer_lleno = 0;

    > ADC12CTL0 |= ADC12SC; // ADC12SC: Empieza conversión

**Attention** This is a public forum