SX Microcontroller IO Routine

Pulse Width Modulation

; 8 channel PWM test program.
;
; Board: SX Demo/Proto Board
;
; 8 LEDs are fed with PWM signal. Each channel starts from a different value
; (0, 32, 64, 96, 128, 160, 192, 224) and goes slowly up and down.
;
; PWM code is based on Scott Dattalo's code.

        DEVICE  SX28L, OSC4MHZ, TURBO, STACKX, OPTIONX
        RESET start


        ORG $08 ;global bank

temp    DS 1    ;short term temp
dir     DS 1    ;direction of PWM inputs change (inverted on reaching
                ;maximum (255) or minimum (1)). each bit is related to a PWM channel
pwm_state DS 1  ;pwm outputs state (0 - off, 1 - on)
pwm_period DS 1 ;pwm period counter

        ORG $10 ;bank0
pwm_bank EQU $
pwm0    DS 1    ;PWM counters
pwm1    DS 1
pwm2    DS 1
pwm3    DS 1
pwm4    DS 1
pwm5    DS 1
pwm6    DS 1
pwm7    DS 1

        ORG $30 ;bank1
        ORG $50 ;bank2
        ORG $70 ;bank3
        ORG $90 ;bank4
        ORG $B0 ;bank5
        ORG $E0 ;bank6
        ORG $F0 ;bank7

RA_DIR  EQU %00000000
RB_DIR  EQU %00000000
;RB.0 .. RB.7 - LED outputs
RC_DIR  EQU %00000000

        ORG $000
start
;init ports
        clr RA
        clr RB
        clr RC
        mov w, #RA_DIR
        mov !RA, w
        mov w, #RB_DIR
        mov !RB, w
        mov w, #RC_DIR
        mov !RC, w
;init vars
        call init_pwms
;****************************************************************************
;pwm loop:
;****************************************************************************
        bank pwm_bank
pwm_loop
        clr w               ;clear w, and then set bits of those
                            ;channels that don't need to be reset

        decsz pwm0          ;don't set bit on reaching the end of a pwm pulse
         or w, #$01
        decsz pwm1
         or w, #$02
        decsz pwm2
         or w, #$04
        decsz pwm3
         or w, #$08
        decsz pwm4
         or w, #$10
        decsz pwm5
         or w, #$20
        decsz pwm6
         or w, #$40
        decsz pwm7
         or w, #$80

        and w, pwm_state    ;clear those bits that are zero already

        dec pwm_period      ;decrement master pwm counter
        snz                 ;on reaching 0, set all outputs
         xor w, #$FF
         
        mov pwm_state, w    ;update state
;        and w, #$7F        ;mask beeper on RB.7
        mov RB, w           ;update outputs

;on zero pwm_period counter update pwm counters
;
;Note: for a completely stable step size, a delay
; equal to update_pwms routine should be added
; on non-zero pwm_period. Can also be done using RTC.

        test pwm_period
        snz
         call update_pwms
        jmp pwm_loop

;****************************************************************************
;Load PWM counters and inputs with 0, 32, 64, 96, 128, 160, 192, 224
init_pwms
        mov w, #pwm_bank    ;use FSR as pointer
        mov FSR, w
        clr temp            ;temp holds value and also is used as a counter
        inc temp
init_pwms_loop
        mov w, temp         ;load value
        mov IND, w          ;save to pwm counter

        inc FSR             ;next pwm channel
        mov w, #32          ;next value
        add temp, w     

        sc                  ;loop until overflow
         jmp init_pwms_loop

        clr pwm_state       ;initially all outputs are OFF
        clr pwm_period      ;reset pwm counter
        clr dir             ;init direction (all up)
        retp
;****************************************************************************
;To each pwm channel counter:
;1) if direction=0, add 1. On reaching 255, change direction
;2) if direction=1, subtract 1. On reaching 1, change direction

update_pwms
        mov w, #pwm_bank    ;use FSR as pointer
        mov FSR, w
        mov w, #$80         ;temp holds direction value and also is used as a counter
        mov temp, w
update_pwms_loop
        ;update pwm input according to direction
        mov w, #$01         ;w = dir == 0? 1 : 255
        snb dir.0           
         mov w, #$FF
        add IND, w          
        
        ;check limits
        add w, IND          ;zero if the limit is reached
        mov w, #$01         ;invert the direction bit on borrow
        snz
         xor dir, w     

        ;select next channel
        inc FSR

        rr dir              ;shift new dir bit in temp
        rr temp             ;and temp to carry (carry is set after 8 shifts)
        sc                  ;loop until overflow
         jmp update_pwms_loop
        
        ;update direction
        mov w, temp
        mov dir, w
        retp
;****************************************************************************
       
        ORG $200
        ORG $400
        ORG $600