PWM vibrator driving code - version 1.00

(c) 2007 Design-Design Technology - contact crem@desdes.com

; This code is an AtTiny15 replacement for a PIC 12C508 driving an Ann Summers
; vibrator, so don't blame me for the curious external hardware.

; Written in about two hours, using the Zeus AVR assembler. It'll port across
; to AVR studio easily enough, though I guess you'd have to tell that brain-
; dead piece of shit which operand to use for all the loads, add some colons
: to the labels and remove the meaningful binary number formatting.

; Motor controlled by PORTB pin 0, high = ON
; A bi-colour LED + series resistor sits between PORTB pins 1 and 2
; The switch is between GND and PORTB pin 3, with an external pull-up resistor
; pressed=low.

; Spot the ghastly last minute 'better give the user some feedback' hack for
; the switch press duration ;)

; Slight changes to rtf formatting 9/9/2008

AVRTINY15       equ true                ; What we are

CPU_Clk         equ 1600000             ; Set the speed

optVoltage      equ 5.0                 ; 5V0 for now. . .
optProgSpeed    equ 20                  ; Slow

optProgFlashSize equ 1024               ; 1K

DriveFuses      equ true                ; Drive them

fsBOD           equ 0                   ; Low
fsClockSrc      equ %00                 ; Default clock
fsRESET_DISABLE equ false               ; Do not disable the reset line

fsLockByte      equ $00

optCopyCalLD    equ true                ; Copy the calibration fuse
optCopyCalFuse  equ 0                   ; Use fuse zero

Options

ShowIndexF      = false                 ; Don't show indexes

Application Registers

rSwitch         reg r15                 ;
rSwTemp         reg r14                 ;

Default registers (these are really defined in "e", duplicated here for reference)

al              reg r16                 ;
ah              reg r17                 ;
bl              reg r18                 ;
bh              reg r19                 ;
cl              reg r20                 ;
ch              reg r21                 ;

rll             reg r22                 ;
rlh             reg r23                 ;
rhl             reg r24                 ;
rhh             reg r25                 ;

xl              reg r26                 ;
xh              reg r27                 ;

yl              reg r28                 ;
yh              reg r29                 ;

zl              reg r30                 ;
zh              reg r31                 ;

                include "e"             ; CPU equates

Code starts here

irqSpurious     equ *                   ; Just restart if we ever have an exception

Start           wdr                     ; Don't bite us

                cli                     ; Disable interrupts

optCalByteAddr  ld al,#$80              ; Setup the oscillator. Device                 
                out OSCCAL,al           ; programmer inserts actual fuse value 

                ; Enable the watchdog

                ld al,#%000 0 1 111     ; Slow, uncritical watchdog
                out WDTCR,al            ;
                out WDTCR,al            ;

                ; Setup the pins

                rcall Setup_Pins        ; Setup the I/O direction and default states

                ; Setup the switch

                clr rSwitch             ; Clear it

                ; Application code. The switch simply moves up/down through
                ; these functions.
                ; When a 'mode' exits it returns the zero flag indicating a
                ; short/long press on the switch
                ; Speeds are approximately percentages, so 0 = motor off, >99 =
                ; full speed
                ; Times are approximately multiples of 10mS

L1              rcall Mode_0            ; OFF
                beq L2                  ; Next

                rjmp L_Last             ; Prev

                ; Fixed speeds ------

L2              rcall Mode_1            ; Speed = 30%
                bne L1                  ;

L3              rcall Mode_2            ; Speed = 60%
                bne L2                  ;

L4              rcall Mode_3            ; Speed = 100%
                bne L3                  ;

                ; Marker?

                ld cl,#1                ; One marker pulse
                rcall IndexMark         ;
                               _
                ; Pulses -> |_| |

L5              ld rll,#5               ; Min speed
                ld rlh,#25              ; Max speed
                ld rhl,#30              ; Min duration
                ld rhh,#30              ; Max duration

                rcall Mode_Pulse        ; SeeSaw
                bne L4                  ;

