Применение сдвиговых регистров таких как 74HC164 обусловлено возможностью управления большим количеством выходных линий с помощью 3-х линий управления, например эти регистры можно использовать для подключения гирлянд или матриц из светодиодов. Они широко используются в бытовой и промышленной аппаратуре для вывода данных на индикаторы или опросе матричной клавиатуры. Из управляющих входов в этой микросхеме есть: DATA, RESET, CLK и есть 8-выходов, получается эта микросхема преобразует последовательный код в параллельный. Вход RESET который сбрасывает установленные значения на выходе обычно не используется, так как занимает лишний вывод у микроконтроллера, он должен всегда быть поднятым к Vcc. Обнулять значения можно посылкой из 8бит логических единиц. Вход CLK продвигает значение по регистру. На вход DATA поступают данные в последовательном режиме.

Для заполнения регистра нужно выполнить такую последовательность:

1. Сравниваем старший бит передаваемого байта с нулем, если он равен нулю передаем в DATA ноль, если не равен передаем единицу;
2. Сдвигаем передаваемое значение на 1 разряд;
3. На тактовый вход CLK подаем ноль;
4. На тактовый вход CLK подаем единицу;
5. Проделываем эти операции пока не передадим весь байт нашего значения.

Подробнее со сдвиговым регистром разберемся на примере вольтметра постоянного напряжения до 55 Вольт. Микроконтроллер на этот раз будет Attiny13, ведь у него всего 5 рабочих линий порта PB он как раз нам подходит. Работает он от внутреннего генератора частотой 4,8 MHz. АЦП настроен стандартно, вход ADC1. Каждый сегмент индикаторов подключается через резистор 220 Ом, индикаторы используются с общим катодом.  Для предотвращения мерцания сегментов индикаторов их общие катоды подключаются через транзистор, который включается только тогда, когда все значение будет передано в регистр. Схема вольтметра для индикаторов с общим катодом  показана ниже:

Подключение семисегментных индикаторов с помощью сдвигового регистра 74HC164

Исходный код примера для индикаторов с общим катодом с подробными комментариями:

// Управление семисегментными индикаторами через регистр сдвига 74HC164(ОК)
#include <avr/io.h>
#include <util/delay.h>
 
// Массив комбинаций сегментов
unsigned char SEGMENTE[] =
{
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F  // 9
};
 
unsigned int volt;
 
// Функция сдвига
void write_byte(unsigned char data)
{
  for(unsigned char i = 0; i < 8 ; i++)
  {
    if((data & 0x80) != 0) // Сравниваем 8-й бит с нулем
    PORTB |= (1 << PB0); // DATA 1
    else
    PORTB &= ~(1 << PB0); // DATA 0
 
    data = data << 1; // Сдвигаем биты
    PORTB |= (1 << PB1); // CLK 1
    PORTB &= ~(1 << PB1); // CLK 0
  }
}
 
int main(void)
{
DDRB |= (1 << PB3)|(1 << PB1)|(1 << PB0);
PORTB = 0x00;

ACSR |= (1 << ACD); // Выключаем аналаговый компаратор
DIDR0 |= (1 << ADC1D); // Отключаем неиспользуемые цифровые входы

ADMUX |= (1 << MUX0); // Вход ADC1 
ADCSRA |= (1 << ADEN) // Разрешение АЦП
       |(1 << ADPS2)|(1 << ADPS1); // Предделитель на 64
 
while(1)
{
// Расчитаем максимальное входное напряжение на делителе
// Umax = Uin*(R1+R2)/R2
// Umax = 5*(100k+10k)/10k = 55V
// Расчитаем коэффициент делителя напряжения
// K = (R1+R2)/R2
// K = (100k + 10k)/10k = 11
// Расчитаем результат преобразования в мВ
// U = ADC*Uref*K*100/1024

ADCSRA |= (1 << ADSC); // Начинаем преобразование
while (ADCSRA & (1 << ADSC)){} // Ждем завершения преобразования
volt = ((unsigned long)ADC*5*11*100)/1024;

PORTB &= ~(1 << PB3); // Выключаем индикатор
write_byte(SEGMENTE[volt%100/10]); // Выводим 1 разряд
write_byte((SEGMENTE[volt%1000/100])|0x80); // Выводим 2 разряд
write_byte(SEGMENTE[volt%10000/1000]); // Выводим 3 разряд
PORTB |= (1 << PB3); // Включаем индикатор
_delay_ms(100);
}
}

