You are on page 1of 20

Intro to Embedded Ping sensor

William OConnell s3330253


Nanthakumar Thangavelu s3207608

Introduction
The goal of our project was to build a device to measure distance based around
the OUSB board using the ATMEGA32, programming the device using assembler.
To achieve this we decided use the parallax ping sensor which is able to measure
distances between 2cm and 3m, and using a serial LCD or the LEDs built onto
the OUSB board to display the distance measured.
The device operation is split into 4 main sections, the device initialisation, the
ping sensor operation subroutine, the display routine and delays. The general
device operation is given in Figure 1.

Figure 1

Operation:
In device initialisation the program sets up any required registers and peripherals
used. For this project it sets the stack pointer to RAMEND allowing the use of
subroutine calls in the code allowing separation of code, then registers R0 and
R1 are set to 0 and 1 respectively so they can be used for 0 and 1. Port B is then
set as an output as this port is used to write to the serial LCD or to drive the
LEDs based on which display routine is selected. Followed by a call to
PingTimerSetup which sets up the 8-bit timer used when communicating with the
ping sensor.
The Ping sensor operation subroutine is the first subroutine call in the main loop
of the program, this subroutine is responsible for controlling the ping sensor and
reading in the value representing the distance measured. The communication
with the sensor occurs over a single pin, with the protocol used defined in the
datasheet. To begin a measurement a single pulse is sent over the line, the
microcontroller must then listen on this line (setting it as an input) waiting for the
response. The response sent by the sensor is a single pulse, with the width of the
pulse representing the distance measured. Our project uses the built in 8-bit
hardware timer to measure the width of this pulse. Once the pulse width has
been measured, the communication line is then reset back to an output and the
value representing the distance is read from the timer and stored in a general
purpose register. Timing of the communication with the sensor required that the
device be able to meet the timing requirements with the sensor. These values
are given in the datasheet shown in figure 1.

Figure 2

The display subroutine uses the value given by the ping subroutine and uses it to
display the distance. For out project there are two methods available to be used
to display the distance though they are unable to be used at the same time. The
first uses the LEDs built onto the OUSB board, lighting up one of the eight LEDs
depending on the distance measured, with shorter distances being represented
by one of the leftmost LEDs being lit up, and longer distances represented by
one of the rightmost LEDs being lit up. To display the distance on the LEDs we
split each LED into representing distances of the maximum distance able to be
measured by the sensor divided by the number of LEDs, resulting in each LED
representing a distance of approximately 37cm. For example if the 4 th LED from
the left was lit up, that would mean a distance between 112cm and 150cm.
The second method for displaying the measured distance using the serial LCD
uses part of the example code given to drive it. This code sends one character at
a time to the LCD using one pin on port b as the output. This required converting
the value read from the ping sensor into ASCII characters that can be displayed
on the LCD. To do this we multiplied the ping sensor value by 145, which is the
distance that a single tick of the timer represents. To then convert that value into
ASCII characters we subtract whichever unit of measurement we are attempting
to display, until the value is less than that unit. For example, if after the
multiplication the sensor value is 234, we subtract 100 from the value in a loop
incrementing a counter each time until the value is less than 100. So at the end
of the 100s loop, we would end up with the counter being 2. We can then add
this to the ASCII value of 0 to determine the ASCII value for 2, which we then
output to the LCD. Repeating this process for 10s and 1s.
The final subroutine in the main loop is one that simply acts as a delay, this
allows time for the ping sensor to get ready for the next measurement and to
slow down the loop somewhat. This delay is equal to approximately 5ms and is
implemented using a nested loop that increments two registers from 0 to 255.

