This shows you the differences between two versions of the page.
|
pm:prj2024:fstancu:cristian.musat2412 [2024/05/11 14:22] cristian.musat2412 |
pm:prj2024:fstancu:cristian.musat2412 [2024/05/27 13:06] (current) cristian.musat2412 |
||
|---|---|---|---|
| Line 13: | Line 13: | ||
| {{: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. \\ | ||