Урок 27
SPI. LED – динамическая индикация
Продолжим работать с шиной SPI. Также работать мы будем с той же микросхемой сдвигового регистра 74HC595, работаем мы с данной микросхемой уже третье занятие. Данная микросхема нас привлекла тем, что она стоит сущие копейки, а также тем, что она не очень сложна в настройке и что она подключается к контроллеру посредством шины SPI. Все эти факторы позволили нам выбрать именно этот регистр для первоначальных занятий по освоению шины SPI.
На прошлом занятии мы подключили к данной микросхеме светодиодный семисегментный индикатор и научились им управлять.
Сегодня мы подключим уже четырёхразрядный индикатор и также им попробуем поуправлять.
Соответственно, для такой цели нам не хватит одного регистра, и мы возьмём ещё один.
Это также даёт нам уникальную возможность позаниматься с интерфейсом SPI, подключив одновременно два ведомых устройства. Причём попробуем мы их подключить, используя кольцевой (или каскадный способ).
Вот наша схема в протеусе (нажмите на картинку для увеличения изображения)
Мы видим здеь то, что одна микросхема у нас отвечает за параллельные катоды каждого разряда, а вторая – за аноды. Мы использовали для подключения анодов, как и в обычных параллельных схемах, ключевые транзисторы в инверсных режимах, также подключили от ножек порта микросхемы базы данных транзисторов через токоограничивающие резисторы, поэтому, чтобы у нас появился активный положительный уровень на аноде каждого индикатора, то с порта должен на транзистор поступить наоборот низкий логический уровень. Всё, вообщем, как обычно, только управлиние данной схемой осуществляется уже не с контроллера, а с двух сдвиговых регистров.
Теперь левая часть схемы. Здесь подключены параллельно ножки синхронизации и ножки выбора а также подачи команды сдвига SS. А вот c ножки MOSI контроллера сигнал идёт на цифровой вход микросхемы отвечающей за аноды, а уже с цифрового выхода этой микросхемы – на цифровой вход микросхемы, отвечающей за кактоды. Соответственно здесь у нас кольцо рвётся, так как подключать вход контроллера MISO с цифрового выхода верхней микросхемы мы не будем за ненадобностью такого мониторинга.
То есть, первым делом мы будем посылать байт для катодов, а затем без задержки – байт для анодов. Тем самым байт для катодов попадёт по кругу на нужную микросхему, а байт для катодов придёт в свою микросхему. Затем мы уже сгенерируем импульс для подачи байтов в регистры хранения микросхем, и немного подождём. И в это время уже наши индикаторы получат нужные для себя сигналы.
Вообщем как-то вот так.
Также давайте посмотрим всё это по-настоящему, с настоящим контроллером, регистрами и индикаторами
Понятное дело, нам понадобится проект, так как без кода ничего вышерассказанного не произойдёт.
Проекту мы присвоим имя SPI_DYN_LED, а сделан он будет полностью из проекта предыдущего занятия SPI_LED.
Также из проекта, который мы писали в занятии по сборке часов на светодиодном индикаторе, мы возьмём библиотеку для индикаторов led.c и led.h и подключим её в наш проект. Как подключать библиотеки, я уже объяснять не буду, это уже, я думаю, знают все. И, понятное дело, данную библиотеку для работы с шиной SPI придется как-то модифицировать. Но это немного позже.
А пока мы займёмся всё-таки основным модулем.
Сначала мы в функцию main() добавим переменную, которая нам будет считать до 9999
int main(void)
{
unsigned int i=0;
Дальше давайте процедуру инициализации шины SPI оформим в отдельную функцию и напишем её выше функции main
void SPI_init(void)
{
DDRB |= ((1<<PORTB2)|(1<<PORTB3)|(1<<PORTB5)); //ножки SPI на выход
PORTB &= ~((1<<PORTB2)|(1<<PORTB3)|(1<<PORTB5)); //низкий уровень
SPCR = ((1<<SPE)|(1<<MSTR));//включим шину, объявим ведущим
}
Соответственно, этот код мы из main() удалим и вместо него вызовем данную функцию
unsigned int i=0;
SPI_init();
Далее инициализируем наш таймер и включим глобальные прерывания
SPI_init();
timer_ini();
sei();
Посылку нулевого байта мы оставим для того, чтобы наш сдвиговый регистр как-то инициализировался, причём мы её не просто оставим, а передадим их целых два, так как регистр у нас тоже не один. Ну только импульс формировать мы будем после передачи второго байта, как и условились выше
SPDR = 0b00000000;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
SPDR = 0b00000000;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
//сгенерируем отрицательный фронт дл¤ записи в STORAGE REGISTER
PORTB |= (1<<PORTB2); //высокий уровень
PORTB &= ~(1<<PORTB2); //низкий уровень
_delay_ms(500);
while(1)
Из бесконечного цикла удалим всё полностью. Затем мы в нём напишем наш стандартный код счёта индикатора
while(1)
{
for(i=0;i<10000;i++)
{
ledprint(i);
_delay_ms(500);
}
}
У нас в функции ledprint также во входных аргументах есть тип проме самого числа, но он нам будет не нужен и мы его уберём в библиотеке.
Сначала мы это подправим в прототипе функции
//——————————————–
void ledprint(unsigned int number);
Уберём теперь из led.c все макросы, так как они нам не нужны, а также уберём переменную для режима
#include "led.h"
//——————————————–
#define MODETIMEVIEW 100
#define MODETEMPERVIEW 101
#define MODEDATEVIEW 102
#define MODEDAYVIEW 103
#define MODEYEARVIEW 104
//——————————————–
#define MODENONEEDIT 0
#define MODEHOUREDIT 1
#define MODEMINEDIT 2
#define MODEDATEEDIT 3
#define MODEMONTHEDIT 4
#define MODEYEAREDIT 5
#define MODEDAYEDIT 6
#define MODEALARMHOUREDIT 7
#define MODEALARMMINEDIT 8
//——————————————–
unsigned char R1=0, R2=0, R3=0, R4=0;
unsigned char clockmode;
//——————————————–
extern unsigned char clockeditmode;
//——————————————–
Сначала в функции segchar мы просто заменим PORTD на SPDR по понятным причинам
//——————————————–
void segchar (unsigned char seg)
{
switch(seg)
{
case 1: SPDR = 0b11111001; break;
case 2: SPDR = 0b10100100; break;
case 3: SPDR = 0b10110000; break;
case 4: SPDR = 0b10011001; break;
case 5: SPDR = 0b10010010; break;
case 6: SPDR = 0b10000010; break;
case 7: SPDR = 0b11111000; break;
case 8: SPDR = 0b10000000; break;
case 9: SPDR = 0b10010000; break;
case 0: SPDR = 0b11000000; break;
case 10: SPDR = 0b10111111; break; // знак –
case 11: SPDR = 0b11111111; break; // пустое место
case 12: SPDR = 0b11000110; break; // буква С для показаний температуры
}
}
//——————————————–
Удалим тела всех наших четырёх условий из обработчика прерываний от таймера, чтобы в них не путаться, так как там вообще будет всё по другому
//——————————————–
ISR (TIMER1_COMPA_vect)
{
if(n_count==0)
{
}
if(n_count==1)
{
}
if(n_count==2)
{
}
if(n_count==3)
{
}
n_count++;
if (n_count>3) n_count=0;
}
//——————————————–
Далее начнём писать код для первого разряда.
Первым делом вызовем segchar, чтобы значение байта для катодов занеслось в регистр SPDR, мы же данное значение передаём первым
if(n_count==0)
{
segchar(R1);
Затем мы данное значение, собственно и передадим сначала в неправильную микросхему, потом она перейдёт в нужную на следующем шаге передачи
segchar(R1);
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
Теперь мы должны передать байт для анодов, помня об инверсии
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
SPDR = 0b00001110;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
Мы видим, что у нас активным будет самый младший анод, что нам и требуется.
Ну и в конце тела данного условия мы сформируем импульс для передачи байтов в нижние регистры
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
//сгенерируем отрицательный фронт для записи в STORAGE REGISTER
PORTB |= (1<<PORTB2); //высокий уровень
PORTB &= ~(1<<PORTB2); //низкий уровень
}
Теперь давайте то же самое проделаем и для остальных разрядов
if(n_count==1)
{
segchar(R2);
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
SPDR = 0b00001101;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
//сгенерируем отрицательный фронт для записи в STORAGE REGISTER
PORTB |= (1<<PORTB2); //высокий уровень
PORTB &= ~(1<<PORTB2); //низкий уровень
}
if(n_count==2)
{
segchar(R3);
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
SPDR = 0b00001011;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
//сгенерируем отрицательный фронт дл¤ записи в STORAGE REGISTER
PORTB |= (1<<PORTB2); //высокий уровень
PORTB &= ~(1<<PORTB2); //низкий уровень
}
if(n_count==3)
{
segchar(R4);
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
SPDR = 0b00000111;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
//сгенерируем отрицательный фронт дл¤ записи в STORAGE REGISTER
PORTB |= (1<<PORTB2); //высокий уровень
PORTB &= ~(1<<PORTB2); //низкий уровень
}
Ну и также подправим функцию ledprint, так как нам нужно оттуда убрать режим
//——————————————–
void ledprint(unsigned int number, unsigned char cm)
{
clockmode=cm;
R1 = number%10;
R2 = number%100/10;
R3 = number%1000/100;
R4 = number/1000;
}
//——————————————–
Теперь можно собрать код и проверить его сначала в протеусе
Всё у нас нормально считается.
Убавим задержку в бесконечном цикле, иначе мы не дождёмся больших цифр
_delay_ms(10);
Ну и также проверим наш код, прошив контроллер, на настоящей схеме
На этом наши занятия по SPI не заканчиваются. В следующий раз мы попробуем подключить по данной шине модуль с индикатором, на котором уже используется специальный драйвер для работы с индикатором, а также восьмиразрядны индикатор. Там уже можно будет управлять яркостью индикатора и делать некоторые интересные вещи.
Предыдущий урок Программирование МК AVR Следующий урок
Техническая документация на сдвиговый регистр 747HC595
Программатор ,сдвиговые регистры и индикатор можно приобрести здесь:
Программатор (продавец надёжный) USBASP USBISP 2.0
Сдвиговые регистры 74HC595N 10 шт
Семисегментный чертырехразрядный индикатор красный с общим анодом 10 шт
Смотреть ВИДЕОУРОК (нажмите на картинку)
Доброе время суток.
Подскажите пожалуйста как подключить точку через 74hc595 для часов.
Спасибо.
Спасибо за уроки!
Всегда пожалуйста!
Как точку вывести в третьем разряде?