В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:
В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:
Светодиоды внутри имеют общий анод (ОА) или общий катод (ОК). То есть, для управления одной цифрой нужно 8 выводов микроконтроллера. А что же делать, когда нужно управлять, например, четырьмя цифрами? Использовать микроконтроллер с 4*8=32 выводами? Не экономично.
Для такого случая придумали динамическую индикацию. Для этого соединяем выводы, которые отвечают за включение сегментов в общую шину, а общими анодами (катодами) будем управлять через транзисторы. В отдельный момент времени горит только одна цифра. Таким образом быстро перебирая цифры на дисплее (как кадры в фильме) мы получим эффект постоянно горящего изображения. В замедленном варианте, как это происходит, можно посмотреть на картинке:
Для индикаторов с общим катодом схема аналогична, только транзисторы следует взять NPN структуры, и управляться индикатор будет высоким уровнем.
Продемонстрируем сказанное, напишем программу, которая будет перебирать числа от 0 до 9999 и выводить их на семисегментный индикатор.
#include <avr/io.h>
#include <avr/interrupt.h>
// 0 1 2 3 4 5 6 7 8 9
const unsigned char codes[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char data[4]={0x00,0x00,0x00,0x00};
unsigned char counter=0;
void pause (unsigned int a)
{ unsigned int i; for (i=a;i>0;i--); }
void init_timer (void)
{ TIMSK=(1<<TOIE0); //Enable timer overflow interrupt
TCCR0=(0<<CS00)|(1<<CS01)|(0<<CS02); //Prescaller = /1
}
void convert_data (unsigned int x)
{unsigned int temp,res;
temp=x;
res=temp/1000; //Calculate 1000-s
data[3]=codes[res];
temp=temp-res*1000;
res=temp/100; //Calculate 100-s
data[2]=codes[res];
temp=temp-res*100;
res=temp/10; //Calculaate 10-s
data[1]=codes[res];
temp=temp-res*10;
data[0]=codes[temp]; //Calculate 1-s
}
ISR (TIMER0_OVF_vect)
{PORTD=0xff;
PORTB=~_BV(counter); //Enable digit
PORTD=~data[counter]; //Write code
counter=(counter+1)%4; //Increment digit counter
TCNT0=0x00; //Clear timer
}
int main(void)
{ unsigned int x=0;
DDRD=0xff;
PORTD=0x00;
DDRB=0x0f;
PORTB=0x0f;
pause(1000); //Settle pause
init_timer(); //Init timer
sei(); //Interrupt enable
while(1)
{convert_data(x); //Conver data to codes
if (x<9999) x=x+1; //Increment data
else x=0;
pause(30000);
}
return 1;
}
Исходный код можно скачать в виде проекта под AVR Studio 4
Также, может кому пригодиться, платка для моего табло в формате .lay для программы Sprint Layout 5
avrlab.com
Динамическая индикация
Итак, девочки и мальчики, сегодня дядя Сережа расскажет о том, как соорудить динамическую индикацию в домашних условиях, че это ваще такое, и зачем оно нам.
Не вдаваясь в нудные предисловия скажем так:
Динамическая индикация – это метод отображения целостной картины через быстрое последовательное отображение отдельных элементов этой картины. Причем, «целостность» восприятия получается благодаря инерционности человеческого зрения.
На этом принципе, как вы догадываетесь, построено кино и телевидение. Не слишком занудно?
Поконкретнее.
Скажем, нам нужно организовать вывод какого-то числа на 7-сегментные индикаторы. Ну, например, мы делаем часы, или термометр, или еще чего-нибудь. Давайте посчитаем, сколько понадобится проводов, чтобы подключить индикаторы.
Кстати, индикаторы выглядят примерно вот так:
Уделим пару слов увиденному.
Перед нами – самый обычный светодиодный 7-сегментный индикатор. Вопрос: почему 7-сегментный, когда сегментов на самом деле – восемь? Ответ: потому что 8-й сегмент – это точка, которая не входит в изображение цифры и вообще, является необязательной. Бывают индикаторы и без точек.
Для нумерации сегментов используются латинские буквы от a до h . У всех всех всех 7-сегментных индикаторов в нашем бренном мире, сегменты нумеруются в том порядке, как это показано на рисунке, и никак иначе.
Теперь смотрим на схемы. Во-первых, почему их две? Да потому что индикаторы бывают разные. Бывают – с общим катодом (ОК), бывают – с общим анодом (ОА). Среди буржуйских более распространены, как ни странно, индикаторы с ОА. Но встречаются и с ОК. Так что – нужно быть осторожным, чтоб не спутать. Хотя, нам-то все равно. Нашими индикаторами будет управлять контроллер. А его можно запрограммировать как на работу с ОК так и с ОА. Тут сложностей нет.У каждого индикатора – 9 ножек. С 1 по 8 – выводы сегментов, 9-я – общий. Допустим, мы хотим отображать 4-разрядное число. Надо 4 индикатора.
Арифметика маленького Пети: 8*4 = 32. То есть, нам потребуется 32 провода (не считая общие) Неслабо? Ничуть. Тем более, учитывая, что у большинства контроллеров количество каналов ввода/вывода как раз равно 32. Как-то меня не очень греет перспектива угрохать все выводы контроллера на индикацию. А вас? Ведь надо ж еще куда-то подключить кнопочки, ручечки, датчички и пр. пр. пр. а мало ли чего? Кстати, у любимого нами AT 90 s 2313 всего 15 каналов ввода/вывода. Как здесь прикажете быть? Вот тут нам и поможет динамическая индикация. Делаем очень просто: подключаем все индикаторы параллельно. Точнее, сажаем выводы сегментов на общую шину. А общие провода оставляем раздельно.
Смотрим небольшой поясняющий мультик:
Че мы делаем?
Мы последовательно подаем напряжение на адресные входы индикаторов, и одновременно выдаем в шину данных 7-сегментный код, соответствующий индикатору, активному в данный момент.
Все. Прекращаю кормить Вас баснями. Сейчас мы пишем прогу. В этой проге мы выведем на 4-разрядный индикатор число 1234. Вот тут то нам и понадобится узнанное в предыдущей главе об оперативной памяти.
Итак, я хочу, чтобы значения цифр, выводимых на индикацию, лежали в ячейках памяти. Поскольку у индикатора 4 разряда – значит цифр тоже будет 4, и соответственно, столько же ячеек памяти. Дабы не заморачиваться, мы объединяем все четыре ячейки в переменную Digit . Итак, пишем:
.include "d:avravrasmappnotes2313def.inc" .def Temp1=R16 .def Temp2=R17 .def Temp3=R18 .def Temp4=R19 .def Temp=R20 .dseg Digit: .byte 4
Написали.
Теперь самое время определиться с портами.
Поскольку у нас на порт B уже повешены светодиоды – наверно не будет большим криминалом повесить туда еще и сегменты индикаторов.
Порт D пока что пустует – ну дык значит посадим туда общие провода. Рисуем схему:
Схему прошивки, как и договаривались, не показываем, но подразумеваем ее наличие.
В данной схеме используется два сдвоенных индикатора C562. Эти индикаторы – с общим катодом (об этом говорит буква C в названии). Будем иметь это ввиду при написании программы.
Вы можете поставить совершенно любые индикаторы, какие будут под рукой. Можно, например, поставить один счетверенный, или четыре одинарных. Также, можно ставить индикаторы с общим анодом – в программе только придется дописать пару строчек – совершенно ничего сложного…
Резисторы в шине данных взяты меньшего номинала, чем были. Зачем? Чтоб ярче светили светодиоды. Поскольку индикация динамическая, то каждый индикатор работает только 1/4 общего времени работы схемы. Глаз это воспринимает как пропорциональное уменьшение яркости свечения (т.е. – тоже в 4 раза.). Чтобы как-то компенсировать этот недостаток, на светодиоды подается завышенный ток (больше максимально допустимого). В статическом режиме этот ток может причинить светодиодам вред. Однако в динамическом режиме, кратковременные воздействия тока не столь фатальны.
Ну вот, вроде все. Теперь пишем программулину дальше.
.cseg .org 0 rjmp RESET ; Reset Handler rjmp EXT_INT0 ; IRQ0 Handler rjmp EXT_INT1 ; IRQ1 Handler rjmp TIM_CAPT1 ; Timer1 Capture Handler rjmp TIM_COMP1 ; Timer1 Compare Handler rjmp TIM_OVF1 ; Timer1 Overflow Handler rjmp TIM_OVF0 ; Timer0 Overflow Handler rjmp UART_RXC ; UART RX Complete Handler rjmp UART_DRE ; UDR Empty Handler rjmp UART_TXC ; UART TX Complete Handler rjmp ANA_COMP ; Analog Comparator Handler EXT_INT0 : ret EXT_INT1 : ret TIM_CAPT1 : ret TIM_OVF0 : ret TIM_OVF1 : ret UART_RXC : ret UART_DRE : ret UART_TXC : ret ANA_COMP : ret TIM_COMP1 : ret reset: ldi Temp1,RamEnd ;инициализация стека out SPL,Temp1 cli ldi Temp,0b11111111 ;настройка портов out ddrb,Temp ldi Temp,0b00001111 out ddrd,Temp ldi Temp,4 sts Digit ,Temp ;загрузка начальных сначений ldi Temp,3 sts Digit+1,Temp ldi Temp,2 sts Digit+2,Temp ldi Temp,1 sts Digit+3,Temp ;********************************************************* ;MAIN ;********************************************************* IndicCycle: rcall Display ;цикл индикации rjmp IndicCycle ;********************************************************* Display: ;последовательный вывод на индикацию содержимого ;переменной Digit lds Temp1,Digit ;загружаем 0-ю ячейку ldi Temp,0b00001110 ;активируем 0-й разряд ;индикации out PortD,Temp rcall Decoder ;вызываем 7-сегм. декодер out PortB,Temp1 ;выводим значение в порт rcall Delay1 ;ждем lds Temp1,Digit+1 ;и .т.д ldi Temp,0b00001101 out PortD,Temp rcall Decoder out PortB,Temp1 rcall Delay1 lds Temp1,Digit+2 ldi Temp,0b00001011 out PortD,Temp rcall Decoder out PortB,Temp1 rcall Delay1 lds Temp1,Digit+3 ldi Temp,0b00000111 out PortD,Temp rcall Decoder out PortB,Temp1 rcall Delay1 ret ;********************************************************* Decoder: ;преобразование двоичного числа ;в код 7-сегментного индикатора ldi ZL,Low(DcMatrix*2) ;инициализация массива ldi ZH,High(DcMatrix*2) ldi Temp2,0 ;прибавление переменной add ZL,Temp1 ;к 0-му адресу массива adc ZH,Temp2 lpm ;загрузка значения mov Temp1,r0 ret DcMatrix: ;массив - таблица истинности декодера ; hgfedcba hgfedcba .db 0b00111111,0b00000110 ;0,1 .db 0b01011011,0b01001111 ;2,3 .db 0b01100110,0b01101101 ;4,5 .db 0b01111101,0b00000111 ;6,7 .db 0b01111111,0b01101111 ;8,9 ;********************************************************* Delay1: ;цикл задержки push Temp1 push Temp2 ldi Temp1,0 ldi Temp2,50 d11: dec Temp1 brne d11 dec Temp2 brne d11 pop Temp2 pop Temp1 ret
Очень внимательно читаем программу.
Сначала мы настраиваем прерывания, порты и стек. Это стандартно.
Затем, мы записываем в переменную Digit число 1234.
Потом мы выходим в главный цикл программы. из этого цикла вызывается функция Display ,
которая, собственно, и занимается динамическим выведением на индикацию числа из переменной Digit .
Функция состоит из 4-х похожих кусков. Эти куски различаются лишь номером вызываемой ячейки Digit
и разрядом текущего индикатора.
Из каждого кусочка вызывается функция Decoder – 7-сегментный декодер.
Этот декодер преобразует значение из ячейки Digit в код 7-сегментного индикатора,
и передает этот код обратно функции Display , которая выводит его на индикацию.
Вот так, в общих чертах, все просто и понятно.
Далее мы сделаем из этого дела секундомер.
<<—Вспомним пройденное—-Поехали дальше—>>
Как вам эта статья? | Заработало ли это устройство у вас? |
www.radiokot.ru
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Реализация динамической индикации для матрицы с разрешением 8х8
;Частота тактового генератора для примера 4 МГц, машинный цикл 1 мкс
org 0000h ;начать выполнение программы с адреса 0000h
goto Start ;переход на метку Start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Подпрограмма обработки прерываний
org 0004h ;начать выполнение подпрограммы с адреса 0004h
movwf W_TEMP ;сохранение значений ключевых регистров
swapf STATUS,W ;
clrf STATUS ;
movwf STATUS_TEMP ;
;
movf FSR,W ;сохранение текущего значения регистра FSR
movwf FSR_osn ;в регистр FSR_osn
movf FSR_prer,W ;восстановление ранее сохраненного значения
movwf FSR ;регистра FSR из регистра FSR_prer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;загрузка содержимого регистра stolb в микросхему
;74HC595 (последовательный сдвиговый регистр)
movf stolb,W ;копирование содержимого регистра stolb
movwf var ;в регистр var
movlw .8 ;запись числа 8 в регистр scetbit, для отсчета
movwf scetbit ;переданных битов
met2 btfsc var,0 ;устанавливаем вывод ds в соответствии с
bsf ds ;значением 7-го бита регистра var
btfss var,0 ;
bcf ds ;
bsf sh_cp ;тактируем вывод sh_cp, для защелкивания данных
bcf sh_cp ;
rrf var,F ;сдвиг регистра var вправо, для подготовки
;следующего бита
decfsz scetbit,F ;декремент с условием регистра scetbit
goto met2 ;scetbit не равен нулю: переход на метку met2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;загрузка содержимого регистра INDF в микросхему
;74HC595 (последовательный сдвиговый регистр)
movf INDF,W ;копирование содержимого регистра INDF
movwf var ;в регистр var
movlw .8 ;запись числа 8 в регистр scetbit, для отсчета
movwf scetbit ;переданных битов
met1 btfsc var,7 ;устанавливаем вывод ds в соответствии с
bsf ds ;значением 7-го бита регистра var
btfss var,7 ;
bcf ds ;
bsf sh_cp ;тактируем вывод sh_cp, для защелкивания данных
bcf sh_cp ;
rlf var,F ;сдвиг регистра var влево, для подготовки
;следующего бита
decfsz scetbit,F ;декремент с условием регистра scetbit
goto met1 ;scetbit не равен нулю: переход на метку met1
;
bsf st_cp ;тактируем вывод st_cp, для передачи загруженных
bcf st_cp ;байтов на выходные линии микросхем 74HC595
;
bcf STATUS,C ;сброс бита C регистра статус перед сдвигом
rrf stolb,F ;сдвиг влево регистра stolb
;
incf FSR,F ;инкремент регистра FSR, подготовка следующего
;регистра для отправки данных на 74HC595
decfsz shet,F ;декремент с условием регистра shet
goto exxit ;регистр shet не равен 0: переход на exxit
movlw data1 ;регистр shet равен 0: запись адреса первого
movwf FSR ;регистра хранения иннформации в регистр FSR
movlw .8 ;запись числа 8 в регистр shet, для ведения
movwf shet ;счета столбцов
movlw b’10000000′ ;запись двоичного числа 10000000 в
movwf stolb ;регистр stolb, для включения 1-го столбца
;
exxit bcf INTCON,T0IF ;сброс флага прерывания по переполнению TMR0
movlw .124 ;запись числа 124 в регистр таймера TMR0
movwf TMR0 ;
;
movf FSR,W ;сохранение текущего значения регистра FSR
movwf FSR_prer ;в регистр FSR_prer
movf FSR_osn ,W ;восстановление ранее сохраненного значения
movwf FSR ;регистра FSR из регистра FSR_osn
;
swapf STATUS_TEMP,W ;восстановление содержимого ключевых регистров
movwf STATUS ;
swapf W_TEMP,F ;
swapf W_TEMP,W ;
;
retfie ;выход из подпрограммы прерывания
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Основная программа
Start …………….. ;первоначальная настройка регистров
…………….. ;специального назначения
……………..
bsf STATUS,RP0 ;запись двоичного числа 11010011 в регистр
movlw b’11010010′ ;OPTION_REG, тем самым устанавливаем внутренний
movwf OPTION_REG ;источник тактового сигнала для TMR0
bcf STATUS,RP0 ;включаем предделитель перед TMR0
;устанавливаем коэффициент предделителя 1:8
;
movlw .8 ;запись числа 8 в регистр shet, перед запуском
movwf shet ;прерываний по переполнению TMR0, выполняется
;однократно, после включения питания
movlw b’10000000′ ;запись двоичного числа 10000000 в
movwf stolb ;регистр stolb, для включения 1-го столбца
;выполняется однократно, после включения питания
;
movlw data1 ;запись адреса первого регистра (регистры хранения
movwf FSR_prer ;информации) в регистр FSR_prer, выполняется
;однократно, после включения питания
;
movlw .8 ;очистка 8-ми регистров вывода информации на
movwf tmp ;матрицу, равнозначно выключению
movlw data1 ;матрицы
movwf FSR ;
met3 clrf INDF ;
incf FSR,F ;
decfsz tmp,F ;
goto met3 ;
;
bcf INTCON,T0IF ;сброс флага прерывания по переполнению TMR0
bsf INTCON,T0IE ;разрешение прерываний по переполнению TMR0
bsf INTCON,GIE ;разрешение глобальных прерываний
;
m1 movlw data1 ;пример вывода буквы R
movwf FSR ;
movlw b’00000000′ ;
movwf INDF ;
incf FSR,F ;
movlw b’01111111′ ;
movwf INDF ;
incf FSR,F ;
movlw b’00001001′ ;
movwf INDF ;
incf FSR,F ;
movlw b’00011001′ ;
movwf INDF ;
incf FSR,F ;
movlw b’00101001′ ;
movwf INDF ;
incf FSR,F ;
movlw b’01000110′ ;
movwf INDF ;
incf FSR,F ;
movlw b’00000000′ ;
movwf INDF ;
incf FSR,F ;
movlw b’00000000′ ;
movwf INDF ;
;
…………….. ;
…………….. ;
…………….. ;
;
end ;конец всей программы
;
radiolaba.ru
Для того чтобы отобразить многоразрядное число на индикаторе с ним нужно предварительно провести хитрую манипуляцию, заключающуюся в разбивке числа на его составляющие. В качестве примера приведу отображение числа 1234 на счетверенный семисегментный индикатор с общим анодом.
Dim W As Integer
Dim N1 As Byte
Dim N2 As Byte
Dim N3 As Byte
Dim N4 As Byte
Dim M1 As Integer
Dim M2 As Integer
Dim M3 As Integer
Dim M4 As Integer
DDRC = &B11111111
DDRD = &B11111111
Здесь я поставил на выход все ноги порта C и D, но можно оставить только те которые будут использоваться в качестве выходов, к примеру так:
DDRC=&B00001111 и DDRD = &B01111111 (четыре первых ноги порта С под аноды и шесть первых порта D под сегменты).
Затем присваиваем переменной W то значение, которое собираемся вывести на индикатор:
W = 1234
В основном цикле программы присваиваем переменным М значение переменной W, я делаю так:
M1 = W
M2 = M1
M3 = M1
M4 = M1
После присвоения переменным значения начинаем работать с
каждой из них, преобразуя таким образом, чтобы в переменную N попало то значение, которое будет
отображаться на индикаторе: в переменной N1 должна оказаться «1», в N2 – «2», в N3 – «3», а в N4 – «4».
M1 = M1 / 1000 ‘ M1 = 1234/1000 = 1,234
N1 = Abs (m1) ‘ N1 = Abs (1,234) = 1
Для присвоения двойки переменной N2 операция будет немного сложнее:
M2= M2 Mod 1000 ‘ M2 =1234 Mod 1000 = 234
M2 = M2 / 100 ‘ M2 = 234 / 100 = 2,34
N2= Abs (m2) ‘ N2 = Abs (2,34) = 2
Для начала функцией Mod мы возвращаем переменной первые три
цифры числа (остаток от деления на 1000), а дальше все как в первом случае.
С двумя последними разрядами практически тоже самое:
M3 = M3 Mod100
M3 = M3 / 10
N3 = Abs(m3)
M4 = M4 Mod 10
N4= Abs(m4)
Теперь в наших переменных лежат те значения, которые мы хотим отобразить, самое время микроконтроллеру подрыгать ногами и вывести эти значения на индикатор, для этого вызываем подпрограмму обработки индикации:
Gosub Led
Процессор перепрыгнет на подпрограмму с меткой Led:
Led:Portc =&B00001000
Здесь подаем высокий уровень на PORTC.3 , к этой ноге у нас подсоединен анод первого разряда. Затем выбираем, какие сегменты необходимо зажечь, чтобы отобразить значение первой переменной. Она у нас единица поэтому ноль будет на ногах Portd.1и Portd.2, что соответствует сегментам B и С индикатора.
Select Case N1
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select
Waitms 5
После того как зажгли нужные сегменты ждем 5 мс и переходим к отображению следующих чисел:
Portc = &B00000100
Select Case N2
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select
Waitms 5
Portc = &B00000010
Select Case N3
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select
Waitms 5
Portc = &B00000001
Select Case N4
Case 0 : Portd = &B11000000
Case 1 : Portd = &B11111001
Case 2 : Portd = &B10100100
Case 3 : Portd = &B10110000
Case 4 : Portd = &B10011001
Case 5 : Portd = &B10010010
Case 6 : Portd = &B10000010
Case 7 : Portd = &B11111000
Case 8 : Portd = &B10000000
Case 9 : Portd = &B10010000
End Select
Waitms 5
После отображения информации на индикаторе необходимо возвратится в основной цикл программы, где нужно завершить цикл и обозначить конец программы.
Вот что получим в итоге:
За счет маленькой задержки переключения не будут заметны человеческому глазу и мы увидим целое число 1234.
Скачать исходник и проект в протеусе можно ниже:
avrproject.ru
Часто бывает ситуация, когда надо выполнить простую задачу с которой справится даже грошовый контроллер вроде ATTiny12, но вот незадача — нужна индикация, а под какой нибудь семисегментный индикатор придется убить дофига выводов, а их всего восемь, из которых два питание, а один Reset. Можно, конечно, взять просто МК потолще, но мне больше по душе разные схемотехнические извраты. Вот и тут камрад Kalobyte подкинул ссылку на прикольную схему управления тройным (а в перспективе хоть десятерным) семисегментным индикатором по одному проводу.
Индикатор зажигается посредством сдвигового регистра 74HC164, всего таких регистров можно навесить очень много, просто соединив по цепи несколько регистров. Если отбросить RESET, то для проталкивания данных в регистр нужны две линии — Data и Clock. Путем небольшого изврата можно эти две линии обьединить в одну.
Для начала немного теории
Обычная RC цепочка. Отличается тем, что напряжение на конденсаторе не может измениться мгновенно. Время заряда и разряда зависит от емкости кондера и сопротивления резистора. Оценить его можно по постоянной времени T=R*C , это время за которое заряд изменится примерно втрое (в е=2.7 раз).
Если подадим короткий импульс, по времени намного меньше чем постоянная времени, то напряжение на конденсаторе изменится совершенно незначительно. Так, дрыгнется да и только. То же относительно короткой паузы. Незначительный провал и все возвращается на круги своя. Чуете куда я клоню?
Правильно!
У нас у регистра есть линия данных (Data) и линия строба (Clock). Когда уровень на Clock меняется с нуля на единицу (передний фронт) с линии Data считывается текущий уровень. Их можно разнести по разные стороны RC фильтра. И одни сигналы передавать узкими, другие широкими импульсами.
А теперь думаем. Строб един для всех регистров, а данные различные для разных битов. Поэтому строб будет всегда один, а данные будут меняться. Делаем строб узким и снимаем его до RC цепи. Узкие импульсы не могут пройти сквозь RC цепь, а данные передаем широкими импульсами, которые спокойно перезаряжают конденсатор. Данные мы снимаем после RC цепи.
Получается вот такая схема:
Осталось теперь хитрым образом формировать биты на линии. Чтобы было понятней, я нарисовал поясняющую картинку, на которой показана передача байта 10011010.
Ниже показаны типовые формы единицы и нуля. Как видишь, тут идет длинный импульс данных, который призван зарядить/разрядить конденсатор до нужного логического уровня, а потом краткий, как иголка, импульс строба. Причем в стробе нам важен только передний фронт.
Вот, взяли и применив копеечный регистр, сэкономили на микроконтроллере. Зажопили кучу выводов и получили моральное удоволетворение от извращенского метода 🙂 Попутно вкурив в очередной пример применения конденсатора и интегрирующей цепочки.
Ссылки по теме:
easyelectronics.ru
Динамическая индикация 9 разрядного индикатора по последовательной шине.
Всем привет.
Попался мне вот этот 9 разрядный семисегментный индикатор с последовательной шиной, выполненный на двух микросхемах 74HC595D. Индикатор стоял в телефоне с АОН русь 27, до того как по нем прошлась гроза, да так что, восстановлению телефон не подлежал, индикатору тоже досталось, сгорели все 2 микрухи.
Проверить индикатор вообще оказалось просто. При рабочих микросхемах и поданном питании, если докоснуться к линиям данных, на индикаторе бегает всякая ерунда(при условии что линии висят в воздухе). Индикатор с динамической индикацией, то есть для отображения информации последовательно зажигаем каждую цифру на определенное время, например на 2 милисекунды. Сложностей с ним почти нет, если не учитывать что выходов у 74HC595D восемь, а цифр на индикаторе девять. Девятая цифра подключена к выводу Q7″ последовательный выход данных. Какие выводы и для чего они нужны, читайте в DATASHEET на микросхему.
Здесь я просто расскажу как я решил проблему вывода 8 и 9 цифры. Работать с этими микросхемами просто. Для того чтобы загрузить данные, устанавливаем бит данных на выводе DS serial data input, и сдвигаем (загружаем) положительным импульсом SHcp shift register clock input. Для сохранения и вывода данных в регистре подаем положительный импульс на STcp storage register clock input. Неувязка получается при выводе 8 и 9 цифры. Индикатор здесь с общим катодом, чтобы включить, например 4 цифру, нужно загрузить число 0b11110111, при этом на выходе Q7″ будет сдвинутое значение из Q6, в данном случае 1, то что нужно. Значение Q6 по положительному импульсу SHcp сдвигается в Q7″. При выводе восьмой цифры ноль сдвигается в Q7 и Q7″ (Q7″=1 0b10111111 Q7″=0 0b01111111), в итоге у нас включены 8 и 9 разряд, импульсом STcp сохраняем и выводим это значение, теперь если дать импульс SHcp копейка из Q6 переместится в Q7″, 9 разряд потушен! По программе импульсы STcp и SHcp идут синхронно. Для включения 9 разряда нужно загрузить число 0b11111111, вывести его импульсом STcp, затем загружаем в регистр нули до тех пор, пока ноль из Q6 не переместится в Q7″.
Может быть все это можно сделать проще. Вот у меня индикатор заработал и я на этом успокоился. Сейчас он работает в часах. Хочу сказать об одном маленьком минусе. В часах есть возможность регулировки яркости индикатора. Период включения каждой цифры 2 ms. Что бы уменьшить яркость индикатора цифру можно включать например на 1 ms, оставшиеся время она будет потушена. При этом яркость свечения снижается. Если пользоваться таким способом, то из за того что 9 разряд (Q7″) самый нагруженный, на низкой яркости индикатора яркость девятой цифры чуть меньше. Про исходник. Сразу предупреждаю программист я никакой, поэтому просьба не ругать. А вот файлы для PROTEUS.
Вопросы складываем тут.
Как вам эта статья? | Заработало ли это устройство у вас? |
www.radiokot.ru
Динамическая индикация — это метод отображения целостной картины путем последовательного отображения отдельных элементов этой картины.
Эта статья научит вас выводить на дисплее сегментного индикатора одновременно несколько цифр. Достигается это за счет «инерционности» человеческого зрения.
В предыдущей статье мы разобрали пример работы с одним разрядом. В ней использовалась статическая индикация. Увеличить количество разрядов можно, если использовать еще один дисплей и еще семь выводов микроконтроллера, но гораздо выгоднее отображать разряды индикатора по очереди с высокой скоростью. Так, чтобы это стало не заметно для глаза.
Для этого на индикаторе должны быть объединены аноды разрядов и катоды сегментов.
Индикатор с динамической индикацией
Все примеры выполнены с использованием EduBoard и TutorShield. На нашем шилде именно такой индикатор. Для его использования установите перемычки, выделенные на рисунке:
Перемычки для включения индикатора
Принципиальная схема подключения индикатора:
Принципиальная схема подключения индикатора
Для начала выведите два разных числа на индикаторе, используя следующий код:
#define DIG1 4 #define DIG2 5 #define A 6 #define B 7 #define C 8 #define D 9 #define E 10 #define FF 11 #define G 12 #define TAKT 1000 void setup() { pinMode(A, OUTPUT); pinMode(B, OUTPUT); pinMode(C, OUTPUT); pinMode(D, OUTPUT); pinMode(E, OUTPUT); pinMode(FF, OUTPUT); pinMode(G, OUTPUT); pinMode(DIG1, OUTPUT); pinMode(DIG2, OUTPUT); digitalWrite(A,HIGH); digitalWrite(B,HIGH); digitalWrite(C,HIGH); digitalWrite(D,HIGH); digitalWrite(E,HIGH); digitalWrite(FF,HIGH); digitalWrite(G,HIGH); digitalWrite(DIG1,HIGH); digitalWrite(DIG2,HIGH); } void loop() { digitalWrite(DIG1,LOW); Show(3); delay(TAKT); Clean(); digitalWrite(DIG1,HIGH); digitalWrite(DIG2,LOW); Show(4); delay(TAKT); Clean(); digitalWrite(DIG2,HIGH); } void Show(int digit) { switch(digit) { case 0: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(FF,LOW); } break; case 1: { digitalWrite(B,LOW); digitalWrite(C,LOW); } break; case 2: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(G,LOW); } break; case 3: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(G,LOW); } break; case 4: { digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 5: { digitalWrite(A,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 6: { digitalWrite(A,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 7: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); } break; case 8: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 9: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; } } void Clean() { digitalWrite(A,HIGH); digitalWrite(B,HIGH); digitalWrite(C,HIGH); digitalWrite(D,HIGH); digitalWrite(E,HIGH); digitalWrite(FF,HIGH); digitalWrite(G,HIGH); }
Основной цикл этого примера линеен и прост. Сначала в первом разряде на одну секунду выводится цифра 3, затем во втором 4.
Если уменьшить интервал TAKT, то можно добиться того, чтобы за счет инерционности зрения казалось, что цифра 34 горит непрерывно. Какую длительность поставить? С одной стороны она должна быть как можно больше, чтобы осталось время на выполнение других задач, с другой стороны как можно меньше, чтобы глаз не видел мерцания.
Эмпирическим путем установлено, что обновления изображения с частотой 50 раз в секунду вполне достаточно для приемлемого восприятия. Это значит, что идентификатор TAKT должен иметь значение меньше 10мс. Попробуйте запустить программу с этим числом, а затем попробуйте увеличить его до 20мс и понаблюдайте за разницей.
Не забудьте после оставить значение TAKT 10.
При динамической индикации разряды горят не на протяжении всего времени и светятся лишь на половину (при двух разрядах). Это нужно учитывать при подборе токоограничивающих резисторов индикатора.
Также можно управлять яркостью. В предыдущем примере добавьте еще один идентификатор BRIGHT и замените основной цикл:
#define BRIGHT 2 void loop() { digitalWrite(DIG1,LOW); Show(3); delay(BRIGHT); Clean(); digitalWrite(DIG1,HIGH); delay(TAKT-BRIGHT); digitalWrite(DIG2,LOW); Show(4); delay(BRIGHT); Clean(); digitalWrite(DIG2,HIGH); delay(TAKT-BRIGHT); }
Переопределение идентификатора BRIGHT от 0 до 10 будет приводить к изменению яркости. На время BRIGHT индикатор будет включен, а на время TAKT-BRIGHT выключен. Надо понимать, что эта константа будет справедлива только для ситуаций, когда TAKT равен 10мс.
На данный момент программа в основном цикле занимается только тем, что выводит числа. Для дальнейшего ее упрощения нужно понимать для чего будет использоваться сегментный индикатор.
С применением индикатора можно сделать, например, вольтметр. В нем блок АЦП микроконтроллера периодически делает замер и сохраняет результат в память. Это результат можно из памяти вытащить и показать на индикаторе. То есть желательно иметь функцию, которая будет разделять двухразрядные числа две цифры. Назовем эту функцию DisplayMath():
void DisplayMath(int data) { dig1 = dig2 = 0; if (data < 100) { while (data >= 10) { data -= 10; dig1++; } dig2 = data; } }
В эту функцию будет передаваться число, которое нужно разобрать по разрядам. Результат обработки функция записывает в глобальные переменные dig1 и dig2.
Для начала происходит проверка входных данных. Если переданное значение больше 100, то функция не будет делать ничего и переменные dig1 и dig2 обнулятся. Если меньше 100, то запускается пересчет.
Далее локальная переменная data декрементируется с шагом 10 и подсчитывается количество итераций. Например, если функции передано значение 48 цикл while будет выполнен 4 раза и концу его выполнения переменные будут иметь состояние dig1=4, data=8. Далее остается только записать остаток data в переменную dig2.
В итоге после запуска этой функции в переменных dig1 и dig2 окажутся значения разрядов.
Сам вывод цифр на дисплей тоже лучше вынести в отдельную функцию. Назовем ее DisplayShow():
void DisplayShow() { digitalWrite(DIG1,LOW); Show(dig1); delay(BRIGHT); Clean(); digitalWrite(DIG1,HIGH); delay(TAKT-BRIGHT); digitalWrite(DIG2,LOW); Show(dig2); delay(BRIGHT); Clean(); digitalWrite(DIG2,HIGH); delay(TAKT-BRIGHT); }
Окончательный код с использованием этих функций:
#define DIG1 4 #define DIG2 5 #define A 6 #define B 7 #define C 8 #define D 9 #define E 10 #define FF 11 #define G 12 #define TAKT 10 #define BRIGHT 2 int dig1 = 0; int dig2 = 0; void setup() { pinMode(A, OUTPUT); pinMode(B, OUTPUT); pinMode(C, OUTPUT); pinMode(D, OUTPUT); pinMode(E, OUTPUT); pinMode(FF, OUTPUT); pinMode(G, OUTPUT); pinMode(DIG1, OUTPUT); pinMode(DIG2, OUTPUT); digitalWrite(A,HIGH); digitalWrite(B,HIGH); digitalWrite(C,HIGH); digitalWrite(D,HIGH); digitalWrite(E,HIGH); digitalWrite(FF,HIGH); digitalWrite(G,HIGH); digitalWrite(DIG1,HIGH); digitalWrite(DIG2,HIGH); } void loop() { DisplayMath(34); DisplayShow(); } void DisplayMath(int data) { dig1 = dig2 = 0; if (data < 100) { while (data >= 10) { data -= 10; dig1++; } dig2 = data; } } void DisplayShow() { digitalWrite(DIG1,LOW); Show(dig1); delay(BRIGHT); Clean(); digitalWrite(DIG1,HIGH); delay(TAKT-BRIGHT); digitalWrite(DIG2,LOW); Show(dig2); delay(BRIGHT); Clean(); digitalWrite(DIG2,HIGH); delay(TAKT-BRIGHT); } void Show(int digit) { switch(digit) { case 0: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(FF,LOW); } break; case 1: { digitalWrite(B,LOW); digitalWrite(C,LOW); } break; case 2: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(G,LOW); } break; case 3: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(G,LOW); } break; case 4: { digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 5: { digitalWrite(A,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 6: { digitalWrite(A,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 7: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); } break; case 8: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(E,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; case 9: { digitalWrite(A,LOW); digitalWrite(B,LOW); digitalWrite(C,LOW); digitalWrite(D,LOW); digitalWrite(FF,LOW); digitalWrite(G,LOW); } break; } } void Clean() { digitalWrite(A,HIGH); digitalWrite(B,HIGH); digitalWrite(C,HIGH); digitalWrite(D,HIGH); digitalWrite(E,HIGH); digitalWrite(FF,HIGH); digitalWrite(G,HIGH); }
Обратите внимание насколько прост основной цикл loop(). В нем всего две строки. Если вы измените значение 34 на любое другое, то оно будет отображено на индикаторе.
Для демонстрации работы индикатора запустим счетчик с произвольной скоростью и будем выводить его состояние. Замените основной цикл и добавьте две переменных:
int value = 0; int i = 0; void loop() { i++; if (i == 7) { i = 0; value ++; if (value == 100) value = 0; } DisplayMath(value); DisplayShow(); }
Вы увидите как на экране побегут цифры от 0 до 99.
В результате выполнения работы вы написали еще две функции для работы с индикатором. В перспективе мы уберем ее в отдельный файл, который легко будет подключить и начать работать с сегментным индикатором.
Остальные статьи цикла можно найти здесь.
Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.
www.customelectronics.ru