Project 8: Output devices. Make
sound with the theremin using the PWM on the microcontroller.
I used a similar set up as for Project 6 (where I used the python sound
library sndobj to create sound) only this time I used the built-in
components of the microcontroller and a speaker to produce a
sawtooth waveform whose frequency (i.e. pitch) is dependent on
the capacitance it senses.
I had to redesign a board to incorporate the PWM elements.
I first wrote the firmware and made sure I could do everything with
just one microcontroller. I was able to do this by just switching pins from the
hello8 pwm code. The code, shown at the end, was difficult to write
because trying to improve the range of pitch coming out of the
microcontroller necessitated learning how to do arithmetic in binary.
I first plugged in my original theremin to see what kind of values it
was reading from the ADC. I then figured out what the best range was
(400-600 out of 1023) and used this part to determine the
frequency of the waveform.
Here is the board layout and what the final setup looked like:
And here it is in action:
... Theremin Movie ...
Here is the assembly code I used:
; suelin.asm 11.14.06
; based on Neil Gershenfeld CBA MIT 10/29/05
.equ chargepin = PB1 ; charging pin
.def temp = R16 ; temporary storage
.def temp1 = R17; temporary storage
.def delaycount = R18 ; sample update delay counter
.def delaycnt = R19 ; delay counter
.def pwm = R20 ; PWM value
.def uphi = R21 ; high part of ADC (ADCH)
; start of code
;puts program at beginning of memory
rjmp reset ; jump to reset routine
; took out all the putchar stuff
; routine to wait for sample to settle
.equ delay = 255
ldi temp, delay
; pwmdelay -- this sets frequency of sawtooth wave
mov temp, delaycount
; main program
ldi temp, low(RAMEND) ; set stack pointer to top of RAM
out SPL, temp ;
; init output pins
;sbi PORTB, txpin ; comm
;sbi DDRB, txpin ; "
sbi PORTB, chargepin ; charging
sbi DDRB, chargepin ; "
; init A/D
cbi ADMUX, REFS0 ; use Vcc as reference
sbi ADMUX, ADLAR ; left-adjust result (to drop the low bits)
sbi ADCSRA, ADEN ; enable A/D
cbi ADCSRA, ADATE ; disable auto-trigger
cbi ADCSRA, ADPS2 ; set prescaler for /2
cbi ADCSRA, ADPS1 ; "
cbi ADCSRA, ADPS0 ; "
cbi ADMUX, MUX1 ; input on ADC1
sbi ADMUX, MUX0 ; "
; initialization for PWM
ldi temp, low(RAMEND)
out SPL, temp ; set stack pointer to top of RAM
ldi temp, (1<