0
votes

I have been working this on this one for a while (2 months), salvaging some old code from a pong clone I made in PIC assembly years ago. I have all the math worked out, and I imagine the program in a pseudocode like format would look like this:

Using timer2, set postscaler bits to 1001, prescaler bits to 11. Initiate countdown at 12,500. Start timer. Whenever timer register overflows, decrement counter 1, and reset timer. When countdown reaches zero, flip bit corresponding to light off or on (if 1, change to 0, if 0 change to 1). Reset timer and countdown.

Relevant Information: LED is wired to RB5.
Don't completely know how to implement a countdown variable (or any variable to begin with in assembly), even with referrence to pong clone, which I only understood part of. Need to create 2 programs, one using interrupts and one not using interrupts.

Here is the actual code of what I have so far(which won't work, its incomplete), its not much (much of it was actually already done, only the part in the application labeled area was done by me), and I don't even know if it is right:

;; -- mode: asm; mode: outline-minor-mode; outline-regexp: ";;;+" --

;;; skeleton.asm list p=16f1938 include "p16f1938.inc"

SETBSR macro target movlb (target >> 7) ; gpasm assember's BANKSEL doesn't work for exhanced 16F parts. ;; BANKSEL target ; Use this instruction for MPASM endm

;;; PIC16F1938 ;; fcs 22 July 2006

    ;; Here is a dummy program to illustrate the how to write an application
    ;; that can be used with the Pikme bootloader or with a hardware
    ;; programmer.

    ;; The bootloader ignores the first 32-word block of memory.

    ;; Put the application's interrupt code at 0x0020 (either put a jump
    ;; to your actual interrupt handler code there or put a retfie
    ;; instruction there.

    ;; Put the application's start code at 0x0024.

    ;; The interrupt vector is effectively 0x0020 (because the bootloader's
    ;; interrupt vector jumps to 0x0020) and the program must begin at
    ;; 0x0024.

    ;; So, ORG your interrupt code at 0x0020 and put interrupt code in
    ;; the first 4 instructions (e.g. rtfie, nop, nop, nop) and then start
    ;; the application at 0x0024.  (The 3 'nop's are not actually necessary
    ;; as long is the 'org 0x0024' statement is present.)

    ;; uncomment and possibly edit following line if you include
    ;; any macros, etc.
;include "macros.inc"

    expand        
    ;noexpand

radix   dec

;========================================================================== ; __CONFIG 0x3F10

__CONFIG _CONFIG1, _FCMEN_OFF & _IESO_OFF & _CLKOUTEN_ON & _BOREN_OFF & _CP_OFF & _CPD_OFF & _MCLRE_OFF & _PWRTE_ON & _WDTE_OFF & _FOSC_INTOSC
__CONFIG _CONFIG2, _LVP_OFF & _BORV_19 & _STVREN_ON & _PLLEN_OFF & _VCAPEN_OFF & _WRT_OFF

    ;; Above configuration word will be ignored when using the bootloader but
    ;; will be programmed into the PIC when you use a hardware programmer.

;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; First 32-word block. ;;; This is ignored by the bootloader but is not ignored when ;;; using a hardware programmer. ;;; By putting this code here, the very same hex file can be used either ;;; with the bootloader or with a hardware programmer. ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0000 goto UserApp

org 0x0004

ISR goto UserISR

;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; First block after protected memory. ;;; This is where the application's interrupt and application code ;;; go. ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0200 ;; Your interrupt handler must be org'd at 0x0020. ;; Use a return from interrupt instruction (retfie) or put ;; a jump to your actual interrupt handler (goto xxxx). UserISR retfie nop nop nop

    org 0x0204
    ;; Your application *must* be org'd at 0x0204, because that is
    ;; the address where the bootloader jumps.

UserApp ;; put your application code here. This code sets the internal oscillator to 32 Mhz.

movlb   (OSCCON >> 7)   ; Use this statement if your assembler does not support Enhanced BANKSEL

;; BANKSEL TRISB ; Use this statement if your assembler supports it.

movlw   0xF0        ; 8 Mhz, internal OSC, PLL on
movwf   OSCCON

hsploop: btfss OSCSTAT,HFIOFR bra hsploop

pllloop: btfss OSCSTAT,PLLR bra pllloop

;; Reset the BSR to Bank 0
SETBSR  PORTA

;; Main: This is where your applications goes. Make sure it is an infinite loop! main: bra main; relative branch main BCF TMR2; Clears register for timer 2.

SETBSR PORTB; Bank 0 movlw 0x01; move 0x01 to W movwf PORTB; move w to PORTB BCF PORTB,5; bit clear PORTB end

I guess my main issue is simply translating my outline into assembly code, particularly getting the countdown to work and setting up timer2. The part I have written in main is to turn the light on and off (I think). I also believe that if correct, the code I have so far can be used for both the interrupt using version as well as the non-interrupt version. However, at the point I am now, I am kind of lost code wise.

1
At the begining make your program without bootloader and test it in MPLAB SIM. After that you can test it on real MCPU.GJ.

1 Answers

2
votes

It's been a while since I've written assembly for a PIC16, so I apologize for any omissions.

I would recommend against using interrupts initially. You only need them if you need precise timing and your main code runs slow enough to throw off the timing.

First, you'll need to setup Timer2. Do this by writing to T2CON

movlw   0b01001111        ; set postscaler bits to 1001, prescaler bits to 11
movwf   T2CON

Then you'll need to make a variable somewhere called CountDownTimer and populate it with your starting value.

movlw CountDownStartValue
movwf CountDownTimer, 1

Do you want the overflow at the full 256 ticks? You can make it shorter by writting to PR2

Then you'll need to check if the interrupt flag has fired on overflow. Note: just because we are using the interrupt flag does not mean we have to use the interrupt service routine. Keep TMR2IE set to zero and then check the routine yourself. This tends to be easier to debug. Once you get it working, you can refactor it into an interrupt service routine if you anticipate the need for performance.

btfsc   PIR1, TMR2IF
    call TimerOverflow

Make sure to clear the flag. Then decrement your counter. Once it hits zero, you want to toggle the output. I recommend writing to LAT instead of PORT for these reasons: Difference between PORT and LATCH on PIC 18F

TimerOverflow:
    bcf  PIR1, TMR2IF              ; Clear the flag or else we'll continue to pop back in.
    decfsz  CountDownTimer, 1      ; Countdown variable. If not zero, then return
        return

    ;We skipped over the return, the countdown must have reached zero
    movlw CountDownStartValue
    movwf CountDownTimer, 1

    BTFSS LATB, 5     ; Is the LED already on or off?
       goto TurnOn
       goto TurnOff

 TurnOn:
    BSF   LATB, 5
    return            ; This returns from TimerOverflow call

 TurnOn:
    BCF   LATB, 5
    return            ; This returns from TimerOverflow call

Well, this is basically what I remember from memory. If you find bugs, let me know so I can edit this response.