L6              ld rll,#5               ; Min speed
                ld rlh,#25              ; Max speed
                ld rhl,#20              ; Min duration
                ld rhh,#20              ; Max duration

                rcall Mode_Pulse        ; SeeSaw
                bne L5                  ;

L7              ld rll,#5               ; Min speed
                ld rlh,#25              ; Max speed
                ld rhl,#10              ; Min duration
                ld rhh,#10              ; Max duration

                rcall Mode_Pulse        ; SeeSaw
                bne L6                  ;

                ; Stronger

L8              ld rll,#5               ; Min speed
                ld rlh,#99              ; Max speed
                ld rhl,#30              ; Min duration
                ld rhh,#30              ; Max duration

                rcall Mode_Pulse        ; SeeSaw
                bne L7                  ;

L9              ld rll,#5               ; Min speed
                ld rlh,#99              ; Max speed
                ld rhl,#20              ; Min duration
                ld rhh,#20              ; Max duration

                rcall Mode_Pulse        ; SeeSaw
                bne L8                  ;

L10             ld rll,#5               ; Min speed
                ld rlh,#99              ; Max speed
                ld rhl,#10              ; Min duration
                ld rhh,#10              ; Max duration

                rcall Mode_Pulse        ; SeeSaw
                bne L9                  ;

                ; Marker

                ld cl,#2                ; Two marker pulses
                rcall IndexMark         ;
                                        
                ; Rising sawtooths -> /|/|/|

L11             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#6               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L10                 ;

L12             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#3               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L11                 ;

L13             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#2               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L12                 ;

L14             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#1               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L13                 ;

                ; Marker

                ld cl,#3                ; Three marker pulses
                rcall IndexMark         ;

                ; Falling sawtooths |\|\|\

L15             ld rll,#99              ; Initial speed
                ld rlh,#5               ; Final speedd
                ld rhl,#-1              ; Ramp dirn
                ld rhh,#6               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L14                 ;

L16             ld rll,#99              ; Initial speed
                ld rlh,#5               ; Final speed
                ld rhl,#-1              ; Ramp dirn
                ld rhh,#3               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L15                 ;

L17             ld rll,#99              ; Initial speed
                ld rlh,#5               ; Final speed
                ld rhl,#-1              ; Ramp dirn
                ld rhh,#2               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L16                 ;

L18             ld rll,#99              ; Initial speed
                ld rlh,#5               ; Final speed
                ld rhl,#-1              ; Ramp dirn
                ld rhh,#1               ; Ramp rate

                rcall Mode_Saw          ; Sawtooth
                bne L17                 ;

                ; Marker

                ld cl,#4                ; Four marker pulses
                rcall IndexMark         ;

                ; See-saw /\/\/\

L19             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#6               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L18                 ;

L20             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#3               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L19                 ;

L21             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#2               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L20                 ;

L22             ld rll,#5               ; Initial speed
                ld rlh,#99              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#1               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L21                 ;

                ; Marker

                ld cl,#5                ; Five marker pulses
                rcall IndexMark         ;

                ; See-saw between limits /\/\/\

L23             ld rll,#15              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#6               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L22                 ;

L24             ld rll,#15              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#3               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L23                 ;

L25             ld rll,#15              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#2               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L24                 ;

L26             ld rll,#15              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#1               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L25                 ;

                ; Marker

                ld cl,#6                ; Six marker pulses
                rcall IndexMark         ;

                ; See-saw between limits /\/\/\

L27             ld rll,#25              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#6               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L26                 ;

L28             ld rll,#25              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#3               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L27                 ;

L29             ld rll,#25              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#2               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L28                 ;

L30             ld rll,#25              ; Initial speed
                ld rlh,#50              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#1               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L29                 ;

                ; Marker

                ld cl,#7                ; Seven marker pulses
                rcall IndexMark         ;

                ; See-saw between limits /\/\/\

L31             ld rll,#25              ; Initial speed
                ld rlh,#75              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#6               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L30                 ;

L32             ld rll,#25              ; Initial speed
                ld rlh,#75              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#3               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L31                 ;

L33             ld rll,#25              ; Initial speed
                ld rlh,#75              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#2               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L32                 ;

