[solved] PWM decoding help

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey everyone,

I picked up a Parallax Ping))) ultra sonic distance sensor (#28015, datasheet attached). It takes a 5-25us pulse and then returns a pulse from 18.5 - 115us. So I wrote up this code to give it a test (avr-gcc, mega16):

#include 
#define F_CPU 3686400UL
#include 
#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

static inline void io_init(void);
void usart_init(void);
void usart_tx(unsigned char c);
void usart_tx_string(unsigned char *string);

unsigned char buffer[20];

int main(void) {
    io_init();

    while(1) {
        // Output pulse to Ping)))
        DDRB = (1 << PB3);
        PORTB = (1 << PB3);
        _delay_us(15);
        
        // Bring pin to high-z input
        DDRB &= ~(1 << PB3);
        //PORTB &= ~(1 << PB3);

        // wait for PB3 to go high
        while((PINB & PB3) == 0) {}
        TCNT1 = 0;

        // wait for PB3 to go low
        while((PINB & PB3) == 1) {}

        itoa(TCNT1, buffer, 10);
        usart_tx_string(buffer);
        usart_tx('\r');
        usart_tx('\n');

        _delay_ms(15);
    }

    return(0);
}

static inline void io_init(void) {
    // Enable output for +5V to Ping-)))
    DDRA = (1 << PA0);
    PORTA = (1 << PA0);

    TCCR1B |= (1 << CS10);
    
    usart_init();
}

void usart_tx_string(unsigned char *string) {
    while(*string) {
        usart_tx(*string++);
    }
}

void usart_tx(unsigned char c) {
    // Wait until UDR is ready for more data
    while((UCSRA & (1 << UDRE)) == 0) {};

    UDR = c;
}

void usart_init(void) {
    // Enable USART rx and tx
    UCSRB |= (1 << RXEN) | (1 << TXEN);

    // Use 8-bit character sizes
    UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);

    // Load lower 8-bits of the baud rate value into the low byte of the UBBR register
    UBRRL = BAUD_PRESCALE;

    // Load upper 8-bits of the baude rate value into the high byte of the UBRR register
    UBRRH = (BAUD_PRESCALE >> 8);
}

The problem is that I'm receiving a value of 4 (transmitted via usart) from TCNT1 no matter how close/far an object is. Attached is a couple of pictures of the pulse shown on a scope. The first being the maximum distance (~3m) and the second with my hand about 3cm from the sensor. So it looks like the sensor is working properly. Can anyone see a problem in the way I'm attempting to read the pulse?

Thanks!

Attachment(s): 

Last Edited: Mon. Jan 14, 2008 - 05:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
        // wait for PB3 to go low
        while((PINB & PB3) == 1) {}

This condition is never true so you fall through.
If PB3 is 3 then you are testing for PINB being 0b00000011

Use something like:-

  while (PINB & (1<<PB3))
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks! I updated both loops to properly read the pin. However, now I'm getting a TCNT1 value of 1 every time.

Any ideas?

Attachment(s): 

Last Edited: Wed. Jan 9, 2008 - 04:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Like this?

        // wait for PB3 to go high
        while((PINB & (1<<PB3)) == 0) {}
        TCNT1 = 0;

        // wait for PB3 to go low
        while((PINB & (1<<PB3))) {}

(quick edit to add the shifts)
edit2:

while ( (PINB & (1<<PB3)) == (1<<PB3) )
Last Edited: Wed. Jan 9, 2008 - 04:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wasn't I supposed to change it to reflect this?:

        // wait for PB3 to go high
        while((PINB & (1 << PB3)) == 0) {}
        TCNT1 = 0;

        // wait for PB3 to go low
        while((PINB & (1 << PB3)) == 1) {}

Edit: oops, you fixed it.

Edit 2:

Ok, now I have:

        // wait for PB3 to go high
        while((PINB & (1 << PB3)) == ~(1 << PB3)) {}
        TCNT1 = 0;

        // wait for PB3 to go low
        while((PINB & (1 << PB3)) == (1 << PB3)) {}

Which always returns 17! I'm starting to lose it =] Any ideas?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

:wink:
To test for a single clear bit (PINB & (1<<PB3) == 0)
To test for a single bit set (PINB & (1<<PB3)) , because anything other that 0 is true. However your second while() will still work just fine.
Back to the first while()....
In long hand...

while ((PINB & (0b00001000)) == 0b11110111)

Never true, hence you fall through.

I hope this is the problem. I'm running out of ideas!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your help!

Here's what I've got now:

        // wait for PB3 to go high
        while(PINB & (1 << PB3) == 0) {}
        TCNT1 = 0;

        // wait for PB3 to go low
        while(PINB & (1 << PB3)) {}

