Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2024:fstancu:cristian.musat2412 [2024/05/11 14:21]
cristian.musat2412
pm:prj2024:fstancu:cristian.musat2412 [2024/05/27 13:06] (current)
cristian.musat2412
Line 8: Line 8:
 </​note>​ </​note>​
 ===== Introducere ===== ===== Introducere =====
-Proiectul presupune crearea unei variante simplificate a jocului clasic Whac-A-Mole. Cârtițele vor fi reprezentate de LED-uri care se vor aprinde la intervale din ce în ce mai mici, pe măsura ce jocul progresează,​ și vor avea un timeout la expirarea căruia se vor stinge, astfel jucătorul ratând șansa de a-și crește scorul. LED-urile vor avea asociate câte un buton care va fi acționat prin apăsare. Scorul va fi calculat în funcție de reacția cât mai rapidă a jucătorului (cu cât omoară cârtițele mai repede cu atât scorul va fi mai mare) și va fi afișat pe un ecran LCD.+Proiectul presupune crearea unei variante simplificate a jocului clasic Whac-A-Mole. Cârtițele vor fi reprezentate de LED-uri care se vor aprinde la intervale din ce în ce mai mici, pe măsura ce jocul progresează,​ și vor avea un timeout la expirarea căruia se vor stinge, astfel jucătorul ratând șansa de a-și crește scorul. LED-urile vor avea asociate câte un buton care va fi acționat prin apăsare, ducând astfel la stingerea LED-ului asociat, respectiv omorârea cârtiței. Scorul va fi calculat în funcție de reacția cât mai rapidă a jucătorului (cu cât omoară cârtițele mai repede cu atât scorul va fi mai mare) și va fi afișat pe un ecran LCD.
 ===== Arhitecură ===== ===== Arhitecură =====
 ==== Schema bloc ==== ==== Schema bloc ====
 {{:​pm:​prj2024:​fstancu:​pm.png|}} {{:​pm:​prj2024:​fstancu:​pm.png|}}
  
-Pentru a aprinde ​LED-uri vor fi folosite timere și întreruperi, pe când butonul asociat LED-ului va trebui apăsat pentru a-l stinge.+Pentru a acționa ​LED-urile vor fi folosite timere și întreruperi.
  
-De asemenea, pentru a contoriza scorul, se va măsura tensiunea la buton printr-un pin analogic.+De asemenea, pentru a contoriza scorul, se va măsura tensiunea la buton printr-un pin analogic ​și cu ajutorul ADC.
  
 Pentru a afișa scorul pe LCD se va folosi protocolul SPI. Pentru a afișa scorul pe LCD se va folosi protocolul SPI.
Line 21: Line 21:
 ===== Design Hardware ===== ===== Design Hardware =====
 ==== Schema electrică ==== ==== Schema electrică ====
-{{:​pm:​prj2024:​fstancu:​schema_electrica.jpg?750|}}+{{:​pm:​prj2024:​fstancu:​schema_electrica_mmcc.jpg?750|}}
 ==== Componente ==== ==== Componente ====
   * 1 Arduino Uno R3 ATmega328p   * 1 Arduino Uno R3 ATmega328p
