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