This shows you the differences between two versions of the page.
pm:prj2024:fstancu:cristian.musat2412 [2024/05/13 00:46] cristian.musat2412 |
pm:prj2024:fstancu:cristian.musat2412 [2024/05/27 13:06] (current) cristian.musat2412 |
||
---|---|---|---|
Line 21: | Line 21: | ||
===== Design Hardware ===== | ===== Design Hardware ===== | ||
==== Schema electrică ==== | ==== Schema electrică ==== | ||
- | {{:pm:prj2024:fstancu:schema_electrica_mmcc.jpg?300|}} | + | {{: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. \\ |