====== Sõrmede lugeja ======
Tegu on mitmefaililise AVR studio projekiga mis demonstreerib servomootori, infrapuna kaugusmõõdiku, LCD ekraani ja 7-segmendilise indikaatori koos kasutamist. Kui kaugusmõõdik kinnitada servomootori külge siis programm hakkab skaneerima objekte mis kaugusmõõdikule ette jäävad. Pannes harali näpud sensori ette loeb see need kokku ja väljastab nende arvu ekraanile.
* {{examples:projects:finger_counter:finger_counter.zip|Kogu AVR Studio projekt}}
===== main.c =====
#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);