The assignment for this week is measure something: add a sensor to a microcontroller board that you've designed and read it. There are hello world example circuits to measure magnetic fields, temperature, light, sound, acceleration, orientation, vibration and more. I chose to build the transmit-receive version of the step response circuit. Variations of this circuit can be used to measure resistance, capacitance, inductance, position, pressure, proximity, tilt, acceleration, humidity, touchpad, multitouch, etc.
For a circuit that can measure so many things, the transmit-receive step response circuit appeared surprisingly simple. The hello.txrx.45 board uses an ATtiny45 8-pin microcontroller, 3 resistors, 1 capacitor, and some connectors. It doesn't get much simpler than that.
The example hello_txrx_45.png circuit.
EagleCAD schematic of transmit-receive step response circuit with two status indicator LEDs.
EagleCAD schematic of transmit-receive step response circuit with two status indicator LEDs.
// // hello.txrx.led.45.c // // step response transmit-receive hello-world // 9600 baud FTDI interface // // Neil Gershenfeld // 11/6/11 // // (c) Massachusetts Institute of Technology 2011 // Permission granted for experimental and personal use; // license for commercial sale available from MIT. // // Modified by Scott Zitek 4/5/2014 // to add more comments and two status indicator LEDs #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 100 // 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 settle_delay() _delay_us(100) // settle delay #define char_delay() _delay_ms(10) // char delay #define nloop 100 // loops to accumulate #define serial_port PORTB #define serial_direction DDRB #define serial_pin_out (1 << PB2) #define transmit_port PORTB #define transmit_direction DDRB #define transmit_pin (1 << PB4) //added to configure LED connected to ATtiny45 pin PB0 #define led1_port PORTB #define led1_direction DDRB #define led1_pin (1 << PB0) //added to configure LED connected to ATtiny45 pin PB1 #define led2_port PORTB #define led2_direction DDRB #define led2_pin (1 << PB1) 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 unsigned char count; static uint16_t up,down; // Added to create variable to store the result of up value minus down value static uint16_t value; // // 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); clear(transmit_port, transmit_pin); output(transmit_direction, transmit_pin); // Initialize LED1 status indicator clear(led1_port, led1_pin); output(led1_direction, led1_pin); // Initialize LED2 status indicator clear(led2_port, led2_pin); output(led2_direction, led2_pin); // // init A/D // // Data sheet Pg134-135 ADMUX is the ADC Multiplexer Selection Register. // REFS2=0, REFS1=0,and REFS0=0 sets "Vcc used as voltage...... // reference, disconnected from PB0 (AREF)" // ALDAR=0 sets ADC presentation for right adjust - see data sheet pg137 // MUX3=0, MUX2=0, MUX1=1, and MUX0=1 - this sets input channel // for Single ended-input ADC3 (PB3). Positive Differential // input, Negative Differential input, and gain N/A. // ADCSRA is the ADC control and status register A // see data sheet pg136. prescaler select bits 1,1,1 set ADC prescaler for // division factor 128. // simplifies by basically truncating 10-bit ADC to 8-bit ADC result. ADMUX = (0 << REFS2) | (0 << REFS1) | (0 << REFS0) // Vcc ref | (0 << ADLAR) // right adjust | (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (1 << MUX0); // PB3 ADCSRA = (1 << ADEN) // enable | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128 // // main loop // while (1) { // // accumulate // up = 0; down = 0; for (count = 0; count < nloop; ++count) { // // settle, charge // settle_delay(); set(transmit_port, transmit_pin); // // initiate conversion // ADCSRA |= (1 << ADSC); // // wait for completion // while (ADCSRA & (1 << ADSC)) ; // // save result // up += ADC; // // settle, discharge // settle_delay(); clear(transmit_port, transmit_pin); // // initiate conversion // ADCSRA |= (1 << ADSC); // // wait for completion // while (ADCSRA & (1 << ADSC)) ; // // save result // down += ADC; } // // Control LED status indicators // // Subtract "down" value from "up" value and store the result in "value" // "value" is the number displayed by the example Python program. value = up - down; // Energize LED1 when the value is above a predefined value (e.g. 5000) if (value > 5000){ set(led1_port, led1_pin); // Execute if condition is met } else{ clear(led1_port, led1_pin); // Execute if condition is not met } // Energize LED2 when the value is above a predefined value (e.g. 7000) if (value > 7000){ set(led2_port, led2_pin); // Execute if condition is met } else{ clear(led2_port, led2_pin); // Execute if condition is not met } // // send framing // put_char(&serial_port, serial_pin_out, 1); 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); // // send result // // "up" and "down" are both unsigned 16-bit integers. // so we must send upper and lower bytes of each as separate characters. put_char(&serial_port, serial_pin_out, (up & 255)); char_delay(); put_char(&serial_port, serial_pin_out, ((up >> 8) & 255)); char_delay(); put_char(&serial_port, serial_pin_out, (down & 255)); char_delay(); put_char(&serial_port, serial_pin_out, ((down >> 8) & 255)); char_delay(); } }
sudo make -f hello.txrx.led.45.make
sudo make -f hello.txrx.45.led.make program-avrisp2
python hello.txrx.45.py /dev/ttyUSB0
Completed transmit-receive step response circuit with both status indicator LEDs illuminated and Python readout program running in the background.
Plastic cup with transmit and receive foil pieces attached.
Results of testing detection of milk and water at various levels. Notice, it did not detect changes well once liquid level was above foil.
Testing a pressure sensor based upon the step response circuit.