четверг, 17 ноября 2011 г.

Оч.умелое изнутри (1)..

Чтобы подключить 4 цифры к контроллеру надо в самом простом случае 12 выводов контроллера:

Цифра - фактически 8 светодиодов в одном корпусе, расположенных так, чтобы визуально образовывать кусочки цифр (7 сегментов и точка), соединенных вместе катодом или анодом

Чтобы не было такой мешанины в схеме (и при разводке) удобнее использовать сразу много цифр в одном корпусе; в библиотеке игла не нашлось с 4-ю, но как-то так:

Каждый сегмент будет гореть, если к нему подать напряжение (резисторы нужны, я их поленился нарисовать); если у цифры общий катод, то этим катодом ее надо соединить с "землей" и на выводы a - g, dp подать напряжение - кусочек цифры загорится; или в конкретном случае на выводы 12 - 19 контроллера надо подать "1", а на 8 - 11 - "0"

Только если так сделать (скажем, подать "1" на a, b, c, d, e, f и "0" на CA1, CA2, CA3), то зажгутся 3 цифры "0", что явно не очень полезно. Поэтому используют такую фигню, которая связана с инертностью человеческого зрения: на CA1 подаем "0", на остальные катоды - "1", на сегменты подаем комбинацию для вывода нужной цифры - на экране горит нужная 1-я цифра, 2-я и 3-я не горят вообще; потом подаем "0" на СА2 и "1" на остальные катоды, выставляем сегменты для другой нужной цифры - горит 2-я цифра, 1-я и 3-я не горят вообще

Точно так же - с 3-й цифрой

А теперь (прямо как моргунов обьяснял за танцы) делаем все это быстро-быстро :) Формально ничего не меняется, горит в каждый момент времени только одна цифра; но глаз видит все 3, прицем каждая показывает свое значение.. Только переключать катоды надо быстро, чтобы не было мерцания

Вот куски кода из вчерашней фотографии; код сложнее, чем мог бы быть, потому что: я же сильно хотел попользовать меги, которые у меня болтаются без дела; они мелкие шо зараза, поэтому разводка платы должна быть чем прощеЮ тем лучше - а с таким условием проще всего если разные сегменты подключены к разным портам - чтобы зажечь скажем цифру "5" мне надо выставить по несколько бит каждому из 3-х портов (не поменяв при этом те биты, которые не относятся к цифрам, т.е. PORTC |= 0x34 не прокатит, потому что обнулит "не те" биты):

// digit 5
#define PORTB_5 ((1 << SEG_D_SHIFT) | (0 << SEG_E_SHIFT) | (0 << SEG_DP_SHIFT))
#define PORTC_5 ((1 << SEG_A_SHIFT) | (1 << SEG_F_SHIFT))
#define PORTD_5 ((0 << SEG_B_SHIFT) | (1 << SEG_C_SHIFT) | (1 << SEG_G_SHIFT))

const u08 pb_d [] = {PORTB_0, PORTB_1, PORTB_2, PORTB_3, ...

Чтобы не мешать остальным "пользователям", которые подключены к тем же портам, цифры выводятся вот так:

void showDigit(u08 digit) {
    PORTB &= ~PORTB_E; // обнуляем все биты, которые используются цифрами
    PORTC &= ~PORTC_E;
    PORTD &= ~PORTD_E;

    PORTB |= pb_d[digit]; // зажигаем биты, которые нужно показать
    PORTC |= pc_d[digit];
    PORTD |= pd_d[digit];
}

т.е. я сначала выключаю те линии, которые используются цифрами (и только их!), а потом зажигаю нужные сегменты (в массивах pbd, pcd, pd_d как раз и используются вот те дефайны для цифр)

Т.е. с формированием цифр понятно; теперь как их показывать: я использую для этого счетчик Counter0, он 8-битный, т.е. он тупо прибавляет себе единичку на каждый тик, до 256, вот я в каждом тике и меняю - какой катод должен быть установлен в "0" (та цифра будет гореть). Прерывание у меня возникает, когда счетчик доходит до заданного значения, почему именно так - я сейчас уже и не помню.. можно в теории и просто по переполнению было сделать

Но кроме этого тут еще один финт ушами: я завел volatile переменную, которая увеличивается с каждым тиком счетчика (volatile - чтобы ее никакая зараза не соптимизировала, потому что ее значение меняется в прерывании, т.е. непредсказуемым образом); доходя до определенного значения она просто обнуляется - все. Ну, не все - есть еще одна переменная, brightnes (ну сэкономил я почему-то на букве, так и болтается), значение которой меняется каким-то сейчас не очень важно каким именно способом

ISR(TIMER0_COMPA_vect) {
    if (++pwm >= pwm_step)
        pwm = 0;
    setNextDigit(show_point);
}

Зачем она нужна? Затем:

void setNextDigit(BOOL pointVisible) {
    switch (digit++) {
        case 0:
            CAT_4 = 1;
            showDigit(d1);
            CAT_1 = brightnes < pwm;

            break;
        case 1:
            CAT_1 = 1;
            showDigit(d2);
            CAT_2 = brightnes < pwm;

            showPoint(pointVisible);
            break;
        case 2:
            CAT_2 = 1;
            showDigit(d3);
            CAT_3 = brightnes < pwm;

            break;
        case 3:
            CAT_3 = 1;
            showDigit(d4);
            CAT_4 = brightnes < pwm;

            break;
    }
    digit = digit % 4;
}

Допустим, pwm считает от 0 до 256 (т.е. pwm_step зададим 256) - а brightnes пусть установлена в 100: значит 100 вызовов функции из 256 на катодах будет "0" (цифры будут гореть), оставшееся время - "1", цифры погашены

Что это означает с учетом опять же инерционности нашего зрения? Что визуально яркость цифр будет меньше - т.е. имеем управление яркостью. Вообще говоря, получили софтварный ШИМ (PWM) - широтно-импульсную модуляцию. А brightnes может меняться например в зависимости от измеренного с помощью АЦП напряжения (я игрался просто переменным резистором, но на его месте должен быть фотодатчик, так что яркость будет меняться в зависимости от освещенности)

Но снова, чтобы не было мерцания, переключения должны делаться быстро-быстро :)

Комментариев нет:

Отправить комментарий