Siren Project With Pic Microcontroller

12
Matthew Hunter 208513233 Computer Design 2 Project 4 report

Transcript of Siren Project With Pic Microcontroller

Page 1: Siren Project With Pic Microcontroller

Matthew Hunter 208513233

Computer Design 2

Project 4 report

Page 2: Siren Project With Pic Microcontroller

Software Design and Overview

More in depth analysis of the code can be found in the comments in the code itself, below is just the

general description of the flow of logic of the design.

This aim of this project was to make a realistic emergency services sound, done through outputting a

frequency varying wave to a speaker. This waveform is created by series of 0’s and 1’s to the output

pin, essentially creating a square wave.

Other goals for this project were to increase modularity of code, something that had been lacking in

previous projects.

Since the output is built around time and frequency, a clock would need to be used to control this

output. This is both more modular and lighter on the processor instruction cycle usage than using

main and delays. Due to the relatively simple requirements of the task, the simplest timer, timer 0

was selected as the basis for the output. Main would be an empty loop.

From here in we have several options based around how we want to use the timer and how we want

to change the frequency. Since there are in effect two different frequencies going on (the frequency

of the siren and the frequency of the rate of change of the sirens frequency), we have the choice of

running them both off one timer or using two. In an effort to keep the code modular, both run off

one timer, timer0, although it would be possible to generate the wave from toggling on the timer0

interrupt and then change the rate of toggling in the interrupt of another timer. However it is more

compact in terms of resource footprint, although not instruction cycles, to base the whole running of

this one application off one timer.

The second main decision is: is the timer frequency going to stay constant? A constant frequency

would mean that the timer could potentially be used for other applications later, such polling for a

button press, or any other periodic event. However this would mean that the timer would have to

trigger more often and a separate count, incremented each timer overflow, would decide when to

toggle the output. This separate count would then be moved up and down as needed to change the

frequency. Unfortunately no real happy medium was able to be reached here. To get a truly smooth

output, the timer would have to trigger very frequently to allow for large count values to manage

small changes between them. This lead to unacceptable amount of time spent in the interrupts,

which are actually more time consuming than at first appears due to the overhead of storing and

restoring registers, as well as then incrementing and checking the count. However if the frequency

of clock interrupts decreased, the count before a frequency change also had to change, leaving to

one being able to hear discrete changes in the siren.

Thus it was determined that the timer frequency itself would manage frequency changes, at each

interrupt, a variable set to a particular value would be loaded into it. Thus its frequency would

change, and the output would be toggled every time it interrupted, rather than maintaining an

internal count. This would allow the timer0 to overflow comparatively rarely, varying between

1000Hz output and 500Hz respectively.

Page 3: Siren Project With Pic Microcontroller

The only real disadvantage with this method apart from rendering timer 0 useless to other

programs, is that because the interrupt gets called at a changing frequency, the performance of the

microprocessor at full instruction cycle usage will vary with the varying output of the siren! However

due to the low rate of it being called, this shouldn’t make too much difference.

Page 4: Siren Project With Pic Microcontroller

Hardware Overview

Figure 1: Hardware connections

The hardware connections to the pic itself are basic, power and ground are connected up as shown

above, and pin RC4 (pin 4 of PORTA) acts as the output. It is connected to a basic push-pull amplifier,

this allows more power to be delivered straight to the siren using the full 5V to power it. This both

allows it to be louder than it would normally be capable of being if it was powered straight from the

PIC, and also ensures that the pic is not damaged trying to source too much current, as the

resistance of the speaker is low. A resistor is connected in series with this arrangement to bring

down the output voltage a bit, as if allowed to amplify the full output current the amplifier might

ramp to saturation. A capacitor is also placed at the output, this leads to a further slight decrease in

volume and helps smooth the waveform.

Page 5: Siren Project With Pic Microcontroller

The Toggle Macro In an attempt to increase code usability and modularity a macro for the toggling of a single bit of a

predefined register was created. At one stage of the design it was going to be in a subroutine, but it

was ultimately decided against in the actual implementation as it would only need to be called in

one place rather than two, and would thus not decrease the overall lines of code, and would also

lead to instruction cycle wastage as it would need to be called to and returned from. Thus a macro

implementation seemed best, as it simplifies the tmr0 overflow interrupt handler visually, and