Line 31: Line 31:
   * LCD SPI 1.44' (128x128) ST7735   * LCD SPI 1.44' (128x128) ST7735
  
 +===== Design Software =====
 +<​code>​
 +#include <​avr/​io.h>​
 +#include <​avr/​interrupt.h>​
 +#include <​stdlib.h>​
 +#include <​stdbool.h>​
 +#include <​SPI.h>​
 +#include <​Adafruit_I2CDevice.h>​
 +#include <​Adafruit_ST7735.h>​
  
 +#define NO_LED 4
 +#define TFT_CS 10
 +#define TFT_RST 8
 +#define TFT_DC 9
 +#define TIMEOUT 2.490368
  
 +Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,​ TFT_DC, TFT_RST);
  
 +double score = 0;
 +volatile bool led_on = false;
 +volatile bool timeout_occurred = false;
 +volatile int sel;
 +int look_at_voltage = -1;
 +volatile int timer2_ovf_count = 0;
 +volatile int timer0_ovf_count = 0;
 +volatile int timer2_total_ticks = 0;
  
 +void init_display() {
 +    tft.initR(INITR_BLACKTAB);​
 +    tft.setTextColor(ST7735_WHITE);​
 +    tft.fillScreen(ST7735_BLACK);​
 +    tft.setTextSize(2);​
 +    tft.setRotation(2);​
 +}
  
 +ISR(TIMER1_COMPA_vect) {
 +    if (led_on == false) {
 +        timer2_total_ticks = 0;
 +        timer2_ovf_count = 0;
 +        TCNT2 = 0;
 +        sel = rand() % NO_LED + 2;
 +        PORTD |= (1 << sel);
 +        led_on = true;
 +        timeout_occurred = false;
 +        look_at_voltage = -1;
 +    }
 +}
 +
 +ISR(TIMER1_COMPB_vect) {
 +    tft.fillRect(40,​ 20, 60, 20, ST7735_BLACK);​
 +}
 +
 +ISR(TIMER2_OVF_vect) {
 +    timer2_total_ticks += 256;
 +    timer2_ovf_count++;​
 +    if (timer2_ovf_count == 76 && led_on == true) {
 +        led_on = false;
 +        timeout_occurred = true;
 +        look_at_voltage = -1;
 +        PORTD &= ~(1 << sel);
 +    }
 +}
 +
 +ISR(TIMER0_COMPA_vect) {
 +  timer0_ovf_count++;​
 +  if (timer0_ovf_count == 611) {
 +    if (OCR1A - 3906 > 15625) {
 +        OCR1A = OCR1A - 3906;
 +    }
 +    else {
 +        OCR1A = 46875;
 +    }
 +    TCNT1 = min(TCNT1, OCR1A - 1);
 +    timer0_ovf_count = 0;
 +  }
 +}
 +
 +void my_setup() {
 +    DDRD |= (1 << PD3);
 +    DDRD |= (1 << PD2);
 +    DDRD |= (1 << PD4);
 +    DDRD |= (1 << PD5);
 +
 +    TCCR1A = 0; 
 +    TCCR1B = 0;
 +    TCNT1 = 0;
 +    OCR1A = 46875;
 +    OCR1B = 15625;
 +    TCCR1B |= (1 << WGM12);
 +    TCCR1B |= (1 << CS12) | (1 << CS10);
 +    TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B);
 +
 +    TCCR2A = 0;
 +    TCCR2B = 0;
 +    TCNT2 = 0;
 +    TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
 +    TIMSK2 |= (1 << TOIE2);
 +
 +    TCCR0A = 0;
 +    TCCR0B = 0;
 +    TCNT0 = 0;
 +    OCR0A = 255;
 +    TCCR0A |= (1 << WGM01);
 +    TCCR0B |= (1 << CS02) | (1 << CS00);
 +    TIMSK0 |= (1 << OCIE0A);
 +}
 +
 +void adc_init(int port) {
 +    ADMUX |= (1 << REFS0);
 +    ADCSRA |= (1 << ADEN);
 +    ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
 +    ADMUX &= ~(1 << MUX0);  ​
 +    ADMUX &= ~(1 << MUX1);  ​
 +    ADMUX &= ~(1 << MUX2);  ​
 +    ADMUX &= ~(1 << MUX3);
 +    if (port == 0) {
 +    }
 +    else if (port == 1) {
 +        ADMUX |= (1 << MUX0);
 +    }
 +    else if (port == 2) {
 +        ADMUX |= (1 << MUX1);
 +    }
 +    else if (port == 3) {
 +        ADMUX |= (1 << MUX1) | (1 << MUX0);
 +    }
 +    else if (port == 4) {
 +        ADMUX |= (1 << MUX2);
 +    }
 +    else if (port == 5) {
 +        ADMUX |= (1 << MUX2) | (1 << MUX0);
 +    }
 +}
 +
 +int adc_read() {
 +    ADCSRA |= (1 << ADSC);
 +    while ((ADCSRA & (1 << ADSC)));
 +    int res = ADC;
 +    return res;
 +}
 +
 +void my_loop() {
 +    for (int analog_pin = 0; analog_pin < NO_LED; analog_pin++) {
 +        adc_init(analog_pin);​
 +        int analog_in = adc_read();
 +        int v_in = analog_in * (5.0 / 1024.0);
 +        if (v_in > 0) {
 +            look_at_voltage = analog_pin;
 +        }
 +
 +        int voltage_to_check = -1;
 +        if (look_at_voltage == analog_pin) {
 +            voltage_to_check = v_in;
 +        } 
 +        if (voltage_to_check == 0 && led_on == true && !timeout_occurred) {
 +            timer2_total_ticks += TCNT2;
 +            TCNT1 = 0;
 +            PORTD &= ~(1 << sel);
 +            led_on = false;
 +            look_at_voltage = -1;
 +            double react_time = 0.000064 * timer2_total_ticks;​
 +            double percent = react_time / TIMEOUT;
 +            score += (1 - percent);
 +            tft.setCursor(40,​ 20);
 +            tft.print("​+"​);​
 +            tft.print(1 - percent);
 +            tft.setCursor(40,​ 40);
 +            tft.print("​SCORE"​);​
 +            tft.fillRect(40,​ 60, 88, 128, ST7735_BLACK);​
 +            tft.setCursor(40,​ 60);
 +            tft.print(score);​
 +        }
 +    }
 +}
 +
 +int main(void) {
 +    init();
 +    init_display();​
 +    sei();
 +    my_setup();
 +    while (1) {
 +        my_loop();
 +    }
 +    return 0;
 +}
 +</​code>​
 +Pentru a interfața cu LCD-ul am folosit o bibliotecă built-in Adafruit_ST7735.h. \\
 +Timer 0 are rolul de a controla durata dintre două aprinderi succesive ale LED-urilor, inițial la 3 secunde, dar la fiecare 10 secunde, cu 0.25 secunde mai puțin până ce se ajunge la o secundă, când se resetează la durata inițială de 3 secunde. \\
 +Timer 2 are rolul de a stinge LED-ul aprins după 1.5 secunde în cazul în care butonul asociat lui nu a fost apăsat. \\
 +Timer 1 are rolul de a aprinde LED-ul pe canalul A și de a afișa scorul obținut la ultima apăsare pe canalul B. \\
 +Scorul obținut la ultima apăsare se calculează în funcție de numărul total de ticks ale Timer 2 la momentul apăsării butonului, iar scorul total este afișat în permanență și actualizat atunci când este cazul. \\
 +Pentru a verifica că un LED a fost stins datorită apăsării butonului sau am folosit ADC pentru a măsura tensiunea la buton. \\
pm/prj2024/fstancu/cristian.musat2412.1715426487.txt.gz · Last modified: 2024/05/11 14:21 by cristian.musat2412
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0