User:Frosti/Sensors, Actuators, and Displays

From Fab Lab Wiki - by NMÍ Kvikan
Revision as of 22:44, 1 June 2010 by Frosti (talk | contribs) (code of third version)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Goal

The goal is to make theremin with our Fab Lab equipment and inventory.

But the still the project is not fully ready. We use variable resistor to change the speed of music.

<video type="youtube">PTvFEJ0FUio</video>

Steps

Desired function of the circuit board

First I have to decide the function of the board.

  • The function should be:
    • It should be musical instrument, in the way that the board develops sounds on the speaker when I move copper foils towards one another.


The schematic and board layout

I tried to use Inkscape to sketch what I wanted it to do. Then I decided to try to start to draw the board in Eagle.

  • I downloaded and installed the Eagle Free version from Cad Soft USA
  • I downloaded and copied Neils library from http://fab.cba.mit.edu/about/fab/dist/ng.lbr to the folder where other libraries are.
  • First I drew hello.speaker.45.MTA.
  • Before starting to draw, remember to change the grid to 0.025 inches. That will help a lot later on.
  • We have two views
    • Schematic
    • Board
  • I added some new components
    • Add
      • Resistor 1 Mega Ohm
      • MTA connector 3 pin


Schematic layout

Making the Board layout

Board layout

At first I had some troubles making the routes.

  • Then I used Route button and connected from one component to another.
  • To change location of the component label, we can go to Board layout and right click on the components and select Smash, then we are able to move the text label.
  • When the routes are ready you can go to View -> Display/hide layers and select what layers you want to see.
  • Turn off all layers except the routes, go to File-> Export -> Browse, .png, 500 dpi.
  • Now turn off all layers except the frame, and go to File-> Export -> Browse, .png, 500 dpi.

Physical Fabrication

We make the board on Modela.

  • Mistakes that we made originally, we changed X min and Y min like we should do, but we also changed X width and Height. But we shouldn't have to change X width and Height.
  • IC1 = ATtiny45_SOIC('IC1\nt45')
  • IC2 = regulator_SOT23('IC2\n5V')
  • R1 = R_1206('R1\n10k')
  • C1 = C_1206('C1\n3.3uF')
  • J1 = MTA_power('J1')
  • J2 = MTA_ICP('J2') BATTERY
  • J3 = MTA_2('J3')
  • T1 = NMOSFET_SOT23('T1: N')
  • Speaker and connector
  • Battery (power), cable, and connector
  • 10 k Ohm variable resistor
Stuffed speaker sensor board


Programming the board

  • Then I have to make program for the board.
  • I downloaded [AVR Data sheet for Attiny45] from Atmel

I take a look at codes from Fab Hello World page specially at hello.speaker.45.pwm.asm and hello.step.45.asm


Some AVR Instructions

  • SBI -> Set Bit in I/O Register
  • CBI -> Clear Bit in I/O Register
  • LDI -> Load Immediate
  • BRNE-> Branch if Not Equal

First we made some experiments

  • Open AVR studio
  • File New Project
  • Write .asm code
  • Build and Run
  • Step into

First we look at our board

  • We look at our hello.speaker.45.cad board and check if we have some available pins to use.
  • We see that we have PB3 and PB4 available.
  • We can now start to check the code.

Lets use hello.speaker.45.pwm.asm

We download hello.speaker.45.pwm.asm and change the code.

We use also a little bit of hello.step.45.asm program.

Alternate the code

Define variablbe. .equ sensor_pin = PB3 ; we call pin PB3 a sensor pin .equ charge_pin = PB4 ; PB4 is our charge pin

