Чтобы подключить 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 может меняться например в зависимости от измеренного с помощью АЦП напряжения (я игрался просто переменным резистором, но на его месте должен быть фотодатчик, так что яркость будет меняться в зависимости от освещенности)
Но снова, чтобы не было мерцания, переключения должны делаться быстро-быстро :)
Комментариев нет:
Отправить комментарий