7 ноября 2011 г.

Arduino: управление 7-сегментным дисплеем по SPI

В этой статье будет проведено краткое знакомство со сдвиговым регистром, семисегментным дисплеем и шиной SPI, будет напомнен перевод из двоичной системы в шестнадцатеричную.
В итоге, мы напишем программу для Arduino, которая будет управлять семисегментным дисплеем через сдвиговый регистр, подключенный к SPI.


Шина SPI

SPI (Serial Peripheral Interface) – это последовательный синхронный стандарт передачи данных, который предназначен для общения контроллера с периферией. Большинство современных микроконтроллеров поддерживают SPI, поскольку она очень проста и удобна в использовании. У Arduino есть библиотека для работы с SPI, которая состоит всего из шести простых функций.


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



Шина SPI состоит из 4х проводов: MOSI, MISO, CS и SCLK:

MOSI (Master Out Slave In) или просто SI – передача данных от ведущего к ведомому.
MISO (Master In Slave Out) или просто SO – передача данных от ведомого к ведущему.
CS (Chip Select) или SS (Slave Select) – выбор ведомого устройства.
SCLK (Serial CLocK) или просто SCK – передача тактового сигнала от ведущего к ведомому.
Передача данных от ведущего производится так:  ведущий выставляет низкий уровень на проводе CS ведомого устройства, с которым он собирается общаться, после чего по MOSI передаются биты. По окончании передачи ведущий "отпускает" провод CS - возвращает его на высокий уровень.

Чтобы подключить несколько устройств к шине SPI, для каждого устройства заводится свой провод CS, и ведущий, поочередная "дергая" ими, общается с тем или иным устройством. Подробнее подключение нескольких устройств мы рассмотрим в следующей статье, а пока двинемся дальше.


Сдвиговый регистр 74РС595
Мы будем использовать в качестве ведомого устройства сдвиговый регистр 74HC595, к которому подключим 7-сегментный дисплей. Полное его название - "8-bit serial-in, serial or parallel-out shift register with output latches" - "восьмибитный регистр с последовательным вводом и последовательным или параллельным выводом и с выходными защелками".


Принцип действия такого регистра заключается в том, что он последовательно получает биты по проводу DS, а при выставлении низкого уровня на проводе STCP «защелкивает» полученные биты так, что на его параллелных выводах аккуратно формируется весь полученный байт. Выход Q7S "повторяет" биты на входе DS, что делает возможным работу регистра как бы в режиме bypass, что пригождается в случае последовательного соединения нескольких регистров (это мы будем использовать в следующий раз).
Здесь:
VCC – питание;
GND – земля;
DS – последовательный ввод. Должен подключаться к проводу MOSI шины SPI;
Q0:Q7 – параллельные выходы;
SHCP – тактовый вход. Должен подключаться к проводу SCK шины SPI;
STCP – защелкивание выводов. Должен подключаться к проводу CS шины SPI;
OE – разрешение работы выходов. При низком уровне выходы работают;
Q7S – последовательный вывод;
MR – общий сброс. Если притянуть его к земле – входы обнулятся.


7-сегментный дисплей
Обычно одиночный семисегментник имеет 10 контактов, из которых 2 – общие, а остальные 8 – отдельные сегменты, включая точку в правом нижнем углу. Семисегментники могут быть с общим анодом или с общим катодом. Необходимо четко понимать, какого именно типа семисегментник у вас в руках!
У меня оказался семисегментник с маркировкой SM111201K, у него общий анод и следующее расположение контактов (весьма непопулярное):
Здесь com общие контакты. Tсли приложить к (любой) ноге com потенциал +5, а к ноге А – 0, то загорится, соответственно, сегмент А. Если у дисплея общий катод, то потенциалы нужно подлючать наоборот!


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


Работа с байтами
В даташите к вашему семисегментнику несомненно есть таблица, какой логический уровень нужно подавать на тот или иной вход, чтобы загорелась та или иная цифра. Скорее всего, эта таблица выглядит так (вместо 0 и 1 могут быть L и H, соответственно):