L34             ld rll,#25              ; Initial speed
                ld rlh,#75              ; Final speed
                ld rhl,#1               ; Ramp dirn
                ld rhh,#1               ; Ramp rate

                rcall Mode_SeeSaw       ; SeeSaw
                bne L33                 ;

                ; Marker

                ld cl,#8                ; Eight marker pulses
                rcall IndexMark         ;
                                           _   _____   _   _____
                ; Sequence of pulses -> |_| |_|     |_| |_|     |

L35             ld rll,#0               ; 'Space' speed
                ld rlh,#25              ; 'Mark' speed
                ld rhl,#6               ; Min period
                ld rhh,#30              ; Max period

                rcall Mode_Seq1         ; Sequence
                bne L34                 ;

L36             ld rll,#25              ; 'Space' speed
                ld rlh,#50              ; 'Mark' speed
                ld rhl,#6               ; Min period
                ld rhh,#30              ; Max period

                rcall Mode_Seq1         ; Sequence
                bne L35                 ;

L37             ld rll,#25              ; 'Space' speed
                ld rlh,#75              ; 'Mark' speed
                ld rhl,#6               ; Min period
                ld rhh,#30              ; Max period

                rcall Mode_Seq1         ; Sequence
                bne L36                 ;

L38             ld rll,#0               ; 'Space' speed
                ld rlh,#98              ; 'Mark' speed
                ld rhl,#2               ; Min period
                ld rhh,#10              ; Max period

                rcall Mode_Seq1         ; Sequence
                bne L37                 ;

                ; Marker

                ld cl,#9                ; Nine marker pulses
                rcall IndexMark         ;
                                              _    __     ___      ____       ______
                ; Variable speed pulses -> |_| |__|  |___|   |____|    |_____|      |

L39             ld rll,#15              ; Min speed
                ld rlh,#99              ; Max speed
                ld rhl,#7               ; Min pulse time
                ld rhh,#70              ; Ramp rate

                rcall Mode_VariPulse    ; Up the tempo
                bne L38                 ;

L40             ld rll,#15              ; Min speed
                ld rlh,#99              ; Max speed
                ld rhl,#7               ; Min pulse time
                ld rhh,#50              ; Ramp rate

                rcall Mode_VariPulse    ; Up the tempo
                bne L39                 ;

L_Last          ld rll,#15              ; Min speed
                ld rlh,#99              ; Max speed
                ld rhl,#7               ; Min pulse time
                ld rhh,#25              ; Ramp rate

                rcall Mode_VariPulse    ; Up the tempo
                bne L40                 ;

                rjmp L1                 ; Back to the start

Index markers?

        if ShowIndexF
IndexMark       rcall Motor_OFF         ; Off

                rcall LongWait          ;

                rcall Motor_ON          ;

                rcall LongWait          ;

                rcall Motor_OFF         ;

                rcall LongWait          ;

                rcall Motor_OFF         ;

                dec cl                  ; Loop
                bne IndexMark           ;

                ret                     ; Done
        else
IndexMark       ret                     ; Don't bother with index markers
        endif

A longer delay, bit naff.

LongWait        ld al,#0                ;
                rcall Wait_n100uS       ;
                rcall Wait_n100uS       ;
                rcall Wait_n100uS       ;
                rcall Wait_n100uS       ;
                rjmp Wait_n100uS        ;

OFF, but emulate orange LED

Mode_0          rcall Test_Sw           ; Pressed?
                bcs Rts                 ; Yes, skip

                ld al,#50               ;
                rcall Wait_n100uS       ;

                rcall Motor_OFF         ;

                ld al,#50               ;
                rcall Wait_n100uS       ;

                rcall LED_RED           ; Off but red

                rjmp Mode_0             ; Loop

Low speed

Mode_1          rcall Test_Sw           ; Pressed?
                bcs Rts                 ; Yes, skip

                rcall Motor_ON          ;

                ld al,#30               ;
                rcall Wait_n100uS       ;

                rcall Motor_OFF         ;

                ld al,#70               ;
                rcall Wait_n100uS       ;

                rjmp Mode_1             ; Loop

