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: Ultrasonic sensor HC-SR04 and a buzzer

Part Number: MSP430F5529

Tool/software: Code Composer Studio

Hi, i'm trying to use the common ultrasonic sensor HC-SR04 for detecting objects by measuring its distance to the sensor and, if the distance between the object and this sensor is lower than 20 cm, i should warn with a buzzer that object is closer than it has to be. As much closer the object is from the sensor (starting to count when distance is lower than 20 cm as i said before), the tone of the buzzer increase proportionally (using PWM). So my problem is (appart from any optimization recommendation or even error) that i want to deal with ECHO signal with the Timer TA0.0 ISR and i don't know how to take into account the rising and falling edge. Is there any register to regulate when this edges occur? Is there something i had not keep in mind from the HC-SR04 datasheet in my code? Let me know it that happens, please. 

Also i want to know if anyone has any good idea where this could be applied as a piece of a main and more difficult project. I will take like a challenge. 

Thanks beforehand.

My code is:

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

uint16_t volatile contador_microsegundos; // microseconds counter
uint32_t volatile start_time = 0; // start time of ECHO pulse
uint32_t volatile end_time = 0; // end time of ECHO pulse 
uint16_t volatile distancia_cm = 0; // distance measured

void Inicializacion_Relojes(void) // to use the SMCLK --> 16 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_0; // Divisor para SMCLK = f(SMCLK)/1; ACLK = f(ACLK)/1
__bic_SR_register(SCG0); // Enable the FLL control loop
}

void init_pines_ultrasonidos(void)
{
P1DIR &= ~BIT1; // Pin ECHO as input
P1SEL |= BIT1; // mode Capture Input Signal (CCI0A)
P2DIR |= BIT1; // Pin TRIGGER as output
P2SEL &= ~BIT1; // Gpio pin
P2OUT &= ~BIT1; // Pin TRIGGER low
}

void pin_buzzer(void) // pin P3.6 as TB0.6 --> P3DIR.6 = 1; P3SEL.6 = 1 (see Table 6-48 Datasheet MSP430F5529)
{
P3SEL |= BIT6; // P3.6 as alternative function (para Timer TB0.6 según datasheet MSP430F5529)
P3DIR |= BIT6; // Pin as buzzer output
}

void delay_microsegundos(uint16_t microsegundos_totales) // Function for delay of microseconds --> SMCLK
{
contador_microsegundos = 0;
TA2CTL |= MC_1; // UP MODE
TA2CCTL0 |= CCIE; 
while(contador_microsegundos < microsegundos_totales);
TA2CTL &= ~MC_3; // stop the timer
}

void TimerA0_capture(void) // Timer for capturing ECHO signal of ultrasonic sensor --> TA0.0 en modo Capture Input (CCI0A)
{
TA0CCTL0 |= CM_3 | SCS | CCIS_0 | CAP | CCIE; // CM_3: Capture both rising and falling edges of signal; CCIS_0: CCIxA --> Capture/Compare in TimerA; CAP: enable capture mode; CCIE: enable interrupciones
TA0CTL |= TASSEL_2 | MC_2 | ID_0 | TACLR; // TASSEL_2: SMCLK; MC_2: Continuous Mode; ID_0: f(SMCLK)/1; TACLR: Reset Timer
}

void TimerA2(void) // Timer for microseconds
{
TA2CCR0 = 15; // f(SMCLK)/1 = 16 MHz  --> (16·10^6 pulses/seg)·(1seg / 10^6 us) = 16 - 1 = 15.
TA2CTL |= TASSEL_2 | ID_0 | MC_0; // TASSEL_2: SMCLK; ID_0: Divisor freq --> f(SMCLK)/1; MC_0: Stop Mode
}

void TimerB0_PWM(void) // Timer for monitoring PWM of buzzer 
{
TB0CCR0 = 249; // Período de PWM: 250 pulsos --> 4kHz (o 0,25 ms) --> 1000 pulses/ms · 0,25 ms (inverse of 4kHz) = 250 - 1 = 249.
TB0CCTL6 |= OUTMOD_7; // Modo CCR6: PWM Reset/Set --> increasing CCR6 value will also increase Duty Cycle --> more buzzer amplitude 
TB0CCR6 = 0; // Duty Cycle inicial: 0 %. TB0.6 --> TB0CCR6.
TB0CTL |= TASSEL_2 | ID_0 | MC_1 | TACLR; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/1; MC_1: Up Mode
}

