====== Project - Finger counter ======
* {{examples:projects:finger_counter:finger_counter.zip|AVRStudio full project}}
===== main.c =====
The project uses IR sensor and servo motor for scanning sector.
Putting hands on the front of scanning sector the result should be number of spread out fingers.
#include
#include
#include
#include "adc.h"
#include "motors.h"
#include "lcd.h"
#include "segment_display.h"
#include "pinops.h"
#include "configuration.h"
// Math operations
#define DIFF(a, b) ((a) < (b) ? (b) - (a) : (a) - (b))
#define INTEGRATE(value, new_value, n) value = (value * (n - 1) + new_value) / n;
//
// Main entrance
//
int main(void)
{
unsigned short distance, compare_distance = 0;
unsigned char matches = 0, num_fingers = 0;
signed short servo_pos = -100, scan_dir = 1;
char text[10];
// Initialize
adc_init();
dcmotors_init();
segment_display_init();
servomotors_init();
setup_output_pin(LED_RED);
lcd_init(LCD_DISP_ON);
lcd_clrscr();
lcd_puts("Finger counter");
// Endless loop
while (1)
{
// Scan
servomotors_position(1, servo_pos);
servo_pos += scan_dir;
// Servo on edge ?
if ((servo_pos < -100) || (servo_pos > 100))
{
scan_dir = (servo_pos > 0 ? -1 : 1);
// Output results
segment_display_write(num_fingers);
itoa(num_fingers, text, 10);
lcd_gotoxy(0, 1);
lcd_puts(text);
lcd_puts(" ");
// Reset fingers count
num_fingers = 0;
}
// Measure distance
distance = adc_sample_value(0, 4);
// Check for drastic changes
// Look for decreasing distance (increasing sensor output voltage)
if (distance > compare_distance + 100)
{
// Light on finger
clear_pin(LED_RED); // inverted!
// Changes has to last for some time
if (matches++ == 5)
{
// Count fingers
num_fingers++;
}
}
else
{
// Reset matches and turn off the LED
set_pin(LED_RED); // inverted!
matches = 0;
}
// Calculate compare distance (integrated distance)
INTEGRATE(compare_distance, distance, 20);
// Pause
_delay_ms(10);
}
}
===== configuration.h =====
// Hardware pin configurations
#define BUTTON_1 PORTPIN(C, 2)
#define BUTTON_2 PORTPIN(C, 1)
#define BUTTON_3 PORTPIN(C, 0)
#define LED_RED PORTPIN(C, 5)
#define LED_YELLOW PORTPIN(C, 4)
#define LED_GREEN PORTPIN(C, 3)
#define DCMOTOR_1A PORTPIN(B, 7)
#define DCMOTOR_1B PORTPIN(B, 4)
#define DCMOTOR_2A PORTPIN(D, 1)
#define DCMOTOR_2B PORTPIN(D, 0)
#define DCMOTOR_3A PORTPIN(D, 7)
#define DCMOTOR_3B PORTPIN(D, 6)
#define DCMOTOR_4A PORTPIN(D, 5)
#define DCMOTOR_4B PORTPIN(D, 4)
#define SERVOMOTOR_1 PORTPIN(B, 5)
#define SERVOMOTOR_2 PORTPIN(B, 6)
#define SEGMENT_DISPLAY_LATCH PORTPIN(G, 2)
#define SEGMENT_DISPLAY_DATA_OUT PORTPIN(C, 6)
#define SEGMENT_DISPLAY_CLOCK PORTPIN(C, 7)
===== adc.c =====
#include
#include "pinops.h"
//
// ADC initialization
//
void adc_init(void)
{
// ADC setup
// Prescaler 8
ADCSRA = BIT(ADEN) | BIT(ADPS1) | BIT(ADPS0);
// Reference voltage to external (+5V)
ADMUX = BIT(REFS0);
}
//
// ADC conversion waiting
//
void adc_wait_until_done(void)
{
while (IS_BIT_SET(ADCSRA, ADSC))
{
asm volatile ("nop");
}
}
//
// ADC channel value sampling
//
unsigned short adc_sample_value(unsigned char channel, unsigned char num_samples)
{
unsigned short result = 0;
// Specify channel
ADMUX &= 0xF0;
ADMUX |= channel & 0x0F;
// Take test sample to "warm up" converter
// Usually the first sample is discarded
SET_BIT(ADCSRA, ADSC);
adc_wait_until_done();
// Real sampling, sum up specifed number of samples
for (unsigned char i = 0; i < num_samples;i++)
{
SET_BIT(ADCSRA, ADSC);
adc_wait_until_done();
// Sum-up
result += ADCW;
}
// Return averaged result
return (result / num_samples);
}
===== adc.h =====
// ADC function headers
void adc_init(void);
unsigned short adc_sample_value(unsigned char channel, unsigned char num_samples);
===== motors.c =====
#include
#include "pinops.h"
#include "configuration.h"
// PWM full period
// 14.745600 Mhz / 8 / 50 Hz = 36864
#define PWM_PERIOD (F_CPU / 8 / 50)
// PWM servo middle position
// 36864 / 20 ms * 1.5 ms = 2764
#define PWM_MIDDLE_POS (PWM_PERIOD * 15 / 200)
// PWM ratio to get position from -100 to 100
// 36864 / 20 ms / 2 / 100
// We add +1 to get a full range and a little bit more
#define PWM_RATIO (PWM_PERIOD / 20 / 2 / 100 + 1)
//
// Initialize motors
//
void dcmotors_init(void)
{
setup_output_pin(DCMOTOR_1A);
setup_output_pin(DCMOTOR_1B);
setup_output_pin(DCMOTOR_2A);
setup_output_pin(DCMOTOR_2B);
setup_output_pin(DCMOTOR_3A);
setup_output_pin(DCMOTOR_3B);
setup_output_pin(DCMOTOR_4A);
setup_output_pin(DCMOTOR_4B);
}
//
// Drive command for specified motor
//
void dcmotors_drive(unsigned char nr, signed char dir)
{
switch (nr)
{
case 1:
set_pin_to(DCMOTOR_1A, dir < 0);
set_pin_to(DCMOTOR_1B, dir > 0);
break;
case 2:
set_pin_to(DCMOTOR_2A, dir < 0);
set_pin_to(DCMOTOR_2B, dir > 0);
break;
case 3:
set_pin_to(DCMOTOR_3A, dir < 0);
set_pin_to(DCMOTOR_3B, dir > 0);
break;
case 4:
set_pin_to(DCMOTOR_4A, dir < 0);
set_pin_to(DCMOTOR_4B, dir > 0);
break;
}
}
//
// Intialize servo motors
//
void servomotors_init(void)
{
setup_output_pin(SERVOMOTOR_1);
setup_output_pin(SERVOMOTOR_2);
// Set timer control registers
// Clear OUTA and OUTB on compare match
// Fast PWM mode with ICR = TOP
// Prescaler 8
TCCR1A = BIT(COM1A1) | BIT(COM1B1) | BIT(WGM11);
TCCR1B = BIT(CS11) | BIT(WGM13) | BIT(WGM12);
// TOP period
ICR1 = PWM_PERIOD;
}
//
// Position servo motors
// pos is from -100 to 100
//
void servomotors_position(unsigned char nr, signed short pos)
{
//
switch (nr)
{
case 1: OCR1A = PWM_MIDDLE_POS + pos * PWM_RATIO; break;
case 2: OCR1B = PWM_MIDDLE_POS + pos * PWM_RATIO; break;
}
}
===== motors.h =====
// Motor function headers
void dcmotors_init(void);
void dcmotors_drive(unsigned char nr, signed char dir);
void servomotors_init(void);
void servomotors_position(unsigned char nr, signed short pos);
===== segment_display.c =====
#include
#include
#include "pinops.h"
#include "configuration.h"
//
// 7 segment display initialization
//
void segment_display_init(void)
{
// Set latch, data out and clock pins as output
setup_output_pin(SEGMENT_DISPLAY_LATCH);
setup_output_pin(SEGMENT_DISPLAY_DATA_OUT);
setup_output_pin(SEGMENT_DISPLAY_CLOCK);
}
//
// Digit writing to 7 segment display
//
void segment_display_write(unsigned char digit)
{
unsigned char map;
// Decimal to segment map
switch (digit)
{
case 0 : map = 0b00111111; break; // Every bit corresponds to one segment
case 1 : map = 0b00000110; break; // "1"
case 2 : map = 0b01011011; break; // "2"
case 3 : map = 0b01001111; break; // "3" and so on
case 4 : map = 0b01100110; break;
case 5 : map = 0b01101101; break;
case 6 : map = 0b01111100; break;
case 7 : map = 0b00000111; break;
case 8 : map = 0b01111111; break;
case 9 : map = 0b01100111; break;
default: map = 0b01111001; // E like Error
}
// Latch low
clear_pin(SEGMENT_DISPLAY_LATCH);
// Send every bit in the byte. MSB (most significant bit) first.
for (signed char i = 7; i >= 0; i--)
{
// If bit is set, sets the data out pin, otherwise not
set_pin_to(SEGMENT_DISPLAY_DATA_OUT, IS_BIT_SET(map, i));
// Clock high for certain period
set_pin(SEGMENT_DISPLAY_CLOCK)
_delay_us(1);
// Clock low for certain period
clear_pin(SEGMENT_DISPLAY_CLOCK)
_delay_us(1);
}
// Latch high
set_pin(SEGMENT_DISPLAY_LATCH);
}
===== segment_display.h =====
// 7 segment display function headers
void segment_display_init(void);
void segment_display_write(unsigned char digit);