Table of Contents

Projekt - Finger zählen

main.c

Mit Hilfe des IR Sensors und dem Servo Motor wird ein Sektor eingescannt. Hält man seine Hand in den gescannten Bereich sollte die Anzahl der gezeigten Finger ausgegeben werden.

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#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 <avr/io.h>
#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 <avr/io.h>
#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 <avr/io.h>
#include <util/delay.h>
#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);