Что такое регистры сдвига и 7-сегментные светодиоды и как их запрограммировать на Raspberry Pi с помощью C и Golang

Содержание

  • Обзор
  • Предпосылки
  • Информация, которая была бы полезна
  • Что такое 7-сегментный дисплей?
  • Что такое сдвиговый регистр и для чего он нужен?
  • Как в этом проекте используется сдвиговый регистр и 7-сегментный дисплей?
  • Настройка и код
  • Сдвиговый регистр / 7-сегментный дисплей в C
  • Сдвиговый регистр/7-сегментный дисплей в Go
  • Краткое содержание
  • Рекомендации

Обзор

Это четвертая статья из серии, посвященной программированию GPIO на Raspberry Pi 3B+. Является дополнением к проекту Sunfounder 7-Segment Display. Полную серию вы можете найти здесь. Код серии можно найти на github.

Как и в случае с проектом Sunfounder RGB LED, в этом проекте есть некоторые сложные аспекты, которые недостаточно подробно описаны в проектной документации Sunfounder. Цель этой статьи — заполнить эти пробелы, а именно:

  1. Что такое «сдвиговый регистр» (микросхема 74HC595)?
  2. Каково использование сдвигового регистра?
  3. Что такое 7-сегментный дисплей?
  4. Как сдвиговый регистр используется в сочетании с 7-сегментным дисплеем?

Дополнительная цель этой статьи — предоставить более полный пример возможностей регистров сдвига и 7-сегментных дисплеев с помощью программы, написанной на C. Эта программа значительно более продвинута в этой области, чем код Sunfounder. В этой статье также будет представлена ​​эта же программа, написанная на Go.

Если вы раньше не работали со светодиодами, вам следует сначала взглянуть на мой Raspberry Pi GPIO в Go и C — мигающий светодиод. В этой статье рассматриваются основы использования светодиода с Raspberry Pi GPIO и соответствующими библиотеками C и Go, которые не обсуждаются в этой статье.

Предпосылки

Этот раздел повторяется во всех статьях моей серии Raspberry Pi GPIO. Если вы уже выполнили проект из одной из этих статей, вы можете просмотреть этот раздел и найти только необходимые элементы, не включенные в другие проекты, в частности сдвиговый регистр и 7-сегментный дисплей.

Если у вас его нет, вам понадобится Raspberry Pi. Я использовал Raspberry Pi 3B+ с версией ОС Raspbian Stretch. На веб-сайте Raspberry Pi есть инструкции о том, как настроить новый Raspberry Pi с нуля, если вы решите пойти по этому пути, а не покупать полный комплект.

Другие предметы, которые вам понадобятся, включают:

Вот простой набор, в котором есть все (большинство?) из вышеперечисленного. На фото комплекта виден сдвиговый регистр, но я не уверен. Я нахожу Sunfounder Raspberry Pi Ultimate Starter Kit особенно полезным. ПРИМЕЧАНИЕ. Ultimate Starter Kit и Raphael Kit — это один и тот же продукт.

Вам также потребуются некоторые базовые знания программирования на C и Go, а также знакомство с входом в терминал Raspberry Pi или в графический интерфейс рабочего стола, который поставляется с некоторыми версиями ОС. В зависимости от выбранного вами подхода вам может потребоваться подключить клавиатуру и монитор к Raspberry Pi. Я просто SSH в Pi. Вам также понадобится знакомство с тем, как использовать такие редакторы, как vim или nano.

Для компиляции и запуска программы на C вам понадобится библиотека WiringPi. Это легко получить:

sudo apt-get install wiringpi

Затем проверьте установку, используя:

pi@pi-node1:~/go/src/github.com/youngkin/gpio/rgbled $ gpio -v
gpio version: 2.50
Copyright (c) 2012-2018 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty
Raspberry Pi Details:
  Type: Pi 3B+, Revision: 03, Memory: 1024MB, Maker: Sony
  * Device tree is enabled.
  *--> Raspberry Pi 3 Model B Plus Rev 1.3
  * This Raspberry Pi supports user-level GPIO access.

В приведенном выше вы заметите gpio version: 2.50. Если вы используете Rasberry Pi 4, воспользуйтесь инструкциями, приведенными в Sunfounder Проверка WiringPi.

WiringPi уникален тем, что включает инструмент командной строки gpio, как показано выше, который можно использовать для управления, контроля и запроса платы GPIO. Это может быть очень удобно. См. Справочник по gpio для получения дополнительной информации о том, что он может делать и как его использовать.

Если вы заинтересованы в разработке Go на Raspberry Pi, вам необходимо установить среду разработки на Raspberry Pi. Вот простой источник, который объясняет, как это сделать. Этот источник немного устарел, но единственная существенная проблема связана с версией Go для установки. Источник показывает установку Go 1.14.4.linux-arm64.tar.gz и 1.14.4.linuxarmv6l.tar.gz. Текущие версии: 1.17.1.linux-arm64.tar.gz и 1.17.1.linuxarmv6l.tar.gz. Для Raspberry Pi 3B+ правильным выбором будет 1.17.1.linuxarmv6l.tar.gz. Другой предназначен для 64-битных систем, таких как серия Raspberry Pi 4. Текущие версии Go для ARM можно найти на сайте загрузки Golang.

