====== 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);