A
B
C
D
E
F
G
dp
hex
0
0
0
0
0
0
0
1
1
C0
1
1
0
0
1
1
1
1
1
F9
2
0
0
1
0
0
1
0
1
A4
3
0
0
0
0
1
1
0
1
B0
4
1
0
0
1
1
0
0
1
99
5
0
1
0
0
1
0
0
1
92
6
0
1
0
0
0
0
0
1
82
7
0
0
0
1
1
1
1
1
F8
8
0
0
0
0
0
0
0
1
80
9
0
0
0
0
1
0
0
1
90
a
0
0
0
1
0
0
0
1
88
b
1
1
0
0
0
0
0
1
83
c
0
1
1
0
0
0
1
1
C6
d
1
0
0
0
0
1
0
1
A1
e
0
1
1
0
0
0
0
1
86
f
0
1
1
1
0
0
0
1
8E

Если у дисплея общий катод, то 1 нужно заменить на 0, а 0 – на 1!
Самый правый столбец под названием hex – это и есть представление цифры в байтовом виде. Его в даташите нет, нужно рассчитывать самостоятельно. Сделать это очень просто: помня, что байт передается со старшего бита (в нашем случае – самого правого), разбить его на два числа по четыре бита и перевести их в шестнадцатеричное представление.

Замечание: вообще говоря, можно заставить контроллер передавать биты, начиная с младшего, с помощью функции SPI.setBitOrder(), однако в этой статье я не буду это использовать.
Разберем цифру 5. В таблице – это 01001001.  Разворачиваем и разбиваем пополам, получаем 1001 0010, видим, что это 9 и 2. Кто подзабыл перевод из двоичной системы в шестнадцатеричную  смотрим таблицу:


0000
0
0001
1
0010
2
0011
3
0100
4
0101
5
0110
6
0111
7
1000
8
1001
9
1010
A
1011
B
1100
C
1101
D
1110
E
1111
F


Схема соединений


DS счетчика подключаем к 11 пину Arduino (желтый провод); выходы мы всегда разрешаем, поэтому садим OE сразу на землю; STCP подключаем к пину Arduino (зеленый провод); SHCP (тактирующий вход) подключаем к 13 пину Arduino (оранжевый провод); MR садим на питание, так как не планируем использовать сброс; Q7S оставляем болтаться, в данный момент он нам не нужен. 


С подключением семисегментника все просто: нужно аккуратно подключить ногу А к выходу регистра Q0, ногу B к выходу Q1 и т.д., ногу dp к Q7


Код Arduino


Запишем все полученные байты в массив так, что нулевой элемент массива соответствует цифре 0, первый – цифре 1 и т.д.
static uint8_t digit[16] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};

Теперь для отрисовки, например, цифры 2, нужно просто выполнить передачу в регистр digit[2]. Вот код для Arduino, который выводит подряд все шестнадцатеричные числа:

#include <SPI.h> // подключаем библиотеку SPI


enum { reg = 8 }; // провод CS подсоединяем к 8-му пину Arduino


void setup()
{
  SPI.begin();  // инициализируем SPI
  pinMode(reg, OUTPUT); // настраиваем 8-й пин как выход
}


void loop()
{
  // храним в массиве все цифры
  static uint8_t digit[16] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
  // выводим цифры по одной
  for (int i=0;i<16;i++){
    digitalWrite(reg, LOW); // притягиваем CS к земле - начало передачи
    SPI.transfer(digit[i]); // передаем байт
    digitalWrite(reg, HIGH); // отпускаем  CS - конец передачи
    delay(1000); // ждем секунду
  }
  //очистим дисплей на секунду
  digitalWrite(reg, LOW);
  SPI.transfer(0xFF);
  digitalWrite(reg, HIGH);
  delay(1000);
}

Видео работы программы:


Ссылки:
Даташит на регистр 74HC595:
http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf
Рисовалка, в которой я сделал красивую схему:
http://www.fritzing.org/

Далее по теме Arduino и SPI