Для разработки на Go вам также понадобится библиотека go-rpio.

Если вы хотите отойти от стиля поваренной книги документации Sunfounder, вам потребуются базовые знания Linux. Например, я не буду объяснять, что такое привилегии root.

Информация, которая была бы полезна

Документация по проекту Sunfounder 7-Segment Display в значительной степени просто рассказывает о том, как подключить макетную плату и код, не упоминая много об аппаратных компонентах, особенно о сдвиговом регистре. Даже описание кода было немного запутанным, учитывая отсутствие у меня знаний о сдвиговых регистрах. Я случайно наткнулся на документацию проекта Sunfounder 7-Segment Python, в которой давалась полезная информация о 7-сегментном дисплее, но не упоминался сдвиговый регистр. Этот проект можно было бы легко сделать без регистра сдвига, на самом деле многие другие статьи о 7-сегментных дисплеях делают именно это. Так почему же в проекте используется сдвиговый регистр? Почему бы просто не подключить 7-сегментный дисплей напрямую к контактам GPIO? Очевидно, в сдвиговом регистре что-то есть, и я хотел знать, что это было. Во всяком случае, для меня оказалось, что этот проект на самом деле о сдвиговом регистре, а 7-сегментный дисплей является просто инструментом для визуализации того, как работают сдвиговые регистры. Но прежде чем мы перейдем к регистрам сдвига, давайте кратко обсудим 7-сегментные дисплеи.

Что такое 7-сегментный дисплей?

Так что же такое 7-сегментный дисплей? Ну, чтобы быть педантичным, он имеет 8 сегментов, если вы включите десятичную точку на устройстве. Кроме того, это обычное устройство, с которым вы уже знакомы, поскольку видели, как они используются во всем, от часов до калькуляторов и других устройств, которым требуется числовой дисплей. Что касается физической реализации, нам необходимо знать несколько деталей.

Прежде всего, на первой схеме выше показаны обозначения распиновки 7-сегментного индикатора. На этой диаграмме показаны средние контакты сверху и снизу с аннотацией (-). Это обозначает, что эти контакты являются контактами заземления для дисплея. Буквы на остальных контактах соответствуют буквам на нижней диаграмме идентификации светодиодов. Например, при подаче питания на контакт «А» на дисплее загорится светодиод, обозначенный «А» на нижней идентификационной диаграмме светодиодов. Буквы «DP» обозначают десятичную точку.

Для этого проекта нам понадобится так называемый 7-сегментный дисплей с общим катодом. Это то, что показано на диаграмме выше (средние булавки вверху и внизу имеют аннотацию (-)). Все это означает, что все отрицательные клеммы светодиодов в дисплее подключены к общему контакту заземления. Общий анод — это другой тип, который, как и следовало ожидать, означает, что все положительные клеммы светодиодов подключены к одному и тому же контакту.

Если вы используете комплект Sunfounder, 7-сегментный дисплей является обычным катодным дисплеем. Если вы получили 7-сегментный дисплей из другого источника, вы можете проверить его тип. Определить, является ли 7-сегментный дисплей общим катодом или общим анодом, довольно легко. Перефразируя статью Как настроить семисегментный дисплей на Arduino, шаги следующие:

Хотя на приведенной выше схеме показан Arduino, подключение его к Raspberry Pi очень похоже.

  1. Подключите питание от Raspberry Pi, как показано выше (или к шине питания макетной платы).
  2. Подключите резистор между двумя сторонами макетной платы или от шины питания к одной стороне макетной платы.
  3. Подсоедините перемычку (красную) к дальнему концу резистора от источника питания.
  4. Подключите перемычку (черную) к контакту заземления GPIO Raspberry Pi.
  5. Подсоедините черную перемычку (заземление) к среднему контакту сверху или снизу дисплея.
  6. Подсоедините красную перемычку (положительную) к любому из других контактов (кроме среднего контакта!).

Если светодиод загорается, дисплей является общим катодом.

Если светодиод не загорается, попробуйте выполнить следующие действия:

  1. Отсоедините перемычки от дисплея
  2. Подсоедините красную перемычку к одному из средних контактов, сверху или снизу.
  3. Подсоедините черный провод к одному из других контактов.

В этот момент должен загореться светодиод, и вы убедились, что дисплей является дисплеем с общим анодом. Если ни один светодиод не загорается, дважды проверьте проводку.

Это видео на YouTube показывает, как это сделать с помощью мультиметра, а не с помощью макетной платы, как описано выше.

Что такое сдвиговый регистр и для чего он нужен?

