Society of Robots - Robot Forum
Software => Software => Topic started by: pomprocker on July 08, 2008, 05:34:54 PM
-
I have the (not so $50) $50 robot with all the upgrades, and now I want to add this PING))) sensor I bought, but looking at the Fuzzy robot code, it looks to be for a sonar where you have to manually deal with it. From what I read about PING))) since it only has one signal line, and you have to change it from input to output somehow. I can't seem to mentally grasp the concept here. Can someone help me figure out how to write code for this sensor for my robot using an ATmega8(for now, soon to be ATMega168)
Thanks
Edit: Now using an ATmega168
-
This is somewhat off topic, but how much did you pay for your PING sensor? My Radio Shack doesn't have the price for it out.
-
Fry's Electronics carries parallax products. I picked one up for about $29.
-
Definitely use the MAXBotics EZ1 Ultrasonic sensor instead, its a lot better (in my biased opinion of course) and easier to use cause there is basically no programming nor timing because it is all handled on the sensor itself, it outputs the information about distance in serial, analog or PWM formats. Plus I can help you out with any code help you need. Hope that helps you make your decision.
-
Here is some code for the BS2, that I need to convert to C for the $50 robot...
' -----[ Revision History ]------------------------------------------------
' -----[ I/O Definitions ]-------------------------------------------------
Ping PIN 15
' -----[ Constants ]-------------------------------------------------------
#SELECT $STAMP
#CASE BS2, BS2E
Trigger CON 5 ' trigger pulse = 10 uS
Scale CON $200 ' raw x 2.00 = uS
#CASE BS2SX, BS2P, BS2PX
Trigger CON 13
Scale CON $0CD ' raw x 0.80 = uS
#CASE BS2PE
Trigger CON 5
Scale CON $1E1 ' raw x 1.88 = uS
#ENDSELECT
RawToIn CON 889 ' 1 / 73.746 (with **)
RawToCm CON 2257 ' 1 / 29.034 (with **)
IsHigh CON 1 ' for PULSOUT
IsLow CON 0
' -----[ Variables ]-------------------------------------------------------
rawDist VAR Word ' raw measurement
inches VAR Word
cm VAR Word
' -----[ EEPROM Data ]-----------------------------------------------------
' -----[ Initialization ]--------------------------------------------------
Reset:
DEBUG CLS, ' setup report screen
"Parallax Ping Sonar ", CR,
"=====================", CR,
CR,
"Time (uS)..... ", CR,
"Inches........ ", CR,
"Centimeters... "
' -----[ Program Code ]----------------------------------------------------
Main:
DO
GOSUB Get_Sonar ' get sensor value
inches = rawDist ** RawToIn ' convert to inches
cm = rawDist ** RawToCm ' convert to centimeters
DEBUG CRSRXY, 15, 3, ' update report screen
DEC rawDist, CLREOL,
CRSRXY, 15, 4,
DEC inches, CLREOL,
CRSRXY, 15, 5,
DEC cm, CLREOL
PAUSE 100
LOOP
END
' -----[ Subroutines ]-----------------------------------------------------
' This subroutine triggers the Ping sonar sensor and measures
' the echo pulse. The raw value from the sensor is converted to
' microseconds based on the Stamp module in use. This value is
' divided by two to remove the return trip -- the result value is
' the distance from the sensor to the target in microseconds.
Get_Sonar:
Ping = IsLow ' make trigger 0-1-0
PULSOUT Ping, Trigger ' activate sensor
PULSIN Ping, IsHigh, rawDist ' measure echo pulse
rawDist = rawDist */ Scale ' convert to uS
rawDist = rawDist / 2 ' remove return trip
RETURN
' {$STAMP BS2}
' {$PBASIC 2.5}
' Conversion constants for room temperature measurements.
CmConstant CON 2260
InConstant CON 890
cmDistance VAR Word
inDistance VAR Word
time VAR Word
DO
PULSOUT 15, 5
PULSIN 15, 1, time
cmDistance = cmConstant ** time
inDistance = inConstant ** time
DEBUG HOME, DEC3 cmDistance, " cm"
DEBUG CR, DEC3 inDistance, " in"
PAUSE 100
LOOP
' -----[ Program Description ]---------------------------------------------
' This programs demonstrates roaming with the Parallax PING))) Mounting
' Bracket, which allows you to pan-scan with your Parallax PING))) sensor.
'
' This program assumes you have already set your BOE-Bot up for roaming
' with IR sensors. You must also have the PING))) Mounting bracket
' installed as per the instructions, with the servo connected to P14 and
' the PING))) sensor connected to P15.
'
' Due to the way the PING))) bracket mounts you may have to trim your IR
' detectors down so that they don't stick up in the way of the moving
' PING))) bracket as it rotates. It's best to angle them off at 45
' degree angles so that the BOE-Bot doesn't run into anything while
' moving along a wall at a narrow angle.
'
' The BOE-Bot will now roam around and when it senses an object, it will
' scan within a 180 degree field of view for the clearest path, which it
' will then navigate scanning for an object again. The IR Sensors prevent
' hitting a wall when coming at it from a narrow angle where the PING)))
' might have trouble seeing it due to the lack of reflection of sound.
'
' Calibration is important to this code working properly. If you do not
' have the correct values for your servos then you may get strange results
' from your BOE-Bot. The necessary calibration information is listed in
' each section of the code where it is required.
' -----[ Revision History ]------------------------------------------------
' This code is basically a modified version of the Roaming With IR code
' from the Robotics With The BOE-Bot Manual, written by Andy Lindsay.
' Much of the original code was left untouched so you can see how it was
' altered.
' -----[ I/O Definitions ]-------------------------------------------------
Piezo PIN 4 ' Piezo Speaker
RightServo PIN 12 ' Right Servo
LeftServo PIN 13 ' Left Servo
PingServo PIN 14 ' PING))) Servo
Ping PIN 15 ' PING))) Sensor
' -----[ Variables ]-------------------------------------------------------
irDetectLeft VAR Bit ' Variable For Left IR Input
irDetectRight VAR Bit ' Variable For Right IR Input
pulseCount VAR Byte ' Used For Measuring Turns
distance VAR Word ' Current Distance Of Object
oldDistance VAR Word ' Old Distance Value
counter VAR Word ' PING))) Cycle Counter
task VAR Nib ' Current Task
' -----[ Initialization ]--------------------------------------------------
FREQOUT Piezo, 2000, 3000 ' Signal Program Start/Reset
' -----[ Program Code ]----------------------------------------------------
Main:
DO
FREQOUT 8, 1, 38500 ' Emit 38.5 kHz IR To Left
irDetectLeft = IN9 ' Store IR Detection Values
FREQOUT 2, 1, 38500 ' Emit 38.5 kHz IR To Right
irDetectRight = IN0 ' Store IR Detection Values
IF (irDetectLeft = 0) AND (irDetectRight = 0) THEN
GOSUB Ping_Around ' Object Detected via IR Forward
ELSEIF (irDetectLeft = 0) THEN
GOSUB Ping_Around ' Object Detected via IR Left
ELSEIF (irDetectRight = 0) THEN
GOSUB Ping_Around ' Object Detected via IR Right
ENDIF
counter = counter + 1 ' Increment Passive Counter
IF counter > 10 THEN ' Wait For 10 Servo Pulses
GOSUB Ping_Out ' Activate PING)))
ENDIF
IF (distance > 30) THEN ' Is Object Farther Than 30 cm?
GOSUB Forward_Pulse ' If Yes Go Forward
ELSE
GOSUB Ping_Around ' Otherwise Scan For Clear Path
ENDIF
LOOP
' -----[ Subroutines ]-----------------------------------------------------
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUES TO MAKE YOUR BOE-BOT MOVE FORWARD *
' * WHILE THE PING))) IS FACING FORWARD. *
' *************************************************************************
Forward_Pulse: ' Send A Single Forward Pulse
PULSOUT PingServo, 750 ' Ping Servo Forward Pulse Value
PULSOUT LeftServo, 850 ' Left Servo Forward Pulse Value
PULSOUT RightServo, 650 ' Right Servo Forward Pulse Value
PAUSE 20 ' Refresh Delay
RETURN
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUES TO MAKE YOUR BOE-BOT TURN LEFT 90 *
' * DEGREES. USE THE SAME VALUE AS ABOVE FOR THE PING))) BRACKET SERVO. *
' *************************************************************************
Turn_Left: ' Left Turn, About 45 Degrees
FOR pulseCount = 0 TO 7 ' Number Of Pulses To Turn
PULSOUT PingServo, 750 ' Ping Servo Forward Pulse Value
PULSOUT LeftServo, 650 ' Left Servo Left Pulse Value
PULSOUT RightServo, 650 ' Right Servo Left Pulse Value
PAUSE 20 ' Refresh Delay
NEXT
RETURN
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUES TO MAKE YOUR BOE-BOT TURN RIGHT 90 *
' * DEGREES. USE THE SAME VALUE AS ABOVE FOR THE PING))) BRACKET SERVO. *
' *************************************************************************
Turn_Right: ' Right Turn, About 45 Degrees
FOR pulseCount = 0 TO 7 ' Number Of Pulses To Turn
PULSOUT PingServo, 750 ' Ping Servo Forward Pulse Value
PULSOUT LeftServo, 850 ' Left Servo Right Pulse Value
PULSOUT RightServo, 850 ' Right Servo Right Pulse Value
PAUSE 20 ' Refresh Delay
NEXT
RETURN
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUES TO MAKE YOUR BOE-BOT MOVE BACKWARD *
' * WHILE THE PING))) IS FACING FORWARD. *
' *************************************************************************
Back_Up: ' Back Up
FOR pulseCount = 0 TO 40 ' Number Of Pulses To Backup
PULSOUT PingServo, 750 ' Ping Servo Forward Pulse Value
PULSOUT LeftServo, 650 ' Left Servo Backup Pulse Value
PULSOUT RightServo, 850 ' Right Servo Backup Pulse Value
PAUSE 20 ' Refresh Delay
NEXT
RETURN
Ping_Out: ' PING)))
counter = 0 ' Reset Passive Delay Counter
LOW Ping ' Force PING))) Line Low
PULSOUT Ping, 5 ' Activate PING))) Pulse
PULSIN Ping, 1, distance ' Receive Return Pulse
distance = distance ** 2257 ' Calculate Distance
RETURN
Ping_Around: ' Start 180 Degree Pan-Scan
counter = 0 ' Reset Passive Delay Counter
oldDistance = 30 ' Current Old Distance Values
task = 0 ' Current Task Priority
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUE TO MAKE YOUR PING))) *
' * TURN 90 DEGREES LEFT. *
' *************************************************************************
FOR pulseCount = 0 TO 20 ' Number Of Pulses To Spin
LOW Ping ' Force PING))) Line Low
PULSOUT PingServo, 1085 ' Ping Servo 90 Left Pulse Value
PULSOUT Ping, 5 ' Activate PING)))
PULSIN Ping, 1, distance ' Receive Distance Value
PAUSE 20 ' Refresh Delay
NEXT
distance = distance ** 2257 ' Calculate Distance In cm
IF distance > oldDistance THEN ' Is distance > Last Clear Path
oldDistance = distance ' Update oldDistance Value
task = 1
ENDIF
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUE TO MAKE YOUR PING))) *
' * TURN 45 DEGREES LEFT. *
' *************************************************************************
FOR pulseCount = 0 TO 20 ' Number Of Pulses To Spin
LOW Ping ' Force PING))) Line Low
PULSOUT PingServo, 850 ' Ping Servo 45 Left Pulse Value
PULSOUT Ping, 5 ' Activate PING)))
PULSIN Ping, 1, distance ' Receive Distance Value
PAUSE 20 ' Refresh Delay
NEXT
distance = distance ** 2257 ' Calculate Distance In cm
IF distance > oldDistance THEN ' Is distance > Last Clear Path
oldDistance = distance ' Update oldDistance Value
task = 2
ENDIF
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUE TO MAKE YOUR PING))) *
' * TURN 45 DEGREES RIGHT. *
' *************************************************************************
FOR pulseCount = 0 TO 20 ' Number Of Pulses To Spin
LOW Ping ' Force PING))) Line Low
PULSOUT PingServo, 400 ' Ping Servo 45 Right Pulse Value
PULSOUT Ping, 5 ' Activate PING)))
PULSIN Ping, 1, distance ' Receive Distance Value
PAUSE 20 ' Refresh Delay
NEXT
distance = distance ** 2257 ' Calculate Distance In cm
IF distance > oldDistance THEN ' Is distance > Last Clear Path
oldDistance = distance ' Update oldDistance Value
task = 3
ENDIF
' *************************************************************************
' * USE THE APPROPRIATE PULSOUT VALUE TO MAKE YOUR PING))) *
' * TURN 90 DEGREES RIGHT. *
' *************************************************************************
FOR pulseCount = 0 TO 20 ' Number Of Pulses To Spin
LOW Ping ' Force PING))) Line Low
PULSOUT PingServo, 225 ' Ping Servo 90 Right Pulse Value
PULSOUT Ping, 5 ' Activate PING)))
PULSIN Ping, 1, distance ' Receive Distance Value
PAUSE 20 ' Refresh Delay
NEXT
distance = distance ** 2257 ' Calculate Distance In cm
IF distance > oldDistance THEN ' Is distance > Last Clear Path
oldDistance = distance ' Update oldDistance Value
task = 4
ENDIF
ON task GOSUB Task0, Task1, Task2, Task3, Task4
distance = 50 ' Prevent Scan From Looping
RETURN
Task0: ' Forward Was Clearest Path
GOSUB Turn_Right ' This Could Mean Narrow Path
GOSUB Turn_Right ' So We'll Turn Around
GOSUB Turn_Right ' You Can Change The Behavior
GOSUB Turn_Right ' Of Any Of The Tasks
RETURN
Task1: ' 90 Degrees Left Was Clearest
GOSUB Turn_Left
GOSUB Turn_Left
RETURN
Task2: ' 45 Degrees Left Was Clearest
GOSUB Turn_Left
RETURN
Task3: ' 45 Degrees Right Was Clearest
GOSUB Turn_Right
RETURN
Task4: ' 90 Degrees Right Was Clearest
GOSUB Turn_Right
GOSUB Turn_Right
RETURN
-
truthfully you need only translate one part of the code
this is the following part
PULSOUT Ping, Trigger ' activate sensor
PULSIN Ping, IsHigh, rawDist ' measure echo pulse
I know how to translate the PULSOUT into C but the PULSIN I'm not sure about , it might need an interrupt or something.
In case you cant translate the above code , just take the easy way out and use this guy's code
http://www.chiefdelphi.com/forums/showthread.php?p=650374&highlight=ping#post650374
Also,
when you do figure it out , please post a C translation for the code I quoted above.
,Eric
-
PULSOUT
To send a PWM signal, you need to specify the port and the amount of time. For example, to send a pulse on port 10 with a length of 4 μs, we would write:
PULSOUT 10, 2
Length Between Pulses
The PAUSE function, as we have seen before, can be used to delay operation of the next command. When using PAUSE to insert a delay between PWM pulses, keep in mind that it uses milliseconds, not 2 μs. Remember that PAUSE 1 takes the same amount of time as PULSOUT 12, 500.
PULSIN
In a similar manner to sending a PWM signal, we can also read a PWM signal from an external device. We must specify which port we are reading from, and we must also provide a variable to receive the length of the pulse. For instance, if we wanted to read a PWM pulse on port 5, we could write:
MyByte VAR Byte
PULSIN 5, MyByte
Like with PULSOUT, the units of time are 2 μs.
One common device that uses PWM input like this is the Parallax ultrasonic sensor. These sensors return a pulse for a length of time equal to the amount of time it takes sound waves to travel to an object and bounce back. Using some simple arithmetic, if we know the speed of sound and if we know the amount of time, we can calculate the distance of the object.
-
I think that is basically saying, talk to it like a servo, then wait, and then listen to it like a sensor.
-
I think that is basically saying, talk to it like a servo, then wait, and then listen to it like a sensor.
exactly , but its not an analog sensor
-
So can one remove the sensor pins they would be using on the $50 robot board from the ADC and just read it as digital?
-
The ping sensor works like this:
You send a pulse (or pull a pin low then high then low again). You then let the pin float in input mode. The ping sensor then holds this pin high sends out its ping and waits for it to bounce back. Once the echo is detected it then pulls the pin back low.
So a simple bit of inefficient psuedocode will go:
(
set timer 0 to 0 here
set portd.o to output here)
portd.0 = 0; //set portd.0 low
portd.0 = 1; //set portd.0 high
portd.0 = 0; //set portd.0 low again. This triggers the ping
set portd.0 to input here
enable timer0 here
wait 200ms (this is how long it takes for the sensor to send its ping. Remember for an accurate reading to account for changeing the prot state and enabling the timer)
while(portd.0 == 1){ //while the port is pulled high by the ping
//do nothing
}
ping reading = timer0; the ping sensor has received an echo and pulled the line back low
//now you just have to do the math
There are many other ways to accomplish this more efficiently probably using the capture module but the code above is to give a better understanding about how to use the ping))).
I personally use the SRF02 which is a single transducer that performs both the send and receive aspect, its i2c and serial capable, can be operated a ping sounder, an echo listener or both at once. It autocalibrates and can actually sense up to 6 metres. It cost £11.25 (about $22.50). Because its only 1 transducer it means that its half the footprint of a normal sonar type sensor. New firmware upgrades for the newer odel mean that it can intelligently pull its minimum detection ring right up to the transducer ring down.
-
In the $50 robot code,
how would one change one of the sensor side ports to output and then to input?
DDRC = 0x00; //configure all C ports for input
PORTC = 0x00; //make sure pull-up resistors are turned off
DDRD = 0xFF; //configure all D ports for output
I wouldn't want to configure ALL the C ports for output, just one pin. and then send PWM to it.
Then switch it back to input. so I can read its PWM?
-
Port Registers
Port registers allow for lower-level and faster manipulation of the i/o pins of the microcontroller on an Arduino board. The chips used on the Arduino board (the ATmega8 and ATmega168) have three ports:
B (digital pin 8 to 13)
C (analog input pins)
D (digital pins 0 to 7)
Each port is controlled by three registers, which are also defined variables in the arduino language. The DDR register, determines whether the pin is an INPUT or OUTPUT. The PORT register controls whether the pin is HIGH or LOW, and the PIN register reads the state of INPUT pins set to input with pinMode(). The maps of the ATmega8 and ATmega168 chips show the ports.
DDR and PORT registers may be both written to, and read. PIN registers correspond to the state of inputs and may only be read.
PORTD maps to Arduino digital pins 0 to 7
DDRD – The Port D Data Direction Register
PORTD – The Port D Data Register
PIND – The Port D Input Pins Register - read only
PORTB maps to Arduino digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable
DDRB – The Port B Data Direction Register
PORTB – The Port B Data Register
PINB – The Port B Input Pins Register - read only
PORTC maps to Arduino analog pins 0 to 5. Pins 6 & 7 are only accessible on the Arduino Mini
DDRC – The Port C Data Direction Register
PORTC – The Port C Data Register
PINC – The Port C Input Pins Register
Each bit of these registers corresponds to a single pin; e.g. the low bit of DDRB, PORTB, and PINB refers to pin PB0 (digital pin 8). For a complete mapping of Arduino pin numbers to ports and bits, see the diagram for your chip: ATmega8, ATmega168. (Note that some bits of a port may be used for things other than i/o; be careful not to change the values of the register bits corresponding to them.)
Examples
Referring to the pin map above, the PortD registers control Arduino digital pins 0 – 7.
You should note, however, that pins 0 & 1 are used for serial communications for programming and debugging the Arduino, so changing these pins should usually be avoided unless needed for serial input or output functions. Be aware that this can interfere with program download or debugging.
DDRD is the direction register for Port D (Arduino digital pins 0-7). The bits in this register control whether the pins in PORTD are configured as inputs or outputs so, for example:
DDRD = B11111110; // sets Arduino pins 1 – 7 as outputs, pin 0 as input
DDRD = DDRD | B11111100; // this is safer – it sets pins 2 to 7 as outputs
// without changing the value of pins 0 & 1, which are RX & TX
//See the bitwise operators reference pages and The Bitmath Tutorial in the Playground
PORTB is the register for the state of the outputs. For example;
PORTD = B10101000; // sets digital pins 7,5,3 HIGH
You will only see 5 volts on these pins however if the pins have been set as outputs using the DDRD register or with pinMode().
PINB is the input register variable – it will read all of the digital input pins at the same time.
-
Working out some code here with the help from Admin's Fuzzy source code, Paulstreats pseudocode above, and the specs of the Parallax Ping.
input trigger - positive ttl pulse 5 microseconds
echo pulse - pos ttl pulse 115microsecs to 18.5 millisecs
echo hold on - 750microseconds from fall of trigger pulse
Code:
int PingPin = 1; // assign a pin to the Ping Sensor
int PingVal = 0; // initialize and assign Ping Sensor Reading Value
PORT_ON(DDRC, PingPin); // Switch PingPin to OUPUT
// ------Trigger Pulse--------------
PORT_OFF(PORTC, PingPin); // Bring PingPin low before starting trigger pulse
delay_us(2); // Wait for 2 microseconds
PORT_ON(PORTC, PingPin); // Bring PingPin High for 5us according to spec sheet.
delay_us(5); // Wait for 5 microseconds
PORT_OFF(PORTC, PingPin);; // Bring PingPin Low and standby
//--------End Trigger Pulse---------------------
FLIP_PORT(DDRC, PingPin); // Switch PingPin to INPUT
loop_until_bit_is_set(PINC, PingPin); // Loop until the the PingPin goes high (macro found in sfr_def.h)
//clears timer, reset overflow counter
reset_timer_0(); //reset timer 0
loop_until_bit_is_clear(PINC, PingPin); // Loop until the the PingPin goes low (macro found in sfr_def.h)
//read timer0's overflow counter
//255 is count before overflow, dependent on clock
int elapsed_time=timer0GetOverflowCount()*255+TCNT0;
// The PING))) returns a pulse width of 29.033 uS per centimeter
PingVal = elapsed_time;
-
Could someone help explain these to me in technical terms please?
#define PORT_ON( port_letter, number ) port_letter |= (1<<number)
#define PORT_OFF( port_letter, number ) port_letter &= ~(1<<number)
#define PORT_ALL_ON( port_letter, number ) port_letter |= (number)
#define PORT_ALL_OFF( port_letter, number ) port_letter &= ~(number)
#define FLIP_PORT( port_letter, number ) port_letter ^= (1<<number)
#define PORT_IS_ON( port_letter, number ) ( port_letter & (1<<number) )
#define PORT_IS_OFF( port_letter, number ) !( port_letter & (1<<number) )
edit: figured it out on my own :P
-
how do I make sure Pin whatever on port C is set to digital mode?
I read that the C ports are set to digital I/O by default is this correct?
I've only used analog sensors on the C ports, digital sensors work too?
-
hmmm it doesnt seem like this thread is posted as new unread to people when I modify only when I post a new reply.
Can someone look at the code I've been developing above?
-
Here is the code that WORKS PERFECTLY for the ATmega128 using the ICP1 pin.
robot.c
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include "hardware.h"
void init_hardware (void);
void USART_Transmit( unsigned char data );
uint16_t a2d_10( uint8_t Channel );
uint8_t a2d_8( uint8_t Channel );
uint16_t ultra_sonic (void);
void init_hardware (void)
{
PORTA=0;
DDRA=0;
PORTB=YELLOW_LED_MASK;
DDRB=YELLOW_LED_MASK;
PORTC=0;
DDRC=0;
PORTD=0;
DDRD=0;
PORTE=0;
DDRE=MOTOR_MASK|SERVO_WHEEL_MASK|SERVO_IR_MASK;
PORTF=0;
DDRF=0;
PORTG=BLUE_LED_MASK|RED_LED_MASK;
DDRG=BLUE_LED_MASK|RED_LED_MASK;
TCCR3A=0b10101010;
TCCR3B=0b00011010;
ICR3=40000;
OCR3A=3000;
OCR3B=3000;
OCR3C=3000;
TCCR1A=0;
TCCR1B=2;
ADCSR = (( 1 << ADEN ) | ( 1 << ADSC ) | ADC_PRESCALAR_128 );
ADMUX = (( 0 << REFS1 ) | ( 1 << REFS0 ));
if (((( 1 << ADEN ) | ( 1 << ADSC ) | ADC_PRESCALAR_128 ) & ADCSR ) != 0 )
{
// Wait for the initial conversion to complete. This initializes
// the ADC.
while (ADCSR & ( 1 << ADSC) )
;
}
UBRR0H = UBRR0_INIT >> 8;
UBRR0L = UBRR0_INIT & 0xFF;
UCSR0A = UCSR0A_INIT;
UCSR0B = UCSR0B_INIT;
UCSR0C = UCSR0C_INIT;
}
void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1<<UDRE0)) );
/* Put data into buffer, sends the data */
UDR0 = data;
}
uint16_t a2d_10( uint8_t Channel )
{
// Select the channel in a manner which leaves REFS0 and REFS1 un touched.
ADMUX = ( ADMUX & (( 1 << REFS1 ) | ( 1 << REFS0 ))) | Channel;
// Start the conversion
ADCSR = ADCSR | ( 1 << ADSC );
// Wait for it to complete
while ( ADCSR & ( 1 << ADSC ));
return ADC;
} // a2d_10
uint8_t a2d_8( uint8_t Channel )
{
// Select the channel in a manner which leaves REFS0 and REFS1 un touched.
ADMUX = ( ADMUX & (( 1 << REFS1 ) | ( 1 << REFS0 ))) | ( 1 << ADLAR ) | Channel;
// Start the conversion
ADCSR = ADCSR | ( 1 << ADSC );
// Wait for it to complete
while ( ADCSR & ( 1 << ADSC ));
// We only need the top 8 bits (left-adjusted)
return ADCH;
} // a2d _8
uint16_t ultra_sonic (void)
{
uint16_t fcap=0,scap=0;
ULTRA_SONIC_OUT;
ULTRA_SONIC_SET;
_delay_us (10);
ULTRA_SONIC_RESET;
ULTRA_SONIC_IN;
Clear_ICF1;
TCNT1=0;
ULTRA_SONIC_CAP_RISING;
while (ICF1_Empty);//wait for capture
Clear_ICF1;
fcap=ICR1;
ULTRA_SONIC_CAP_FALLING;
while (ICF1_Empty);//wait for capture
Clear_ICF1;
scap=ICR1;
return scap-fcap;
}
int main (void)
{
uint16_t ac=0;
char txt[]=" ";
init_hardware();
_delay_ms(100);
while (1)
{
ac=ultra_sonic();
sprintf(txt,"%u ",ac);
USART_Transmit(txt[0]);
USART_Transmit(txt[1]);
USART_Transmit(txt[2]);
USART_Transmit(txt[3]);
USART_Transmit(txt[4]);
USART_Transmit('\n');
USART_Transmit('\r');
_delay_ms(500);
}
}
hardware.h
#define RED_LED_PIN 4
#define RED_LED_MASK ( 1 << RED_LED_PIN )
#define RED_LED_DDR DDRG
#define RED_LED_PORT PORTG
#define BLUE_LED_PIN 3
#define BLUE_LED_MASK ( 1 << BLUE_LED_PIN )
#define BLUE_LED_DDR DDRG
#define BLUE_LED_PORT PORTG
#define YELLOW_LED_PIN 4
#define YELLOW_LED_MASK ( 1 << YELLOW_LED_PIN )
#define YELLOW_LED_DDR DDRB
#define YELLOW_LED_PORT PORTB
#define LED_ON( color ) color ## _LED_PORT &= ~color ## _LED_MASK;
#define LED_OFF( color ) color ## _LED_PORT |= color ## _LED_MASK;
#define MOTOR_MASK ( 1 << 3)
#define SERVO_WHEEL_MASK ( 1 << 4)
#define SERVO_IR_MASK ( 1 << 5)
#define ULTRA_SONIC_MASK (1<<4)
#define ULTRA_SONIC_OUT (DDRD|=ULTRA_SONIC_MASK)
#define ULTRA_SONIC_IN (DDRD&=~ULTRA_SONIC_MASK)
#define ULTRA_SONIC_SET (PORTD|=ULTRA_SONIC_MASK)
#define ULTRA_SONIC_RESET (PORTD&=~ULTRA_SONIC_MASK)
#define ULTRA_SONIC_CAP_RISING (TCCR1B|=(1<<ICES1))
#define ULTRA_SONIC_CAP_FALLING (TCCR1B&=~(1<<ICES1))
#define Clear_ICF1 TIFR|=(1<<5)
#define ICF1_Empty !(TIFR & ( 1 << 5 ))
#define ADC_PRESCALAR_2 (( 0 << ADPS2 ) | ( 0 << ADPS1 ) | ( 1 << ADPS0 ))
#define ADC_PRESCALAR_4 (( 0 << ADPS2 ) | ( 1 << ADPS1 ) | ( 0 << ADPS0 ))
#define ADC_PRESCALAR_8 (( 0 << ADPS2 ) | ( 1 << ADPS1 ) | ( 1 << ADPS0 ))
#define ADC_PRESCALAR_16 (( 1 << ADPS2 ) | ( 0 << ADPS1 ) | ( 0 << ADPS0 ))
#define ADC_PRESCALAR_32 (( 1 << ADPS2 ) | ( 0 << ADPS1 ) | ( 1 << ADPS0 ))
#define ADC_PRESCALAR_64 (( 1 << ADPS2 ) | ( 1 << ADPS1 ) | ( 0 << ADPS0 ))
#define ADC_PRESCALAR_128 (( 1 << ADPS2 ) | ( 1 << ADPS1 ) | ( 1 << ADPS0 ))
#define UART0_BAUD_RATE 9600
#define UART1_BAUD_RATE 38400
#define UART_DATA_BIT_8 (( 1 << UCSZ1 ) | ( 1 << UCSZ0 ))
#define UART_PARITY_NONE (( 0 << UPM1 ) | ( 0 << UPM0 ))
#define UART_STOP_BIT_1 ( 1 << USBS )
#define UBRR0_INIT (( 1000000 / UART0_BAUD_RATE ) - 1 )
#define UBRR1_INIT (( 1000000 / UART1_BAUD_RATE ) - 1 )
#define UCSR0A_INIT 0
#define UCSR0B_INIT (( 1 << RXEN ) | ( 1 << TXEN ))
#define UCSR0C_INIT ( UART_DATA_BIT_8 | UART_PARITY_NONE | UART_STOP_BIT_1 )
#define UCSR1A_INIT 0
#define UCSR1B_INIT (( 1 << RXEN ) | ( 1 << TXEN ))
#define UCSR1C_INIT ( UART_DATA_BIT_8 | UART_PARITY_NONE | UART_STOP_BIT_1 )
Justin
-
So then changing the trigger sequence this should work
#define PORT_ON( port_letter, number ) port_letter |= (1<<number) // ultrasonic out, ultrasonic set
#define PORT_OFF( port_letter, number ) port_letter &= ~(1<<number) // ultrasonic in, ultrasonic reset
#define PORT_ALL_ON( port_letter, number ) port_letter |= (number)
#define PORT_ALL_OFF( port_letter, number ) port_letter &= ~(number)
#define FLIP_PORT( port_letter, number ) port_letter ^= (1<<number)
#define PORT_IS_ON( port_letter, number ) ( port_letter & (1<<number) )
#define PORT_IS_OFF( port_letter, number ) !( port_letter & (1<<number) )
PORT_ON(DDRC, PingPin); // Set Pin to OUTPUT
//----Trigger Pulse----------
PORT_ON(PORTC, PingPin);
delay_us(10);
PORT_OFF(PORTC, PingPin);
//-----End Trigger Pulse-----
PORT_OFF(DDRC, PingPin); // Set Pin to INPUT
but can you explain the last half of your code to me?
#define ULTRA_SONIC_CAP_RISING (TCCR1B|=(1<<ICES1))
#define Clear_ICF1 TIFR|=(1<<5)
#define ICF1_Empty !(TIFR & ( 1 << 5 ))
Clear_ICF1;
TCNT1=0;
ULTRA_SONIC_CAP_RISING;
while (ICF1_Empty);//wait for capture
Clear_ICF1;
fcap=ICR1;
ULTRA_SONIC_CAP_FALLING;
while (ICF1_Empty);//wait for capture
Clear_ICF1;
scap=ICR1;
return scap-fcap;
heavy commenting would be great ;D
-
Also, I'm using the atmega168
(http://www.sparkfun.com/tutorial/BeginningEmbedded/2-MicroProgramming/ATmega168-Pinout.jpg)
PC1 is connected to the Ping Sensor. I heard that the Ping sensor is digital. Would this port work for that? Or do I need to breakout the PB0 pin?
BTW, what do all those acronyms in parenthesis mean?
-
OK. Sorry I did not comment before I just got it to work. I found my old working code.
uint16_t ultra_sonic (void)
{
uint16_t fcap=0,scap=0;//fcap stores first capture, scap stores second
ULTRA_SONIC_OUT;//set pin to output
ULTRA_SONIC_SET;//set pin to high
_delay_us (10);//delay for a pulse
ULTRA_SONIC_RESET;//set pin low
ULTRA_SONIC_IN;//set pin to input
Clear_ICF1;//clear the capture flag
TCNT1=0;//set timer 1 to 0 to prevent overflow
ULTRA_SONIC_CAP_RISING;//capture the rising edge of the pulse
while (ICF1_Empty);//wait for capture
Clear_ICF1;//clear the capture flag
fcap=ICR1;//save the captured value
ULTRA_SONIC_CAP_FALLING;//capture the falling edge of the pulse
while (ICF1_Empty);//wait for capture
Clear_ICF1;//clear the capture flag
scap=ICR1;//save the captured value
return scap-fcap;//subtract the timer values to find the pulse lenght
}
This code uses the input capture unit on timer 1. You must configure timer 1 with TCCR1A=0;
TCCR1B=2;
You could modify it a bit if you needed servos as well but it is much better to use timer 1 for just the PING. I had it set up once with two servos and the PING but it would sometimes overflow so you would have to account for that. You also don't get as good of resolution.
The capture unit saves the timer value in ICR1 when the pin goes from low to high or high to low. By subtracting the begging value from the end value you get the time of the pulse. You would then divide it by 2 and use the speed of sound to find how far away the object is.
You must breakout PB0 as it is the only pin connected to the capture unit. You could use PC1 but your results would not be as accurate.
As for the acronyms I don't know all of them off the top of my head, they are all in the datasheet, but RXD is receive for the USART, TXD is transmit, OCxy is "Output Compare timer# letter" they are used for PWM. ADC is analog to digital converter. I think PCINT is pin change interrupt. SCK, MISO, MOSI, and SS are all for SPI or I2C.
All the things in the parenthesis can be turned on or off. You can use portc for digital I/O but since it is the only port with an ADC it is used mostly for analog inputs. When in doubt read the datasheet.
For more on the capture unit look on page 116 of the datasheet.
Justin
-
hmmm my new code didn't do anything ???
void ping(void)
{
int PingPin = 0x02;// assign a pin to the Ping Sensor
int PingVal = 0;// initialize and assign Ping Sensor Reading Value
PORT_ON(DDRC, PingPin); // Set Pin to OUTPUT
//----Trigger Pulse----------
PORT_ON(PORTC, PingPin);
delay_us(10);
PORT_OFF(PORTC, PingPin);
//-----End Trigger Pulse-----
PORT_OFF(DDRC, PingPin); // Set Pin to INPUT
//PORT_ON(DDRC, 1);// Switch signalpin to output
// ------Trigger Pulse--------------
//PORT_OFF(PORTC, 1); // Send low pulse, 0=low, 1=high
//delay_us(2); // Wait for 2 microseconds
//PORT_ON(PORTC, 1); // Send high pulse, 0=low, 1=high
//delay_us(5); // Wait for 5 microseconds
//PORT_OFF(PORTC, 1);; // Holdoff low
//--------End Trigger Pulse---------------------
//FLIP_PORT(DDRC, 1); // Switch signalpin to input
//clears timer, reset overflow counter
reset_timer_0();//reset timer 0
delay_us(200); // wait for burst frequency to end.
PINC = PINC | PingPin;
while(PINC == 1) {
// wait until pin goes low or times out
//asm volatile ("NOP");
nop(); // to prevent an optimized compiler from removing the loop
}
//read timer0's overflow counter
//255 is count before overflow, dependent on clock
int elapsed_time=timer0GetOverflowCount()*255+TCNT0;
PingVal = elapsed_time/93;
rprintf("Ping: %d", PingVal );
delay_us(200); // delay before next measurment
} // end ping function
-
Why use timer 0 and not timer 1? timer 1 is 16 bit = 256 times better resolution. I am getting data accurate to the mm. I am actually now converting it to mm. If you have your heart set on using portC I'll help you, but I would strongly recommend using the capture unit.
What is
delay_us(200);
for? You should not be waiting for anything. If you want to do it with a general I/O you should have two whiles.
while low //wait for the pulse to start
reset timer here
while high //wait for the pulse to end
save time here //rough measure of the pulse length
Also what is PINC?
-
i don't know nothin about timers
I'm using the $50 robot board the B port isnt broken out
delay_us(); is in timer.h in the avrlib
PINC:
http://ccrma.stanford.edu/courses/250a/lectures/programming/node19.html (http://ccrma.stanford.edu/courses/250a/lectures/programming/node19.html)
-
Ha Ha Ha ;D That is why my other code did not work! Another side effect from using PIC. With PIC you use portx for input and output.
I know what delay_us is but why are you waiting?
Try something like this #define ULTRA_SONIC_MASK (1<<4)
#define ULTRA_SONIC_OUT (DDRD|=ULTRA_SONIC_MASK)
#define ULTRA_SONIC_IN (DDRD&=~ULTRA_SONIC_MASK)
#define ULTRA_SONIC_SET (PORTD|=ULTRA_SONIC_MASK)
#define ULTRA_SONIC_RESET (PORTD&=~ULTRA_SONIC_MASK)
#define ULTRA_SONIC_IS_LOW (!(PIND&ULTRA_SONIC_MASK))
#define ULTRA_SONIC_IS_HIGH ((PIND&ULTRA_SONIC_MASK)==ULTRA_SONIC_MASK)
uint16_t ultra_sonic (void)
{
uint16_t fcap=0,scap=0;
ULTRA_SONIC_OUT;
ULTRA_SONIC_SET;
_delay_ms (1);
ULTRA_SONIC_RESET;
ULTRA_SONIC_IN;
TCNT1=0;
while(ULTRA_SONIC_IS_LOW);
fcap=TCNT1;
while(ULTRA_SONIC_IS_HIGH);
scap=TCNT1;
return scap-fcap;
}
Change the ports/pins for your setup.
How hard would it be to break out PortB 0? I would really consider using the capture unit. It is your friend. Also use timer1 if you can. Post your entire code and I can help you more.
Justin
-
I just learned this....
sfr_def.h is included by io.h and has these macros:
------------------------------------------------------------------
#define _BV ( bit ) (1 << (bit))
#include <avr/io.h>
Converts a bit number into a byte value.
Note:
The bit shift is performed by the compiler which then inserts the result into the code. Thus, there is no run-time overhead when using _BV().
------------------------------------------------------------------
#define bit_is_clear ( sfr,
bit ) (!(_SFR_BYTE(sfr) & _BV(bit)))
#include <avr/io.h>
Test whether bit bit in IO register sfr is clear. This will return non-zero if the bit is clear, and a 0 if the bit is set.
------------------------------------------------------------------
#define bit_is_set ( sfr,
bit ) (_SFR_BYTE(sfr) & _BV(bit))
#include <avr/io.h>
Test whether bit bit in IO register sfr is set. This will return a 0 if the bit is clear, and non-zero if the bit is set.
------------------------------------------------------------------
#define loop_until_bit_is_clear ( sfr,
bit ) do { } while (bit_is_set(sfr, bit))
#include <avr/io.h>
Wait until bit bit in IO register sfr is clear.
------------------------------------------------------------------
#define loop_until_bit_is_set ( sfr,
bit ) do { } while (bit_is_clear(sfr, bit))
#include <avr/io.h>
Wait until bit bit in IO register sfr is set.
------------------------------------------------------------------
-
That is nice to know. I usually just make my own using the same method. Do you need any help setting up the timers? You could also do with out the timer and do something like
while (is_set)++pulse_time;
Justin
-
I need math help now that I got it working with my code below.
When PingVal returns 145 that is the max reading which is equal to 300cm
When PingVal returns 1 which is the min reading that equals 3cm
How can I do the math do get PingVal into human readable format?
int PingPin = 1; // assign a pin to the Ping Sensor
int PingVal = 0; // initialize and assign Ping Sensor Reading Value
PORT_ON(DDRC, PingPin); // Switch PingPin to OUPUT
// ------Trigger Pulse--------------
PORT_OFF(PORTC, PingPin); // Bring PingPin low before starting trigger pulse
delay_us(2); // Wait for 2 microseconds
PORT_ON(PORTC, PingPin); // Bring PingPin High for 5us according to spec sheet.
delay_us(5); // Wait for 5 microseconds
PORT_OFF(PORTC, PingPin);; // Bring PingPin Low and standby
//--------End Trigger Pulse---------------------
FLIP_PORT(DDRC, PingPin); // Switch PingPin to INPUT
loop_until_bit_is_set(PINC, PingPin); // Loop until the the PingPin goes high (macro found in sfr_def.h)
//clears timer, reset overflow counter
reset_timer_0(); //reset timer 0
loop_until_bit_is_clear(PINC, PingPin); // Loop until the the PingPin goes low (macro found in sfr_def.h)
//read timer0's overflow counter
//255 is count before overflow, dependent on clock
int elapsed_time=timer0GetOverflowCount()*255+TCNT0;
// The PING))) returns a pulse width of 29.033 uS per centimeter
PingVal = elapsed_time;
-
First you divide by 2. That is because the sound has to travel to the object and back. So 145/2=72.5
According to what you have said 1cm = 29.033 us. So 72.5=8709.9us or 72.5=300cm. I take it you want the distance in cm.
300/72.5=4.1379...
divide by two and you get 2.068965517
To convert your reading to cm use
elapsed_time*= 2.068965517;
you can shorten the decimal length to probably 2.068 with out any loss.
This accommodates for the round trip.
Here is a quick check if elapsed_time=145
145*2.068965517=300
You should note that since you are using timer0 an 8 bit timer you will not even get cm resolution. You will get 300/145=2.0689cm resolution. If you can break out PB0 you could get 0.0850725mm resolution! That is what I get, 243 times better!
Justin
-
Thanks so much Justin you've been a big help.
Ill break out the PORT B soon. But for now I'm just going to stick with PORT C.
-
OK, you could use timer 1 with port c but it won't be quite as good as the capture unit. I am just glad to help as the first time I had to get this working took a few weeks. I notice with my code it can sometimes get stuck. I added a piece that checks the timer to see if it is over the max. If it is the function returns 0xFFFF. another function will keep trying until it gets a valid reading. This hardly happens but if it does and the time out feature is not there the system gets stuck. Just a word of warning. You should not have a problem as you are reading the pin and not waiting for a capture.
Justin
-
cant wait for the entire PING function to be finished . When do you think you'll have the code fully done ( using PortB ) ?
-
I already have it done! For the ATmega128. It may work with out any changes for the $50 robot. A lot of the AVR stuff is compatible.
Justin
-
I already have it done! For the ATmega128. It may work with out any changes for the $50 robot. A lot of the AVR stuff is compatible.
Justin
yep just found it before
thanks for uploading it
-
Here is the new improved one with error checking! All you need to do is change the define lines to fit your AVR.
#define ULTRA_SONIC_MASK (1<<4)
#define ULTRA_SONIC_OUT (DDRD|=ULTRA_SONIC_MASK)
#define ULTRA_SONIC_IN (DDRD&=~ULTRA_SONIC_MASK)
#define ULTRA_SONIC_SET (PORTD|=ULTRA_SONIC_MASK)
#define ULTRA_SONIC_RESET (PORTD&=~ULTRA_SONIC_MASK)
#define ULTRA_SONIC_CAP_RISING (TCCR1B|=(1<<ICES1))
#define ULTRA_SONIC_CAP_FALLING (TCCR1B&=~(1<<ICES1))
#define ULTRA_SONIC_IS_LOW (!(PIND&ULTRA_SONIC_MASK))
#define ULTRA_SONIC_IS_HIGH (PIND&ULTRA_SONIC_MASK)
void PINT_INIT (void)//call FIRST sets up timer 1 for capture, keeps output compare pins digital I/O
{
TCCR1A=0;
TCCR1B=2;
}
uint16_t get_ultra_sonic (void)
{
uint16_t fcap=0,scap=0,dist=0;//fcap stores first capture, scap stores second
ULTRA_SONIC_OUT;//set pin to output
ULTRA_SONIC_SET;//set pin to high
_delay_us (10);//delay for a pulse
ULTRA_SONIC_RESET;//set pin low
ULTRA_SONIC_IN;//set pin to input
Clear_ICF1;//clear the capture flag
TCNT1=0;//set timer 1 to 0 to prevent overflow
ULTRA_SONIC_CAP_RISING;//capture the rising edge of the pulse
while (ICF1_Empty)//wait for capture
{
if (TCNT1>50000) return 0xFFFF;
}
Clear_ICF1;//clear the capture flag
fcap=ICR1;//save the captured value
ULTRA_SONIC_CAP_FALLING;//capture the falling edge of the pulse
while (ICF1_Empty)//wait for capture
{
if (TCNT1>50000) return 0xFFFF;
}
Clear_ICF1;//clear the capture flag
scap=ICR1;//save the captured value
dist=scap-fcap;//subtract the timer values to find the pulse lenght
dist*=0.0850725;//convert to mm
return dist;
}
uint16_t ultra_sonic (void)//call this function for error checking!
{
uint16_t dist;
dist=0xFFFF;
while (dist==0xFFFF)
{
dist=get_ultra_sonic();
_delay_ms(10);//prevent echos if there was an error.
}
return dist;
}
-
I have a member tutorial up now (under construction)
http://www.societyofrobots.com/member_tutorials/node/174 (http://www.societyofrobots.com/member_tutorials/node/174)
-
You should also put in about timer1 and the input capture unit once you try it. Also put up how you set up timer0 a lot of people don't know how.
Justin
-
yeah definitely! thanks for the help!
-
Though only tested on an ATmega8 and ATmega168 is should port over to any AVR 8-bit RISC microcontroller
I used it with an ATmega128. ;)
Justin
-
updated ;)
-
posting for reference:
-
This is what I was trying to do for my balancing $50 robot, but failed, because of lack of understanding how low level C works. Pomprocker, this is the help I needed, to write the specific code for reading the Ping sensor in the bare C language. Easy for me to do in Basic and Arduino, hard as hell to do in C. As soon as I will get a little free time to work on the robot, I'll update the tutorial. Thanks.