To test and develop our program we primarily used VMLab, checking that our
registers displayed the values we were expecting, though to check timing
required testing the device with the sensor.
One special feature of our program is that is uses one of the hardware timers to
measure the width of the pulse received from the ping sensor. This simply allows
us to start the timer at the rising edge of the pulse and stop the timer at the
falling edge of the pulse. To ensure that the timer was able to measure the entire
pulse width required setting the timer prescaler. For the 8-bit timer to be able to
measure from the rising edge to the falling edge (t IN-MAX in figure 1) a prescaler of
1024 was used. One of the problems with this approach is that the distance will
be measured using 8-bit resolution, meaning the measurement will be within one
of the 255 values available. But this is not entirely correct as t IN-MAX does not
represent the same time as the maximum time for the timer. t IN-MAX actually
occurs when the timer is at 217. Meaning 3m is represented by a value of 217,
and any intermediate value a fraction of that (1.5m would be represented by a
timer value of 108). This gives our device a measurement resolution of 1.3cm,
which for displaying the distance in centimeters is just acceptable for out
purposes. To make this more accurate there are two options, the first is to use a
16-bit timer as opposed to the 8-bit. This would allow for a lower prescaler as
well as more bits representing the distance. With 65536 values for the 16-bit
timer, and tIN-MAX corresponding with a value of approximately 27777, each tick
would represent a distance of about 0.1mm. The second possible method is a
loop that increments a counter in the subroutine itself, this has the benefit of not
requiring the hardware counter, but requires more programming within the
subroutine to make the loops and requires accurate knowledge of the amount of
time each loop represents. Another added benefit of using the 8-bit timer is that
it mostly avoids 16-bit values which can increase code complexity.

Improvements
At the start of the project we thought that such system would be easy to design
and implement but as we got closer to develop the code and looked at more
small details we realised that its much more complicated due to having an LCD
in the system but using ATMEGA 32 was a really good option due to it having
enough memory of 32K.

Ghannt chart

Refection:
William O'Connell
Terrisa Pro Forma
-

Your mark 3
I have not marked it as one more difficult because there were only a
couple of times when I had difficulty in getting a portion of the program
working as I wanted it to.
No change
To become more confident with a similar task I will need to become more
familiar with coding in assembler and how to use two or more registers
when calculating larger numbers (such as needing to multiply a 16-bit or
larger number).

I personally found the most difficult part of this project to be using the provided
code for the serial LCD. Initially I had some difficulty getting the example working
and converting the sensor reading to ASCII codes to display, though this was
overcome with some trial and error. I initially thought that the most difficult part
would be communicating with the ping sensor, but the timing restrictions on the
device were easier to meet than I expected making it easy to design code that
could meet the requirements.
If I were doing this as part of a paid project, I would be more concerned about the
accuracy of the timing (using the 16-bit timer instead of the 8-bit) and possibly
attempt to move the measuring of the input pulse from the timer from simply
polling the pin to watch for the rising and falling edge, to attaching the sensor to
a pin that is able to trigger interrupts on the rising and falling edges, and using
those interrupts to control the timer. Allowing for more processing time for other
parts of the program (assuming additional functions are added, as its not really
required for this project as is).

Terissa: Nanthakumar Thangavelu

I expect that the most difficult part of this project will be

Familiarizing our team with the code associated with the LCD module and
ATMega32.

Before Evaluation:
Evaluate the complexity of this project:
4

Write down the reasons why you have not evaluated the question as
one level less difficult:
Our team has yet to familiarize ourselves with the components being used for
the project.

Appendix:
Appendix 1 Program code
.include "C:\VMLAB\include\m32def.inc"

; Define here the variables


;
.def Temp =r16
.def Temp2 = r17
.def Temp3 = r23

.Set TicksPerLED =27


.Set

TickDistance = 145 ; represents 1.45mm

.DEF ZERO = R0;


.DEF ONE = R1;
.DEF SER_REG = R18
.DEF SER_REG1 = R19
.DEF DELAYL = R20 ; 24 bit counter to generate long delays
.DEF DELAYM = R21 ;
.DEF DELAYH = R22 ;
.EQU B192 = 205;

; Define here Reset and interrupt vectors, if any


;
reset:
rjmp start
reti

; Addr $01

reti

; Addr $02

reti

; Addr $03

reti

; Addr $04

reti

; Addr $05

reti

; Addr $06

Use 'rjmp myVector'

reti

; Addr $07

to define a interrupt vector

reti

; Addr $08

reti

; Addr $09

reti

; Addr $0A

reti

; Addr $0B

This is just an example

reti

; Addr $0C

Not all MCUs have the same

reti

; Addr $0D

number of interrupt vectors

