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: Traffic light with speaker

Part Number: MSP430F5529

Tool/software: Code Composer Studio

Hi, i want to control a traffic light in that way: 

Firstly i have to say that orange colour is when leds green and red are ON at same time. 

At first place traffic light will be with green led ON. Then, when someone press the botton S2 of the launchpad (P1.1), green led blinks with a period of 1/8 second. After one second, traffic light change its colour to orange without blinking (as i said before both green and red are ON now) during 2 seconds. After that, traffic light change its colour to Red for 3 seconds and, when these 3 seconds finish, it begins to blink in red colour at period of 1/16 seconds during 1 last second. After this sequence, it starts over by changing traffic light colour again to Green without blinking and waiting for someone to push the button again. 

A bit 'extra' is that i wanna count the number of people that pressed the button with a variable --> numero_peatones. And also i want to add a buzzer (perhaps a passive one) which i can control with a PWM signal. So, for example, when is near to finish one state and begin next one (such as changing colour green to orange), i want to increase PWM signal in order to increase the sound it goes off the buzzer. 

This last part i haven't done yet, because i have some sort of a problem with the sequence. I think the problem is in the delay_segundos function but i don't know why. If anyone can take a look on my code with ideas of optimization and also to fix my issue i will apreciatte it a lot! Thanks:)

My code:

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

uint8_t numero_peatones = 0;
uint16_t volatile contador_segundos; 
uint8_t conteo_pulsador = 0; 

void Inicializacion_Relojes(void) // Para usar el reloj SMCLK a 1 MHz
{
__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) = 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_4; // Divisor para SMCLK = f(SMCLK)/16; ACLK = f(ACLK)/1
__bic_SR_register(SCG0); // Enable the FLL control loop
}

void inicializacion_pulsador(void) 
{
P1SEL = 0x00; // GPIO pins
P1DIR &= ~BIT1; // input P1.1
P1REN |= BIT1; // enable Pull up/down
P1OUT |= BIT1; // Pull-up interna en pin 1.1
P1IES |= BIT1; 
P1IE |= BIT1;
P1IFG &= ~BIT1; 
}

void inicializacion_LEDs(void) 
{
P1SEL &= ~BIT0; // LED1 of Launchpad as GPIO
P1DIR |= BIT0; // LED1 as output
P1OUT &= ~BIT0; // RED LED off at first place
P4SEL &= ~BIT7; 
P4DIR |= BIT7; // LED2 as output
P4OUT |= BIT7; // GREEN LED on at first place
}

void delay_segundos(uint8_t segundos) // Function for delay sec --> Reloj ACLK
{
contador_segundos = 0;
TA2CTL |= MC_1; // UP MODE
TA2CCTL0 |= CCIE; // enable interrupts timer A2
while(contador_segundos < segundos);
TA2CTL &= ~MC_3; // Stop timer
}

void TimerA0(void) // Timer to count 1/8 sec
{
TA0CCR0 = 15624; // f(SMCLK)/8 = 125kHz --> want to count 1/8 sec --> TA0CCR0 = (125kHz/8) - 1 = 15624.
TA0CTL |= TASSEL_2 | ID_3 | MC_0; // TASSEL_2: SMCLK; ID_3: Divisor freq --> f(SMCLK)/8; MC_0: Stop Mode
}

void TimerA1(void) // Timer to count 1/16 seconds
{
TA1CCR0 = 15624; // f(SMCLK)/4 = 250kHz --> want to count 1/16 sec --> TA1CCR0 = (250kHz/16) - 1 = 15624. 
TA1CTL |= TASSEL_2 | ID_2 | MC_0; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/4;
}

void TimerA2(void) // Timer for seconds
{
TA2CCR0 = 8191; // f(ACLK)/4 = 8192Hz
TA2CTL |= TASSEL_1 | ID_2 | MC_0; // TASSEL_2: ACLK; ID_2: Divisor freq --> f(ACLK)/4; MC_0: Stop Mode
}