consumes no more system resources than normal. Also the macro allows for any pin to be toggled

as simply as changing the macro statement in the main block of code. The final code looks as follows:

PORTATOGGLE MACRO pin

movlw b'00000010' ;moves 2 into w register to enable a jump to be performed later

btfss PORTA,pin ;Checks to see whether the pin is set or not

addwf PCL,f ;if not, it needs to be set, jumps to the "bsf PORTA,4" line

bcf PORTA,pin ;otherwise it is cleared

incf PCL ;once being cleared, it needs to jump over the set bit

;instruction

bsf PORTA,pin ;sets the pin of PRORTA

ENDM

The implementation of this in subroutine form is shown below:

TogglePin:

movlw b'00000010' ;moves 2 into w register to enable a jump to be performed later

btfss PORTA,4 ;Checks to see whether te pin is set or not

addwf PCL,f ;if not, it needs to be set, jumps to the "bsf PORTA,4" line

bcf PORTA,4 ;otherwise it is cleared

incf PCL ;once being cleared, it needs to jump over the set bit

instruction:

bsf PORTA,4 ;sets the pin of PRORTA

return

The above could also be placed inline, but would not increase efficacy over the macro

However the simplist and most efficient way is to simply use the XORWF statement as

shown below.

clrw

xorwf PORTA,f

The above should toggle all the pins of PORTA, which for this application would be all that is

needed, at a much lower processor cost. However this decreases the codes reusability, as if

the other pins of PORTA are used in later additions or in whatever code this siren is being

added to, the siren code would mess with it, causing problems that will waste time trying to

find the cause of later.

Page 6: Siren Project With Pic Microcontroller

Results

All the below values are including calls and returns from interrupt

Number of total instructions in code: 67

Number of instruction cycles used when the timer0 interrupt occurs and the frequency

does not need to be changed: 32 (including interrupt handling, storing and returning values

to the W register etc) or 21 (excluding interrupt handling and register storing etc)

Number of instruction cycles used when the timer0 interrupt occurs and the frequency

needs to be changed: 43 (including interrupt handling, storing and returning values to the

W register etc) or 32 (excluding interrupt handling and register storing etc)

Number of instruction cycles used when initializing: 31

Number of instruction cycles used in one period of the siren (0.9375s):

