VCC, GND, RESET, SCK, MOSI, MISO
programmer to the corresponding outputs of the microcontroller. For simplicity, I put together an auxiliary circuit right on the breadboard:/etc/udev/rules.d/41-atmega.rules
file: # USBasp programmer SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="05dc", GROUP="plugdev", MODE="0666"
service udev restart
β 1β
, β 2β
, ββ
, ββ
and ββ
. Well, do not forget about the sound indication of the end of the game. Look like that's it. The figure below shows the general scheme of connecting the microcontroller to the indicators and buttons. We will need it when parsing the program source code:main
function. In fact, there is nothing remarkable in it β setting up ports, initializing data, and an endless loop of handling keystrokes. Well, the call to sei()
is the resolution of interrupt handling, about them a little later. int main(void) { init_io(); init_data(); sound_off(); sei(); while(1) { handle_buttons(); } return 0; }
void init_io() { // set output DDRB = 0xFF; DDRD = 0xFF; // set input DDRC = 0b11100000; // pull-up resistors PORTC |= 0b00011111; // timer interrupts TIMSK = (1<<OCIE1A) | (1<<TOIE0); TCCR0 |= (1 << CS01) | (1 << CS00); TCCR1B = (1<<CS12|1<<WGM12); //OCRn = (clock_speed / prescaler) * seconds - 1 OCR1A = (F_CPU / 256) * 1 -1; }
DDRC = 0b11100000;
command DDRC = 0b11100000;
turns the first 5 pins of port C into the input pins, and the rest on the weekend. Team PORTC |= 0b00011111;
includes internal pull-up resistors at 5 controller inputs. According to the diagram, buttons are connected to these inputs, which, when pressed, close them to the ground. Thus, the controller understands that the button is pressed. ISR (TIMER0_OVF_vect) { display(); if (_buzzer > 0) { _buzzer--; if (_buzzer == 0) sound_off(); } } ISR(TIMER1_COMPA_vect) { if (ActiveTimer == 1 && Timer1 > 0) { Timer1--; if (Timer1 == 0) process_timeoff(); } if (ActiveTimer == 2 && Timer2 > 0) { Timer2--; if (Timer2 == 0) process_timeoff(); } }
void display() { display_number((Timer1/60)/10, 0b00001000); _delay_ms(0.25); display_number((Timer1/60)%10, 0b00000100); _delay_ms(0.25); display_number((Timer1%60)/10, 0b00000010); _delay_ms(0.25); display_number((Timer1%60)%10, 0b00000001); _delay_ms(0.25); display_number((Timer2/60)/10, 0b10000000); _delay_ms(0.25); display_number((Timer2/60)%10, 0b01000000); _delay_ms(0.25); display_number((Timer2%60)/10, 0b00100000); _delay_ms(0.25); display_number((Timer2%60)%10, 0b00010000); _delay_ms(0.25); PORTD = 0; } void display_number(int number, int mask) { PORTB = number_mask(number); PORTD = mask; }
display
function uses the dynamic display method. The fact is that each individual indicator has 9 contacts (7 for controlling segments, 1 for a point and 1 for power). To manage 4 digits would need 36 contacts. Too wasteful. Therefore, the output of digits to the indicator with several numbers is organized according to the following principle:PORTD = 0;
). If this is not done, the last displayed digit will continue to be lit until the next call to the display function, which will result in a brighter glow than the others. void handle_buttons() { handle_button(KEY_SETUP); handle_button(KEY_RESET); handle_button(KEY_PAUSE); handle_button(KEY_PLAYER1); handle_button(KEY_PLAYER2); } void handle_button(int key) { int bit; switch (key) { case KEY_SETUP: bit = SETUP_BIT; break; case KEY_RESET: bit = RESET_BIT; break; case KEY_PAUSE: bit = PAUSE_BIT; break; case KEY_PLAYER1: bit = PLAYER1_BIT; break; case KEY_PLAYER2: bit = PLAYER2_BIT; break; default: return; } if (bit_is_clear(BUTTON_PIN, bit)) { if (_pressed == 0) { _delay_ms(DEBOUNCE_TIME); if (bit_is_clear(BUTTON_PIN, bit)) { _pressed |= key; // key action switch (key) { case KEY_SETUP: process_setup(); break; case KEY_RESET: process_reset(); break; case KEY_PAUSE: process_pause(); break; case KEY_PLAYER1: process_player1(); break; case KEY_PLAYER2: process_player2(); break; } sound_on(15); } } } else { _pressed &= ~key; } }
bit_is_clear(BUTTON_PIN, bit)
registered by checking bit_is_clear(BUTTON_PIN, bit)
, i.e. a button is pressed if its corresponding input is connected to ground, which is what will happen, according to the diagram, when the button is pressed. The delay of the DEBOUNCE_TIME
duration and the re-check is needed to avoid multiple unnecessary operations due to contact bounce. Storing the pressing status in the corresponding bits of the _pressed
variable _pressed
used to prevent repeated operation when the button is pressed for a long time. #define F_CPU 4000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #define DEBOUNCE_TIME 20 #define BUTTON_PIN PINC #define SETUP_BIT PC0 #define RESET_BIT PC1 #define PAUSE_BIT PC2 #define PLAYER1_BIT PC3 #define PLAYER2_BIT PC4 #define KEY_SETUP 0b00000001 #define KEY_RESET 0b00000010 #define KEY_PAUSE 0b00000100 #define KEY_PLAYER1 0b00001000 #define KEY_PLAYER2 0b00010000 volatile int ActiveTimer = 0; volatile int Timer1 = 0; volatile int Timer2 = 0; volatile int _buzzer = 0; volatile int _pressed = 0; // function declarations void init_io(); void init_data(); int number_mask(int num); void handle_buttons(); void handle_button(int key); void process_setup(); void process_reset(); void process_pause(); void process_timeoff(); void process_player1(); void process_player2(); void display(); void display_number(int mask, int number); void sound_on(int interval); void sound_off(); // interrupts ISR (TIMER0_OVF_vect) { display(); if (_buzzer > 0) { _buzzer--; if (_buzzer == 0) sound_off(); } } ISR(TIMER1_COMPA_vect) { if (ActiveTimer == 1 && Timer1 > 0) { Timer1--; if (Timer1 == 0) process_timeoff(); } if (ActiveTimer == 2 && Timer2 > 0) { Timer2--; if (Timer2 == 0) process_timeoff(); } } int main(void) { init_io(); init_data(); sound_off(); sei(); while(1) { handle_buttons(); } return 0; } void init_io() { // set output DDRB = 0xFF; DDRD = 0xFF; // set input DDRC = 0b11100000; // pull-up resistors PORTC |= 0b00011111; // timer interrupts TIMSK = (1<<OCIE1A) | (1<<TOIE0); TCCR0 |= (1 << CS01) | (1 << CS00); TCCR1B = (1<<CS12|1<<WGM12); //OCRn = (clock_speed / prescaler) * seconds - 1 OCR1A = (F_CPU / 256) * 1 -1; } void init_data() { Timer1 = 0; Timer2 = 0; ActiveTimer = 0; } int number_mask(int num) { switch (num) { case 0 : return 0xC0; case 1 : return 0xF9; case 2 : return 0xA4; case 3 : return 0xB0; case 4 : return 0x99; case 5 : return 0x92; case 6 : return 0x82; case 7 : return 0xF8; case 8 : return 0x80; case 9 : return 0x90; }; return 0; } void process_setup() { Timer1 += 60; Timer2 += 60; // overflow check (5940 seconds == 99 minutes) if (Timer1 > 5940 || Timer2 > 5940) { Timer1 = 0; Timer2 = 0; } } void process_reset() { init_data(); } void process_timeoff() { init_data(); sound_on(30); } void process_pause() { ActiveTimer = 0; } void process_player1() { ActiveTimer = 2; } void process_player2() { ActiveTimer = 1; } void handle_button(int key) { int bit; switch (key) { case KEY_SETUP: bit = SETUP_BIT; break; case KEY_RESET: bit = RESET_BIT; break; case KEY_PAUSE: bit = PAUSE_BIT; break; case KEY_PLAYER1: bit = PLAYER1_BIT; break; case KEY_PLAYER2: bit = PLAYER2_BIT; break; default: return; } if (bit_is_clear(BUTTON_PIN, bit)) { if (_pressed == 0) { _delay_ms(DEBOUNCE_TIME); if (bit_is_clear(BUTTON_PIN, bit)) { _pressed |= key; // key action switch (key) { case KEY_SETUP: process_setup(); break; case KEY_RESET: process_reset(); break; case KEY_PAUSE: process_pause(); break; case KEY_PLAYER1: process_player1(); break; case KEY_PLAYER2: process_player2(); break; } sound_on(15); } } } else { _pressed &= ~key; } } void handle_buttons() { handle_button(KEY_SETUP); handle_button(KEY_RESET); handle_button(KEY_PAUSE); handle_button(KEY_PLAYER1); handle_button(KEY_PLAYER2); } void display() { display_number((Timer1/60)/10, 0b00001000); _delay_ms(0.25); display_number((Timer1/60)%10, 0b00000100); _delay_ms(0.25); display_number((Timer1%60)/10, 0b00000010); _delay_ms(0.25); display_number((Timer1%60)%10, 0b00000001); _delay_ms(0.25); display_number((Timer2/60)/10, 0b10000000); _delay_ms(0.25); display_number((Timer2/60)%10, 0b01000000); _delay_ms(0.25); display_number((Timer2%60)/10, 0b00100000); _delay_ms(0.25); display_number((Timer2%60)%10, 0b00010000); _delay_ms(0.25); PORTD = 0; } void display_number(int number, int mask) { PORTB = number_mask(number); PORTD = mask; } void sound_on(int interval) { _buzzer = interval; // put buzzer pin high PORTC |= 0b00100000; } void sound_off() { // put buzzer pin low PORTC &= ~0b00100000; }
Source: https://habr.com/ru/post/205972/
All Articles