void main()
{
Inicializacion_Relojes();
inicializacion_pulsador();
inicializacion_LEDs();
TimerA0();
TimerA1();
TimerA2();
__enable_interrupt(); 
while(1){
if(conteo_pulsador == 1) 
{
delay_segundos(1);
conteo_pulsador = 0;
P4OUT |= BIT7; // ORANGE LED
P1OUT |= BIT0;
delay_segundos(2);
P1OUT |= BIT0; when 2 seconds had passed, RED LED is ON
delay_segundos(3); 
TA1CCTL0 |= CCIE; // enable interrupts of RED LED --> 1/16 sec
delay_segundos(1);
P4OUT |= BIT7; // GREEN LED on
P1OUT &= ~BIT0; // RED LED off
}
}
}


#pragma vector=TIMER0_A0_VECTOR
__interrupt void timerA0_0_isr(void)
{
P4OUT ^= BIT7; // GREEN LED blinking
TA0CCTL0 &= ~CCIFG; 
}

#pragma vector=TIMER1_A0_VECTOR
__interrupt void timerA1_0_isr(void)
{
P1OUT ^= BIT0; // RED LED blinking
TA1CCTL0 &= ~CCIFG; 
}

#pragma vector=TIMER2_A0_VECTOR
__interrupt void timer2_A0_ISR(void) // ISR of Timer for counting seconds to delay
{
contador_segundos++; // Increase the variable that counts the seconds
TA2CCTL0 &= ~CCIFG;
}