((

(

(

( ( instrution cycles

Level of Stack used: 2, one for the interrupt and one for the subroutine the interrupt calls

Actual implimentation time via simulation : 946.242000ms

Page 7: Siren Project With Pic Microcontroller

;*********************************************************************************************

;

;Filename: 208513233p4.asm

;Date: 11/09/2011

;File Version 1.2

;

;Author Matthew Hunter

;Student Number 208513233

;

;*********************************************************************************************

;

;File Description:

;

; Project 4-Emergency Siren ;

;This program is designed to produce a siren noise that could be suitably used for an

;emergency services siren. It outputs a square wave of varying frequency to an output

;pin to make the siren noise. The frequency of this output changes over a 1 second period,

;oscillating between 500Hz and 1000Hz.

;

;---------------------------------------------------------------------------------------------

;

;Device Used: PIC16F690

;

;*********************************************************************************************

; Basic Overview

;*********************************************************************************************

;

;

;Waveform output explained:

;

;The basic square wave is output by toggling an output pin every time the TMR0 register

;overflows. The frequency of this is altered to the values needed by writing different

;values for the TMR0 register to start incrementing from.

;

;So for example, when outputting at the lowest frequency (500Hz), 5 is loaded into the

;TMR0 register, thus it counts from 5 to 255, effective 0 to 250. The counter increments

;every 4 clock pulses, and each clock pulse takes place at 1uS. Therefor the period of

;toggling is 1/(250x4x1us) = 1000Hz, but this is the frequency of the TOGGLING, thus it

;needs to be divided by a factor of 2 (the period of the WAVEFORM is twice that of each

;high or low period, which leaves a frequency of 500hz.

;

;Similarly when 130 is loaded into the timer register, the timer counter from 130-255,

;effectively 0-125. This leads to a frequency of 1000Hz.

;

;Frequency-variation explained:

;

;The varying of the frequency needs to take place every 1 second, but the actual time

;between overflows of timer 0 will vary as well due to how the square wave is output (see

;above). However it was found during testing that rather than use a separate timing

;structure it is possible to base this frequency change off timer0, it means that

;the changes in frequency at higher frequencies happen at faster frequencies, so it the

;siren spends more time low than high. Testing found this to be a nice, recognisable siren

;noise.

;

;To ramp the frequency up and down, during a TMR0 overflow interrupt, the register containing

;the starting value of TMR0 is set to a value one higher or one lower than it was previously

;as needed. The rate at which this change would be implemented at was calculated thusly:

;

;The period between two tmr0 overflows oscillates between 1/((125)/2)x4us) and

;1/((250)/2)x4us). Therefor on average the period between two tmr0 overflows would be

;((125+250)/2)x4us =750us, making the period 1.5ms on average for a waveform. There are

;going to be 125 different steps up and down 250 steps in total. Therefor if the frequency

;was to be changed every time tmr0 overflowed, the time of one taken for one oscillation would

;be 0.75ms x 250 = 187.5ms. However through the use of a count value: frequency_change_counter

;the frequency will only be altered every 5 overflows, giving 187.5ms x 5 = 0.9375s, the

;closest to 1 second that can be achieved under the circumstances. This is ok though as high

Page 8: Siren Project With Pic Microcontroller

;precision isn't really a criteria here.

;

;Other Comments:

;

;In the code below whenever a value is loaded into the timerstart variable (the variable

;that is always loaded into the tmr0 value at an overflow) is three more than advertised or

;predicted. This is because it takes exactly 12 clock pulses, or 3 tmr0 incrementations

;from the time tmr0 overflows to the time the tmr0 overflow interrupt handler places a new

;value in the tmr0 register. This is due to it need needing to jump and work out which

;interrupt just occurred and storing the w-register etc. THIS VALUE OF +3 IS VERY

;CIRCUMSTANCIAL and will need to be recalculated if the program is modified. However the

;slight timing change probably won't make too much difference, it's merely done here

;for accuracy.

;

;Another problem is that after placing a value in tmr0, the timer will stop incrementing

;for 2 clock cycles, but since this is only 0.5 of a full incrementation cycle for the

;tmr0 register, there is no right or wrong way to deal with this, so in this code its

;just ignored, although the value of +3 explained above could simply be changed to +4

;with a similar amount of inaccuracy being introduced.

;

;

;

;*********************************************************************************************

; Hardware Setup and Connections:

;*********************************************************************************************

;

;Necessary hardware for implementation:

; 1 100uF Capacitor

; 1 NPN Transistor

; 1 PNP Transistor

; 1 2k2 resistor

; Any necessary wire to make connections

;

;Hardware setup and connections are largely simple, VDD pin should be connected to +5V and

;VSS to ground. PIN3 (RA4) is used as the programs main output. This will be connected to a

;simple push-pull amplifier. The RA4 pin is connected to a 2k2 Ohm resistor, which in turn

;is connected to the bases of a NPN and a PNP transistors. The collector of the NPN is

;connected to VDD (+5V) whilst the connector of the PNP should be connected to ground.

;The remaining emitter pin of the NPN and collector pin of the PNP transistor are connected

;together and this then goes to a 100uF capacitor,, preferably non-directional. Although

;it doesn't have to be. The other pin of capacitor feeds into the high voltage pin of a

;speaker. The low voltage pin of the speaker should go to ground.

;

;*********************************************************************************************

; Configuration and Definitions

;*********************************************************************************************

;

;CPU Configuration:

;

processor 16f90

#include <p16F690.inc>

__CONFIG _CP_OFF & _CPD_OFF & _MCLRE_ON & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT

;

;---------------------------------------------------------------------------------------------

;

;Variable Definitions:

Cblock 0x20 ;Assigns variables to the free registers starting at address ;0x20

w_temp ;This stores the W-register for when an interrupt is called

status_temp ;This stores the Status-register for when an interrupt is called

Alarmstate ;This keeps track of whether the alarm is rising in frequency or

;falling in frequency. The 0 bit of the register keeps track of

;this. If its set to 1 the frequency is falling, period is

;increasing, and the starting value is deceasing

;If set to 0 the frequency is rising, period is decreasing and

;the starting value is increasing

timerstart ;This register stores the value the timer is to start counting

;at (+3)

Page 9: Siren Project With Pic Microcontroller

frequency_change_counter ;A small counter used to time how long it needs to be before

;the frequency changes

Count_btn ;This is the count value used in the delay of the button

debouncing

ENDC

;

;---------------------------------------------------------------------------------------------

;

;Macros:

;

;PORTATOGGLE

;

;This macro toggles the pin (sets the pin if clear or clears it if its set), determined

;by the number passed by variable "pin" of PORTA specifically, although it could be easily

;modified to handle any port or 8 bit register. As a macro, it can’t used goto and label

;definitions for the purpose of branching, as those labels need to be unique. Thus jumps

;and branches are done by directly adding to the program counter PCL

;

;Execution time: 5 or 6 instruction cycles depending on whether it is setting or clearing

;the pin, respectively.

PORTATOGGLE MACRO pin

movlw b'00000010' ;moves 2 into w register to enable a jump to be performed later

btfss PORTA,4 ;Checks to see whether te pin is set or not

addwf PCL,f ;if not, it needs to be set, jumps to the "bsf PORTA,4" line

bcf PORTA,4 ;otherwise it is cleared

incf PCL ;once being cleared, it needs to jump over the set bit

instruction

bsf PORTA,4 ;sets the pin of PRORTA

ENDM

;

;---------------------------------------------------------------------------------------------

;

;Program Start:

;

RESET_VECTOR CODE 0x0000 ; processor reset vector

goto INIT ; go to the initialisation

;*********************************************************************************************

; INTERRUPTS

;*********************************************************************************************

;

;Interrupt handler:

;

org 0x0004 ; processor interrupt vector

;Save the register values that will be affected by the interrupt for

restoration later

movwf w_temp ; save off current W register contents

movf STATUS,w ; move status register into W register

movwf status_temp ; save off contents of STATUS register

;Determine the source of the interrupt and thus the programs destination

btfsc INTCON,T0IF

goto CLK0

;--------------------------------------------------------------------------------------------

;

;Clock 0 overflow Interrupt:

;

CLK0:

bcf INTCON,2 ;resets the interrupt bit

call SIREN_SUBROUTINE ;calls the main body of code to be implemented each time the

;counter overflows.

goto Clk0_exit

;

;Before exiting restore the affected registers to their pre-interrupt state

;

Clk0_exit:

Page 10: Siren Project With Pic Microcontroller

movf status_temp,w ;retrieve copy of STATUS register

movwf STATUS ;restore pre-isr STATUS register contents

swapf w_temp,f

swapf w_temp,w ;restore pre-isr W register contents, done using

;a swap to avoid affecting W

retfie ;returns from the interrupt

;

;*********************************************************************************************

; SUBROUTINES

;*********************************************************************************************

;

;SIREN_SUBROUTINE

;

;This subroutine contains most of the maintenance that needs to be done to keep a siren

;based off timer 0 working. It is separated into a separate subroutine just in case

;anything else needs to also use timer 0, it too could have its own subroutine

;and thus keep the two functions separate. This is slightly unlikely though as this

;sub-routine will actually manipulate how often timer 0 overflows, meaning that

;it will be hard to use it for something else. Still it is possible though. For example

;some other subroutine that works out a frequency based on a count value and how often

;timer 0 is called for example etc.

;

;This subroutine has 2 main tasks:

; 1) Toggle the output, creating the wave pattern in the first place

;

; 2) Work out whether the frequency at which the timer 0 overflows (due

; to 1) this is now synonymous with "frequency at which the output

; is toggled") needs to be changed. As part of this:

;

; 2.1) It needs to check to see if a change is due via looking at the

; frequency_change_counter and

; 2.2) It needs to check whether it needs to increment to decrement the period

; 2.3) It needs to check whether it has reached its limits for increasing

