Yoikes, over a year since I last looked into this project... Time passes so quickly, I blame real life sadly along with a few computer games too including Star Wars amongst others... So anyway someone at work was talking about programming at home and as a result it got me interested again in the microprocessor project...
When I last left you I had been trying to get Timer interrupts to work and after a few hours of trying to copy samples and not actually getting anywhere I decided to have another look at it. And this time YAY I managed to get a timer to cause an interrupt. So I'll go through code and see if I can explain it all as I go...
First the plan is to have a main program just flashing the status LED, while we have a timer interrupt which increases a counter and then display the last four bits of that counter as a hex number on the LED display.
Originally I tried to get TIMER0 to work, however for some reason, (I think it was because I needed an external clock or something), timer0 didn't seem to be workable and so I changed to using the Timer1 counter to count clock cycles and increment the LED display each time the counter ticks over. Of course it helped when I finally realised that I needed to turn on the interrupt processing as well as turning on the Timer modules.
;******************************************************************************
; Filename: Timer_Interrupt.asm *
; Date: 20th July 2012 *
; Author: M.J.Andrews *
;******************************************************************************
LIST P=18F2455, F=INHX32 ; Directive to define processor
#include <P18F2455.INC> ; Processor specific variable definitions
#include <LED_Segment.INC> ; LED Segment Macro's
We'll still use the same old LED segment setting include file and the default P18F2455 include file to define all the constants we're going to want to be using.
;******************************************************************************
; Configuration bits
CONFIG FOSC = INTOSC_HS ;HS - HS OSCILLATOR
Still using the internal oscillator
;******************************************************************************
; Variable definitions
UDATA
WREG_TEMP RES 1 ; Variable in RAM for context saving
STATUS_TEMP RES 1 ; Variable in RAM for context saving
BSR_TEMP RES 1 ; Variable in RAM for context saving
UDATA_ACS
CNT_INNER RES 1 ; Delay procedure, Inner Loop Count
CNT_OUTER RES 1 ; Delay procedure, Outer Loop Count
CNT_TIMER RES 1 ; Timer Interrupt counter
We need variables to store the old values in the various registers while the interrupt service routines run. We also keep the inner and outer counters for our delay procedure from before and also a new counter which we will increment on each timer interrupt.
;******************************************************************************
; Reset vector's
RESET_VECTOR CODE 0x0000
goto Main ; Go to start of main code
HI_INT_VECTOR CODE 0x0008
bra HighInt ; Go to high priority interrupt routine
LOW_INT_VECTOR CODE 0x0018
bra LowInt ; Go to low priority interrupt routine
Standard code to provide the appropriate commands to point to the appropriate code points when starting u or receiving any high or low priority interrupt.
;******************************************************************************
;**** Start of the Program Code Section ****
;******************************************************************************
CODE
;******************************************************************************
; High priority interrupt routine
;******************************************************************************
HighInt:
MOVWF WREG_TEMP ; Save working register
MOVFF STATUS,STATUS_TEMP ; Save STATUS register
MOVFF BSR,BSR_TEMP ; Save BSR register
MOVFF CNT_TIMER, WREG ; Copy the current counter to W-REG
CALL DisplaySegments ; Display bottom 4 bits of the counter
INCF CNT_TIMER, F ; Increment the timer interrupt counter
BCF PIR1, TMR1IF ; Clear interrupt flag so ints continue
MOVFF BSR_TEMP,BSR ; Restore BSR register
MOVFF WREG_TEMP,WREG ; Restore working register
MOVFF STATUS_TEMP,STATUS ; Restore STATUS register
RETFIE FAST
Start the main code section with the High-priority interrupt routine. This starts by saving a number of registers which could contain data at the time of the interrupt. We then update the display segment with the number currently in the counter which we then increment. Clearing the interrupt flag lets the program continue and we then return the registers to their previous state, (hmmmm should the TMR1IF clear happen after returning the interrupts). Finally we return from the interrupt.
;******************************************************************************
; Low priority interrupt routine
;******************************************************************************
LowInt:
RETFIE
We skip the low priority interrupt as we;re not using it
;******************************************************************************
;**** M A I N C O D E ****
;******************************************************************************
Main:
Initialize
BCF TRISA, TRISA0 ; RA0 as an output for the "working" led
BCF TRISB, TRISB0 ; RB0 as an output for the 7-segment LED
BCF TRISB, TRISB1 ; RB1 as an output for the 7-segment LED
CLRF WREG ; Clear the Working Register
MOVWF TRISC ; W-REG to PORTC to set as all outputs
We initialise the registers so we can output the LED segments and the status led.
BSF OSCCON, IRCF2 ; Set the internal Fosc value to "111"
BSF OSCCON, IRCF1 ; which I believe results in an 8Mhz
BSF OSCCON, IRCF0 ; clock speed according to the specs
I noticed that there were three bits to set the internal oscillator speed which I set to "111" which may mean an 8Mhz clock speed, (although I need to check that).
RTCinit
; Default the start values for the counters
CLRF CNT_TIMER ; Initialize interrupt counter
CLRF TMR1H ; Start with a zero high bit (no update)
CLRF TMR1L ; Start with a zero low bit (& updates TMR0H)
; Set the base Interrupt registers to turn interrupts on
BSF RCON, IPEN ; Set IPEN bit to enable priority interrupts
BSF INTCON, GIEH ; Set GIEH bit to enable high prio ints
BCF INTCON, GIEL ; Clear GIEL bit to disable low prio ints
; Configure the TIMER1 settings
MOVLW b'00110001' ; Set T1CON settings
; * RD16 = 0 - 8-bit TIMER0 count
; * T1RUN = 0 - Clocked from ext source
; * T1CKPS1:T1CKPS0 = 11 - 1:8 prescaling
; * T10SCEN = 0 - Timer1 Oscillator off
; * T1SYNC = X - N/A with TMR1CS at 0
; * TMR1CS = 0 - Internal Clock (Fosc/4)
; * TMR1ON = 1 - Turn Timer1 on!
MOVWF T1CON ; Set the T1CON from W-Reg set last command
BSF PIR1, TMR1IF ; Timer1 Flag Bit
BSF IPR1, TMR1IP
BSF PIE1, TMR1IE ; Timer1 Enable Bit
SET_SEGMENT_TO_0
See the comments in the code above, but basically this initialises the timer and interrupt processing so that it all works... Finally strart the LED segment with a display of "0".
Start_Loop
TOGGLE_STATUS_LED
call Delay ; Call the delay procedure so we see value
goto Start_Loop ; Go back to the start of the loop
This is the main loop, it toggles the LED and then runs the delay procedure I used previously, (I copied this into the code file but won't re-show it here). After delaying this then goes back up to toggle the led again.
After all this playing I have the status LED flashing away while the LED segment counts up from 0 to F repeatedly over a constant time period showing that the interrupt is working correctly. It took a while but I'm glad I'm at this point. I now want to try and get the USB interface to work... Hopefully sometime in the next year this time though!
No comments:
Post a Comment