Same problem though =[. I'm still missing something here, ugh.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, these are late night guesses/questions/things to try.
1. Your scope waveforms look clean. Did you still have the the line that pulls PB3 low commented out at that point?
2. Could you be catching the outgoing pulse dropping low instead of getting the echo? Rangefinders are often gated to disregard any really quick returns for this reason.
3. Perhaps a delay after setting PB3 low and before your first while is required.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Broxbourne wrote:
OK, these are late night guesses/questions/things to try.
1. Your scope waveforms look clean. Did you still have the the line that pulls PB3 low commented out at that point?
2. Could you be catching the outgoing pulse dropping low instead of getting the echo? Rangefinders are often gated to disregard any really quick returns for this reason.
3. Perhaps a delay after setting PB3 low and before your first while is required.

I left that line commented out - although I've tried it both ways. I tried setting a couple of different delays after I pull PB3 low (750us from the datasheet, 10, 20, 100, 300us delays as well). I also tried setting a 100ms delay after the measurement completed just to make sure I wasn't overloading it somehow - still no luck =[. Thanks again for your help, this is definitely a learning process for me =]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well I kept banging my head on this thing pretty late last night, still no luck. It seems like this is a pretty basic thing to do, I'm lost!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well it seems like I must be checking for the wrong bit or something here, I've tried the following with the same result:

        // wait for PB3 to go high
        while(PINB & (1 << PINB3) == 0) {}
        //while(PINB == 0b00000000) {}
        //loop_until_bit_is_set(PINB, PINB2);
        //loop_until_bit_is_set(PINB, PINB3);        
        //loop_until_bit_is_set(PINB, PINB4);

        //TCNT1 = 0;
        TCNT0 = 0;

        // wait for PB3 to go low
        while(PINB & (1 << PB3)) {}
        //while(PINB == 0b00001000) {}
        //loop_until_bit_is_clear(PINB, PINB2);
        //loop_until_bit_is_clear(PINB, PINB3);        
        //loop_until_bit_is_clear(PINB, PINB4);

At this point I'm starting to question if I understand this at all. I've tried every different combination of timers/pins/outputs I can think of. What could possibly be going on here?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well I was finally able to get this going, thanks Broxbourne for all of your help. In the end I think it was a combination of strange timing and pin settings.

Here's the updated code if anyone ever runs into this:

#include 
#define F_CPU 3686400UL
#include 
#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

static inline void io_init(void);
void usart_init(void);
void usart_tx(unsigned char c);
void usart_tx_string(unsigned char *string);

unsigned char buffer[40];
unsigned short int distance;

int main(void) {
    io_init();

    while(1) {
        // Output pulse to Ping)))
        DDRB = (1 << PB3);
        _delay_us(8);

        // Bring pin to high-z input
        DDRB = 0x00;

        _delay_us(750);
 
       // wait for PB3 to go high
        loop_until_bit_is_set(PINB, PINB3);
        TCNT1 = 0;

        // wait for PB3 to go low
        loop_until_bit_is_clear(PINB, PINB3);
        //distance = (0.27 * TCNT1) / 29.033;    // 3.6864MHz = 0.27uS/cycle; 29.033uS/cm
        distance = ((0.27 * TCNT1) / 73.746) / 2;    // 3.6864MHz = 0.27uS/cycle; 73.746uS/in; 2 (round trip time)
        //itoa(TCNT1, buffer, 10);
        itoa(distance, buffer, 10);

        usart_tx_string(buffer);
        usart_tx_string("in");
        usart_tx('\r');
        usart_tx('\n');
        _delay_ms(1);
    }

    return(0);
}

static inline void io_init(void) {
    DDRB = (1 << PB3);
    PORTB = (1 << PB3);
    TCCR1B = (1 << CS10);
    //TCCR1B = (1 << CS11);
    //TCCR1B = (1 << CS11) | (1 << CS10);

    usart_init();
}

void usart_tx_string(unsigned char *string) {
    while(*string) {
        usart_tx(*string++);
    }
}

void usart_tx(unsigned char c) {
    // Wait until UDR is ready for more data
    while((UCSRA & (1 << UDRE)) == 0) {};

    UDR = c;
}

void usart_init(void) {
    // Enable USART rx and tx
    UCSRB |= (1 << RXEN) | (1 << TXEN);

    // Use 8-bit character sizes
    UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);

    // Load lower 8-bits of the baud rate value into the low byte of the UBBR register
    UBRRL = BAUD_PRESCALE;

    // Load upper 8-bits of the baude rate value into the high byte of the UBRR register
    UBRRH = (BAUD_PRESCALE >> 8);
}