; or decreasing the period and change the state accordingly

;

;

;

SIREN_SUBROUTINE

movf timerstart,w ;These two lines reset the starting timer value, its important

movwf TMR0 ;that this is done as soon as possible to keep frequency

;accuracy

;

PORTATOGGLE b'00000100' ;Toggles the output, this toggling creates the square

;wave output.

;Once every two times the program makes it to this point

; a period has been output. ;

;In the six lines of code below, the counter which determines when the frequency will

; change is incremented, and if found to match the value 5, is cleared and then the ; code will continue on to change the frequency. Else if not equal to 5, the code ; simply goes to this subroutines exit. The number 5 was chose so as to give the ; overall oscillation between a high and low frequency a period of 1 second, as ;explained previously.

;

incf frequency_change_counter ;increment the frequency change counter

movf frequency_change_counter,w ;and then move it into the w-register

xorlw b'00000101' ;so that it can be XORed with 5

btfss STATUS,Z ;check if the result was zero eg, if it was equal

;to 5

goto Sirensubroutineexit ;if it was not, exit from this subroutine

clrf frequency_change_counter ;if it was however, clear the counter and

; continue on down the code ;

;This simple skip statement acts as an if-then-else type node branch,

;determined by the state register whether the period needs to be increased or

;decreased

btfss Alarmstate,0

