Sei sulla pagina 1di 12

Matthew Hunter

208513233

Computer Design 2

Project 4 report

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 0s and 1s 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.

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 shouldnt make too much difference.

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.

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' btfss PORTA,pin addwf PCL,f bcf PORTA,pin incf PCL bsf PORTA,pin ENDM

;moves 2 into w register to enable a jump to be performed later ;Checks to see whether the pin is set or not ;if not, it needs to be set, jumps to the "bsf PORTA,4" line ;otherwise it is cleared ;once being cleared, it needs to jump over the set bit ;instruction ;sets the pin of PRORTA

The implementation of this in subroutine form is shown below:


TogglePin: movlw b'00000010' btfss PORTA,4 addwf PCL,f bcf PORTA,4 incf PCL instruction: bsf PORTA,4 return

;moves 2 into w register to enable a jump to be performed later ;Checks to see whether te pin is set or not ;if not, it needs to be set, jumps to the "bsf PORTA,4" line ;otherwise it is cleared ;once being cleared, it needs to jump over the set bit ;sets the pin of PRORTA

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.

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

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

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

frequency_change_counter

Count_btn 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 cant 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:

;A small counter used to time how long it needs to be before ;the frequency changes ;This is the count value used in the delay of the button

movf status_temp,w movwf STATUS swapf w_temp,f swapf w_temp,w

;retrieve copy of STATUS register ;restore pre-isr STATUS register contents ;restore pre-isr W register contents, done using ;a swap to avoid affecting W ;returns from the interrupt

retfie ; ;********************************************************************************************* ; 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

goto increasetimerstart goto decreasetimerstart

;if bit 0 of alarm state is clear, increase the starting ;time/decrease period/increase frequency ;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 movf timerstart,w xorlw b'00001000' btfsc STATUS,Z bcf Alarmstate,0

;decrement the timerstart value and then ;moves the timerstart into the w-register ;so that it can be compared with its lower bound (5+3)=8 ;see if it was a match (eg the above operation was zero) ;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 ;

bcf STATUS, RP0 ; movlw b'00001000' movwf timerstart

;selects bank ;starts by loading (5+3)=8 into the timerstart register, so that ;when timer0 overflows ;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 ;*********************************************************************************************

Potrebbero piacerti anche