Учебный курс AVR. Работа с SPI модулем. Управление сдвиговыми регистрами. Ч3
06/12/2012 - 16:52
Павел Бобков
Введение
При разработке электронных устройств иногда бывает ситуация, когда выводов микроконтроллера не хватает, а использовать другой чип нет возможности. В таких случаях обычно прибегают к схемотехническим трюкам или используют дополнительные внешние микросхемы, например, расширители портов, сдвиговые регистры или мультиплексоры.
Расширители портов позволяют добавить микроконтроллеру “полноценные” выводы, работающие как на выход, так и на вход, и, как правило, управляются с помощью стандартных интерфейсов - SPI или I2C. Это очень удобно, но подобные микросхемы не из дешевых.
Бюджетный вариант – использование сдвиговых регистров и мультиплексоров, однако в этом случае полученные дополнительные выводы будут работать только на выход или на вход (исключение составляют универсальные сдвиговые регистры). Впрочем, в большинстве приложений этого вполне достаточно.
В этой статье мы рассмотрим расширение портов микроконтроллера с помощью сдвигового регистра 74HC595. Им можно управлять как стандартными выводами микроконтроллера, так и с помощью SPI модуля. Также его можно каскадировать, соединяя несколько микросхем в один большой регистр.
Чтобы пример был наглядным, с помощью регистров 74HC595 к микроконтроллеру будет подключен семисегментный индикатор.
Сдвиговый регистр 74HC595
Микросхема 74HC595 представляет собой 8-ми разрядный сдвиговый регистр с регистром хранения и выходными буферами с тремя состояниями.
Назначение ее выводов следующее:
DS – вход данных сдвигового регистра
SHCP – тактовый вход сдвигового регистра
MR – вход сброса сдвигового регистра
STCP – тактовый вход регистра хранения
OE – вход разрешения выходных буферов
Q0…Q7 – выходы буферов
Q7S - выход каскадирования
GND, VCC – выводы питания
Рассмотрим логику работы сдвигового регистра 74HC595.
Вход сброса MR в рабочем состоянии подтянут к плюсу питания. Низкий логический уровень на этом выводе стирает содержимое сдвигового регистра. Содержимое регистра хранения при этом никак не меняется.
На входе DS устанавливается требуемый логический уровень. По положительному перепаду тактового сигнала на входе SHCP содержимое сдвигового регистра (разряды с 0 по 7-ой) смещается на один разряд (в направлении старших разрядов), а нулевой разряд регистра сохраняет логический уровень, установленный на входе DS. При смещении 7-ой разряд сдвигового регистра не затирается, а сохраняется во внутреннем триггере и транслируется на вывод Q7S, который предназначен для каскадирования сдвигового регистра. Для записи одного байта данных описанную последовательность нужно повторить 8 раз.
По положительному перепаду тактового сигнала на входе STCP данные с выхода сдвигового регистра записываются в регистр хранения. Если на выводе OE будет низкий логический уровень, то данные регистра хранения установятся на выходах Q0 … Q7, в противном случае эти выходы будут находиться в третьем состоянии (Hi-Z).
Для каскадирования сдвигового регистра выход Q7S подключают к входу данных DS следующего регистра, а выводы SHCP, MR, STCP, OE одного регистра соединяют с аналогичными выводами другого. Пример каскадного включения двух сдвиговых регистров можно видеть на схеме ниже.
Управление сдвиговым регистром 74HC595 можно реализовать как программно, так и аппаратно, используя SPI модуль микроконтроллера AVR. Последнее возможно благодаря тому, что временная диаграмма сигналов управления 74HC595 совпадает с диаграммой формируемой модулем SPI в нулевом режиме (SPI mode 0). Что собственно неудивительно, поскольку SPI модуль содержит сдвиговый регистр в своем составе.
В случае программного управления - выводы сдвигового регистра подключаются к любым выводам общего назначения. Для аппаратного управления сдвиговым регистром его нужно подключить следующим образом:
DS - > MOSI
SHCP - > SCK
STCP - > SS
MR - > через резистор к VCC
OE - > GND
Схема подключения семисегментного индикатора
Стандартная схема управления 4-ех символьным семисегментным индикатором требует для своей работы 12-ти выводов, поэтому в схеме используются два сдвиговых регистра 74HC595, включенных каскадно.
Предельный ток одного выходного вывода сдвигового регистра составляет ~35 мА в обе стороны (втекающий/вытекающий). Этого может быть недостаточно для обеспечения яркого свечения индикатора, поэтому его общие выводы (5, 8, 9, 12) подключены к сдвиговому регистру не напрямую, а через транзисторы.
Выводы OE регистров подключены к нулю питания, выводы MR подтянуты через резистор к плюсу питания. В данной схеме эти выводы не используются.
Управление регистрами осуществляется по трем линиям, подключенным к SPI модулю микроконтроллера AVR:
MOSI – последовательный ввод данных
SCK - тактовый сигнал для сдвига данных
SS – сигнал для защелкивания данных в регистре хранения.
В схеме использован индикатор с общим анодом, поскольку он просто был под рукой. Это не принципиально и можно собрать аналогичную схему для индикатора с общим катодом.
Код
Учебный проект не делает ничего полезного, вся его работа заключается в выводе значения программного счетчика на индикатор с помощью модуля SPI.
Чтобы облегчить себе задачу, я взял старый драйвер семисегментного индикатора из статьи "Вольтметр на микроконтроллере" и доработал его.
Изменений было немного, они коснулись только кода отвечающего за вывод в порт микроконтроллера. Я реализовал его в виде макросов, подключаемых с помощью директив условной компиляции. Если вы посмотрите код драйвера, то поймете, о чем я говорю.
Теперь драйвер позволяет управлять индикатором, как в стандартной схеме включения, так и в схеме с использованием сдвиговых регистров. Для управления семисегментным индикатором по SPI нужно в файле indicator.h раскомментировать строчку #define IND_SHIFT_REG. При этом, к проекту (помимо модуля indicator.c) должен быть подключен модуль реализующий функцию вывода данных по SPI - void SPI_WriteArray(uint8_t num, uint8_t *data). У меня это модуль spi.c, у вас может быть какой то другой.
Несколько слов о проекте. Он состоит из трех модулей:
main.c - главный программный модуль,
spi.c - драйвер spi, рассмотренный в предыдущей статье,
indicator.c - доработанный драйвер семисегментного индикатора.
В начале main`а происходит инициализация spi модуля и переменных драйвера индикатора. Затем в бесконечном цикле while инкрементируется счетчик и на индикатор выводится его значение. Чтобы не загромождать код, я не стал использовать для этих целей прерывание таймера, а просто добавил в цикл программную задержку.
Для вывода данных в сдвиговый регистр используется функция модуля spi.c:
void SPI_WriteArray(uint8_t num, uint8_t *data);
num – количество передаваемых байтов (обычно размерность массива),
*data – указатель на массив с передаваемыми данными.
Вызовы этой функции прописаны в модуле indicator.c.
Если возникнут вопросы про инициализацию SPI модуля, почитайте предыдущую статью.
Файлы
Проект для IAR AVR
Проект для CodeVision AVR
Проект для WINAVR
Проект для Atmel Studio 6
Проекта для Proteus`a нет, так как я не смог заставить его нормально работать с семисегментным индикатором. Зато есть проект для Atmel Studio 6. Все проекты рабочие и тестировались на макете, что впрочем не исключает возможность ошибок.
Остальные части
Учебный курс AVR. Работа с модулем SPI. Ч1
Учебный курс AVR. Работа с SPI модулем. Чтение и запись данных. Ч2
Comments
И как лучше отправить 21 бит информацию на микру? через массив
У тебя микросхема не управляет SPI линиями, поэтому мое мнение, что можно.
Как лучше передавать 21 бит? Затрудняюсь ответить. Массив удобно использовать для потоковых данных. А микросхема "нормально" воспримет, что ты передашь ей 24 бита (3 байта)?
Но тут в spi.c вроде не совсем корректно:
/*настройка портов ввода-вывода
все выводы, кроме MISO выходы*/
SPI_DDRX = (1
Повторюсь без них:
Но тут в spi.c вроде не совсем корректно:
/*настройка портов ввода-вывода
все выводы, кроме MISO выходы*/
SPI_DDRX = (1 SPI_MOSI)|(1 SPI_SCK)|(1 SPI_SS)|(0 SPI_MISO);
SPI_PORTX = (1 SPI_MOSI)|(1 SPI_SCK)|(1 SPI_SS)|(1 SPI_MISO);
Получается, если другие ножки порта В (не для SPI) до этого как-то устанавливались , то тут "портятся".
Вроде на это наступил. Верно ли?
SPI_DDRX |= (1 SPI_MOSI)|(1 SPI_SCK).....
А в чем была проблема в Proteus?
Т.к. мне удалось заставить его работать, как с индикатором с общим анодом так и с катодом. Я столкнулся только с тем что не очень корректно работала модель транзистора. Пришлось их убрать, из-за этого
поменял в коде пару строк. Также в модели у меня идет Q1 на Digi1 и т.д.
Спасибо
Может есть идеи как это выполнить с мин переферией?
Давненько захожу на Ваш сайт и очень доволен тем, что Вы доходчиво все объясняете.
И в связи с этим возник вопрос об полном управлении двумя или тремя группами светодиодных индикаторов по SPI (управление разрядами тоже через SPI)
Да чего там приводить-то?
Code:
void SPI_WriteArray(uint8_t num, uint8_t *data)
{
while (num--) {
SpiWriteByte(*data++);
}
}
Без разницы: за передачу байта отвечает функция SpiWriteByte, и в ней может быть как программный, так и аппаратный SPI.
Но повторил ваш код, а у меня он не работает.
Где я мог накосячить?
https://www.dropbox.com/s/sm2wm1h0bpuf7ie/CV_SPI_ShiftReg.rar?dl=0
RSS feed for comments to this post