Retarded Blinkenlights

Jeden z pierwszych moich projektów, jednak staram się by pomimo mojej niewiedzy wyglądał tak, jak powinien i działał na tyle, by nie stanowić alternatywy dla materiałów wybuchowych.

Projekt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#define KEY1 (1 << PD2)

volatile uint8_t mode = 0;
volatile uint8_t changed = 0;

ISR(INT0_vect){
    _delay_ms(100);
    if(!(PIND & KEY1)){
        mode++;
        changed++;
    }
}

void main(void){
    uint8_t state = 0;

    //SLEEP
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);

    //IO
    //All PBx as outputs
    DDRC |= (1 << PC1) | (1 << PC2) | (1 << PC3) | 1 << PC4; 
    PORTC |= 0xFF; //PBx in high state (off)
    PORTD |= KEY1; //Key as input in high state (PD2)

    //INTERRUPTS
    GICR |= (1 << INT0); //Enable external 0
    sei(); //Enable interrupts

    //TIMER
    TCCR0 |= (1 << CS02) | (1 << CS00); //Timer0 at 1/1024 of IO Clock

    //ADC
    // Internal reference; Left-aligned bits in ADC
    ADMUX |= (1 << REFS1) | (1 << REFS0) | (1 << ADLAR);
    ADCSRA |= (1 << ADEN) | (1 << ADPS2); // Enable ADC with prescaler /16

    //PWM
    DDRB |= (1 << PB3);
    // Fast PWM; None-inverted mode; 1/1024 of IO Clock
    TCCR2 |= (1 << WGM21) | (1 << WGM20) | (1 << COM21) | \
             (1 << CS21) | (1 << CS20) | (1 << CS20);
    OCR2 = 255;
    _delay_ms(100);

    while(1){
        if(changed){
            state = 0;
            PORTC = 0xFF;
            changed = 0;
        }
        switch(mode){
            case 0:
                ADCSRA &= ~(1 << ADEN);
                sleep_mode();
                ADCSRA |= (1 << ADEN);
                break;
            case 1:
                ADCSRA |= (1 << ADSC); //Start ADC
                while(ADCSRA & (1 << ADSC)); //And wait for result
                OCR2 = ADCH;
                if(TCNT0 >= 100){
                    PORTC ^= (1 << (state + 1));
                    state = (state + 1) % 5;
                    TCNT0 = 0;
                }
                break;
            case 2:
                ADCSRA |= (1 << ADSC); //Start ADC
                while(ADCSRA & (1 << ADSC)); //And wait for result
                OCR2 = ADCH;
                if(TCNT0 >= 100){
                PORTC ^= (1 << (4 - state));
                state = (state + 1) % 5;
                TCNT0 = 0;
            }
            break;
            case 3:
                OCR2 = 255;
                ADCSRA |= (1 << ADSC); //Start ADC
                while(ADCSRA & (1 << ADSC)); //And wait for result
                PORTC = ~(1 << (ADCH >> 6) + 1); //256 >> 6 -> 0..3
                break;
            default:
                mode = 0;
                break;
        }
    }
}

Demo

Poza kadrem “ukryłem” przycisk oraz potencjometr. Problemy ze sterowaniem wynikają głównie z niewygody obsługi latających luzem elementów.

Podsumowanie

Wiem, że kod można napisać lepiej, a nawet zlikwidować konieczność użycia PWM i ADC (poza trybem wskaźnika “stanu” potencjometru), w końcu potencjometr sam w sobie mógłby regulować jasność. Prosiłbym także nie zrażać się pseudoangielszczyzną komentarzy, pisałem je wyłącznie dla siebie. Cały projekt jest jedynie zabawką służącą mi do nauki, stąd robienie na wyrost — może jednak przyda się też innym początkującym.