void main()
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
Inicializacion_Relojes();
init_pines_ultrasonidos();
pin_buzzer();
TimerA0_capture();
TimerA2();
TimerB0_PWM();
__enable_interrupt(); 
while(1){
P2OUT |= BIT1; // generate pulse TRIGGER 
delay_microsegundos(10); //  pulse of 10 us in TRIGGER pin for obtaining the measures (see datasheet HC-SR04)
P2OUT &= ~BIT1; // disable TRIGGER pulse
if(distancia_cm < 20 && distance != 0){ // if distance between object and the sensor is lower than 20 cm and also different from zero..
TB0CCR6 = 0; // Reset value of Timer that decides the final PWM value
TB0CCR6 += 249 - (distancia_cm * 12); // Aumentamos la frecuencia del tono del buzzer conforme estamos más cerca del objeto (a menor distancia)
delay_microsegundos(60000); //  60 ms measurement cycle in order to prevent trigger signal to the echo signal (see Datasheet HC-SR04)
}
}


#pragma vector=TIMER2_A0_VECTOR
__interrupt void timer2_A0_ISR(void) // Timer for us
{
contador_microsegundos++; 
TA2CCTL0 &= ~CCIFG; 
}

#pragma vector=TIMER0_A0_VECTOR // ISR de Capture Signal TA0.0
__interrupt void Timer0_A0(void)
{
// if rising edge condition:  -->> HERE COMES THE PROBLEM
start_time = TA0CCR0; //takes first time measure in us
// else --> ALSO HERE
end_time = TA0CCR0; //takes second time measure in us
distancia_cm = (end_time - start_time)/58; // to obtain distance in centimeters: (time in us/58)
}

  • > TA2CCR0 = 15; // f(SMCLK)/1 = 16 MHz  --> (16·10^6 pulses/seg)·(1seg / 10^6 us) = 16 - 1 = 15.

    Short answer: Trying to time single microseconds with a timer won't succeed. Longer answer: It takes more than 1 usec to get into and out of the ISR (I usually estimate 20-30 CPU clocks), so your TA0 ISR will be running all the time (and still not keeping up). I suggest using __delay_cycles for your 10usec delay and maybe piggyback on TB0 for your longer delay. Here's a suggested usage for __delay_cycles:

    > #define HZ 16000000UL // 16MHz MCLK

    > __delay_cycles(10*HZ/1000000UL);   // Spin for 10 usec (10*16 CPU clocks)

    ---------

    To answer your question: In your TA0 ISR, check the CCI bit, which reflects the state of the capture pin at the moment you check it. I say "at the moment" since it's possible the pin has changed since the capture, but I think the minimum pulse width is (2*58 usec), so you have at least 116*16=1856 clocks to work with, which sounds like plenty.

  • I've made the changes you told me but seems like the program has stopped in __delay_cycles(10); sentence. Why could this be happening? Also if you see anything that could give me problems, let me know it please. Thanks beforehand!

    My new code is this: 

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

    uint16_t volatile contador_milisegundos; // miliseconds counter
    uint32_t volatile start_time = 0; // start time of ECHO pulse
    uint32_t volatile end_time = 0; // end time of ECHO pulse 
    uint16_t volatile distancia_cm = 0; // distance measured

    #define HZ 16000000UL // 16MHz MCLK

    void Inicializacion_Relojes(void) // to use the SMCLK --> 16 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_0; // Divisor para SMCLK = f(SMCLK)/1; ACLK = f(ACLK)/1
    __bic_SR_register(SCG0); // Enable the FLL control loop
    }

    void init_pines_ultrasonidos(void)
    {
    P1DIR &= ~BIT1; // Pin ECHO as input
    P1SEL |= BIT1; // mode Capture Input Signal (CCI0A)
    P3DIR |= BIT7; // Pin TRIGGER as output
    P3SEL &= ~BIT7; // Gpio pin
    P3SEL &= ~BIT7; // Pin TRIGGER low
    }

    void pin_buzzer(void) // pin P3.6 as TB0.6 --> P3DIR.6 = 1; P3SEL.6 = 1 (see Table 6-48 Datasheet MSP430F5529)
    {
    P3SEL |= BIT6; // P3.6 as alternative function (para Timer TB0.6 según datasheet MSP430F5529)
    P3DIR |= BIT6; // Pin as buzzer output
    }

    void delay_ms(uint16_t tiempo_milisegundos) // Function for delay of miliseconds --> SMCLK
    {
    contador_milisegundos = 0;
    TA2CTL |= MC_1; // UP MODE
    TA2CCTL0 |= CCIE; 
    while(contador_milisegundos < tiempo_milisegundos);
    TA2CTL &= ~MC_3; // stop the timer
    }

    void TimerA0_capture(void) // Timer for capturing ECHO signal of ultrasonic sensor --> TA0.0 en modo Capture Input (CCI0A)
    {
    TA0CCTL0 |= CM_3 | SCS | CCIS_0 | CAP | CCIE; // CM_3: Capture both rising and falling edges of signal; CCIS_0: CCIxA --> Capture/Compare in TimerA; CAP: enable capture mode; CCIE: enable interrupciones
    TA0CTL |= TASSEL_2 | MC_2 | ID_0 | TACLR; // TASSEL_2: SMCLK; MC_2: Continuous Mode; ID_0: f(SMCLK)/1; TACLR: Reset Timer
    }

    void init_TimerA1_ms(void) // for delay
    {
    TA1CCR0 = 16000-1; // pulses for 1 milisecond
    TA1CTL |= TASSEL_2 | MC_1; // SMCLK, Freq: 16 MHz; MC_1: UP Mode

    void TimerB0_PWM(void) // Timer for monitoring PWM of buzzer 
    {
    TB0CCR0 = 249; // Período de PWM: 250 pulsos --> 4kHz (o 0,25 ms) --> 1000 pulses/ms · 0,25 ms (inverse of 4kHz) = 250 - 1 = 249.
    TB0CCTL6 |= OUTMOD_7; // Modo CCR6: PWM Reset/Set --> increasing CCR6 value will also increase Duty Cycle --> more buzzer amplitude 
    TB0CCR6 = 0; // Duty Cycle inicial: 0 %. TB0.6 --> TB0CCR6.
    TB0CTL |= TASSEL_2 | ID_0 | MC_1 | TACLR; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/1; MC_1: Up Mode
    }

    void main()
    {
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    Inicializacion_Relojes();
    init_pines_ultrasonidos();
    pin_buzzer();
    TimerA0_capture();
    init_TimerA1_ms();
    TimerB0_PWM();
    __enable_interrupt(); 
    while(1){
    P3OUT |= BIT7; // generate pulse TRIGGER 
    __delay_cycles(10*HZ/1000000UL); //  pulse of 10 us in TRIGGER pin for obtaining the measures (see datasheet HC-SR04)
    P3OUT &= ~BIT7; // disable TRIGGER pulse
    if(distancia_cm < 20 && distancia_cm != 0){ // if distance between object and the sensor is lower than 20 cm and also different from zero..
    TB0CCR6 = 0; // Reset value of Timer that decides the final PWM value
    TB0CCR6 += 249 - (distancia_cm * 12); // Aumentamos la frecuencia del tono del buzzer conforme estamos más cerca del objeto (a menor distancia)
    delay_ms(60); //  60 ms measurement cycle in order to prevent trigger signal to the echo signal (see Datasheet HC-SR04)
    }
    }

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

    #pragma vector=TIMER0_A0_VECTOR // ISR de Capture Signal TA0.0
    __interrupt void Timer0_A0(void)
    {
    if(TA0CCTL0 & CCI){
    start_time = TA0CCR0; //takes first time measure in us

    }
    else{
    end_time = TA0CCR0; //takes second time measure in us
    distancia_cm = (end_time - start_time)/58; // to obtain distance in centimeters: (time in us/58)
    }

    TA0CTL &= ~TAIFG;

    }

  • If there isn't a capture (yet) __delay_cycles is where that loop will spend most of its time. (The call does complete.)

    1) You're not waiting for the capture(s) to happen. At 4m, it could take up to 4*100*0.058 = 23 milliseconds for the echo to come back, so you don't want to send a new pulse sooner than that. If you're dealing with humans, one pulse every 100ms is probably more than enough. I suggest you just move the delay_ms(60) up to follow the pulse, so it serves two (related) purposes.

    2) I don't see where you initialize TA2. Without that, I expect that delay_ms() will hang.

  • Okey, i change on my code what you said (i've understood that delay function, as delay_ms(100) for example, should go directly after P3OUT &= ~BIT7; // disable TRIGGER pulse. But the problem hasn't fixed yet and it stops everytime in the while loop of delay_ms function. So i conclude that the sensor is not answering and detecting my hand as i approach to it and that's why it stops there because the main function see nothing happening as you said. But i don't know exactly why is this happening? Could be because i use a wrong register in TimerA0.0 ISR? Thanks. 

  • Ah, I missed that you changed the ms-timer to TA1. (Using "code tags" ("</>" button, second line toward the right) makes it much easier to read.)

    You need to add to init_TimerA1_ms:

    >   TA1CCTL0 |= CCIE;  // generate interrupt every 1ms

    The capture should occur (once) in spite of the delay_ms hang. Do you see anything in start_time? You might want to set a breakpoint at Timer0_A0().

  • Hi, i've made a few changes to the code. For example, I have change the capture input signal to P1.2 because of the pin P1.1 is a button and i cannot connect any wire to the ECHO signal then. 
    Also i've created my own delay us function, but when i compile all the code sensor doesn't detect my hand in distance and variable distance_cm keeps in value of 0 everytime.
    I then stop the execution to see where it stops and it appears a message 'ISR_TRAP' (i think related to delay_ms function) but i don't know why, because this function i've used in
    other projects before and hasn't come with any problem. Do you see anything weird in the code that i might not see now? Thanks beforehand!

    #include <msp430.h> #include <stdint.h> uint16_t volatile contador_milisegundos; // miliseconds counter uint32_t volatile start_time = 0; // start time of ECHO pulse uint32_t volatile end_time = 0; // end time of ECHO pulse uint16_t volatile distancia_cm = 0; // distance measured void Inicializacion_Relojes(void) // to use the SMCLK --> 16 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_0; // Divisor para SMCLK = f(SMCLK)/1; ACLK = f(ACLK)/1 __bic_SR_register(SCG0); // Enable the FLL control loop } void init_pines_ultrasonidos(void) { P1DIR &= ~BIT2; // Pin ECHO as input P1SEL |= BIT2; // mode Capture Input Signal (CCI0A) P3DIR |= BIT7; // Pin TRIGGER as output P3SEL &= ~BIT7; // Gpio pin P3SEL &= ~BIT7; // Pin TRIGGER low } void pin_buzzer_and_LED(void) // pin P3.6 as TB0.6 --> P3DIR.6 = 1; P3SEL.6 = 1 (see Table 6-48 Datasheet MSP430F5529) { P3SEL |= BIT6; // P3.6 as alternative function (para Timer TB0.6 según datasheet MSP430F5529) P3DIR |= BIT6; // Pin as buzzer output P4SEL &= ~BIT7; // Pin GPIO P4DIR |= BIT7; // como salida (Led2, verde) P4OUT &= ~BIT7; // led inicialmente apagado } void delay_ms(uint16_t tiempo_milisegundos) // Function for delay of miliseconds --> SMCLK { contador_milisegundos = 0; TA1CTL |= MC_1; // UP MODE TA1CCTL0 |= CCIE; while(contador_milisegundos < tiempo_milisegundos); TA1CTL &= ~MC_3; // stop the timer }

    void delay_us(uint16_t tiempo_microsegundos) // Function for delay de us --> SMCLK a 16 MHz
    {
    while(tiempo_microsegundos){
    __delay_cycles(16); // put a value of 16 for 16 MHz
    tiempo_microsegundos--;
    }
    }
    
    void TimerA0_capture(void) // Timer for capturing ECHO signal of ultrasonic sensor --> TA0.0 en modo Capture Input (CCI0A)
    {
        TA0CCTL1 |= CM_3 | SCS | CCIS_0 | CAP | CCIE; // CM_3: Capture both rising and falling edges of signal; CCIS_0: CCIxA --> Capture/Compare in TimerA; CAP: enable capture mode; CCIE: enable interrupciones
        TA0CTL |= TASSEL_2 | MC_2 | ID_0 | TACLR; // TASSEL_2: SMCLK; MC_2: Continuous Mode; ID_0: f(SMCLK)/1; TACLR: Reset Timer
    }
    
    void init_TimerA1_ms(void) // for delay
    {
        TA1CCR0 = 16000-1; // pulses for 1 milisecond
        TA1CTL |= TASSEL_2 | MC_0; // SMCLK, Freq: 16 MHz; MC_1: Stop Mode
    }
    
    void TimerB0_PWM(void) // Timer for monitoring PWM of buzzer
    {
        TB0CCR0 = 249; // Período de PWM: 250 pulsos --> 4kHz (o 0,25 ms) --> 1000 pulses/ms · 0,25 ms (inverse of 4kHz) = 250 - 1 = 249.
        TB0CCTL6 |= OUTMOD_7; // Modo CCR6: PWM Reset/Set --> increasing CCR6 value will also increase Duty Cycle --> more buzzer amplitude
        TB0CCR6 = 0; // Duty Cycle inicial: 0 %. TB0.6 --> TB0CCR6.
        TB0CTL |= TASSEL_2 | ID_0 | MC_1 | TACLR; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/1; MC_1: Up Mode
    }
    
    void main()
    {
        WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
        Inicializacion_Relojes();
        init_pines_ultrasonidos();
        pin_buzzer_and_LED();
        TimerA0_capture();
        init_TimerA1_ms();
        TimerB0_PWM();
        __enable_interrupt();
        while(1){
            P3OUT |= BIT7; // generate pulse TRIGGER
            delay_us(10); // pulse of 10 us in TRIGGER pin for obtaining the measures (see datasheet HC-SR04)
            P3OUT &= ~BIT7; // disable TRIGGER pulse
            delay_ms(100); // delay over 60 ms measurement cycle in order to prevent trigger signal to the echo signal (see Datasheet HC-SR04)
            if(distancia_cm >= 2 && distancia_cm <= 400){ // if distance between sensor range.. 
                P4OUT |= BIT7; // LED green ON
    if(distancia_cm < 50){ TB0CCR6 = 0; // Reset value of Timer that decides the final PWM value TB0CCR6 += 249 - (distancia_cm * 12); // increase amplitude of the buzzer tone as approaching to the sensor
    } } } } #pragma vector=TIMER1_A0_VECTOR __interrupt void timer1_A0_ISR(void) { contador_milisegundos++; TA1CCTL0 &= ~CCIFG; } #pragma vector=TIMER0_A0_VECTOR // ISR de Capture Signal TA0.1 __interrupt void Timer0_A0(void) { if(TA0CCTL1 & CCI){ // Read the CCI bit (ECHO signal) --> CCI reflects the state of Captura pin --> ECHO signal start_time = TA0CCR1; //takes first time measure in us } else{ end_time = TA0CCR1; //takes second time measure in us distancia_cm = (uint16_t)((end_time - start_time)/58); // to obtain distance in centimeters: (time in us/58) --> (see Datasheet HC-SR04) } TA0CCTL1 &= ~CCIFG; }
  • ISR_TRAP means you've enabled an interrupt (that occurred) but there's no ISR for it. In this case:

    >TA0CCTL1 |= CM_3 | SCS | CCIS_0 | CAP | CCIE;

    >#pragma vector=TIMER0_A0_VECTOR // ISR de Capture Signal TA0.1

    Using TA0CCTL1:CCIE requires a different vector:

    >#pragma vector=TIMER0_A1_VECTOR // ISR de Capture Signal TA0.1

    There are two TimerA vectors -- one for CCR0 and one for all the others. [Ref User Guide (SLAU208Q) Secs 17.2.6.1-2]

  • Really thanks! That was the problem as you said. Seems like the project is working but it takes wrong distance values like i put my hand in front of the sensor (20 cm or 30 cm maximum) and, as i see in the expressions in CCS debugging, distance value measures 134 cm. But anyway, thanks for your help!

  • Btw when the distance value is lower than 50 (which in main function goes should go inside the second if-condition and it really does) the buzzer doesn't sound. Any idea why this happening, appart from getting wrong distance values?

  • >TB0CCR6 += 249 - (distancia_cm * 12);

    I'm not sure you want that "12" there. I started by supposing that the distance was 50cm, in which case 249-(50*12)=0xFEA1 (-351). When added to anything 0-250 that results in a negative (large unsigned) value. If the counter never reaches the value in CCR6, it won't change the signal, and it will never reach 0xFEA1. The output will be constant (high) thus no sound.

    More generally, you only have 250 (=CCR0) possible values for duty cycle, and 400 (nominal) possibilities for the cm measurement, so the first approximation would be a divide-by-2, not a multiply-by-12. That's just a place to start, since you may find some of the range uninteresting (too loud, too soft). In any case, you'll want to bound the CCR6 value you compute (within 0-250) before you set it.

    [Edit: Fixed typo]

  • I've made a lot of changes in the code, since i realized that, by using 16 MHz frequency on TimerA0_Capture, i will get a Timer overflow with just 4 ms of echo signal. So I decided to apply a frequency divider by 8 (which means that this Timer is now working at 2 MHz and I can avoid overflow in vast majoritity of cases, as i have not overlflow until 32.7 ms).
    Another issue I

    #include <msp430.h> #include <stdint.h> uint16_t volatile contador_milisegundos; // miliseconds counter uint16_t volatile distancia_cm = 0; // distance measured
    uint16_t volatile capture_overflow = 0; // Variable to take into account if an overflow occurs or not --> modified by ISR void Inicializacion_Relojes(void) // to use the SMCLK --> 16 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_0; // Divisor para SMCLK = f(SMCLK)/1; ACLK = f(ACLK)/1 __bic_SR_register(SCG0); // Enable the FLL control loop } void init_pines_ultrasonidos(void) { P1DIR &= ~BIT2; // Pin ECHO as input P1SEL |= BIT2; // mode Capture Input Signal (CCI0A) P3DIR |= BIT7; // Pin TRIGGER as output P3SEL &= ~BIT7; // Gpio pin P3SEL &= ~BIT7; // Pin TRIGGER low } void pin_buzzer_and_LED(void) // pin P3.6 as TB0.6 --> P3DIR.6 = 1; P3SEL.6 = 1 (see Table 6-48 Datasheet MSP430F5529) { P3SEL |= BIT6; // P3.6 as alternative function (para Timer TB0.6 según datasheet MSP430F5529) P3DIR |= BIT6; // Pin as buzzer output P4SEL &= ~BIT7; // Pin GPIO P4DIR |= BIT7; // como salida (Led2, verde) P4OUT &= ~BIT7; // led inicialmente apagado } void delay_ms(uint16_t tiempo_milisegundos) // Function for delay of miliseconds --> SMCLK { contador_milisegundos = 0; TA1CTL |= MC_1; // UP MODE TA1CCTL0 |= CCIE; while(contador_milisegundos < tiempo_milisegundos); TA1CTL &= ~MC_3; // stop the timer }

    void delay_us(uint16_t tiempo_microsegundos) // Function for delay de us --> SMCLK a 16 MHz
    {
    while(tiempo_microsegundos){
    __delay_cycles(16); // put a value of 16 for 16 MHz
    tiempo_microsegundos--;
    }
    }
    
    void TimerA0_capture(void) // Timer for capturing ECHO signal of ultrasonic sensor --> TA0.0 en modo Capture Input (CCI0A)
    {
        TA0CCTL1 |= CM_3 | SCS | CCIS_0 | CAP | CCIE; // CM_3: Capture both rising and falling edges of signal; CCIS_0: CCIxA --> Capture/Compare in TimerA; CAP: enable capture mode; CCIE: enable interrupciones
        TA0CTL |= TASSEL_2 | MC_2 | ID_3 | TACLR; // TASSEL_2: SMCLK; MC_2: Continuous Mode; ID_3: f(SMCLK)/8 --> f = 2 MHz
    }
    
    void init_TimerA1_ms(void) // for delay
    {
        TA1CCR0 = 16000-1; // pulses for 1 milisecond
        TA1CTL |= TASSEL_2 | MC_0; // SMCLK, Freq: 16 MHz; MC_1: Stop Mode
    }
    
    void TimerB0_PWM(void) // Timer for monitoring PWM of buzzer
    {
        TB0CCR0 = 249; // Período de PWM: 250 pulsos --> 4kHz (o 0,25 ms) --> 1000 pulses/ms · 0,25 ms (inverse of 4kHz) = 250 - 1 = 249.
        TB0CCTL6 |= OUTMOD_7; // Modo CCR6: PWM Reset/Set --> increasing CCR6 value will also increase Duty Cycle --> more buzzer amplitude
        TB0CCR6 = 0; // Duty Cycle inicial: 0 %. TB0.6 --> TB0CCR6.
        TB0CTL |= TASSEL_2 | ID_0 | MC_1 | TACLR; // TASSEL_2: SMCLK; ID_2: Divisor freq --> f(SMCLK)/1; MC_1: Up Mode
    }
    
    void main()
    {
        WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
        Inicializacion_Relojes();
        init_pines_ultrasonidos();
        pin_buzzer_and_LED();
        TimerA0_capture();
        init_TimerA1_ms();
        TimerB0_PWM();
        __enable_interrupt();
        while(1){
            P3OUT |= BIT7; // generate pulse TRIGGER
            delay_us(10); // pulse of 10 us in TRIGGER pin for obtaining the measures (see datasheet HC-SR04)
            P3OUT &= ~BIT7; // disable TRIGGER pulse
            delay_ms(100); // delay over 60 ms measurement cycle in order to prevent trigger signal to the echo signal (see Datasheet HC-SR04)
            if(distancia_cm >= 2 && distancia_cm <= 400){ // if distance between sensor range.. 
                P4OUT |= BIT7; // LED green ON
    if(distancia_cm < 50){ TB0CCR6 = 0; // Reset value of Timer that decides the final PWM value TB0CCR6 += 249 - (distancia_cm >> 2); // increase amplitude of the buzzer tone as approaching to the sensor
    } }
    capture_overflow = 0; // reset } } #pragma vector=TIMER1_A0_VECTOR __interrupt void timer1_A0_ISR(void) { contador_milisegundos++; TA1CCTL0 &= ~CCIFG; } #pragma vector=TIMER0_A0_VECTOR // ISR de Capture Signal TA0.1 __interrupt void Timer0_A0(void) { if(TA0CCTL1 & CCI){ // Read the CCI bit (ECHO signal) --> CCI reflects the state of Captura pin --> ECHO signal TA0CTL |= TACLR; //first time measure --> reset timer to begin with zero in order to have more odds to avoid overflow and also avoid using useless start_time variable. } else{ if(TA0CCTL1 & COV){ // if an overflow occurs
    capture_overflow++;
    }
    // distance in cm --> we add to the TA0CCR1 value the times overflow has ocurred and we also know that everytime overflow happens means that we had arrived to 65535 value --> taht's why i multiply
    // also i multiply by 0.5 because is the duration of each pulse signal in us, since i am using 2 MHz freq in capture timer --> 1/(2*10^6) = 0.5 microseconds
    distancia_cm = ((TA0CCR1+(capture_overflow*65535))*0.5)/58; // to obtain distance in centimeters: (time in us/58) --> (see Datasheet HC-SR04) } TA0CCTL1 &= ~CCIFG; }
    But seems like still no working at all. What do you think about the changes i made? Thanks for repplying!
  • You can divide TA0 down further using TA0EX0. You don't really need to, since 23 ms is (now) 46000, which still fits. On the other hand, since you're computing cm, your quantum is (58usec / 2).

    COV doesn't mean that the counter has wrapped around to the starting (last capture) point, it means that you've lost a capture by not picking it up fast enough. (In this program that actually seems improbable.) The response to a COV would be to give up on the measurement, until the next pulse.

    If you want to detect when the counter has wrapped back to the start, here's what I do: Choose another CCR, say CCR1, and set the CCIE. On each capture, subtract 1 from the captured value (CCR0) and store it in CCR1. If the CCIFG on CCR1 ever goes off (compare match), that means you've wrapped --- effectively a timeout -- and you should give up on that reading.

    It's tempting to try to count these occurrences (the way you're using COV now) but my experience is that getting this correct (in all the corner cases) is more difficult than it looks. And you don't need to -- it will wrap in just over 32ms, which is more than the specified range of the sensor -- so a timeout should be treated as a failure.

  • Thanks for all that help dude! I own you lot. Btw, I firstly tried to communicate with that sensor but i don't know a bigger project to apply on it. do you have you any suggestion to think about it? 

  • I'm glad you got it working.

    I don't have any other HC-SR04 applications immediately in mind, but Google will probably find you plenty of people who have done imaginative things with it.

  • Hi! One last question to finally solve all the issues i've found with Timer Capture mode. I've made this changes on my code because i want to give a try on taking into account overflow and my idea is that one:

    Firstly i paste Timer function that controls the capture input signal, in which i set TAIE bit to be able to use TAIFG bit of TACTL to control overflow situation. I use TimerA0.1 because in P1.1 there was a button i cannot connect any wire to protobard to make sure it is connected also to ECHO pin. 

    void TimerA0_capture(void) // Timer for capturing ECHO signal of ultrasonic sensor --> TA0.1 en modo Capture Input (CCI1A)
    {
                TA0CCTL1 |= CM_3 | SCS | CCIS_0 | CAP | CCIE; // CM_3: Capture both rising and falling edges of signal; CCIS_0: CCIxA --> Capture/Compare in interrupciones
                TA0CTL |= TASSEL_2 | MC_2 | ID_3 | TAIE; // TASSEL_2: SMCLK; MC_2: Continuous Mode; ID_3: f(SMCLK)/8 --> f = 2 MHz; TAIE: enable TAIFG interrupt
    }

    As i am using 2 MHz timer frequency i thought that each pulse should measure (in time therms), so I decide to create a constant that i can add to distance like if it is an offset. That constant counts the seconds that has passed everytime an overflow occurs.

    const float OVERFLOW = (65535/(2*10^6)); // 65535 pulses/2 MHz = 0.5 us

    #pragma vector=TIMER0_A1_VECTOR // ISR de Capture Signal TA0.1: TAIE, CCR1... --> interruption ISR that happens because of TAIE/TAIFG appart from CCR1
    __interrupt void Timer0_A1(void)
    {
             switch(TAIV){ // This line gives me an execution error, like if TAIV doesn't exist at all but i saw it on Users Guide and also in TI examples. 
                     case 2: // TA0CCR1 (see 17.3.5 Users Guide --> TAxIV Register)
                            if(TA0CCTL1 & CCI){ // reading bit CCI (ECHO signal) 
                                  TA0CTL |= TACLR; // ECHO high --> reset time --> begins to count ECHO signal
                            }
                            else{
                                   // distance equation (Datasheet HC-SR04) --> Distance in cm = (Time in us)/58
                                   // ECHO low --> stop counting
                                   distancia_cm = (TA0CCR1*0.5 + capture_overflow)/58; //: (value_TA0CCR1*duration of a pulse in us --> 0.5 because i am working at 2 MHz)/58 --> (see Datasheet HC-SR04)
                            }
                            break;

                      case 14: // Timer overflow; Interrupt Flag: TAxCTL TAIFG
                            capture_overflow += OVERFLOW; // interrupt made by TAIFG --> everytime it arrives to its maximum value, it resets the counter to 0 and bit TAIFG is set (see Users Guide)
                            break;
                      }
    }

    How do you see it in that way? It appears a problem in TAIV register, as it doesn't seem like existing. And also, should i put the other cases (case 4,6,8.. and so on) of TAIV even in they don't care on my project?

  • You need TA0IV, not TAIV. You don't need to list all the cases, though it's a good habit to include a "default:" case.

    Doing it this way will read slightly low (short) since your 0-point is some time later than the first capture (latency). Subtracting the two captures avoids this.

**Attention** This is a public forum