.def sensor_value_high = R23	; temporary storage for sensor value  (here we store our sensor values (because   we have 10 bit value, here we have the higher value)
.def sensor_value_low = R24	; temporary storage for sensor value (here we store our sensor values (because  we have 10 bit value, here we have the lower value)


; set Chargepin to output
   ;
   cbi PORTB, charge_pin (the charge_pin is set to low)
   sbi DDRB, charge_pin ( charge pin is output

Next we define measuring method.

   ; init A/D
   ;
   cbi ADMUX, REFS2 ; use Vcc as reference
   cbi ADMUX, REFS1 ; "
   cbi ADMUX, REFS0 ; "
   cbi ADMUX, ADLAR ; right-adjust result
   cbi ADMUX, MUX3 ; set MUX to ADC2 (PB4) here we decide what pin we use for measurement
   cbi ADMUX, MUX2 ; "
   sbi ADMUX, MUX1 ; "
   cbi ADMUX, MUX0 ; "
   sbi ADCSRA, ADEN ; enable A/D
   cbi ADCSRA, ADPS2 ; set prescaler to /2 (here we define at what speed we measure)
   cbi ADCSRA, ADPS1 ; "
   cbi ADCSRA, ADPS0 ; "


Mistakes that we made:

  • First we defined PB4 as a sensor pin and a charge pin, we fixed that by defining PB3 as a charge pin and PB4 as a sensor pin.
  • Then we started to get some response from the sensor.
  • At first we only used lower value.
;==First version==
;
; hello.speaker.45.pwm.asm
;
; software PWM speaker hello-world program
;
; Neil Gershenfeld MIT CBA 8/2/07
;
; (c) Massachusetts Institute of Technology 2007
; Permission granted for experimental and personal use;
; license for commercial sale available from MIT.
;

 .include "tn45def.inc"

 .equ mosfet_pin = PB1 ; MOSFET pin
.equ max_pwm = 20 ; maximum PWM on value
.equ pwm_length = 40 ; PWM loop length
.equ sensor_pin = PB4	; we call pin PB4 a sensor pin
.equ charge_pin = PB3	; PB3 is our charge pin


.def temp = R16	; temporary storage
.def temp1 = R17 ; temporary storage
.def pwm = R18 ; PWM value
.def pwm_count = R19 ; PWM counter
.def pwm_cycle = R20 ; PWM cycle count
.def pwm_cycle_count = R21 ; number of PWM cycles per audio cycle
.def cycle_count = R22 ; audio cycle count 
.def sensor_value_high = R23	; temporary storage for sensor value
.def sensor_value_low = R24	; temporary storage for sensor value

.cseg
.org 0
rjmp reset

;
; main program
;
reset:	   
   ;
   ; initialization
   ;
   ; set clock divider to /1
   ;
   ldi temp, (1 << CLKPCE)
   ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0)
   out CLKPR, temp
   out CLKPR, temp1
   ;
   ; set stack pointer to top of RAM
   ;
   ldi temp, high(RAMEND)
   out SPH, temp
   ldi temp, low(RAMEND)
   out SPL, temp
   ;
   ; set MOSFET to output
   ;
   cbi PORTB, mosfet_pin
   sbi DDRB, mosfet_pin
   ;

 ; set Chargepin to output
   ;
   cbi PORTB, charge_pin
   sbi DDRB, charge_pin

;
   ; init A/D
  ;
   cbi ADMUX, REFS2 ; use Vcc as reference
   cbi ADMUX, REFS1 ; "
   cbi ADMUX, REFS0 ; "
   cbi ADMUX, ADLAR ; right-adjust result
   cbi ADMUX, MUX3 ; set MUX to ADC2 (PB4) here we decide what pin we use for measurement
   cbi ADMUX, MUX2 ; "
   sbi ADMUX, MUX1 ; "
   cbi ADMUX, MUX0 ; "
   sbi ADCSRA, ADEN ; enable A/D
   cbi ADCSRA, ADPS2 ; set prescaler to /2 (here we define at what speed we measure)
   cbi ADCSRA, ADPS1 ; "
   cbi ADCSRA, ADPS0 ; "

 

   ; infinite main loop
   ;
   main_loop:

	;first we fetch one measurement

	 ; read response
         ;
         sbi ADCSRA, ADSC ; start conversion
         adloopup:
            sbic ADCSRA, ADSC ; loop until complete
            rjmp adloopup
         ;
         ; save conversion
         ;
         in sensor_value_low, ADCL ; get low byte from sensor
         in sensor_value_high, ADCH ; get high byte from sensor


      ldi pwm_cycle_count, 5
      frequency_loop:
         ; we will not use this line ldi cycle_count, sensor_value_low 
		 mov cycle_count, sensor_value_low
         cycle_count_loop:
            ldi pwm, max_pwm
           cycle_loop:
                mov pwm_cycle, pwm_cycle_count
               pwm_loop:
                  mov pwm_count, pwm
                  sbi PORTB, mosfet_pin
                  pwm_on_loop:
                     dec pwm_count
                     brne pwm_on_loop
                  ldi pwm_count, pwm_length
                  sub pwm_count, pwm
                  cbi PORTB, mosfet_pin
                  pwm_off_loop:
                     dec pwm_count
                     brne pwm_off_loop
                  dec pwm_cycle
                  brne pwm_loop
               dec pwm
               brne cycle_loop
             dec cycle_count
             brne cycle_count_loop
          dec pwm_cycle_count
          brne frequency_loop
       rjmp main_loop 

Code of second version

This is second version of our program and now it changes the speed of the music depending on how we turn the variable resistor.

	;
	; hello.speaker.45.pwm.asm
	;
	; software PWM speaker hello-world program
	;
	; Neil Gershenfeld MIT CBA 8/2/07
	;
	; (c) Massachusetts Institute of Technology 2007
	; Permission granted for experimental and personal use;
	; license for commercial sale available from MIT.
	; changed by Fab Academy students in Vestmannaeyjar
	
	.include "tn45def.inc"
	
	.equ mosfet_pin = PB1 ; MOSFET pin
	.equ max_pwm = 20 ; maximum PWM on value
	.equ pwm_length = 40 ; PWM loop length
	.equ sensor_pin = PB4    ; we call pin PB4 a sensor pin
	.equ charge_pin = PB3    ; PB3 is our charge pin
	
	
	.def temp = R16    ; temporary storage
	.def temp1 = R17 ; temporary storage
	.def pwm = R18 ; PWM value
	.def pwm_count = R19 ; PWM counter
	.def pwm_cycle = R20 ; PWM cycle count
	.def pwm_cycle_count = R21 ; number of PWM cycles per audio cycle
	.def cycle_count = R22 ; audio cycle count
	.def sensor_value_high = R23    ; temporary storage for sensor value
	.def sensor_value_low = R24    ; temporary storage for sensor value
	.DEF rd1l = R0 ; LSB 16-bit-number to be divided
	.DEF rd1h = R1 ; MSB 16-bit-number to be divided
	.DEF rd1u = R2 ; interim register
	.DEF rd2 = R3 ; 8-bit-number to divide with
	.DEF rel = R4 ; LSB result
	.DEF reh = R5 ; MSB result
	.DEF rmp = R25; multipurpose register for loading
	
	
	.cseg
	.org 0
	rjmp reset
	
	;
	; main program
	;
	reset:      
	   ;
	   ; initialization
	   ;
	   ; set clock divider to /1
	   ;
	   ldi temp, (1 << CLKPCE)
	   ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0)
	   out CLKPR, temp
	   out CLKPR, temp1
	   ;
	   ; set stack pointer to top of RAM
	   ;
	   ldi temp, high(RAMEND)
	   out SPH, temp
	   ldi temp, low(RAMEND)
	   out SPL, temp
	   ;
	   ; set MOSFET to output
	   ;
	   cbi PORTB, mosfet_pin
	   sbi DDRB, mosfet_pin
	   ;
	
	 ; set Chargepin to output
	   ;
	   cbi PORTB, charge_pin
	   sbi DDRB, charge_pin
	
	;
	   ; init A/D
	   ;
	   cbi ADMUX, REFS2 ; use Vcc as reference
	   cbi ADMUX, REFS1 ; "
	   cbi ADMUX, REFS0 ; "
	   cbi ADMUX, ADLAR ; right-adjust result
	   cbi ADMUX, MUX3 ; set MUX to ADC2 (PB4) here we decide what pin we use for measurement
	   cbi ADMUX, MUX2 ; "
	   sbi ADMUX, MUX1 ; "
	   cbi ADMUX, MUX0 ; "
	   sbi ADCSRA, ADEN ; enable A/D
	   cbi ADCSRA, ADPS2 ; set prescaler to /2 (here we define at what speed we measure)
	   cbi ADCSRA, ADPS1 ; "
	   cbi ADCSRA, ADPS0 ; "
	
	
	
	   ; infinite main loop
	   ;
	   main_loop:
	
	    ;first we fetch one measurement
	
	     ; read response
	         ;
	         sbi ADCSRA, ADSC ; start conversion
	         adloopup:
	            sbic ADCSRA, ADSC ; loop until complete
	            rjmp adloopup
	         ;
	         ; save conversion
	       
	         
	         ;
	         in sensor_value_low, ADCL ; get low byte from sensor
	         in sensor_value_high, ADCH ; get high byte from sensor
	
	            ;Here we divide the results from the sensor Devision starts
	        ; Load the test numbers to the appropriate registers
	        ;
	           
	            mov rd1h,sensor_value_high ; use the high sensor value
	            mov rd1l,sensor_value_low ; use the low sensor value
	            ldi rmp,0x4 ; 0x4 to be divided with
	            mov rd2,rmp
	        ;
	        ; Divide rd1h:rd1l by rd2
	        ;
	        div8:
	            clr rd1u ; clear interim register
	            clr reh ; clear result (the result registers
	            clr rel ; are also used to count to 16 for the
	            inc rel ; division steps, is set to 1 at start)
	        ;
	        ; Here the division loop starts
	        ;
	        div8a:
	            clc ; clear carry-bit
	            rol rd1l ; rotate the next-upper bit of the number
	            rol rd1h ; to the interim register (multiply by 2)
	            rol rd1u
	            brcs div8b ; a one has rolled left, so subtract
	            cp rd1u,rd2 ; Division result 1 or 0?
	            brcs div8c ; jump over subtraction, if smaller
	        div8b:
	            sub rd1u,rd2; subtract number to divide with
	            sec ; set carry-bit, result is a 1
	            rjmp div8d ; jump to shift of the result bit
	        div8c:
	            clc ; clear carry-bit, resulting bit is a 0
	        div8d:
	            rol rel ; rotate carry-bit into result registers
	            rol reh
	            brcc div8a ; as long as zero rotate out of the result
	             ; registers: go on with the division loop
	        ; End of the division reached
	
	      ldi pwm_cycle_count, 5
	      frequency_loop:
	         ; we will not use this line ldi cycle_count, sensor_value_low
	         mov cycle_count, rel ; we use the result from the devision, lower value
	         cycle_count_loop:
	            ldi pwm, max_pwm
	            cycle_loop:
	               mov pwm_cycle, pwm_cycle_count
	               pwm_loop:
	                  mov pwm_count, pwm
	                  sbi PORTB, mosfet_pin
	                  pwm_on_loop:
	                     dec pwm_count
	                     brne pwm_on_loop
	                  ldi pwm_count, pwm_length
	                  sub pwm_count, pwm
	                  cbi PORTB, mosfet_pin
	                  pwm_off_loop:
	                     dec pwm_count
	                     brne pwm_off_loop
	                  dec pwm_cycle
	                  brne pwm_loop
	               dec pwm
	               brne cycle_loop
	            dec cycle_count
	            brne cycle_count_loop
	         dec pwm_cycle_count
	         brne frequency_loop
	      rjmp main_loop



Code of third version

In third version of the code, we use different code to start from in favor of Neil Gershenfeld, Ed Baafi and Carl Scheffler.

	;
	; hello5.wave.asm
	; Neil Gershenfeld MIT CBA 7/31/05
	; PWM wavetable program
	; 
	; Modified by Ed Baafi and Carl Scheffler to play
	; a tune (Mary Had a Little Lamb).
	;
	; Remodified by Halldor and Frosti in Fab Lab ; Vestmannaeyjar, 
	
	Iceland
	; to play different music tones depending on variable voltage 
	;from distance meter
	
	; Last modified: 31st of  May 2010
	;
	; Register definitions
	;
	.include "tn45def.inc"
	.def temp = R16
	.def temp1 = R17 ; temporary storage
	.def sample_count = R18 ; sample counter
	.def sample_delay = R19 ; delay between samples
	.def delay_count = R20 ; delay loop counter
	.def cycle_count = R21 ; cycle counter
	.def min_cycle = R22 ; cycle_count => cycle_count + min_cycle
	.def note_count = R23 ; note counter
	.def note_lo = R24 ; note low byte
	;.def note_hi = R25 ; note high byte
	
	.def sensor_value_high = R25
	.def sensor_value_low = R26
	
	
	
	
	
	
	
	
	;
	; interrupt vectors
	;
	.cseg
	.org 0
	rjmp reset
	;
	; waveform
	; first byte = number of samples
	;
	wave:
	   .db 99,191,195,199,203,207,211,215,218,222
	   .db 225,229,232,235,238,240,243,245,247,249
	   .db 250,252,253,254,254,254,254,254,254,253
	   .db 252,251,250,248,246,244,242,239,236,233
	   .db 230,227,224,220,216,213,209,205,201,197
	   .db 193,189,185,181,177,173,169,166,162,158
	   .db 155,152,149,146,143,140,138,136,134,132
	   .db 131,130,129,128,128,128,128,128,128,129
	   .db 130,132,133,135,137,139,142,144,147,150
	   .db 153,157,160,164,167,171,175,179,183,187
	;
	; notes
	; first byte  = minimum number of cycles
	; second byte = number of notes
	; following notes are specified by the delay between samples
	;   and then the number of cycles
	;
	notes:
	
	;; Let me call you sweetheart
	;.db 48,52
	;.db 95,33,79,0,59,80,52,24,46,33,79,48,84,43,79,0,70,114,70,114
	;.db 
	
	70,114,70,114,62,73,66,9,62,73,59,16,52,24,62,73,70,60,62,13
	
	;.db 79,96,79,96,79,96,79,96,95,33,79,0,59,80,52,24,46,33,79,48
	
	;.db 
	
	84,43,79,0,70,114,70,114,52,168,52,168,52,96,59,16,62,73,59,16
	;.db 46,33,79,48,43,123,46,33,70,114,62,134,59,144,59,144
	
	; Mary had a little lamb
	.db 10,1
	.db 
	
	60,250,110,10,90,80,70,18,62,25,62,25,62,85,70,18,70,18,70,72
	.db 
	
	62,25,52,36,52,108,62,25,70,18,79,12,70,18,62,25,62,25,62,25
	.db 
	
	62,25,70,18,70,18,62,25,70,18,79,156,62,25,70,18,79,12,70,18
	.db 
	
	62,25,62,25,62,85,70,18,70,18,70,72,62,25,52,36,52,108,62,25
	.db 70,18,79,12,70,18,62,25,62,25,62,25,62,25,70,18,70,18,62,25
	.db 
	
	70,18,79,108,107,0,62,25,70,18,79,12,70,18,62,25,62,25,62,85
	.db 70,18,70,18,70,126,62,25,52,36,52,36,52,36
	
	;
	; main program
	;
	reset:
	   ;
	   ; initialization
	   ;
	   ldi temp, low(RAMEND)
	   out SPL, temp 
	   ldi temp, (1<<CLKPCE)
	   ldi temp1, ((0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << 
	
	CLKPS0))
	   out CLKPR, temp 
	   out CLKPR, temp1 ; set clock to 9.6MHz/1
	   ldi temp, ((1 << COM0B0) | (1 << COM0B1) | (1 << WGM01) | (1 
	
	<< WGM00))
	   out TCCR0A, temp ; set OC0B on compare match and set fast 
	
	PWM mode, 0xFF TOP
	   ldi temp, (1 << CS00)
	   out TCCR0B, temp ; set timer 0 prescalar to 1
	   sbi DDRB, PB1 
	   ldi sample_delay, 10
	   ldi cycle_count, 50
	   ;
	   ; main loop
	   ;
	
	;setup AD mælingu
	
	   ; init A/D
	  ;
	   cbi ADMUX, REFS2 ; use Vcc as reference
	   cbi ADMUX, REFS1 ; "
	   cbi ADMUX, REFS0 ; "
	   cbi ADMUX, ADLAR ; right-adjust result
	   cbi ADMUX, MUX3 ; set MUX to ADC2 (PB4) here we decide 
	
	what pin we use for measurement
	   cbi ADMUX, MUX2 ; "
	   sbi ADMUX, MUX1 ; "
	   cbi ADMUX, MUX0 ; "
	   sbi ADCSRA, ADEN ; enable A/D
	   cbi ADCSRA, ADPS2 ; set prescaler to /2 (here we define at what 
	
	speed we measure)
	   cbi ADCSRA, ADPS1 ; "
	   cbi ADCSRA, ADPS0 ; "
	
	
	
	   main_loop:
	      ;
	         ;
	
	
	 
	 ; read number of notes and set note pointer
	 
	      ldi zl, low(notes*2)
	      ldi zh, high(notes*2)
	      lpm
	      mov min_cycle, R0   ; Lesun fyrsta byte, sveiflufjöldi
	      adiw zl, 1
	      lpm
	      mov note_count, R0  ; þetta er fjöldi nótna
	      adiw zl, 1
	      movw note_lo, zl
	      ;
	      ; loop over notes  löbbun eftir nótunum
	      ;
	      inc note_count
	      note_loop:
	
	
	
	         ldi delay_count, 255
	         inter_note_delay1:
	
	
	
	
	
	
	
	         dec note_count
	
	
	         movw zl, note_lo
	
	
	nóta lesin
	
	
	 ; read response
	
	
	
	
	
	
	
	
	
	
	
	
	sensor
	
	
	sensor
	 
	
	       ;Here we divide the results from the sensor Devision starts
	
	
	registers
	
	
	
	
	sensor value
	
	
	value
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	result
	
	
	
	
	
	
	
	
	
	
	         movw zl, note_lo
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	            continue_cycle:
	            ;
	            ; read number of samples
	            ;
	            ldi zl, low(wave*2)
	            ldi zh, high(wave*2)
	            lpm
	            mov sample_count, R0
	
	
	
	            sample_loop:
	
	
	
	
	               adiw zl, 1 ; move to next sample
	               lpm ; read sample
	
	
	               out OCR0B, R0 ; update PWM
	          
	
	               sample_delay_loop:
	                  dec delay_count
	                  brne sample_delay_loop
	               dec sample_count ; decrease sample count
	               brne sample_loop ; get next sample, ...
	                  rjmp cycle_loop ; ... or repeat cycle if done



Using AVR Studio

  • Build and Run
  • Create .hex file
  • Attention -> Remember to unplug the speaker before you program the micro controler.
  • Load program to circuit board.
  • avrdude -p t45 -c bsd -U flash:w:file.hex


Rethinking

  • Some thoughts how to improve our board.
    • We could have some button to indicate what kind of sensor we have connected


Links


Simple test program=

include "tn44def.inc";
sbi DDRA, 0;
ldi R18, 10;
LoopA:
	sbi PORTA, 0;
	ldi R19, 15; 
	Loop2:
		dec R19;
		brne Loop2;
		cbi PORTA, 0;
		ldi R19, 20;
		Loop3:
			dec R19;
			brne Loop3
  	dec R18;
  	brne LoopA;