Существует несколько типов сдвиговых регистров. Для этого проекта мы используем сдвиговый регистр Serial In, Parallel Out (SIPO). Этот тип сдвигового регистра преобразует последовательный ввод в параллельный вывод. Например, несколько импульсов сигнала, генерируемого на одном выводе GPIO Raspberry Pi, могут накапливаться сдвиговым регистром, а затем отправляться в виде одного параллельного вывода на 7-сегментный дисплей. Именно это и делается в этом проекте.

Так зачем использовать сдвиговый регистр? Ну, во-первых, как отмечалось выше, требуется всего один контакт GPIO для управления 8 контактами на 7-сегментном дисплее (не совсем так, это будет более подробно объяснено далее в статье). Версии этого проекта без сдвигового регистра занимают 8 контактов GPIO для выполнения той же задачи. Таким образом, сдвиговый регистр можно использовать для экономии контактов GPIO. Это особенно верно для светодиодных дисплеев с очень большим количеством светодиодов, которые, возможно, нельзя было бы использовать из-за отсутствия доступных контактов GPIO. Хотя это не относится к 7-сегментному дисплею, некоторые устройства принимают только параллельный ввод. Таким образом, в такой ситуации сдвиговый регистр SIPO обязателен при преобразовании последовательного источника в параллельный выход.

Анатомия регистра сдвига

Сдвиговый регистр 74HC595 имеет несколько логических компонентов, которые имеют отношение к этой статье:

  • Входной регистр сдвига — это ряд подключенных триггерных устройств, которые принимают входящие (последовательные) данные по приходу каждого синхронизирующего тактового импульса (подробнее об этом позже). Этот регистр состоит из выходов, названных в порядке от Qa’ до Qh’, то есть Qa’, Qb’, …, Qh’.
  • Регистр вывода или хранения — это еще одна серия подключенных триггерных устройств, которые передают последовательные данные параллельно подключенному устройству. Этот регистр состоит из выходов, названных в порядке от Qa до Qh и Qh’ (подробнее о Qh’ ниже).

Эта анатомия показана графически на следующей, хотя и несколько сложной, диаграмме из Технического описания 8-битных сдвиговых регистров CD74HC595 Texas Instruments с выходными регистрами с 3 состояниями. Первый столбец из 8 полей слева, помеченный C1 или C2, содержит входной регистр сдвига. Второй столбец из 8 полей справа, помеченный C3, содержит регистр сдвига вывода. Выходные контакты выходного регистра можно увидеть в крайнем правом углу, помеченные от Qa до Qh и Qh’. Каналы ввода и управления, как показано OE, RCLK, SRCLR, SRCLK и SER в левом верхнем углу диаграммы, более подробно обсуждаются ниже.

Более подробная информация, относящаяся к именам Q* для выходов регистров, представлена ​​ниже.

Немного сбивает с толку то, что все устройство называется сдвиговым регистром, а компонент ввода также называется сдвиговым регистром. Чтобы провести различие между ними, если это неясно из контекста, я обязательно укажу, на какой из них я ссылаюсь, когда этот термин будет использоваться позже в статье.

На первой диаграмме выше показаны выводы сдвигового регистра SN74HC595. Фотография на схеме SN74HC595N выше имеет ту же ориентацию, что и схема контактов, а именно GND находится внизу слева, Vcc — вверху справа, а «выемка» — вверху по центру. Выводы на приведенных выше схемах определены следующим образом:

  • Контакт 15 (правая сторона, второй сверху), Qa, является первым выходным контактом. Qa также известен как Q0.
  • Выводы 1–7, Qb-Qh, — это оставшиеся выходные контакты. Обратите внимание, что имеется 8 выходных контактов (включая Qa выше). Наличие 8 выходов делает его 8-битным сдвиговым регистром. Qb-Qh также известны как Q1-Q7.
  • Контакт 8, GND — это контакт заземления.
  • Контакт 16, Vcc, является контактом питания.
  • Контакт 14, SER, является контактом последовательного входа. Это контакт, который принимает входящие импульсы последовательных данных от Raspberry Pi. Иногда это называют DS, SI или SDI.
  • Контакт 11, SRCLK, — это контакт, который принимает сигнал синхронизации, указывающий, что 1 бит данных был передан на контакт SER. Этот тип сигнала называется тактовым. SRCLK обозначает часы регистра сдвига. SRCLK также известен как sh_cp.
  • Контакт 12, RCLK, является контактом, который принимает сигнал синхронизации, указывающий, что все 8 битов регистра сдвига заполнены и пришло время передать биты параллельно в регистр вывода/хранения и тем самым устройство, подключенное к выходным контактам, Qa-Qh. RCLK также известен как st_cp или защелка.
  • Контакт 10, SRCLR, используется для очистки текущих значений сдвигового регистра. SRCLR означает очистку сдвигового регистра. SRCLR также известен как MR. Если очистка регистра сдвига не требуется, его можно подключить к источнику питания, генерирующему ВЫСОКИЙ сигнал (или 1). Для очистки регистра сдвига вывод SRCLR устанавливается в LOW (или 0). Он должен быть снова установлен в HIGH, прежде чем сдвиговый регистр снова заработает.
  • Контакт 13, OE, используется для блокировки доступа к выходному регистру или регистру хранения. Эффект штифта OE является временным. Когда он установлен в LOW (0), данные в выходном регистре доступны. Когда он ВЫСОКИЙ (1), данные недоступны. Установка его с ВЫСОКОГО на НИЗКИЙ делает данные снова доступными. В отличие от вывода SRCLR, он не очищает регистр. Если отключение выходного регистра не требуется, его можно подключить к земле.
  • Контакт 9, Qh’, используется для последовательного подключения нескольких сдвиговых регистров. Например, представьте себе проект, в котором необходимо параллельно управлять 16 светодиодами. В этом случае одного 8-битного регистра сдвига недостаточно, нам нужны два, которые могут каким-то образом взаимодействовать. Мы можем получить остальные необходимые 8 бит из второго 8-битного сдвигового регистра. Чтобы связать 2 сдвиговых регистра, мы подключаем выход Qh первого к контакту 14, SER, второго сдвигового регистра. Гирляндное соединение регистров сдвига выходит за рамки этого проекта, но вы можете прочитать об этом подробнее в статье Несколько регистров сдвига на Arduino — часть 1.

