управление регистрами 74hc595 и 4094 с 7 сегментными индикаторами
Февраль 21, 2010
Иногда возникает задача, когда не хватает выводов для светодиодов или индикаторов, вобщем там, где требуется что-то задействовать из дискретных исполнительных или указательных устройств в большом количестве. Для этого используются расширители портов на регистрах.
В данной статье разбираются 2 регистра, управление которыми отличается немного, хотя они по своему назначению совпадают. Эти регистры относятся к регистрам с последовательной загрузкой и параллельным выходом с выходом переноса, который обозначается QS или Q7’. При помощи этого выхода можно задействовать следущий разряд регистра, но это будет в следущем уроке.
Разберем самый простой индикатор 4094. Он использует всего 2 входа. Вход STB (строб) подтянут к лог1 и при каждом такте производит вывод данных на параллельную шину.
Работает это так.
На входе данных D выставляется 1 или 0, потом тактовый вход CLK из 0 переходит в 1 и потом снова в 0. При этом на выходе происходит моментальное изменение состояния. Далее на входе данных снова выставляется нужный уровень и дергается клок.
Для надежности перед записью данных в регистр его надо инициализировать. Инициализация проходит так же в регистрах самого мк и означает запись в него нулей по длине его памяти. В данном случае это 8 бит. Всеразличная периферия типа синтезаторов частот так же имеет в себе сдвиговые регистры и они так же требуют инициализации, что обычно указывается в даташитах. Однако там регистры могут быть и по 10,12,15 и более бит.
Для простоты работы будем считать, что сферический регистрв в протеусе уже инициализирован.
Чтобы код был понятен и мы не оперировали непонятными цифрами и знаками, мы делаем дефайны выводов, портов, и даже действий.
Из предыдущих статей понятно, что конструкция var|=1<<n; выставляет бит с номером n в лог1.
Конструкция var & = ~(1<<n); выставляет сначала на нужном месте n лог1, потом весь байт инвертируется и на этом месте остается 0. Потом происходит логическое И с содержимым регистра. Где есть 0, в том месте в порту и будет 0.
1: #define CD4094_PORT PORTD
2: #define CD4094_DDR DDRD
3: #define CD4094_PIN_DATA 0
4: #define CD4094_PIN_CLK 1
5:
6: #define CD4094_DATA_ON CD4094_PORT|=1<<CD4094_PIN_DATA;
7: #define CD4094_DATA_OFF CD4094_PORT&=~(1<<CD4094_PIN_DATA);
8: #define CD4094_CLK {CD4094_PORT|=1<<CD4094_PIN_CLK;CD4094_PORT&=~(1<<CD4094_PIN_CLK);}
Использование обозначения PIN говорит о том, что речь идет конкретно о выводе, который мы определили. В нашем случае регистр 4094 подключен к порту D с выводами 0 и 1.
В строках 6 и 7 идет определение действия по выставлению вывода данных в 1 или 0.
В последней строке один макрос определяет сразу 2 действия. Т.к. Скорость работы мк всего 1мгц, то между операциями пауза не требуется и для экономии кода можно не ставить макрос задержки.
Дальше уже идет глобальная переменная, так же знакомая из прошлой статьи, где храняться коды цифр. Только в этот раз процесс вывода в порт немного усложнен. Надо не сразу выводить 8 бит, а пропустить их по очереди. Для организации очередей используется цикл и сдвиг влево. Сдвигать надо 8 раз.
Для начала сконфигурируем регистр направления CD4094_DDR на выход, записав в нужные места по единице.
CD4094_DDR = (1<<CD4094_PIN_DATA)|(1<<CD4094_PIN_CLK);
1: dig = cathode[0];
2:
3: for(i=8;i>0;i--){
4:
5: if(dig & 0x80){ CD4094_DATA_ON } else {CD4094_DATA_OFF}
6:
7: dig <<=1;
8: CD4094_CLK
9: }
Строка 0 - записываем во временную переменную dig код цифры 0. Это необходимо, т.к. массив только для чтения, а операция сдвига будет менять содержимое.
Строка 3 - организуем очередь от 0 до 8, т.к. бит 8.
Напомню, что код цифры 0 в двоичной системе выглядит как 0011 1111, где сегмент а будет самый правый бит. Биты нумеруются слева направо 76543210. Тут используется принцип первый ушел, первый и пришел. Операция AND служит для получения логического условия для работы оператора if, который срабатывает тогда, когда там не 0. Код 0х80 в двоичной системе выглядит как 1000 0000.
Получается
0 0111111
1 0000000
------------
0
Т.к. в if - 0, то макрос CD4094_DATA_ON не сработает. В этом случае сработает else { CD4094_DATA_OFF}. Это нужно для того, чтобы сбросить в 0 пин, если следущий бит в переменной dig будет 0. Если же не сбросить, то этот бит будет засчитан как 1, т.к. с предыдущего бита состояние вывода не изменилось. В результате этого цифрф будут неправильно показываться, что усложнит отладку. Или же вместо цифр будет набор горящих сегментов.
Строка 7 dig <<=1; сдвигает на 1 разряд влево.
В оригинале было
0011 1111 и при сдвиге влево на 1 мы получаем
0111 1110 - этот 0 добавляется автоматом, чтобы байт всегда состоял из 8 бит.
При следущем проходе цикла у нас будет уже 1111 1100.
Прогнав цикл 8 раз, в переменной dig будет число 0000 0000. При повторном вызове процедуры эти нули будут снова переписаны на код какой-то цифры.
Теперь напишем 2 функции: инициализацию регистра и отправку цифры.
1: oid cd4094(char tmp){
2: char i, dig;
3:
4: dig = cathode[tmp];
5: for(i=8;i>0;i--){
6:
7: if(dig & 0x80){ CD4094_DATA_ON } else {CD4094_DATA_OFF}
8:
9: dig <<=1;
10: CD4094_CLK
11: }
12:
13: }
14:
15: void cd4094_init(void){
16: char i;
17:
18: for(i=0;i<8;i++){ CD4094_DATA_OFF; CD4094_CLK }
19: }
Теперь рассмотрим второй регистр 74hc595. Этот регистр хорош тем, что у него почти все выходы на одной стороне корпуса, что облегчает разводку платы под светодиоды или реле. Однако этот регистр управляется немного сложнее. В нем задействуется 3й вход стробирования и еще этот регистр можно сбрасывать подачей лог0 на вход /MR и при этом дернуть строб.
Вот мы это все и напишем.
Для начала определим порт и пины.
1: //74hc595
2: #define HC595_PORT PORTC
3: #define HC595_DDR DDRC
4: #define HC595_PIN_DATA 1
5: #define HC595_PIN_CLK 0
6: #define HC595_PIN_STROBE 2
7: #define HC595_PIN_RESET 3
8:
9: #define HC595_DATA_ON HC595_PORT|=1<<HC595_PIN_DATA;
10: #define HC595_DATA_OFF HC595_PORT&=~(1<<HC595_PIN_DATA);
11: #define HC595_CLK {HC595_PORT|=1<<HC595_PIN_CLK;HC595_PORT&=~(1<<HC595_PIN_CLK);}
12: #define HC595_STROBE {HC595_PORT|=1<<HC595_PIN_STROBE;HC595_PORT&=~(1<<HC595_PIN_STROBE);}
13: #define HC595_RESET {HC595_PORT&=~(1<<HC595_PIN_RESET); HC595_STROBE; HC595_PORT|=1<<HC595_PIN_RESET;}
Теперь напишем всю функцию. Она на 95% копирует предыдущую, только теперь добавляется макрос стробирования после полного окончания очереди.
1: void hc595(char tmp){
2: char dig, i;
3:
4: dig = cathode[tmp];
5:
6: for(i=8;i>0;i--){
7:
8: if(dig & 0x80){ HC595_DATA_ON } else {HC595_DATA_OFF}
9:
10: dig <<=1;
11: HC595_CLK
12:
13: }
14: HC595_STROBE// вот этот макрос
15:
16: }
Под конец для проверки всех этих функций напишем код для зажигания цифр с паузой и потом выключим индикаторы.
1: int main(void){
2:
3: CD4094_DDR = (1<<CD4094_PIN_DATA)|(1<<CD4094_PIN_CLK);
4: CD4094_PORT = 0x00;
5:
6: HC595_DDR = (1<<HC595_PIN_DATA)|(1<<HC595_PIN_CLK)|(1<<HC595_PIN_STROBE)|(1<<HC595_PIN_RESET);
7: HC595_PORT = 1<<HC595_PIN_RESET;// вывод сброса нужно выставить в 1
8:
9:
10: cd4094_init();
11: cd4094(0);
12:
13: _delay_ms(1000);
14:
15: hc595(2);
16:
17: _delay_ms(1000);
18: HC595_RESET
19:
20: _delay_ms(1000);
21: cd4094_init();
22:
23: return 0;
24:
25: }
Исходник и файл протеуса.
категория: учим мк avr
Здравствуйте, есть вопросы, если возможно, могу ли я обратиться на почту, либо другой вариант связи?
пиши crap kalobyte com
Некропост, понимаю. Но он открыл мне 4094, который дешевле 595, а это сейчас важно.
В статье написано:
Разберем самый простой индикатор 4094. Он использует всего 2 входа. Вход STB (строб) подтянут к лог1 и при каждом такте производит вывод данных на параллельную шину.
Сначала напрягся, но заглянув в даташит, понял, что 4094 аналогичен 595 по логике работы. У обоих есть возможность перекидывать данные из регистра сдвига в регистр хранения. Просто у 595 быстродействие покруче.
Спасибо за статью.