AVR Урок 27. SPI. LED — динамическая индикация



 

Урок 27

SPI. LED — динамическая индикация

 

Продолжим работать с шиной SPI. Также работать мы будем с той же микросхемой сдвигового регистра 74HC595, работаем мы с данной микросхемой уже третье занятие. Данная микросхема нас привлекла тем, что она стоит сущие копейки, а также тем, что она не очень сложна в настройке и что она подключается к контроллеру посредством шины SPI. Все эти факторы позволили нам выбрать именно этот регистр для первоначальных занятий по освоению шины SPI.

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

Сегодня мы подключим уже четырёхразрядный индикатор и также им попробуем поуправлять.

Соответственно, для такой цели нам не хватит одного регистра, и мы возьмём ещё один.

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

Вот наша схема в протеусе (нажмите на картинку для увеличения изображения)

 

image00_500

 

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

Теперь левая часть схемы. Здесь подключены параллельно ножки синхронизации и ножки выбора а также подачи команды сдвига SS. А вот c ножки MOSI контроллера сигнал идёт на цифровой вход микросхемы отвечающей за аноды, а уже с цифрового выхода этой микросхемы — на цифровой вход микросхемы, отвечающей за катоды. Соответственно здесь у нас кольцо рвётся, так как подключать вход контроллера MISO с цифрового выхода верхней микросхемы мы не будем за ненадобностью такого мониторинга.

То есть, первым делом мы будем посылать байт для катодов, а затем без задержки — байт для анодов. Тем самым байт для катодов попадёт по кругу на нужную микросхему, а байт для катодов придёт в свою микросхему. Затем мы уже сгенерируем импульс для подачи байтов в регистры хранения микросхем, и немного подождём. И в это время уже наши индикаторы получат нужные для себя сигналы.

Вообщем как-то вот так.

Также давайте посмотрим всё это по-настоящему, с настоящим контроллером, регистрами и индикаторами

 

image02

 

Понятное дело, нам понадобится проект, так как без кода ничего вышерассказанного не произойдёт.

Проекту мы присвоим имя 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;

}

//———————————————

 

Теперь можно собрать код и проверить его сначала в протеусе

 

image01

 

Всё у нас нормально считается.

Убавим задержку в бесконечном цикле, иначе мы не дождёмся больших цифр

 

_delay_ms(10);

 

Ну и также проверим наш код, прошив контроллер, на настоящей схеме

 

image03

 

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

 

 

Предыдущий урок Программирование МК AVR Следующий урок

 

Исходный код

 

Техническая документация на сдвиговый регистр 747HC595

 

Программатор ,сдвиговые регистры и индикатор можно приобрести здесь:

Программатор (продавец надёжный) USBASP USBISP 2.0

Сдвиговые регистры 74HC595N 10 шт

Семисегментный чертырехразрядный индикатор красный с общим анодом 10 шт

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR SPI. LED - динамическая индикация

7 комментариев на “AVR Урок 27. SPI. LED — динамическая индикация
  1. Коля:

    Доброе время суток.
    Подскажите пожалуйста как подключить точку через 74hc595 для часов.
    Спасибо.

  2. Димитрий:

    Спасибо за уроки!

  3. Как точку вывести в третьем разряде?

  4. Алексей:

    в 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
    изучение темы программирования на начальном этапе, по этому прошу сильно не пинать

  5. Джамшед:

    Доброе время суток.
    Подскажите пожалуйста как подключить точку через 74hc595 для часов.
    Спасибо.

    • Artur:

      Надо заменить число 0b1xxxxxx на 0b0xxxxxx, подав собственно логический '0' на катод сегмента точки. Для данной реализации не очень удобно получается, но, немного поразмыслив, решение найдется

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*