Page 11: Siren Project With Pic Microcontroller

goto increasetimerstart ;if bit 0 of alarm state is clear, increase the starting

;time/decrease period/increase frequency

goto decreasetimerstart ;if bit 0 of alarm state is set, decrease the starting

; time/increase period/decrease frequency ;

;The incease and decrease subsections are mirror images of each other, they both move

;the timerstart value up or down one, check to see if its at its upper or lower limit,

;and if so then changes the state

increasetimerstart:

incf timerstart ;increment the timerstart value and then

movf timerstart,w ;moves the timerstart into the w-register

xorlw b'10000101' ;so that it can be compared with its upper bound

;(130+3)=133

btfsc STATUS,Z ;see if it was a match (eg the above operation was zero)

bsf Alarmstate,0 ;if it was, change the Alarmstate bit so that

; the period will be increased (timerstart decreased) goto Sirensubroutineexit ;else simply exit

decreasetimerstart:

decf timerstart ;decrement the timerstart value and then

movf timerstart,w ;moves the timerstart into the w-register

xorlw b'00001000' ;so that it can be compared with its lower bound (5+3)=8

btfsc STATUS,Z ;see if it was a match (eg the above operation was zero)

bcf Alarmstate,0 ;if it was, change the Alarmstate bit so that the period

; will be decreased (timerstart increased) goto Sirensubroutineexit ;else simply exit

;

;This is the exit from the subroutine, the program has to go through here to exit.

;It will come here at the end of decreasetimerstart, increasetimerstart and if

;frequency_change_counter was found to be less than 5

;

Sirensubroutineexit:

return

;

;*********************************************************************************************

; INITIALISATION

;*********************************************************************************************

;

INIT:

movlw b'00000001' ;Prescalar of 4

banksel OPTION_REG

movwf OPTION_REG ;Sets the option_reg to 00000000. Of particular relevance here

; that 7 and 6 are set to 0 as they allow pullups to be enabled ;on a bit by bit basis as opposed to all being disabled and

;setting the interrupt edge trigger to high to low,

;respectively.

;

banksel ANSEL ;This section clears the Analogue control registers so that

PORTA,

clrf ANSEL ;where the INT pin is situated is able to function as an

banksel ANSELH ; external interrupt input as intended. Clearing these registers ;turn the A/DC converters off

clrf ANSELH ;and makes the pins digital i/o pins

;

movlw b'11110000'

movwf INTCON ;sets the interrupt register to allow external interrupts.

;

banksel TRISC

clrw

movwf TRISC ;sets port C to all outputs

;

movlw b'00000000' ;sets port A to outputs as outputs

movwf TRISA

movwf WPUA ;weak pull up on PORTA<2> (for switch)

banksel PORTA

clrf PORTA ;clears PORTA

;

Page 12: Siren Project With Pic Microcontroller

bcf STATUS, RP0 ;selects bank

;

movlw b'00001000' ;starts by loading (5+3)=8 into the timerstart register, so that

;when timer0 overflows

movwf timerstart ;for the first time it will be reset to a value within the

;calculated range from the very beginning. Not strictly speaking

;necessary, but ensures the siren is even from the beginning

bcf Alarmstate,0 ;initialises the alarm state bit to 0, eg: rising timer count,

;falling period

clrf frequency_change_counter ;initialises the counter that determines when to change

;frequency

goto MAIN

;

;*********************************************************************************************

; MAIN

;*********************************************************************************************

;The main loop of the program loops indefinitely waiting for an interrupt to occur

;

MAIN:

goto MAIN

end

;*********************************************************************************************