Схема вольтметра для индикаторов с общим анодом показана ниже:

Исходный код примера для индикаторов с общим анодом с подробными комментариями:

// Управление семисегментными индикаторами через регистр сдвига 74HC164(ОА)
#include <avr/io.h>
#include <util/delay.h>
 
// Массив комбинаций сегментов
unsigned char SEGMENTE[] =
{
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F  // 9
};
 
unsigned int volt;
 
// Функция сдвига
void write_byte(unsigned char data)
{
  for(unsigned char i = 0; i < 8 ; i++)
  {
    if((data & 0x80) != 0) // Сравниваем 8-й бит с нулем
    PORTB |= (1 << PB0); // DATA 1
    else
    PORTB &= ~(1 << PB0); // DATA 0
 
    data = data << 1; // Сдвигаем биты
    PORTB |= (1 << PB1); // CLK 1
    PORTB &= ~(1 << PB1); // CLK 0
  }
}
 
int main(void)
{
DDRB |= (1 << PB3)|(1 << PB1)|(1 << PB0);
PORTB = 0x00;

ACSR |= (1 << ACD); // Выключаем аналаговый компаратор
DIDR0 |= (1 << ADC1D); // Отключаем неиспользуемые цифровые входы

ADMUX |= (1 << MUX0); // Вход ADC1 
ADCSRA |= (1 << ADEN) // Разрешение АЦП
       |(1 << ADPS2)|(1 << ADPS1); // Предделитель на 64
 
while(1)
{
// Расчитаем максимальное входное напряжение на делителе
// Umax = Uin*(R1+R2)/R2
// Umax = 5*(100k+10k)/10k = 55V
// Расчитаем коэффициент делителя напряжения
// K = (R1+R2)/R2
// K = (100k + 10k)/10k = 11
// Расчитаем результат преобразования в мВ
// U = (ADC*Uref*K*100)/1024

ADCSRA |= (1 << ADSC); // Начинаем преобразование
while (ADCSRA & (1 << ADSC)){} // Ждем завершения преобразования
volt = ((unsigned long)ADC*5*11*100)/1024;

PORTB |= (1 << PB3); // Выключаем индикатор
write_byte(~SEGMENTE[volt%100/10]); // Выводим 1 разряд
write_byte(~((SEGMENTE[volt%1000/100])|0x80)); // Выводим 2 разряд
write_byte(~SEGMENTE[volt%10000/1000]); // Выводим 3 разряд
PORTB &= ~(1 << PB3); // Включаем индикатор
_delay_ms(100);
}
}


Архив для статьи "Подключение семисегментных индикаторов с помощью сдвигового регистра 74HC164"
Описание: Проект AVRStudio 4
Размер файла: 45.98 KB Количество загрузок: 2 167 Скачать

Комментарии  

+4 #1 skullhead 23.10.2013 14:25
спасибо за статью!очень наглядно и доступно.
Сообщить модератору
+1 #2 Alyes 22.10.2014 12:43
Спасибо! хорошая статья! attiny13 с ее АЦП и + работа с сдвиговым регистром.
Сообщить модератору
0 #3 thespawn007 14.02.2015 02:19
И сколько их можно подключить на одну пару ног контроллера? 8?
Сообщить модератору
0 #4 AntonChip 14.02.2015 11:25
Цитирую thespawn007:
И сколько их можно подключить на одну пару ног контроллера? 8?

