Mixer

The code below running on a 4MHz 16F628 will output PWM from 244Hz to around 6700Hz. This frequency is set by a 47k linear pot. The Duty Cycle is settable with a second 47k linear pot. As the frequency rises, the frequency and duty cycle resolution becomes coarser. The 6700Hz limit is caused primarily by the time needed for the ADC conversion. The 244Hz limit is the PIC speed.
The higher the PWM frequency, the less of the Duty Cycle pot is needed to set a DC between 0% and 100%
Improvements -
Use post-scaler to generate fewer IRQs and therefore fewer ADC readings
Stagger ADC readings to minimise call time to ADC routine (ie read one pot per IRQ, particularly when 10 pots/switches need to be read)
Scale duty cycle pot's reading proportional to frequency so as to use full range of pot - possible use of tables
Reduce PIC speed to lower bottom end frequency - possible use of RC or ExtR with manually variable resistor or digital selection by PIC itself
;tlc549.asm - read pots and switches via 4051
; 10/02/02 15/04/02
;
; mixer.gif
;
list p=16F628
#include
;Sample sequence ; CS low (previous MSB at DATA pin)
; 4 falling edges at I/O to shift out
; bits 2,3,4,5 of previous conversion
; Sample taken
; 3 more falling edges to shift out
; 6,7 & LSB of previous conversion
; 8th falling I/O holds the sample
; CS must go high or I/O must go low
; for the chip's 36 internal cycles
; Conversion time is 17uS max.
;READ subroutine gets last sample data and performs
;new sample/hold
;Ref+ = 5.12V (Ref- = 0) is 2mV/LSB [Vcc max = 6V]
;Analogue in > Ref+ = 11111111
;Analogue in < Ref- = 00000000
;Read pot postions and modify PWM output
;
;porta.0 4051 A o/p
; 1 B o/p
; 2 C o/p
; 3 PWM o/p
;portb.0 start button i/p
; 1 o/p
; 2 o/p
; 3 o/p
; 4 TLC549 Clock o/p
; 5 Data i/p
; 6 CS o/p
; 7 LED o/p
trigger equ 0x00 ;start
pout1 equ 0x02
pwm equ 0x03 ;PWM
clk equ 0x04 ;ADC Clock
dta equ 0x05 ;ADC data
cs equ 0x06 ;ADC Chip Select
led equ 0x07 ;test point
start equ 0x00 ;program start vector
ram equ 0x20 ;start of RAM in bank0
dbuff equ ram+0x00 ;buffer for serial data
dbit equ 0x00 ;serial bit value
temp equ ram+0x01
endlp equ 0x03 ;"end loop" test bit
delay equ ram+0x02 ;delay counter
;RAM locations from 0x03 to 0x08 used for pot readings
adc1 equ ram+0x03 ;set PWM duty cycle
adc2 equ ram+0x04 ;set PWM frequency
adc3 equ ram+0x05
adc4 equ ram+0x06
adc5 equ ram+0x07
adc6 equ ram+0x08
;RAM locations from 0x09 to 0x0e used for counters
on1 equ ram+0x09
off1 equ ram+0x0a
wtemp equ ram+0x10 ;temporary IRQ store for W and STATUS
stemp equ ram+0x11
tcnt equ ram+0x12 ;count TMR0 IRQs
change equ ram+0x13
ch equ 0x00
ashad equ ram+0x14 ;porta shadow register
__config _intrc_osc_noclkout & _wdt_off & _pwrte_on & _lvp_off & _boden_off
org 0x00
goto entry
org 0x04
goto irq
entry clrf porta
movlw 0x07 ;comparators off
movwf cmcon
clrf status ;set rp0, rp1 = 0 = Bank0
bsf status,rp0 ;tris registers in bank1
bcf status,rp1
movlw b'00000000' ;oooo oooo
movwf trisa
movlw b'00100001' ;ooio oooo all o/p except Serial Data and Trigger
movwf trisb
movlw 0x86 ;pull-ups off, timer pre-scaler= 128 = 30Hz IRQ
movwf option_reg
bcf status,rp0
bcf status,rp1
clrf porta ;4051 A = B = C = 0
clrf ashad ;clear shadow
movlw b'11010000' ;TLC549 Clock high, CS high
movwf portb
movlw 0x10
movwf change
clrf fsr
clrf ccp1con ;CCP Module off, set up PWM
clrf tmr2
;initial PWM values until first IRQ - low speed, low duty cycle
movlw b'11111111' ;TMR2/PR2 match (frequency) 244-6700Hz
movwf pr2
;Tpwm = (PR2+1) x 4 x Tosc x Prescaler
;Tpwm = 256 x 4 x .25us x 16
;Tpwm = 4096us
;f = 1000000us/4096us = 244Hz
movlw b'00000000' ;duty cycle MSBs
movwf ccpr1l
clrf intcon ;clear control registers
bsf status,rp0
clrf pie1
bcf status,rp0
clrf pir1
movlw b'00011100' ;duty cycle LSBs <5:4>, xxxx 11xx sets PWM mode
movwf ccp1con
bcf t2con,t2ckps0 ;pre-scaler, sets PWM frequency
bsf t2con,t2ckps1
bcf t2con,toutps0 ;post-scaler, sets IRQ frequency
bcf t2con,toutps1
bcf t2con,toutps2
bcf t2con,toutps3
bsf t2con,tmr2on ;Timer2 enabled
bsf intcon,peie ;Peripherals enabled
bsf status,rp0
bsf pie1,tmr2ie ;Timer2 IRQ enabled
bcf status,rp0
bcf pir1,tmr2if ;clear Timer2 IRQ flag
bsf intcon,gie ;enable interrupts
loop goto loop
;=============================
; ISR
;=============================
irq bcf pir1,tmr2if
bsf portb,led
nop
bcf portb,led
movwf wtemp
swapf status,w
movwf stemp
call readadc ;read just first two pots for now
movf adc1,w ;new duty cycle MSB value
movwf ccpr1l
movf adc2,w ;new PR2 (ie frequency) value
bsf status,rp0
movwf pr2
bcf status,rp0
swapf stemp,w
movwf status
swapf wtemp,f
swapf wtemp,w
retfie
;=============================
; Read ADC
;=============================
readadc clrf porta ;reset 4051
clrf ashad ;and shadow
movlw 0x23 ;RAM address for pot readings
movwf fsr
read call adcread ;read last conversion and initiate current
call adcread ;read current conversion
movf dbuff,w
movwf indf ;store in RAM+0x03 to RAM+0x08 (adc1 to adc6)
incf ashad,f ;select new pot
movf ashad,w
movwf porta
incf fsr,f ;destination RAM address
movlw 0x25 ;test for > 2 ( set number of pots here )
xorwf fsr,w
btfss status,z
goto read
return ;back to ISR call
adcread bcf portb,cs ;CS low, puts previous MSB on Data line
clrf dbuff ;wait for TLC549's noise filter
nop
nop
nop
nop
bsf dbuff,dbit ;assume previous MSB bit = 1
btfss portb,dta
bcf dbuff,dbit ;or clear if = 0
movlw 0x01
movwf temp ;bit counter
prev bcf status,c
rlf dbuff,f ;move bits through buffer
bcf portb,clk ;get next 7 bits on falling clock edge
bsf dbuff,dbit ;assume bit = 1
btfss portb,dta
bcf dbuff,dbit ;or clear if = 0
bsf portb,clk ;clock back high
incf temp,f
btfss temp,endlp ;if count limit reached
goto prev ;no
bcf portb,clk ;8th clock, start hold, conversion
nop
bsf portb,clk
hold bsf portb,cs ;send CS high
movlw 0xc0 ;short delay during conversion
movwf delay
shdel incfsz delay,f
goto shdel
return ;back to READ call
end