Динамическая индикация обычно съедает у микроконтроллера целый порт под сегменты. Сдвиговый регистр 74HC595 позволяет обойтись тремя выводами — и на этой связке легко собрать реверсивный счётчик на кнопках.
Что делает устройство
Схема представляет собой цифровой счётчик с ручным управлением: две кнопки увеличивают и уменьшают отображаемое число. Число выводится на мультиплексированный четырёхразрядный семисегментный индикатор, а роль «расширителя портов» для сегментов играет сдвиговый регистр 74HC595. Управляет всем этим микроконтроллер PIC16F877A, программа написана на CCS C.
Идея та же, что и в схемах с регистром 74HC164N: вместо восьми линий на сегменты микроконтроллер отдаёт данные последовательно, по трём проводам. Разница — в наличии у 74HC595 выходного регистра-защёлки, благодаря которому индикатор не «моргает» промежуточными состояниями во время загрузки байта.
Сдвиговый регистр 74HC595
Согласно даташиту, 74HC595 — это быстродействующий восьмиразрядный последовательный сдвиговый регистр с регистром хранения и выходами с тремя состояниями. Ключевая особенность: у сдвигового регистра и регистра хранения раздельные тактовые входы.
- Данные сдвигаются по положительному фронту на входе тактирования сдвигового регистра SHCP.
- Содержимое сдвигового регистра переписывается в регистр хранения по положительному фронту на входе STCP.
- Если оба тактовых входа соединить вместе, сдвиговый регистр всегда будет опережать регистр хранения на один тактовый импульс — это нужно учитывать при таком включении.
Именно раздельное тактирование и даёт главный выигрыш: пока восемь бит вдвигаются в регистр, на выходах держится предыдущий байт, и только импульс защёлки показывает новую комбинацию сегментов целиком.
Назначение выводов микросхемы приведено ниже.
Схема
На схеме четыре мультиплексированных разряда подключены к 74HC595. Использован индикатор с общим анодом — от этого зависят и коды сегментов в программе, и логика включения разрядов. Выходы регистра идут на сегменты, общие для всех четырёх знакомест, а выбором активного разряда занимается сам микроконтроллер.
В схеме предусмотрены две кнопки: одна увеличивает, другая уменьшает выводимое число.
Нажмите для увеличения (1024x709)
Так выглядит собранная конструкция «в железе».
Нажмите для увеличения (1000x600)
Программа для CCS C
Код получился компактным и прозрачным:
// 4-Digit 7-Segment display with 74HC595 interfacing with PIC16F877A CCS C code
#define data_pin PIN_B0
#define clock_pin PIN_B1
#define latch_pin PIN_B2
#include <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock = 8000000)
#use fast_io(B)
#use fast_io(D)
short s; // Used to know buttons position
unsigned int j, digit;
unsigned long i = 0;
unsigned int seg(unsigned int num)
{
switch(num)
{
case 0 : return 0x80;
case 1 : return 0xF2;
case 2 : return 0x48;
case 3 : return 0x60;
case 4 : return 0x32;
case 5 : return 0x24;
case 6 : return 0x04;
case 7 : return 0xF0;
case 8 : return 0;
case 9 : return 0x20;
}
}
void write_data(unsigned int number)
{
for(j = 0x80; j > 0; j = j >> 1)
{
if(number & j)
output_high(data_pin);
else
output_low(data_pin);
output_high(clock_pin);
output_low(clock_pin);
}
output_high(latch_pin);
output_low(latch_pin);
}
void main()
{
port_b_pullups(TRUE); // Enable PORTB pull-ups
output_b(0); // PORTB initial state
set_tris_b(0x18); // Configure RB3 & RB4 pins as inputs
output_d(0); // PORTD initial state
set_tris_d(0); // Configure PORTD pins as inputs
while(TRUE)
{
if(input(PIN_B3) && input(PIN_B4))
s = 1;
if(s == 1)
{
if(input(PIN_B3) == 0)
{
s = 0;
i++;
if(i > 9999)
i = 0;
}
if(input(PIN_B4) == 0)
{
s = 0;
if(i < 1)
i = 1;
i--;
}
}
digit = seg(i % 10); // Prepare to display ones
output_d(0x0F); // Turn off all displays
write_data(digit);
output_d(0x07); // Turn on display for ones
delay_ms(1);
digit = seg((i / 10) % 10); // Prepare to display tens
output_d(0x0F); // Turn off all displays
write_data(digit);
output_d(0x0B); // Turn on display for tens
delay_ms(1);
digit = seg((i / 100) % 10); // Prepare to display hundreds
output_d(0x0F); // Turn off all displays
write_data(digit);
output_d(0x0D); // Turn on display for hundreds
delay_ms(1);
digit = seg((i / 1000) % 10);// Prepare to display thousands
output_d(0x0F); // Turn off all displays
write_data(digit);
output_d(0x0E); // Turn on display for thousands
delay_ms(1);
}
}
Как работает программа
Выводы и настройки. Линия данных регистра заведена на RB0, тактирование сдвига — на RB1, защёлка — на RB2. Кварцевый генератор работает в режиме HS на частоте 8 МГц, сторожевой таймер, защита кода и низковольтное программирование отключены. Директивы fast_io для портов B и D означают, что направлением выводов программа управляет вручную — через set_tris.
Кнопки. Внутренние подтяжки PORTB включены, а set_tris_b(0x18) переводит RB3 и RB4 на вход — это и есть кнопки «больше» и «меньше», замыкающие вывод на землю. Переменная s реализует простейшую защиту от многократного срабатывания: она устанавливается в единицу только тогда, когда обе кнопки отпущены, и сбрасывается при обработке нажатия. Пока кнопку держат, счёт не идёт — новое нажатие учитывается лишь после отпускания. Счётчик i закольцован: при переполнении выше 9999 он обнуляется, а при попытке уйти ниже нуля значение предварительно поднимается до единицы, после чего декремент даёт ноль.
Функция seg(). Она переводит цифру 0…9 в байт сегментов. Значения подобраны под конкретную разводку индикатора с общим анодом, поэтому активному сегменту соответствует нулевой бит — отсюда и код 0 для восьмёрки, у которой горят все сегменты.
Функция write_data(). Это программная реализация последовательной передачи. Маска j начинается с 0x80 и сдвигается вправо, то есть байт уходит в регистр старшим битом вперёд. Для каждого бита на линии данных выставляется соответствующий уровень, после чего формируется импульс на линии тактирования — по его фронту бит вдвигается в 74HC595. Когда все восемь бит переданы, короткий импульс на линии защёлки переносит их в выходной регистр, и сегменты меняются разом.
Динамическая индикация. PORTD сконфигурирован на выход (set_tris_d(0)) и управляет разрядами. Порядок действий для каждого знакоместа одинаков: сначала output_d(0x0F) гасит все разряды, затем в регистр загружается новый код сегментов, и только после этого включается нужный разряд — 0x07 для единиц, 0x0B для десятков, 0x0D для сотен, 0x0E для тысяч. Гашение перед загрузкой исключает паразитную засветку соседнего разряда чужой цифрой. Каждый разряд светится 1 мс, то есть полный цикл обхода занимает около 4 мс — глаз воспринимает это как непрерывное свечение всех четырёх цифр. Сами цифры выделяются из числа классическим способом: остаток от деления на 10 даёт единицы, деление на 10, 100 и 1000 с последующим взятием остатка — десятки, сотни и тысячи.
Похожие материалы на сайте