Для данного примера можно 32
Сообщить модератору
0 #5 aiw 07.03.2016 12:53
а возможно ли использовать вместо трех одноразрядных индикаторов один триразрядный? Понадобится один регистр и изменение программы?
Сообщить модератору
+2 #6 AntonChip 11.03.2016 22:58
1 регистр, изменение программы, но у attiny13 не хватит ножек
Сообщить модератору
0 #7 Eugene 15.06.2016 15:01
Уважаемый автор вы бы не могли скинуть хекс файл проэкта. А то я уже и забыл какой программой его создать с данного кода. Заранее спасибо.
Сообщить модератору
+2 #8 AntonChip 19.06.2016 11:48
Цитирую Eugene:
Уважаемый автор вы бы не могли скинуть хекс файл проэкта. А то я уже и забыл какой программой его создать с данного кода. Заранее спасибо.

Добавил в архив
Сообщить модератору
0 #9 Eugene 23.06.2016 13:02
Спасибо вам!!! Все заработало с первого раза. Но меня смущает мигание индикатора. Вы не подскажите как решить этот вопрос??? Заранее спасибо.
Сообщить модератору
0 #10 ArthurMelkumov 08.08.2016 19:51
Здравствуйте. Подскажите, что выполняется в строках кода №79-81? А именно интересуют действия в скобках с процентом.
Сообщить модератору
+1 #11 AntonChip 11.08.2016 22:24
Цитирую ArthurMelkumov:
Здравствуйте. Подскажите, что выполняется в строках кода №79-81? А именно интересуют действия в скобках с процентом.

% - это операция деления с остатком, таким способом переменную value "разделяем" на отдельные разряды
Сообщить модератору
0 #12 Warovarin 16.02.2018 06:52
Всё собрал,прошил.. .на индикаторах высветились три тире(- - -) и минают. Это не связанно ли с полярностью индикаторов. У меня на схеме общий анод. А еще fuse :sut0,skdiv8,sk sel0 отключены,прави льно?
Сообщить модератору
0 #13 AntonChip 16.02.2018 08:15
Индикаторы должны быть с общим катодом
Сообщить модератору
0 #14 Warovarin 16.02.2018 10:10
У меня три платы и на всех с анодами... Мне очень хотелось сделать из них вольтметры,а тут не всё так просто. У меня к вам просьба не могли б вы скинуть прошивку на анодную индикацию.
Сообщить модератору
0 #15 Warovarin 18.02.2018 18:12
У меня есть индикатор с общим катодом,но там 12 ножек,может его получиться как нибудь подключить? Если нет помогите с инверсией.
Сообщить модератору
0 #16 Warovarin 19.02.2018 08:55
Я так пологаю надо в in main(void) изменить PORTB=00*FF, А в функциях void write_byte 1,2,3, убрать c PORTB& знак~,и на PORTB| поставить,верно ?
Сообщить модератору
0 #17 AntonChip 19.02.2018 11:04
Цитирую Warovarin:
У меня есть индикатор с общим катодом,но там 12 ножек,может его получиться как нибудь подключить? Если нет помогите с инверсией.

Код:
PORTB |= (1 << PB3); // Выключаем индикатор
write_byte_1(~SEGMENTE[value%10]); // Выводим 1 разряд
write_byte_2(~((SEGMENTE[value%100/10])|0x80)); // Выводим 2 разряд
write_byte_3(~SEGMENTE[value%1000/100]); // Выводим 3 разряд
PORTB &= ~(1 << PB3); // Включаем индикатор

и поменять транзистор на PNP
Сообщить модератору
0 #18 AntonChip 21.02.2018 23:32
Добавил программу для индикаторов с ОА, а также оптимизировал код
Сообщить модератору
0 #19 ник0лай 03.02.2019 16:48
Если поставить 74HC595 вместо 74HC164 нужно будет что то поменять в исходнике или схеме, что бы всё хорошо работало ?
Сообщить модератору
0 #20 sergi 21.08.2022 18:01
Добра. Не высвечиваются цифры 1 и 4, Что только не делал. Может ли быть это из-за микросхемы 74HC164 ?
Сообщить модератору

Последние комментарии

Сейчас на сайте

Сейчас на сайте 74 гостя и нет пользователей