Сдвиговый регистр в работе

Как указано выше, входные данные хранятся во входном сдвиговом регистре. Данные передаются при каждом импульсе входа SRCLK. Когда принимается вход SRCLK, данные на предыдущем выводе Q*’ сдвигаются к следующему регистру Q*’ в последовательности. Когда входной регистр сдвига заполнен, сигнал RCLK вызывает перенос всего содержимого входного регистра в выходной регистр. Следующая таблица иллюстрирует это поведение. Для простоты я ограничу пример четырьмя битами, от Qa* до Qd*. Последовательность входных битов будет 1011. Первый бит, 1, будет передан в момент времени t0. Передаваемые биты указаны в круглых скобках рядом с указанием времени (например, t0(1)). 4 бита становятся доступными в выходном регистре по сигналу от RCLK в момент t4.

Обратите внимание, что в выходной последовательности битов, если выходные биты считываются от Qa до Qd, последовательность битов меняется на обратную. То есть 1101 против входной последовательности 1011. Такой способ сдвига называется сдвигом наиболее значимого бита (MSB). Он начинается с самого левого бита, как в этом примере. Это нужно иметь в виду, иначе могут возникнуть неожиданные результаты. Можно сдвинуть более интуитивным способом, Least Significant Bit (LSB). Для этого сдвиг должен начинаться с наименьшего или самого правого входного бита. В C&Go сдвиг MSB выполняется с помощью оператора сдвига <<. Сдвиг LSB выполняется с помощью оператора сдвига >>. Влияние метода сдвига станет более очевидным, когда мы доберемся до того, как сдвиговый регистр (а не входной сдвиговый регистр) используется в сочетании с 7-сегментным дисплеем. Подробнее о MSB и LSB см. в Википедии.

Как отмечалось ранее, я немного упростил ситуацию, заявив выше, что для управления 8 сегментами светодиодов на 7-сегментном дисплее требуется только один контакт Raspberry Pi GPIO. Один контакт, который я определил, используется для контакта SER (ввод последовательных данных). Необходимо как минимум еще 2; 1 для контакта SRCLK, а другой для контактов RCLK. Как показано выше, они необходимы для перевода часов во входной сдвиговый регистр и передачи данных из входного сдвигового регистра в выходной регистр соответственно. Итак, теперь у нас есть 3 контакта для управления 8 светодиодами на 7-сегментном дисплее. Это все еще хороший компромисс. Но необходимы еще 2 контакта GPIO, если нужны контакты SRCLR и OE. Итак, теперь у нас есть 5 контактов GPIO для управления 8 светодиодными сегментами. Тем не менее чистый выигрыш в 3 кегля. Однако экономия становится еще больше, когда сдвиговые регистры соединены последовательно, как обсуждалось выше. Не требуя дополнительных контактов GPIO, можно управлять 16 или даже более светодиодами с этих 5 контактов GPIO. Экономия становится более значительной, поскольку необходимо контролировать большее количество устройств, например, светодиодов.

Как в этом проекте используется сдвиговый регистр и 7-сегментный дисплей?