reti

; Addr $0E

reti

; Addr $0F

reti

; Addr $10

; Program starts here after Reset


;
start:

; Set up stack pointer


LDI Temp, LOW(RAMEND)
OUT SPL, Temp
LDI Temp, HIGH(RAMEND)
OUT SPH, Temp

;Initialise timer for ping subroutine


CALL PingTimerSetup
CLR Temp
MOV R0, Temp
CLR Temp
DEC Temp
MOV R1, Temp

; Set PortB as output for LEDs


LDI Temp, 0xFF
OUT DDRB, Temp

forever:
CALL Ping

; Read the ping sensor

;CALL Display ; Display the distance on the LEDs, cannot be used at the same
time as the LCD
CALL DisplayLCD ; Displays the distance on the LCD in centimeters
CALL Delay ; delay for ~5Ms

rjmp forever

;;
;; Converts the value stored in temp to thermometer LED display
;;
Display:
; If Temp < 27 Light LED 1

LDI Temp2, TicksPerLED


CP Temp, Temp2
BRSH Higher1 ; Branch if same or higher
; If Temp < 27 (27 * 1) Light LED 1
LDI Temp, (1<<0)
OUT PORTB, Temp
RET
Higher1:

LDI Temp2, TicksPerLED*2


CP Temp, Temp2
BRSH Higher2
; If Temp < 54 (27 * 2) Light LED 2
LDI Temp, (1<<1)
OUT PORTB, Temp
RET
Higher2:

LDI Temp2, TicksPerLED*3


CP Temp, Temp2
BRSH Higher3
; If Temp < 54 (27 * 3) Light LED 3
LDI Temp, (1<<2)
OUT PORTB, Temp
RET
Higher3:

LDI Temp2, TicksPerLED*4


CP Temp, Temp2
BRSH Higher4
; If Temp < 54 (27 * 4) Light LED 4
LDI Temp, (1<<3)
OUT PORTB, Temp

RET
Higher4:

LDI Temp2, TicksPerLED*5


CP Temp, Temp2
BRSH Higher5
; If Temp < 54 (27 * 5) Light LED 5
LDI Temp, (1<<4)
OUT PORTB, Temp
RET
Higher5:

LDI Temp2, TicksPerLED*6


CP Temp, Temp2
BRSH Higher6
; If Temp < 54 (27 * 6) Light LED 6
LDI Temp, (1<<5)
OUT PORTB, Temp
RET
Higher6:

LDI Temp2, TicksPerLED*7


CP Temp, Temp2
BRSH Higher7
; If Temp < 54 (27 * 7) Light LED 7
LDI Temp, (1<<6)
OUT PORTB, Temp
RET
Higher7:

; else light LED 8


LDI Temp, (1<<7)
OUT PORTB, Temp

RET

; End Display

;; Uses Port C bit 0 as I/O to control Ping sensor, as defined below.


;; Also uses timer1 to measure input pulse.
;; This function determines the distance detected by the ping sensor.
;; The output value is returned in "Temp"

.set waitTime = 0x0E ; 55 cycles / cycles per loop (4) = 14 (0x0E)

;
; Sets up timer 1 for the Ping function
;
PingTimerSetup:
; Set Timer0 to normal mode
; TCCR0 &= ~(1<<WGM01 | 1<<WGM00)

; Set Compare match output mode to normal port operation


; (no automatic pin toggling)
; TCCR0 &= ~(1<<COM01 | 1<<COM00)

; Clock select
; Ensure clock is stopped
; TCCR0 &= ~(1<<CS02 | 1<<CS01 | 1<<CS00)

; Setting TCCR0 to 0 based on above


;FOC0 also written to 0, not that it matters in this case
CLR Temp
OUT TCCR0, Temp

; Set counting from 0


; TCNT0 = 0;

CLR Temp
OUT TCNT0, Temp

; Disable timer interrupts interrupts


; TIMSK &= ~(1<<OCIE0 | 1<<TOIE0)
IN Temp, TIMSK
ANDI Temp, ~(1<<OCIE0 | 1<<TOIE0)
OUT TIMSK, Temp

RET ; End PingTimerSetup

