Урок 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 для часов.
Спасибо.
Спасибо за уроки!
Всегда пожалуйста!
Как точку вывести в третьем разряде?
в Microchip Studio 7 пытаюсь запустить сей проект — не получается.
Ссылается на
1
Severity Code Description Project File Line Project Rank
Error recipe for target 'SPI_DYN_LED.elf' failed SPI_DYN_LED c:\users\1\Documents\Atmel Studio\7.0\SPI_DYN_LED\SPI_DYN_LED\Debug\Makefile 118 1
2
Severity Code Description Project File Line Project Rank
Error undefined reference to
timer_ini()' SPI_DYN_LED c:\users\1\Documents\Atmel Studio\7.0\SPI_DYN_LED\SPI_DYN_LED\main.cpp 14 1
3
Severity Code Description Project File Line Project Rank
Error undefined reference to ledprint(unsigned int)' SPI_DYN_LED c:\users\1\Documents\Atmel Studio\7.0\SPI_DYN_LED\SPI_DYN_LED\main.cpp 28 1
4
Severity Code Description Project File Line Project Rank
Error ld returned 1 exit status SPI_DYN_LED collect2.exe 0 1
изучение темы программирования на начальном этапе, по этому прошу сильно не пинать
Доброе время суток.
Подскажите пожалуйста как подключить точку через 74hc595 для часов.
Спасибо.
Надо заменить число 0b1xxxxxx на 0b0xxxxxx, подав собственно логический '0' на катод сегмента точки. Для данной реализации не очень удобно получается, но, немного поразмыслив, решение найдется