Этот проект использует компонент сдвигового регистра для захвата последовательного ввода, накопления его до тех пор, пока не будут записаны 8 бит, а затем сделать его доступным для выходного регистра. Поскольку компонент сдвигового регистра подключен к 7-сегментному дисплею, содержимое выходного регистра становится доступным для 7-сегментного дисплея, управляя тем, что в конечном итоге отображается. Детали соединений контактов от компонента регистра сдвига к контактам 7-сегментного дисплея в сочетании с используемым методом сдвига, в данном случае MSB, определяют, какие светодиоды будут гореть на 7-сегментном дисплее. В следующей таблице приведены конкретные назначения контактов, а также то, какие окончательные значения в выходном регистре будут заданы, если входной номер 0x3f, 0011 1111 используется со сдвигом MSB.

Используя диаграммы ниже, мы можем определить, какое число будет отображаться на 7-сегментном светодиоде.

Контакты 7-сегментного дисплея A-F будут иметь значение 1, G и DP будут иметь значения 0. Сопоставив это с приведенной выше диаграммой идентификации светодиодов, мы увидим, что отображаемое число будет 0.

Настройка и код

Макетная плата должна быть подключена, как показано на приведенной выше схеме (как указано в документации проекта Sunfounder 7-Segment Display). Следует отметить одну очень важную вещь, на отладку которой я потратил слишком много времени, заключается в том, что резистор, соединяющий контакт заземления на 7-сегментном дисплее, должен быть подключен к заземлению или отрицательной шине на макетной плате. Во всех моих предыдущих проектах я подключал резистор к положительной шине макета. Сначала я упустил эту деталь, и 7-сегментный дисплей ничего не отображал. Еще одна вещь, которую я допустил неправильно при первоначальном подключении, заключается в том, что у меня были контакты выходного регистра 74HC595, неправильно подключенные к 7-сегментному дисплею. Это легко сделать. Я отладил это, отметив, какие сегменты светодиодов загораются для какого ожидаемого числа. После просмотра нескольких цифр стало очевидно, что у меня перепутаны контакты G и E на 7-сегментном дисплее.

Если вы не знакомы с макетами и схемами макетов, этот учебник по макетам должен быть вам полезен.

Сдвиговый регистр / 7-сегментный дисплей в C

Этот код сильно отличается от кода Sunfounder. Это потому, что я решил добавить несколько возможностей, включая:

  1. Поддержка обработчика прерываний. Это корректно завершит программу, очистив входной регистр сдвига, тем самым отключив все светодиоды. Программа Sunfounder оставит светодиоды в том состоянии, в котором они находились на момент выхода этой версии.
  2. Он поддерживает настройку контактов сдвигового регистра SRCLR и OE.
  3. Добавлена ​​поддержка подсветки десятичной точки на 7-сегментном дисплее.
  4. Он добавляет несколько функций, которые позволяют тестировать различные возможности сдвигового регистра 74HC595, включая использование выводов SRCLR и OE.
  5. Он принимает пользовательский ввод с клавиатуры, позволяя пользователю указать, как он хотел бы, чтобы программа управляла сдвиговым регистром и, следовательно, светодиодом. Это включает в себя такие вещи, как очистка входного регистра сдвига, отключение и повторное включение выходного регистра, а также запись всех шестнадцатеричных цифр/десятичной точки, а также запись 8. на дисплей.

Большая часть этого довольно хорошо объясняется встроенными комментариями, но я упомяну несколько основных моментов.

Строки 12 и 13 предоставляют информацию о том, как собрать и запустить программу.

Строки 27 и 28 определяют выводы GPIO, которые управляют выводами SRCLR и OE соответственно. Обратите внимание, что номера контактов GPIO — это номера контактов WiringPi. Если вы решите, что хотите увидеть это поведение в действии, подключите контакт 24 к контакту регистра сдвига SRCLR, а контакт 29 к контакту регистра сдвига OE вместо плюса и земли соответственно.

Строки 32 и 33 определяют массив SegCode, содержащий шестнадцатеричные числа, которые будут сдвинуты в регистр сдвига, чтобы отобразить число, соответствующее индексу определенного числа в массиве. Например, для отображения 8 следует использовать SegCode[8]. Обратите внимание, что эти числа отражают использование формы сдвига MSB. Цифры были бы другими, если бы использовалась форма сдвига LSB. Например, число, используемое для отображения 0, как показано в строке 32, — это 0x3F (0011 1111). Чтобы отобразить 0 с помощью метода LSB, следует использовать шестнадцатеричное число 0xFC(1111 1100) и оператор сдвига вправо >> в C.

Следующий фрагмент кода показывает инициализацию сдвигового регистра. В двух словах, все выводы, которые мы используем, переведены в режим OUTPUT, чтобы мы могли писать на них. Затем все контакты, кроме контакта SRCLR, устанавливаются в состояние LOW (0). Напомним, что вывод SRCLR должен быть установлен в HIGH, чтобы сдвиговый регистр работал.

Альтернативой контактному режиму OUTPUT является PWM (широтно-импульсная модуляция). Поскольку ШИМ имитирует более низкие напряжения, быстро пульсируя на соответствующем выводе, он не подходит для использования со сдвиговыми регистрами. Напомним, что импульс на выводах SRCLK и RCLK представляет собой опережение тактового сигнала. Использование ШИМ и возникающие в результате быстрые пульсации будут мешать надежному продвижению часов.