Med speed

Mode_2          rcall Test_Sw           ; Pressed?
                bcs Rts                 ; Yes, skip

                rcall Motor_ON          ;

                ld al,#60               ;
                rcall Wait_n100uS       ;

                rcall Motor_OFF         ;

                ld al,#40               ;
                rcall Wait_n100uS       ;

                rjmp Mode_2             ; Loop

Full speed

Mode_3          rcall Test_Sw           ; Pressed?
                bcs Rts                 ; Yes, skip

                rcall Motor_ON          ;

                ld al,#100              ;
                rcall Wait_n100uS       ;

                rjmp Mode_3             ; Loop

Rts             ret                     ; Done

Sawtooth. rll=Min, rlh=Max rhl=Dirn, rhh=Rate

Mode_Saw        ld bl,rll               ; Sawtooth value

Mode_Saw_Lp     ld bh,rhh               ; Update rate

Mode_Saw_Lp2    rcall Mode_Phase        ; Do it
                bcs Rts                 ; Yes, skip

                ; Update phase

                add bl,rhl              ; Step it

                cp bl,rlh               ; Max?
                beq Mode_Saw            ; Yes, go back to min

                rjmp Mode_Saw_Lp        ; Loop

See-Saw. rll=Min, rlh=Max rhl=Dirn, rhh=Rate

Mode_SeeSaw     ld bl,rll               ; Sawtooth value
                neg rhl                 ;

Mode_SeeSawFlip neg rhl                 ; Flip the direction

Mode_SeeSaw_Lp  ld bh,rhh               ; Update rate

Mode_SeeSaw_Lp2 rcall Mode_Phase        ; Do it
                bcs Rts                 ; Yes, skip

                ; Update phase

                add bl,rhl              ; Step it

                cp bl,rlh               ; Max?
                beq Mode_SeeSawFlip     ; Yes, flip
                cp bl,rll               ; Min?
                beq Mode_SeeSawFlip     ; Yes, flip

                rjmp Mode_SeeSaw_Lp     ; Loop

Pulse. rll for rhl, then rlh for rhh

Mode_Pulse      ld bl,rll               ; First
                ld bh,rhl               ; Time

                rcall Mode_Phase        ; Do it
                bcs Rts                 ; Yes, skip

                ; Other mode

                ld bl,rlh               ; Second
                ld bh,rhh               ; Time

                rcall Mode_Phase        ; Do it
                bcs Rts                 ; Yes, skip

                ; Other mode

                rjmp Mode_Pulse         ; Loop

VariPulse. rll for rhl, then rlh for rhl, then change rhl

Mode_VariPulse  ld cl,rhh               ; Max

Mode_VariPulseL ld bl,rll               ; First
                ld bh,cl                ; Time

                rcall Mode_Phase        ; Do it
                bcs Rts                 ; Yes, skip

                ; Other mode

                ld bl,rlh               ; Second
                ld bh,cl                ; Time

                rcall Mode_Phase        ; Do it
                bcs Rts                 ; Yes, skip

                ; Change the time

                dec cl                  ;
                cp cl,rhl               ; Min?
                beq Mode_VariPulse      ; Yes, reset

                ; Other mode

                rjmp Mode_VariPulseL    ; Loop

Do a phase. BL speed for BH time

Mode_Phase      rcall Test_Sw           ; Pressed?
                bcs MP_X                ; Yes, skip

                cp bl,#0                ; Never ON
                beq MP_1                ;

                rcall Motor_ON          ;

MP_1            ld al,bl                ;
                rcall Wait_n100uS       ;

                cp bl,#99               ; Never OFF?
                bcc MP_2                ;

                rcall Motor_OFF         ;

MP_2            ld al,#101              ;
                sub al,bl               ;
                rcall Wait_n100uS       ;

                ; Timer

                dec bh                  ;
                bne Mode_Phase          ;

MP_X            ret                     ; Done

A sequence. rll=min, rlh=max, rhl=short, rhh=long