;
; Uses the ping sensor to find the distance.
;
Ping:
; Set sig as output
SBI DDRC, PC7

; Set sig HIGH


SBI PORTC, PC7

; wait ~5us (no shorter than 2us)


; approx 55 - 60 cycles
LDI Temp, waitTime
pingWait1:
DEC Temp

; loop begin, 4 cycles per loop


; decrement Temp, 1 cycle

CPSE Temp, r0 ; Compare skip if equal to 0, 1 cycle if !0, 2 cycles on exiting


loop
RJMP pingWait1 ; 2 cycles

; Set sig low


CBI PORTC, PC7

; Set sig input

CBI DDRC, PC7

pingWait2:
SBIS PINC, PC7
RJMP pingWait2

; Timer/counter
; Set time to start at 0
CLR Temp
OUT TCNT0, Temp

; Enable timer with 1024 prescaler


IN Temp, TCCR0
ORI Temp, (1<<CS02 | 1<<CS00)
OUT TCCR0, Temp

; while (sig == HIGH) { wait }


pingWait3:
SBIC PINC, PC7
RJMP pingWait3

; Stop timer
IN Temp, TCCR0
ANDI Temp, ~(1<<CS02 | 1<<CS01 | 1<<CS00)
OUT TCCR0, Temp

; Calculate distance based on time elapsed


; Read in current timer value
IN Temp, TCNT0

RET ; End Ping

Delay:
clr r20
clr r21

delay_inner: ; Inner loop


DEC r21
CP r21, r0
BREQ delay_outer_end
rjmp delay_inner
delay_outer_end:
DEC r20
CP r20, r0
BREQ end_delay
rjmp delay_inner

end_delay:
RET

;---------------Convert binary distance value to decimal--------------------; requires multilplying the distance by 145 (representing 1.45mm)
; by the number of timer ticks that occurred (value in Temp)
; Output will be a 16-bit value, to then be converted into decimal
DisplayLCD:
; Set LCD cursor to start
LDI SER_REG, 0x0C
CALL Delay

; 48 = 0 (from ascii table)

ldi Temp2, TickDistance


MUL Temp, Temp2 ; MUL stores the output in R0 and R1
MOVW Temp, R0 ; moves word from R0 and R1 into Temp and Temp2 (R16 and
R17)
CLR R0 ; Resets r0 back to 0
; output the result one digit (decimal) at a time
clr Temp3 ; clear Temp3, this is used as a counter
; Temp4 is used to store the value to subtract from current
measurement
; Hundreds... count how many hundred
HundredsLoop:
INC Temp3
; Temp:Temp2 - 0:Temp4
SUBI Temp, 10000 ; subtract low byte
SBCI Temp2, 0

; subtract high byte with carry

CPI Temp, 10000


BRSH HundredsLoop ; while number >= 100

; print hundreds digit to LCD


clr SER_REG
LDI SER_REG, 48

;add offset of ascii char for '0'

ADD SER_REG, Temp3

CALL serial_out

CLR Temp3 ; reset counter


TensLoop:
INC Temp3
; Temp:Temp2 - 0:Temp4
SUBI Temp, 1000 ; subtract low byte
SBCI Temp2, 0

; subtract high byte with carry

CPI Temp, 1000


BRSH TensLoop ; while number >= 100 (same or higher)

; print hundreds digit to LCD


clr SER_REG
LDI SER_REG, 48

;add offset of ascii char for '0'

ADD SER_REG, Temp3

CALL serial_out
CLR Temp3

OnesLoop:
INC Temp3
; Temp:Temp2 - 0:Temp4
SUBI Temp, 100 ; subtract low byte
SBCI Temp2, 0

; subtract high byte with carry

CPI Temp, 100


BRSH OnesLoop ; while number >= 100 (same or higher)

; print hundreds digit to LCD


clr SER_REG
LDI SER_REG, 48

;add offset of ascii char for '0'

ADD SER_REG, Temp3

CALL serial_out

LDI SER_REG, 99 ;'c'


CALL serial_out
LDI SER_REG, 109
CALL serial_out
RET

;'m'

;----------------sends byte to port on bit at a time -------------------; emulates rs232 serial output
; assumes we're connected to PB7