Этот фрагмент кода демонстрирует 2 способа выключения всех светодиодов на 7-сегментном дисплее. В частности, показано, как использовать вывод SRCLR и записывать нули во все биты входного регистра сдвига, чтобы очистить входной регистр сдвига, что приведет к тому, что 7-сегментный дисплей ничего не покажет.

В shiftRegClr, строки с 1 по 11, обратите внимание на использование вывода RCLK в строках 4 и 6. Тактовый сигнал на этот вывод должен быть импульсным, т. е. установленным в HIGH, а затем в LOW, чтобы сделать входное содержимое сдвигового регистра доступным. в выходной регистр. Также обратите внимание, что вывод SRCLR должен быть сброшен в HIGH после операции, чтобы снова включить сдвиговый регистр.

В строках с 16 по 28 показан метод записи нулей во все биты входного регистра сдвига для очистки входного регистра сдвига (zeroClear()). Функционально это эквивалентно использованию метода SRCLR, они оба устанавливают входные биты регистра сдвига в 0. Однако это требует гораздо больше усилий. Цикл for в строках с 18 по 23 сдвигает ноль в каждый бит входного сдвигового регистра. Он повторяет процесс 8 раз, увеличивая значение SRCLK при каждой записи, чтобы заполнить входные биты сдвигового регистра. Строки 20 и 22 отправляют импульс SRCLK для продвижения битов входного регистра сдвига. Строго говоря, нет необходимости сдвигать 0 и & на 0x80 в строке 19. Достаточно будет просто записать 0 на вывод SDI. Однако необходимо использовать этот метод записи на вывод SDI, записав любое другое число, например, 0x3F, во входной регистр сдвига.

Как и в случае shiftRegClr() выше, строки 25 и 27 переключают вывод RCLK для передачи битов в выходной регистр.

В следующем фрагменте кода показано, как зажечь все светодиоды, включая десятичную точку, а также переключить вывод OE для временного отключения выходного регистра.

Строки с 3 по 15 показывают writeAllOnes(). Он реализован почти так же, как zeroClear() выше. Единственное заметное исключение состоит в том, что он не сдвигает 1 и & влево до 0x80 перед записью на вывод. Это демонстрирует, что когда все входные биты регистра сдвига будут иметь одинаковое значение, т. е. 1 или 0, в этом нет необходимости. Однако он повторяет процесс записи на вывод 8 раз, переводя тактовый сигнал SRCLK после каждой записи.

Строки с 22 по 29 реализуют oeToggle(), который переключает вывод OE в HIGH (1), а затем снова в LOW. Между записями есть 1-секундная задержка, чтобы убедиться, что эффект виден. Напомним, что при установке на вывод OE значения HIGH выходной регистр отключается. С точки зрения наблюдателя, 7-сегментный дисплей отключится на 1 секунду, а затем вернется к тому состоянию, которое отображалось до переключения. Вызывающая функция должна решить, что будет отображаться на 7-сегментном дисплее до вызова этой функции.

hc595_shift() отвечает за сдвиг различных битов входного числа dat во входной сдвиговый регистр. За исключением того, что входное число зависит от значения dat, функция реализована точно так же, как zeroClr() выше. На самом деле, и zeroClr(), и writeAllOnes() могут и, вероятно, должны были быть реализованы с использованием hc595_shift().

Для тех, кому трудно представить, как << работает в сочетании с оператором &, как это сделал я, вот небольшой пример с использованием 0x3f или 0011 1111 в качестве примера. Обратите внимание, что порядок приоритета операций таков, что сначала происходит <<, а затем &.

Последняя запись, i = 8, — это то, что произойдет, если dat будет сдвинуто 9 раз вместо 8. Это было включено только для того, чтобы показать, что dat было сдвинуто на 8 бит влево, оставив все 8 бит равными 0.

Большинство функций в приведенном выше фрагменте кода просто настраивают тест, устанавливая отображение на 8, а затем делегируют поведение функциям, которые мы видели реализованными выше (например, shiftRegClr()). Вместо отображения только шестнадцатеричных чисел 0-F, testWriteNums() будет чередовать все шестнадцатеричные числа и десятичную точку.

Наконец, этот фрагмент кода реализует функции main() и interruptHandler().

Строка 3 регистрирует обработчик сигнала interruptHandler(), интересующийся сигналом SIGINT. SIGINT — это то, что будет сгенерировано при вводе ctl-C на терминале.

Строки с 6 по 9 инициализируют библиотеку WiringPi и завершаются, если инициализация не удалась.

Строка 16 подсказывает пользователю, какое поведение он хотел бы продемонстрировать.

Строки с 17 по 25 сначала считывают ввод терминала от пользователя и заполняют строку с завершающим нулем результатами. Код не заботится ни о каких символах, которые могли быть введены, кроме первого символа. Любые дополнительные символы между ним и новой строкой будут игнорироваться.