Mode_Seq1       ld bl,rll               ; Off
                ld bh,rhl               ; [short period]

                rcall Mode_Phase        ; Do it
                bcs MS_X                ; Yes, skip

                ld bl,rlh               ; ON
                ld bh,rhl               ; [short]

                rcall Mode_Phase        ; Do it
                bcs MS_X                ; Yes, skip

                ld bl,rll               ; Off
                ld bh,rhl               ; [short]

                rcall Mode_Phase        ; Do it
                bcs MS_X                ; Yes, skip

                ld bl,rlh               ; On
                ld bh,rhh               ; [long]

                rcall Mode_Phase        ; Do it
                bcs MS_X                ; Yes, skip

                rjmp Mode_Seq1          ;

MS_X            ret                     ; Done

Wait a number of 100uS

Wait_n100uS     ld ah,#52               ; Set using 'scope

Wait_Lp         dec ah                  ; mS Loop
                bne Wait_Lp             ;

                dec al                  ; Loop
                bne Wait_n100uS         ;

                ret                     ; Done

Test the switch. If pressed returns CF=1, Z=1 for short press, Z=0 for long.
This routine also drives the LED GREEN/RED to indicate press duration.

Test_Sw         wdr                     ; Don't bite us

                clc                     ; Assume not pressed

                ld rSwTemp,al           ; Save it

                sbis PINB,#3            ; Skip if not pressed
                rjmp TS_Pressed         ;

                ; Not pressed, but was it pressed? and how long for?

                ld al,rSwitch           ; Get it

                cp al,#70               ; Long?
                bcc TS_Long             ; Yes, skip

                cp al,#5                ; Short?
                bcc TS_Short            ; Yes, skip

                ld al,rSwTemp           ; Restore

                ; Nope, not long enough to debounce...

                clr rSwitch             ; Clear it
                clc                     ; Nothing
                ret                     ; Done

        ; Short press

TS_Short        clr rSwitch             ;
                ld al,rSwTemp           ;

                sec                     ; C + Z
                sez                     ;
                ret                     ;

        ; Long press

TS_Long         clr rSwitch             ;
                ld al,rSwTemp           ;

                sec                     ; C + NZ
                clz                     ;
                ret                     ;

        ; Pressed, inc the count upto a max of 255

TS_Pressed      inc rSwitch             ; Inc the count
                bne TSP_NC              ; Not wrapped

                dec rSwitch             ; Clip to 255

TSP_NC          ld al,rSwitch           ; Get it

                cp al,#70               ; Long?
                bcc TSS_Long            ; Yes, skip

                cp al,#10               ; Short?
                bcc TSS_Short           ; Yes, skip

                ; Clear the LED

                cbi PORTB,#1            ; LED OFF
                cbi PORTB,#2            ; LED OFF

TS_X            ld al,rSwTemp           ;
                clc                     ;
                ret                     ;

        ; Show GREEN

TSS_Short       sbi PORTB,#1            ;
                cbi PORTB,#2            ;
                rjmp TS_X               ;

        ; Show RED

TSS_Long        cbi PORTB,#1            ;
                sbi PORTB,#2            ;
                rjmp TS_X               ;

Motor ON. This drives the LED RED if the switch is NOT pressed.

Motor_ON        sbi PORTB,#0            ; Enable the driver

LED_RED         sbis PINB,#3            ; Skip if not pressed
                rjmp MO_X               ;

                ; Show the motor state

                cbi PORTB,#1            ; LED := RED
                sbi PORTB,#2            ;

MO_X            ret                     ;

Motor OFF. This drives the LED GREEN if the switch is NOT pressed.

Motor_OFF       cbi PORTB,#0            ; Disable the driver

LED_GREEN       sbis PINB,#3            ; Skip if not pressed
                rjmp MO_X               ;

                sbi PORTB,#1            ; LED := GREEN
                cbi PORTB,#2            ;

                ret                     ;

Setup the pins

Setup_Pins      ld al,#%111 00 0 0 0    ; All external pins start out low, except /reset
                out PORTB,al            ;

                ld al,#%000 00 1 1 1    ; Set I/O
                out DDRB,al             ;

                ret                     ; Done