AVR Урок 11. Динамическая индикация. Часть 2

 

 

 

    Урок 11

 

Часть 2

 

Динамическая индикация

 

 

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

Теперь закончим со сборкой и перейдём в проект, так как без соответствующего кода ничего работать на будет.

Начнем с порта B. Он у нас был сконфигурирован весь полностью на вход, но теперь так дело не пойдёт. Давайте половину порта сделаем на вход, а ту половину, где у нас подключены через транзисторы аноды индикаторов, сконфигурируем на выход. А подтягивающий резистор на ножке порта B переместим на другую ножку, к которой мы переподключили кнопку

 

DDRD = 0xFF;

DDRB = 0b00001111;

PORTB = 0b00100000;

 

Теперь, если мы соберём код и запустим его в протеусе, то у нас будут работать одновременно два индикатора. Это нам не подходит. Поэтому думаем дальше.

Прибавим разрядность переменной i. Пока под две цифры нам и char подойдёт, но на будущее нам этого не хватит. Поэтому сделаем это заранее

 

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

unsigned int i;

 

И ещё нам под цифры каждого разряда потребуются также переменные

 

unsigned int i;

unsigned char R1=0, R2=0;

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

 

Также добавим ещё одну функцию, которая будет заниматься выводом двухзначной цифры на дисплей. Поэтому в качестве входного параметра здесь будет unsigned int. Добавим данную функцию перед функцией main()

 

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

void ledprint(unsigned int number)

{

 

}

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

 

В данной функции мы распределим всю двухзначную цифру по разрядам.

Для этого мы применим математическую операцию, которая вычисляет остаток от деления. Обозначается она знаком %. Через данную операцию мы вычислим единицы. А затем, чтобы вычислить десятки, мы просто поделим на 10 входной параметр. Но так как у нас везде целочисленные операнды, то в результате получится целое число с выброшенным остатком, что и будет соответствовать десяткам, так как число у нас двухзначное

 

void ledprint(unsigned int number)

{

  R1 = number%10;

  R2 = number/10;

}

 

А таймер мы сегодня применим не для того, чтобы он наращивал цифры, а для того, чтобы он поочерёдно зажигал цифры на индикаторах. Добавим ещё одну переменную. Добавить мы её также можем необязательно в начале файла, а можем непосредственно перед кодом функции-обработчика прерываний, главное чтобы переменная в функции уже была определена

 

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

unsigned char n_count=0;

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

ISR (TIMER1_COMPA_vect)

 

И в зависимости от значения данной переменной, мы будем включать определённую ножку порта B, разрешая при этом вывод цифры на соответствующем индикаторе. Другую ножку порта мы будем отключать. Таже в том же условии мы будем вызывать функцию, которая будет включать комбинацию сегментов в зависимости от посланной переменной. Но так как определённая ножка порта B будет у нас отключена, то сегменты другого индикатора светиться на будут. Затем после всего этого мы будем прибавлять на 1 (инкрементировать) данную переменную. Но делать мы это будем до тех пор, пока она не достигнет значения 1. Вообщем, в связи с этим, переменная n_count будет равна либо 1, либо 0, так как пока у нас только 2 индикатора и соответственно будет только 2 варианта

 

ISR (TIMER1_COMPA_vect)

{

  if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB|=(1<<PORTB1);segchar(R1);}

  if(n_count==1) {PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB0);segchar(R2);}

  n_count++;

  if (n_count>1) n_count=0;

}

 

Теперь нам осталось только лишь в функцию ledprint() отправить какую-нибудь цифру. Давайте пока подадим любую и наращивать её пока не будем. Для этого мы вызовем функцию в main() после всех инициализаций, отправив в неё, например, число 97

 

sei();

ledprint(97);

while(1)

 

 

Попробуем собрать проект и запустить его в протеусе. Мы видим, что код работает, динамическая индикация присутствует, но слишком наглядно, так как цифры светятся по очереди раз в 2 секунды.

Для наглядности работы динамической индикации это хорошо, но для постоянной работы не пойдёт. Глаз вообще не должен видимть динамику индикации. Должно быть впечатление, что цифры светятся постоянно.

Поэтому нам нужно настроить таймер на другую частоту. Попробуем порегулировать частоту делителем. Сделаем делитель 8 вместо 256 и, произведя нехитрые расчеты мы получим частоту 32. но так как у нас индикатора два, то получится на каждый по 16 герц. Этого конечно маловато, но попробовать можно. Чтобы нам выставить делитель 8, то мы в регистре TCCR1B должны включить бит CS11

 

image05

 

Так и поступим

 

  OCR1AL = 0b01000010;

  TCCR1B |= (1<<CS11);//установим делитель

}

 

Соберём код и посмотрим работу в протеусе. Вроде бы не мерцает. На живых индикаторах пока смотреть не будем. Если что, то потом подравим частоту. Пока займёмся счетчиком, нам нужно будет считать до 99, а затем начинать с нуля. Для этого раскомментируем весь код в бесконечном цикле, также раскомментируем переменные для кнопки

 

int main(void)