Строка 26 освобождает память, выделенную getline() в строке 19. Это необходимо, чтобы избежать утечек памяти. Не беспокойтесь, если вы этого не понимаете. Неважно, что демонстрируется в этой программе. Если вам интересно, изучите распределение памяти в C, в том числе то, как память выделяется (malloc), как она освобождается (free), почему это важно и, наконец, работа функции getline(). В противном случае просто помните, что это необходимо.

ВНИМАНИЕ: строго говоря, в этой программе это на самом деле не требуется, так как возникающая в результате утечка памяти, скорее всего, будет тривиальной при обычном использовании. Как минимум, это дурной тон не free всей выделенной памяти, когда она больше не нужна. В худшем случае программа может выйти из строя по разным причинам, как только будет выделена вся доступная память. Итак, вызывайте free, когда память, связанная с такой переменной, как line, больше не нужна.

В Википедии есть очень подробное обсуждение распределения и управления памятью.

Строки с 28 по 56 определяют, какую опцию выбрал пользователь, и вызывают функцию, связанную с этой опцией. Весь процесс, описанный начиная со строки 15, повторяется до тех пор, пока пользователь не выберет опцию (q)uit.

Строки с 63 по 67 регистрируют обработчик сигнала, который изящно завершит программу, если на терминале будет введено ctl-C. Он вызывает toggle8(), который на короткое время отображает 8 перед выключением всех светодиодов на 7-сегментном дисплее.

Сдвиговый регистр/7-сегментный дисплей в Go

Программа Go очень похожа на версию C. Есть некоторые незначительные различия в реализации и выводе во время выполнения. Некоторые из этих различий обусловлены структурными различиями между языками, а также различиями в соглашениях о кодировании. Фрагменты ниже представляют большую часть, но не всю программу. Я пропустил часть шаблона и пробелов.

Этот фрагмент состоит из 4 основных частей. Первая — это определение переменной segcode в строке 3. Она служит той же цели, что и SegCode в программе на C, а именно определяет значения, которые должны быть сдвинуты влево в сдвиговый регистр. Следует отметить, что, как и в версии C, эти значения зависят от использования сдвига наиболее значимого бита (MSB). Для удобства вот пояснительный текст из версии C:

SegCode … содержит шестнадцатеричные числа, которые следует сдвинуть в сдвиговый регистр, чтобы отобразить число, совпадающее с индексом определенного числа в массиве. Например, для отображения 8 следует использовать SegCode[8]. Обратите внимание, что эти числа отражают использование формы сдвига MSB. Цифры были бы другими, если бы использовалась форма сдвига LSB. Например, число, используемое для отображения 0, как показано в строке 32, — это 0x3F (0011 1111). Чтобы отобразить 0 с помощью метода LSB, следует использовать шестнадцатеричное число 0xFC(1111 1100) и оператор сдвига вправо >> в C.

Во-вторых, строки 7–13 в main() инициализируют библиотеку go-rpio и завершают работу в случае возникновения проблемы. Строка 13 гарантирует, что ресурсы, хранящиеся в библиотеке go-rpio, будут освобождены после завершения программы.

В-третьих, строки 15–20 создают объекты go-rpio rpio.Pin, которыми управляет остальная часть программы.

Наконец, в строках 22–35 определяется обработчик сигналов/прерываний для перехвата ctl-C входных данных с терминала. Это необходимо для корректного выхода из программы, если пользователь вводит ctl-C с клавиатуры. Ключевым моментом, на который следует обратить внимание в этом фрагменте, является использование канала с именем stop. Обработчик сигналов/прерываний работает в собственной горутине (строка 35). На данный момент выполняются 2 горутины: горутина main() и горутина, связанная с обработчиком сигналов/прерываний. Они запускаются и планируются независимо, и в результате управление может переходить от одной горутины к другой произвольным и непредсказуемым образом. Чтобы корректно закрыть программу, обе они должны быть остановлены контролируемым образом. В противном случае выход из программы приведет к непредсказуемому поведению. Например, при выходе из программы на 7-сегментном дисплее могут еще гореть некоторые светодиоды. Синхронизация выхода программы по каналу stop предотвращает это. См. уроки Tour of Go по Горутинам и Каналам для быстрого ознакомления с горутинами и каналами. Не волнуйтесь, если вы не совсем понимаете все это, параллелизм в Go — это сложная тема. В основном я просто хотел дать общее объяснение того, почему код написан таким образом.

Комментарии к программе предоставляют дополнительную информацию. Остальная часть main() находится в следующем фрагменте.