#pragma vector=PORT1_VECTOR // ISR del pulsador P1.1 (S2)
__interrupt void port1_ISR(void)
{
if(P1IFG & BIT1){ // when the button is pressed --> GREEN LED blinks to 1/8 sec
numero_peatones++; 
P1IE &= ~BIT1; // disable temporaly interrupts on the button
TA0CTL |= MC_1; // MC_1: Up Mode 
TA0CCTL0 |= CCIE; // enable interrupts
P1IFG &= ~BIT1; // clear flag
conteo_pulsador++;
}
}

  • 1) conteo_pulsador should be declared "volatile". This may not be your problem now, but it will be eventually.

    2) In the PORT1 ISR, you're disabling the button IE bit. This is a reasonable way to do de-bouncing, but I don't see where you re-enable it. Maybe in timer0_a0_isr, since 1/8th of a second is probably plenty of de-bounce time.

    3) I see where you're starting the red and green blinkers, by setting the relevant CCIE, but I don't see where you stop the blinkers (perhaps by clearing the CCIE). As long as they keep running they'll interfere with your attempts to keep the LED(s) on-steady.

  • I've changed what you told me and i have the same issue. problem I see by debugging step by step the code is that, when the delay function is called in the main function, it doesn't seem to go to the ISR associated to the Timer A2 when i set: TA2CCTL0 |= CCIE; // enable interrupts timer A2. So, as a consequence of it, the variable contador_segundos doesn't increase in value, having an infinitive while loop in delay_segundos. But i do not see why this happen.

  • Add this as your first line in main():

    >     WDTCTL = WDTPW | WDTHOLD;

    My project had "Build Settings->Build->Linker->Basic->Hold watchdog timer" set to "on", but somehow I still got a watchdog timeout. I don't know what's up with that. Once I added that line, I got what (I think) you intended.

    [Edit: I dug a little further, and that option (--cinit_hold_wdt) literally means "during cinit" -- it holds it before, then releases it afterward. Now I know.]

  • Thank you for fixing my problem, but now i have some doubts about why is necessary to stop the watchdog timer in the main function of every program and also why i should set the variable volatile. Btw, the othe part of the project, which is sounding more or less a passive buzzer, connected to a NPN transistor to a microcontroller pin.. do you know if i have to use a DAC to output an AC signal from a microcontroller pin and also if there is a chance to control how it sounds (more intensive or less) depending on the duty cycle of a PWM signal?

  • 1) The watchdog starts automatically at MCU reset [Ref User Guide (SLAU445I) Sec 1.2.1]. If you create an empty ("main.c") project or import an Example, that line I recommended gets supplied for you.

    2) It needs to be volatile since it's used to communicate between main and an ISR. It tells the compiler the variable might change without notice.

    3) If you're using one of the simple (cheap) piezo buzzers from e.g. Adafruit, a square wave is fine. The piezo and resonator act as a (physical) low-pass filter. Those things aren't particularly high-fidelity in any case. You'll want a higher frequency (e.g. 400Hz) than you're using with your other timers, so you'll probably need to use TimerB.

  • I have done this. My idea is that when is near to finish one state and begin next one (such as changing colour green to orange), i want to increase PWM signal in order to increase the sound it goes off the buzzer to warn the person, who has the intention to cross the street, that has to hurry up.
    My problem is related to the buzzer (leds changing and the delays between them work well), because i want to connect it to the pin P3.6 of microcontroller MSP430F5529 by a NPN transistor, that has a 1k resistor in its base. As it seems when i saw the MSP430F5529 datasheet, pin P3.6 has TB0.6 associated to it. So i made a function of TimerB_PWM that sets a period of 0,25 ms (or 4 kHz) and a initial duty cycle of 0%. Then, when red or green leds blink i want to increase the value of the Duty Cycle of the TimerB0 to increase the PWM of the buzzer. That's my idea of monitoring the volume of the buffer as time passes.

    I have used the typpical passive buzzer yep, from an Arduino kit. I have supposed 4kHz as the frequency of the maximum tone. 

    My code:

    #include <msp430.h>
    #include <stdint.h>
    
    uint8_t numero_peatones = 0;
    uint16_t volatile contador_segundos;
    uint8_t volatile conteo_pulsador = 0;
    
    void Inicializacion_Relojes(void) //  SMCLK at 1 MHz
    {
        __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) = 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_4; // Divisor para SMCLK = f(SMCLK)/16; ACLK = f(ACLK)/1
        __bic_SR_register(SCG0); // Enable the FLL control loop
    }
    
    void inicializacion_pulsador(void)
    {
        P1SEL = 0x00; // GPIO pins
        P1DIR &= ~BIT1;  // input P1.1
        P1REN |= BIT1; // enable Pull up/down
        P1OUT |= BIT1; // Pull-up interna en pin 1.1
        P1IES |= BIT1;
        P1IE |= BIT1;
        P1IFG &= ~BIT1;
    }
    
    void inicializacion_LEDs(void)
    {
        P1SEL &= ~BIT0; // LED1 of Launchpad as GPIO
        P1DIR |= BIT0; // LED1 as output
        P1OUT &= ~BIT0;  // RED LED off at first place
        P4SEL &= ~BIT7;
        P4DIR |= BIT7; // LED2 as output
        P4OUT |= BIT7; // GREEN LED on at first place
    }
    
    void pin_buzzer(void){
        P3SEL |= BIT6; // P3.6 as alternative function (for Timer TB0.6 --> see datasheet MSP430F5529)
        P3DIR |= BIT6; // Pin as output connected to buzzer
    }
    
    void delay_segundos(uint8_t segundos) // Function for delay sec --> Reloj ACLK
    {
        contador_segundos = 0;
        TA2CTL |= MC_1; // UP MODE
        TA2CCTL0 |= CCIE;
        while(contador_segundos < segundos);
        TA2CTL &= ~MC_3; // stop timer
    }
    
    void TimerA0(void) // Timer to count 1/8 sec
    {
        TA0CCR0 = 15624; // f(SMCLK)/8 = 125kHz --> want to count 1/8 sec --> TA0CCR0 = (125kHz/8) - 1 = 15624.
        TA0CTL |= TASSEL_2 | ID_3 | MC_0; // TASSEL_2: SMCLK; ID_3: Divisor freq --> f(SMCLK)/8; MC_0: Stop Mode
    }
    
    void TimerA1(void) // Timer to count 1/16 seconds
    {
        TA1CCR0 = 15624; // f(SMCLK)/4 = 250kHz --> want to count 1/16 sec --> TA1CCR0 = (250kHz/16) - 1 = 15624. 
        TA1CTL |= TASSEL_2 | ID_2 | MC_1; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/4; MC_1: Up Mode
    }
    
    void TimerA2(void) // Timer for seconds
    {
        TA2CCR0 = 8191; // f(ACLK)/4 = 8192Hz
        TA2CTL |= TASSEL_1 | ID_2 | MC_0; // TASSEL_2: ACLK; ID_2: Divisor freq --> f(ACLK)/4; MC_0: Stop Mode
    }
    
    void TimerB0_PWM(void) // Timer for monitoring PWM of a buzzer
    {
        TB0CCR0 = 999; // Periode of PWM: 1000 pulses --> 4kHz (o 0,25 ms)
        TB0CCTL1 |= OUTMOD_3; // Mode CCR1: PWM set/reset
        TB0CCR1 = 0; // Duty Cycle initial: 0 %
        TB0CLT |= TASSEL_2 | ID_2 | MC_1 | TACLR; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/4; MC_1: Up Mode
    }
    
    void main()
    {
        WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
        Inicializacion_Relojes();
        inicializacion_pulsador();
        inicializacion_LEDs();
        pin_buzzer();
        TimerA0();
        TimerA1();
        TimerA2();
        TimerB0_PWM();
        __enable_interrupt(); // Para poder usar las ISR
        while(1){
            if(conteo_pulsador == 1)
            {
                delay_segundos(1);
                TA0CCTL0 &= ~CCIE; // disable Timer A0
                conteo_pulsador = 0;
                TB0CCR1 = 0; // reset in value
                P4OUT |= BIT7; // ORANGE LED --> both led ON
                P1OUT |= BIT0;
                delay_segundos(2);
                P4OUT &= ~BIT7; // GREEN LED off
                delay_segundos(3);
                TA1CCTL0 |= CCIE; // enable ISR RED LED--> 1/16 sec
                delay_segundos(1);
                TA1CCTL0 &= ~CCIE; // disable Timer A1
                TB0CCR1 = 0; // reset in value
                P4OUT |= BIT7; // GREEN LED on
                P1OUT &= ~BIT0; // RED LED off
                P1IE |= BIT1; // reenable interrupts in button
            }
        }
    }
    
    
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void timerA0_0_isr(void)
    {
        P4OUT ^= BIT7; // GREEN LED blinking
        TB0CCR1 += 125; // increase the tone frequency of the buzzer every time it goes into that ISR
        TA0CCTL0 &= ~CCIFG;
    }
    
    #pragma vector=TIMER1_A0_VECTOR
    __interrupt void timerA1_0_isr(void)
    {
        P1OUT ^= BIT0; // RED LED blinking
        TB0CCR1 += 125; // increase the tone frequency of the buzzer every time it goes into that ISR
        TA1CCTL0 &= ~CCIFG;
    }
    
    #pragma vector=TIMER2_A0_VECTOR
    __interrupt void timer2_A0_ISR(void)  // ISR of Timer for counting seconds to delay
    {
        contador_segundos++; // Increase the variable that counts the seconds
        TA2CCTL0 &= ~CCIFG;
    }
    
    
    #pragma vector=PORT1_VECTOR // ISR del pulsador P1.1 (S2)
    __interrupt void port1_ISR(void)
    {
        if(P1IFG & BIT1){ // when the button is pressed --> GREEN LED blinks to 1/8 sec
            numero_peatones++;
            P1IE &= ~BIT1; // disable temporaly interrupts on the button
            TA0CTL |= MC_1; // MC_1: Up Mode 
            TA0CCTL0 |= CCIE;
            P1IFG &= ~BIT1;
            conteo_pulsador++;
        }
    }
  • What is your symptom exactly?

    1) TB0.6 is controlled by TB0CCR6, not TB0CCR1.

    2) Set/Reset (OUTMOD=3) goes high at the CCR value and low at CCR0, at the end (beginning) of the cycle, i.e. the high portion is at the end, not the beginning of the PWM cycle, and increasing the CCR value makes the pulse shorter and amplitude (volume) lower. From your description, I suspect you want Reset/Set (OUTMOD=7) instead.

    [Edit: I just noticed you're dividing SMCLK (/16) down to 1MHz, so TB0 is cycling at 1MHz/4/1000=250Hz, not 4kHz. To get 4kHz you can try ID=0 and CCR0=(1MHz/1/4000)=250-1.]

  • Really thanks! Now it works perfectly. 

**Attention** This is a public forum