11. Input devices¶
Assignment¶
-
Group assignment
Measure the analog levels and digital signals in an input device -
Individual assignment
Measure something: add a sensor to a microcontroller board that you have designed and read it.
Group Assignment¶
Planning¶
My plan is to use light (visible) sensor for my board. It will be used for my final project to detect user interaction. For example, a user put a wood branch into a hole of the table I created at week8, then the output of the mist changes accordingly.
Light sensors (visible) PT15-21C/TR8 / Data sheet
Board example
Based on the example board, I modified following points to fit the final project plan.
I first planned to use five ADC ports of an ATTINT44 for sensors. But my instructor advised me that if you might change the number, it might be interesting to divide sensors into each micon board and network them. So I decided to try the scalable plan.
- ATTINY45 –> ATTINY44
- Extra serial port to network with other sensor boards.
Parts List
parts | description | number |
---|---|---|
micro controller | ATTiny 44 | 1 |
Light sensor | PT15-21C/TR8 | 1 |
capacitor | 1uF | 1 |
resistor | 10KΩ | 2 |
resistor | 0KΩ | 2 |
header | 6pin for FTDI | 1 |
header | 6pin for ISP | 1 |
header | 4pin for serial | 1 |
Design¶
I designed the board with Eagle.
To make the schematics was not difficult as I referred the example, however the routing was very tough work for me. I first laid out the parts the following left pattern, but I couldn’t connect properly. So I tried right pattern and struggled for hours, then finally I did it with two jumpers (0Ω resister).
At DRC, the two jumper points were listed up as Airwire errors as follows. So I approved them.
The long name was affected the export of png image. Unnecessary space was created in the png file, so I shortened the name.
The designed board ant outline are as follows.
Milling¶
I milled the board using SRM-20 and soldered the parts.
I realized that the capacitor between VCC & GND would prevent the soldering of header. So I relocated the capacitor to beside the header. It is important to design the board considering the 3d shape of each parts.
First I couldn’t measured the analogue signal with oscilloscope. I realized that I soldered the light sensor at opposite orientation. So I changed the orientation, then I could measure the signal as follows.
C programming¶
I modified the example code of ATTINY45 to ATTINY44. Some of the registers are different from ATTINY45, so I read the ATtiny44A datasheet carefully.
Changes from ATtiny45 to ATtiny44
- ADMUX : delete REFS2
- ADLAR : From ADMUX to ADCSRB
- MUX : Add MUX4 and MUX5
Initialization of ADC¶
ADMUX is the ADC Multiplexer Selection Register. Initialization value is as follows.
- VCC used as analog reference (REFS1,0 = 00)
- Select ADC0 (MUX=00000)
ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref | (0 << MUX5) | (0 << MUX4) | (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); // ADC0
ADCSRA / ADCSRB is the ADC Control and Status Register.
- Enable ADC (ADEN = 1)
- Prescaler = 128 (ADPS2,1,0 = 111)
- Right adjust the result (ADLAR = 0)
ADCSRA = (1 << ADEN) // enable | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128 ADCSRB = (0 << ADLAR); // right adjust
Read ADC data¶
ADC timing chart is as follows. When the ADSC bit of ADSCRA is set, the conversion starts. A normal conversion takes 13 ADC clock cycles, however takes 25 clock in case of first conversion after the ADEN bit is set.
After the conversion is complete (ADSC is Low), the conversion result can be found in the ADC Data Registers (ADCL, ADCH). As the data is 10 bit, combine the High part(ADCL) and Low part(ADCH) as follows.
// initiate conversion // ADCSRA |= (1 << ADSC); //Start ADC // // wait for completion // while (ADCSRA & (1 << ADSC)) ; // // send result // chr = ADCL; put_char(&serial_port, serial_pin_out, chr); char_delay(); chr = ADCH; put_char(&serial_port, serial_pin_out, chr);
Test serial data¶
I tested the serial data using Arduino serial monitor. The framing code “1234” are not ASCII, so I changed them to visible alphabet code ‘F’ ‘A’ ‘B’ ‘K’ temporary to check the serial communication. The alphabet codes were displayed properly as follows, so I confirmed the serial function.
C code¶
//====================================================== // light.44.c //====================================================== #include <avr/io.h> #include <util/delay.h> #define output(directions,pin) (directions |= pin) // set port direction for output #define set(port,pin) (port |= pin) // set port pin #define clear(port,pin) (port &= (~pin)) // clear port pin #define pin_test(pins,pin) (pins & pin) // test for port pin #define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set #define bit_delay_time 102 // bit delay for 9600 with overhead #define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay #define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay #define char_delay() _delay_ms(10) // char delay #define serial_port PORTB #define serial_direction DDRB #define serial_pin_out (1 << PB0) //week11 void put_char(volatile unsigned char *port, unsigned char pin, char txchar) { // // send character in txchar on port pin // assumes line driver (inverts bits) // // start bit // clear(*port,pin); bit_delay(); // // unrolled loop to write data bits // if bit_test(txchar,0) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,1) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,2) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,3) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,4) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,5) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,6) set(*port,pin); else clear(*port,pin); bit_delay(); if bit_test(txchar,7) set(*port,pin); else clear(*port,pin); bit_delay(); // // stop bit // set(*port,pin); bit_delay(); // // char delay // bit_delay(); } int main(void) { // // main // static char chr; // // set clock divider to /1 // CLKPR = (1 << CLKPCE); CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); // // initialize output pins // set(serial_port, serial_pin_out); output(serial_direction, serial_pin_out); // // init A/D // // ADMUX = (0 << REFS2) | (0 << REFS1) | (0 << REFS0) // Vcc ref ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref week11 // | (0 << ADLAR) // right adjust // | (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (1 << MUX0); // ADC3 | (0 << MUX5) | (0 << MUX4) | (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); // ADC0 week11 ADCSRB = (0 << ADLAR); // right adjust week11 ADCSRA = (1 << ADEN) // enable | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128 // // main loop // while (1) { // // send framing // put_char(&serial_port, serial_pin_out, 1); // put_char(&serial_port, serial_pin_out, 'F'); char_delay(); put_char(&serial_port, serial_pin_out, 2); char_delay(); put_char(&serial_port, serial_pin_out, 3); char_delay(); put_char(&serial_port, serial_pin_out, 4); char_delay(); // // initiate conversion // ADCSRA |= (1 << ADSC); // // wait for completion // while (ADCSRA & (1 << ADSC)) ; // // send result // chr = ADCL; put_char(&serial_port, serial_pin_out, chr); char_delay(); chr = ADCH; put_char(&serial_port, serial_pin_out, chr); } }
Application : Processing¶
I programmed processing to visualize the light sensor signal.
The upper one is real time graph, and lower one shows the change of time axis.
I confirmed that the analogue signal changed properly according to the hand interaction.
Processing code:
/**************************************************************************** Week11 Input device Light sensor grough *****************************************************************************/ import processing.serial.*; Serial myPort; // Create object from Serial class int val; // Data received from the serial port int cnt= 0; int ltL=0; int ltH=0; int ltDat=0; int xx=0; float eps = 0.5; float filter = 0.0; void setup() { size(550,300); myPort = new Serial(this, "/dev/cu.usbserial-A90808PQ", 9600); background(255); // Set background to white } void draw() { noStroke(); fill(0, 130, 130); text("Light Sensor ", 50,25); float x1= map ( filter, 0, 1024, 0, 450); fill(172, 255, 255); rect(50, 50, 450, 50); fill(0, 130, 130); rect(50, 50, x1, 50); float x2 = map ( filter, 0, 1024, 0, 100); fill(0,130,130); rect(50+xx, 120, 1, 100); fill(172, 255, 255); rect(50+xx, 120, 1, 100-x2); if(xx++ == 450){xx=0;}; } void serialEvent(Serial myPort) { readLight(); } void readLight(){ val = myPort.read(); // read it and store it in val // println(val); // println ("cnt = " + cnt); switch(cnt){ case 0: if(val==1){ cnt = 1;} break; case 1: if(val==2){ cnt = 2; }else if(val!=1) { cnt=0; } break; case 2: if(val==3){ cnt = 3; }else{ cnt=0; } break; case 3: if(val==4){ cnt = 4; }else{ cnt=0; } break; case 4: ltL = val; cnt = 5; break; case 5: ltH = val; ltDat = 256*ltH + ltL; filter = (1-eps)*filter + eps*ltDat; println ("filter = " + filter); cnt = 0; break; } }