|
|
Atmel Ultrasonic Ranger Source Code/* Filename : test8535.c * * The first test of the ATmega8535 microcontroller. * * Use an ultrasonic transmitter and receiver to calculate the distance * to a target. * * Author : Craig Dunn (craig@craigsarea.com) * Website : www.craigsarea.com * Date Started : 2 August 2004 * Crystal : 16MHz * **********************************************************************/ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #include <avr/iom8535.h> #include <string.h> /* Defines **********************************************************************/ #define FOSC 16000000L #define BAUDRATE 38400L #define UBRR ((FOSC / (16 * BAUDRATE)) - 1) #define NUM_40KHZ_PULSES 5 /* Always does one less pulse. */ #define TRUE 1 #define FALSE 0 #define F_CPU 4000000 #define K_DELAY_100us F_CPU/61349 #define K_DELAY_1ms F_CPU/6013 #define K_DELAY_10ms F_CPU/600 /* Custom Types **********************************************************************/ typedef unsigned char U8; typedef unsigned int U16; typedef char S8; typedef int S16; typedef unsigned char BOOL; /* Function Prototypes **********************************************************************/ void usart_init (void); void usart_tx_byte (U8 tx_reg); void usart_tx_str (char *str); /* Global Variables **********************************************************************/ U8 pulse_count; U16 distance; volatile BOOL pulses_sent; volatile BOOL echo_received; volatile BOOL echo_missed; /* FUNCTION : usart_init * * Initialise the USART. **********************************************************************/ void usart_init(void) { UCSRB = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE); UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); UBRRH = (U8)UBRR >> 8; UBRRL = (U8)UBRR; } /* FUNCTION : usart_tx_byte * * Wait for the USART to become available and then send out tx_reg. **********************************************************************/ void usart_tx_byte(U8 tx_reg) { while(!(UCSRA & (1 << UDRE))) { /* Make sure GCC doesn't optimize this while loop out. */ asm volatile("nop"); } UDR = tx_reg; } /* FUNCTION : usart_tx_str * * Send the null terminated string pointed to by *str out of the serial * port. **********************************************************************/ void usart_tx_str(char *str) { char *ptr; for(ptr = str; *ptr != '\0'; *ptr++) usart_tx_byte(*ptr); } /* FUNCTION : * * (15 + t*( ((K_DELAY_100us-1)*6)+5 )) **********************************************************************/ void delay_100us(U16 t) { volatile U16 i; while(t > 0) { for(i=0; i < K_DELAY_100us; i++) { } --t; } } /* FUNCTION : * * (15 + t*( ((K_DELAY_1ms-1)*6)+5 )) **********************************************************************/ void delay_1ms(U16 t) { volatile U16 i; while(t > 0) { for(i=0; i < K_DELAY_1ms; i++) { } --t; } } /* FUNCTION : SIGNAL(SIG_UART_RECV) * * USART RX interrupt. Just send back whatever is received. **********************************************************************/ SIGNAL(SIG_UART_RECV) { usart_tx_byte(UDR); } /* FUNCTION : SIGNAL(SIG_OVERFLOW2) * * At the end of each PWM pulse this interrupt occurs. Either get * ready for the next pulse or stop the PWM (the correct number of * pulses has been sent). **********************************************************************/ SIGNAL(SIG_OVERFLOW2) { if(pulse_count == 0) { pulse_count = NUM_40KHZ_PULSES; /* Get ready for the next set of pulses. */ TCCR2 = TCCR2 & (~0x07); /* Stop the PWM generation. */ pulses_sent = TRUE; } else { TCNT2 = 130; pulse_count--; } } /* FUNCTION : SIGNAL(SIG_OVERFLOW1) * * The Capture/Compare timer has overflowed, therefore no echo was * received in time. **********************************************************************/ SIGNAL(SIG_OVERFLOW1) { TCNT1 = 0; echo_missed = TRUE; } /* FUNCTION : SIGNAL(SIG_INPUT_CAPTURE1) * * A rising edge has been detected by the Capture/Compare module. This * is the start of the echo. **********************************************************************/ SIGNAL(SIG_INPUT_CAPTURE1) { distance = ICR1; echo_received = TRUE; } /* FUNCTION : main * * Main implementation. **********************************************************************/ int main(void) { char *string; cli(); usart_init(); /* D7(PWM OUT) D6(CAPTURE IN) */ DDRD = 0xBF; PORTD = 0x00; while(1) { echo_received = FALSE; echo_missed = FALSE; pulses_sent = FALSE; pulse_count = NUM_40KHZ_PULSES; /* PWM Output. The overflow interrupt counts the number of PWM pulses sent out and disables the PWM channel when the correct number have gone. */ TCCR2 = _BV(WGM20) /* PWM Waveform. */ | _BV(CS20) /* No Prescaler. */ | _BV(COM21); /* Clear on up counting. */ OCR2 = 50; /* 50% Duty Cycle on the PWM signal. */ TIMSK = _BV(TOIE2); /* Overflow Interrupt (PWM Output). */ sei(); /* wait for the pulses to go. */ while(pulses_sent == FALSE) { } /* Make sure we don't pick up any cross talk between the transmitter and receiver. */ delay_100us(2); cli(); /* Counter Input Capture. Start a timer and generate an interrupt on the first rising edge of the received ultrasonic echo. */ TCCR1B = _BV(ICES1) /* Interrupt on the rising edge. */ | _BV(ICNC1) /* Enable the noise canceller. */ | _BV(CS11) | _BV(CS10); /* CS10 and CS11 create a prescaler of 64. */ TCNT1 = 0; TIFR = _BV(TOV1) /* Enable an Input Capture Overflow Interrupt. */ | _BV(ICF1); /* Enable an Input Capture Interrupt. */ TIMSK = _BV(TOIE1) /* Overflow Interrupt (Capture Input). */ | _BV(TICIE1); /* Capture Interrupt. */ sei(); /* Wait for some echo activity. */ while(echo_received == FALSE && echo_missed == FALSE) { } /* Convert the echo to a distance and send it out of the serial port. */ if(echo_received == TRUE) { /* Speed of sound = 1cm every 30us Timer Prescaler = 64 External Crystal = 16MHz Timer Updates Every (1/16MHz) * 64 = 4uS distance = (TIMER_VALUE * 4) / 30 / 2 = TIMER_VALUE / 15 */ distance = distance / 15; sprintf(string, "%dcm\r", distance); usart_tx_str(string); } /* Create a long delay before starting again. This is so we don't detect any double echos. */ delay_1ms(1000); } return 0; } |