serial_out:
clr SER_REG1
out PORTB, Zero ; start bit
call B_DELAY

ror SER_REG
ror SER_REG1

; rotate B0 into carry


; rotate carry into B7

out PORTB, SER_REG1 ; output B0


call B_DELAY
ror SER_REG
ror SER_REG1

; rotate B1 into carry


; rotate carry into B7

out PORTB, SER_REG1 ;output B1


call B_DELAY
ror SER_REG
ror SER_REG1

; rotate B2 into carry


; rotate carry into B7

out PORTB, SER_REG1 ;output B2


call B_DELAY
ror SER_REG
ror SER_REG1

; rotate B3 into carry


; rotate carry into B7

out PORTB, SER_REG1 ;output B3


call B_DELAY
ror SER_REG
ror SER_REG1

; rotate B4 into carry


; rotate carry into B7

out PORTB, SER_REG1


call B_DELAY

;output B4

ror SER_REG

; rotate B5 into carry

ror SER_REG1

; rotate carry into B7

out PORTB, SER_REG1

;output B5

call B_DELAY
ror SER_REG

; rotate B6 into carry

ror SER_REG1

; rotate carry into B7

out PORTB, SER_REG1 ;B6


call B_DELAY
ror SER_REG

; rotate B7 into carry

ror SER_REG1

; rotate carry into B7

out PORTB, SER_REG1

;output B7

call B_DELAY
; out PORTB, zero

; send zero as the parity bit

; call B_DELAY
out PORTB, one
call B_DELAY
call B_DELAY

; two stop bits

ret

;--------------- Baud-rate DELAY -----------------------------------------;


; PURPOSE: 8 bit counter for very short delays.
;
; DETAILS: total delay = 6+ 3x(Delay-1) x 83.3uS @ 12 MHz
; Enters with delay value in DELAYL
; e.g 207 -> 5.2uS -> 19,230 baud
B_DELAY:
; ret

; uncomment this line for simulation to

LDI DELAYL, B192


BD_0:
dec DELAYL

; Start at 0xFF

brne BD_0

; inner loop 3 cycles =

LDI DELAYL, B192


BD_1:
dec DELAYL

; Start at 0xFF

brne BD_1

; inner loop 3 cycles =

ret

; 4 cycles

;--------------- Short DELAY -----------------------------------------;


; PURPOSE: 8 bit counter for very short delays.
;
; DETAILS: total delay = 6+ 3x(Delay-1) x 83.3uS @ 12 MHz
; Enters with delay value in DELAYL
; e.g 207 -> 5.2uS -> 19,230 baud
S_DELAY:
; ret

; uncomment this line for simulation to

SD_0:
dec DELAYL

; Start at 0xFF

brne SD_0

; inner loop 3 cycles =

ret

; 4 cycles

;--------------- Long DELAY -----------------------------------------;


; PURPOSE: 24 bit counter for long delays.
;
; DETAILS: L and M loops about 256 * 256 * 3 cycles / clock MHz.
;

very approx 16.4 ms @ 12 MHz

;
L_DELAY:
; ret

; uncomment this line for simulation to


; eliminate very long delays. Comment out
; to run on real hardware to get 260ms delay.

clr DELAYM
clr DELAYL

D_0:
dec DELAYL
brne D_0
dec DELAYM
brne D_0

; Start at 0xFF
; inner loop 3 cycles.
;
; middle loop

dec DELAYH
brne D_0

;outer loop.

ret

;--------------- Long DELAY -----------------------------------------;


; PURPOSE: 24 bit counter for long delays.
;
; DETAILS: L and M loops about 256 * 256 * 3 cycles / clock MHz.
;

very approx 16.4 ms @ 12 MHz

;
;
DELAY_96:
; ret

; uncomment this line for simulation to


; eliminate very long delays. Comment out
; to run on real hardware to get 260ms delay.

clr DELAYL
ldi DELAYH, 5

;about 9600 baud

D_96:
dec DELAYL

; Start at 0xFF

brne D_96

; inner loop 3 cycles.

dec DELAYH
brne D_0

ret

;outer loop.

You might also like