Остальная часть main(), как показано выше, обрабатывает подсказки пользователю о том, какую способность он хотел бы видеть продемонстрированной, довольно очевидна. Однако следует отметить одну вещь: цикл for/select, начинающийся в строках 2-8. for/select — это общий шаблон, используемый в программах Go. Его использование здесь восходит к предыдущему обсуждению канала stop. Часть select шаблона for/select прослушивает stop сообщение, указывающее, что эта горутина должна выйти. Обратите внимание, что у select было 2 case выбора. Первый предназначен для прослушивания канала stop. Если сообщение получено, цикл for и программа завершатся. Если сообщение не получено, управление немедленно переходит к случаю default, продолжая обычный ход программы. В случае default пользователю в строках 18-57 предлагается сделать выбор, который приведет к вызову одной из тестовых функций.

Следующий фрагмент кода состоит из 3 частей. Во-первых, это создание объектов go-rpio rpio.Pin, которые представляют собой логическое соединение с физическими контактами сдвигового регистра. Контакты 17, 18, 27, 19 и 21 GPIO/BCM используются для контактов SDI, RCLK, SRCLK, SRCLR и OE соответственно.

Вторая часть устанавливает контакты в режим OUTPUT, чтобы на них можно было производить запись.

Заключительная часть устанавливает напряжение на контактах до их начальных значений. Обратите внимание, что вывод SRCLR установлен в состояние HIGH. Это необходимо для того, чтобы сдвиговый регистр работал. В более поздних фрагментах кода мы увидим, что очистка входного регистра сдвига выполняется путем установки вывода SRCLR в LOW для очистки входного регистра сдвига, а затем обратно в HIGH для возобновления нормальной работы регистра сдвига.

Этот фрагмент кода показывает реализацию тестовых функций, которые вызываются в ответ на выбор пользователей в отношении того, какие возможности они хотели бы продемонстрировать. Если какие-либо данные были записаны на 7-сегментный дисплей, они будут удалены перед выходом из функций.

В этом фрагменте кода показана реализация функции hc595_shift(), лежащей в основе программы, которая выполняет фактическую запись во входной сдвиговый регистр. Комментарии дают хорошее подробное объяснение того, что делает функция. См. версию C выше для более подробной информации о том, как работает сдвиг битов.

Этот последний фрагмент кода показывает реализацию двух вспомогательных функций и обработчика сигналов/прерываний. Первая вспомогательная функция, writeNums(), записывает фактические данные, необходимые для отображения шестнадцатеричных чисел на 7-сегментном дисплее.

Второй, shiftRegClr(), показывает, как использовать комбинацию выводов SRCLR и RCLK для очистки регистра сдвига, что в конечном итоге приводит к очистке 7-сегментного дисплея. Обратите внимание, что, как описано выше, вывод SRCLR должен быть снова установлен в HIGH, чтобы снова включить сдвиговый регистр.

В shiftRegClr, строки с 12 по 18, обратите внимание на использование вывода RCLK в строках 14 и 16. Тактовый сигнал на этот вывод должен быть импульсным, т. е. установленным в HIGH, а затем в LOW, чтобы содержимое входного сдвигового регистра было доступно для выходной регистр. Также обратите внимание, что вывод SRCLR должен быть сброшен в HIGH после операции, чтобы снова включить сдвиговый регистр.

Наконец, функция signalHandler() ловит сигнал ОС (строка 23) по каналу sigs. Затем (строка 25) закрывает стоп-канал. Побочным эффектом закрытия канала является уведомление, отправленное всем слушателям на другом конце канала. Это распространенный шаблон в Go. Наконец, он очищает сдвиговый регистр, освобождает ресурсы go-rpio (строка 28) и завершает работу программы.

Краткое содержание

Эта статья охватила довольно много земли. Если вы следили за моей серией, начиная с проекта Raspberry Pi GPIO в Go и C — мигающий светодиод, вы уже знакомы с использованием GPIO для управления светодиодами. Вы можете согласиться с тем, что проект Sunfounder 7-Segment Display на самом деле не расширил ваши знания о GPIO и светодиодах, которые еще не были рассмотрены в предыдущих проектах. Вот почему, во всяком случае, для меня этот проект действительно посвящен изучению основ сдвигового регистра и их нетривиальному использованию.

В этой статье было рассмотрено несколько интересных вещей:

  1. Он продемонстрировал подключение относительно сложной схемы на макетной плате. Как и я, вы, возможно, сделали несколько ошибок, которые дали вам полезные знания о том, как отлаживать схемы.
  2. Благодаря академическому обсуждению и практической практике вы довольно много узнали о сдвиговых регистрах и о том, для чего их можно использовать.
  3. Несмотря на то, что я несколько упростил использование светодиодов в этом проекте, этот проект действительно пролил свет на то, как использовать светодиоды более реалистично. Управление отдельным светодиодом интересно, но возможность параллельно управлять массивом светодиодов и отображать цифры на этом массиве — большой шаг вперед. Вы также узнали, что есть 2 типа 7-сегментных дисплеев, общий анод и общий катод.

Комментарии и вопросы по этой статье приветствуются (как и «хлопки» :) ).

Рекомендации