Friday 20 July 2012

Timers and interrupts

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!