{

  unsigned char butcount=0, butstate=0;

 

Также, раз уж считать мы будем до 99, то также исправим код вот здесь

 

while(1)

{

  for(i=0;i<100;i++)

  {

 

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

 

if (!(PINB&0b00100000))

 

Также вместо функции segchar будет функция ledprint

 

ledprint(i);

_delay_ms(500);

 

Ну, собственно, и всё.

Соберём наш код и проверим его сначала в протеусе. Всё у нас считается и сбрасывается кнопкой. Отлично!

Теперь прошьём контроллер и посмотрим на живых индикаторах работу кода.

Код работает, но индикаторы мерцают. Поэтому немножко ещё поиграем с частотой в таймере. Если убрать делитель совсем, то эксперементальным путём было выявлено, что наши индикаторы перестанут светиться вообще. Этот эксперимент вы можете увидеть во второй части видеоурока (ссылки на видеоурок внизу, достаточно кликнуть по нужной картинке, в принципе, как и в любом уроке). Поэтому придется нам поиграть с цифрой в регистре OCR1A. Причём мы докажем, что виновата не слишком высокая частота, а то, что без делителя просто отказывается работать таймер. Поэтому цифру мы рассчитаем так, чтобы частота при делителе была примерно такая же как и без делителя, но на предыдущем значении в OCR1A. Цифру мы получим примерно 3906. Превратим её в двоичный вид и занесём в регистровую пару OCR1A. Код станет вот таким

 

OCR1AH = 0b00001111; //записываем в регистр число для сравнения

OCR1AL = 0b01000010;

 

Теперь, если собрать код и прошить контроллер, мы увидим, что всё работает как надо

 

image06

 

 

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

 

Исходный код

 

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

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

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

 

 

Смотреть ВИДЕОУРОК

 

AVR Динамическая индикация

24 комментария на “AVR Урок 11. Динамическая индикация. Часть 2
  1. Dmitriy:

    Подскажите, нам обязательно нужно использовать прерывание для смены индикатора для отображения. Можем ли мы просто давать команду на включение и выключение соответствующего порта, который через транзистор питает наш индикатор в общем цикле программы? Нечто подобное для числа, скажем, "88":

    PORTC |= (1<<PC0); Зажигаем старший разряд 

    PORTB = 0b00000000;

    PORTC &= ~(1<<PC0); Тушим старший разряд

    PORTC |= (1<<PC1); Зажигаем младший разряд 

    PORTB = 0b00000000;

    PORTC &= ~(1<<PC1); Тушим младший разряд

    По идее частоты в 8МГц должно хватить для того, что бы индикация не мерцала. У меня Т0 и Т1 в программе задействованы для других задач.

    • Прервывание от таймера помогает достичь постоянной частоты.

      • Dmitriy:

        Я так и полагал. Тем более, что моя схема при симуляции в Протеусе не отображает динамичискую индикацию в общем цикле без прерываний (я имею ввиду визуально). Загрузка процессора при этом колеблется в пределах 70-80%, о чём я получаю предупредительное сообщение. Но если запустить пошаговый режим симуляции — динамическая индикация присутствует. Но частоста отображения не стабильна в ходе последовательности шагов. Надо будет собрать реальное устройство и посмотреть как глаз будет воспринимать такой режим динамической индикации.

  2. vega:

    Здравствуйте, не могу сообразить как добавить еще один индикатор

     

    • По идее так же как и все остальные.

    • Алексей:

      void ledprint(unsigned int number)
      {
      // сделаем пересчет для трехзначного числа
      R1 = number%10;
      R2 = number/10;
      R3 = R2/10
      R2 = R2%10

      }
      unsigned char R1=0, R2=0; R3=0; //Добавим переменную R3

      ISR (TIMER1_COMPA_vect)

      {
      if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB2);segchar(R1);}
      if(n_count==1) {PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB0);PORTB&=~(1<<PORTB2);segchar(R2);}
      if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB0);PORTB&=~(1<2) n_count=0; //Сброс счетчика индикаторов на один выше
      //С портами какие выключать и включать не разобрался просто не понял какие отвечают за включение какие за выключение,если интересно кому будет протестирую и сделаю правильно
      }

      //Блин математику понял хотя не дружу с ней и опыта ноль начал с этих уроков. боялся что такая схема для двухзначного коряво получится. но по расчетам все правильно на практике завтра проверю. Прям заинтересовался а смогу я без дальнейших уроков сам сделать третий индикатор для двух и трехзначных чисел))

    • Алексей:

      {
      R1 = number%10;
      R2 = number/10;
      R3 = R2/10;
      R2 = R2%10;

      }
      {
      if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB2);segchar(R1);}
      if(n_count==1) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB1);segchar(R2);}
      if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB&=~(1<<PORTB1);PORTB|=(1<2) n_count=0;
      }

      unsigned char R1=0, R2=0, R3=0;

  3. Вит:

    я пересчитал частоту индикации 50 гц на канал — все работает отлично

  4. Алексей:

    //Странно писал все правильно а отобразилось с косяком, даже скопировал чтоб не потерять этот текст
    // короче поправка

    if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB2);segchar(R1);}
    if(n_count==1) {PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB0);PORTB&=~(1<<PORTB2);segchar(R2);}
    if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB0);PORTB&=~(1<2) n_count=0; //Сброс счетчика индикаторов на один выше

  5. Алексей:

    Админ. У меня часть текста не отправляется
    if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB2);segchar(R1);}
    if(n_count==1) {PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB0);PORTB&=~(1<<PORTB2);segchar(R2);}
    if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB0);PORTB&=~(1<2) n_count=0; //Сброс счетчика индикаторов на один выше

  6. Алексей:

    if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB0);PORTB&=~(1<2) n_count=0; //Сброс счетчика индикаторов на один выше

  7. Алексей:

    «if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB0);PORTB&=~(1<2) n_count=0; //Сброс счетчика индикаторов на один выше»
    Пробую так. если что скобки убрать

  8. Алексей:

    Да простит меня Модератор:
    if(n_count==2) {PORTB&=~(1<<PORTB2); PORTB|=(1<<PORTB0); PORTB&=~(1<2) n_count=0; //Сброс счетчика индикаторов на один выше

  9. Алексей:

    Да простит меня Модератор Еще раз:
    if(n_count==2) {PORTB&=~(1< <PORTB2); PORTB|=(1< <PORTB0); PORTB&=~(1< 2) n_count=0; //Сброс счетчика индикаторов на один выше
    Пробелы между стрелочками и плюсиками убрать

  10. Алексей:

    Администратор! у меня проблема! Часть кода в тексте пропадает! И не понятно откуда «2» взялось. Как будто текст перерабатывается в комментарии в код и удаляется
    »
    1)
    «

    • Алексей:

      У меня между скобками текст а отправилось только
      «1)»
      Попробую через пробелы написать, где пробелы та часть текста не отправляется, какаято она специфическая видимо

      if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB&=~(1<<PORTB0);PORTB|=(1 < 1 ) n_count=0;

  11. Алексей:

    {
    R1 = number%10;
    R2 = number/10;
    R3 = R2/10;
    R2 = R2%10;

    }
    {
    if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB2);segchar(R1);}
    if(n_count==1) {PORTB&=~(1<<PORTB0);PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB1);segchar(R2);}
    if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB0);segchar(R3);}
    n_count++;
    if (n_count x 2) n_count=0; //Где x Заменить на знак больше. Просто так комментарий не отправлялся получалось скобка открывается а после n_count Закрывается и HTML Видимо его кушал. Спасибо Брату подсказал
    }

    unsigned char R1=0, R2=0, R3=0;

    • Алексей:

      Это для трехзначной Индикации. Администрацию Попрошу Удалить мои комментарии. Я потом заново отвечу А то я засрал сайт.

  12. Айдар:

    Добрый день, я переписывал код по ходу урока, собрал схему в проеусе.
    на индикаторах цифры обозначаются не верно, т.е. 1 нормально а потом на 2 один сегмент не горит, средний и так на всех цифрах, и на 2 один лишний!
    подскажите что проверить!

  13. Айдар:

    Для динамической индикации 6 цифр, счет до 235960
    подойдет такой код, по вашему примеру

    ISR (TIMER1_COMPA_vect)
    {
    if(n_count==0) {PORTB&=~(1<<PORTB0);PORTB|=(1<<PORTB1);segchar(R1);}
    if(n_count==1) {PORTB&=~(1<<PORTB1);PORTB|=(1<<PORTB2);segchar(R2);}
    if(n_count==2) {PORTB&=~(1<<PORTB2);PORTB|=(1<<PORTB3);segchar(R3);}
    if(n_count==3) {PORTB&=~(1<<PORTB3);PORTB|=(1<<PORTB4);segchar(R4);}
    if(n_count==4) {PORTB&=~(1<<PORTB4);PORTB|=(1<<PORTB5);segchar(R5);}
    if(n_count==5) {PORTB&=~(1<<PORTB5);PORTB|=(1<5) n_count=0;
    }
    //———————————————
    void ledprint(unsigned int number)
    {
    // R1 = number%10;
    // R2 = number/10;
    R1 = number%10; // единицы
    R2 = number/10; // десятки
    R3 = number/100; // сотни
    R4 = number/1000; // тысячи
    R5 = number/10000;
    R6= number/100000;
    }

      • Айдар:

        Добрый день! Не хочу сильно Вас отвлекать, подскажите пожалуйста где найти подробное описание Таймера ?

        Не понял какое число мне записать для сравнения в этот таймер для счета до 235959.

        TIMSK |= (1<<OCIE1A); //устанавливаем бит разрешения прерывания 1ого счетчика по совпадению с OCR1A(H и L)
        OCR1AH = 0b10000000; //записываем в регистр число для сравнения
        OCR1AL = 0b00000000;
        TCCR1B |= ();//установим делитель.

  14. Сергей:

    Автор, добавьте в статью понятную методу расчета таймера(а не «делаем нехитрые вычисления в уме».Выкладка всех математических действий. Это важно в самом начале. То, что очевидно для вас, у других может вызывать вопросы.

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

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

*