Подписаться на получение новых статей на почту:

Семи сегментный индикатор и AVR. Динамическая индикация. Программа на Си. Шаг №5

Обновлено 3.04.15. Всем привет. В прошлой статье мы с Вами рассмотрели алгоритм общения с ЖКИ, а также вывод информациина нее, и протестировали в симуляторе. В этой записи я кратенько расскажу о “недорогом” способе вывода информации — это семисегментный индикатор, который является наиболее простым из индикаторов, для отображения арабских цифр, а также некоторых символов, которые возможно на нем вывести. Также рассмотрим программу на Си для AVR, и подключение в железе и симуляторе.

Для отображения букв используются более сложные много сегментные и матричные индикаторы. Но здесь речь пойдет о семи сегментных... Также рассмотрим что такое динамическая индикация, как способ мгновенного отображения измеряемой величины. Для этого в программе рассмотрим использование прерываний.
         Итак следует знать, что индикаторы бывают с общим анодом и катодом как на рисунке ниже. У меня под руками был индикатор с общим катодом (нижняя часть рисунка), управляющий вывод которого подключают к минусу. Вот с ним мы и будем работать.

 

Анодный и катодный семи сегментный индикатор Если индикаторов несколько, следовательно и управляются катоды несколькими ножками МК. !!! Но всегда используйте транзисторы, т.к. порты ввода-вывода могут сгореть из-за относительно большого тока. Я использовал обычные 315 транзисторы. На рисунке ниже я отобразил примерное подключение, через них, управляющего вывода индикатора и контроллера. Для монтажа нам потребуется 11 ножек микроконтроллера, т.е. для отображения информации на сегментах 8 ножек (7 +точка) и по одной ножки на каждый индикатор для его управления, у меня их три, поэтому и ножки управления тоже три. Ниже я привел и описал программу. Для управления сегментами  будем использовать пины одного порта, что б не путаться. Писал под микроконтроллер ATmega8. Если Вы хотите отвязаться от “камня” то это не проблема, например в разбиваем код на библиотеки, где без проблем можно изменить настройки под другой “камень”, в основном это номера пинов и портов. Там же описаны общие правила универсальности и переноса.

 

Рисунок подключения транзистора к МК и индикатору.

Подключение индикатора через транзистор к AVR

Перейдем к программе. В этой небольшой программе (на Си) я привел пример включения трех элементов индикатора и вывод числа с запятой. Использование таймера и прерывания для вывода на индикатор. В ходе написания программы мы должны определиться какой пин порта должен соответствовать сегменту на индикаторе. Сам элемент индикатора приведен на рисунке ниже. Сбоку описание подключения выводов к сегментам элемента индикатора(пин порта – номер ножки элемента (рис. выше) – буква сегмента – число в массиве, отвечающее за включение сегментов на элементе).

семисегментный-элемент ЖК - индикатора

PB0 — 12 — управление первым элементом

PB6 — 9 — управление вторым элементом
PB7 — 8 — управление третьим элементом
PD7 – 11 — (A) — 128
PD6 – 10 — (F) — 64
PD5 – 7 — (B) — 32
PD4 – 5 — (G) — 16
PD3 – 4 — © — 8
PD2 – 3 — (DP) — 4
PD1 – 2 — (D) — 2
PD0 – 1 — (E) — 1

 #include <avr/io.h>
 #include <util/delay.h>
 #include <math.h>
 /*Определим каждому пину порта элемент семи сегментника (риунок выше)*/
 #define a 128
 #define b 32
 #define c 8
 #define d 2
 #define e 1
 #define f 64
 #define g 16
 #define dp 4
 /*Эти макросы содержат числа, соответствующие двойке, возведенной в степень, равной номеру    "ножки" того порта, к которому подключен сегмент индикатора с одноименным макросу названием.*/
 short unsigned int j, k = 0;  /*переменные исп-ся в макросе прерывания*/
 float i = 0;    /*Переменная для вывода на индикатор*/
short unsigned int w = 0; /*Переменная индикатор для включения точки*/
unsigned char Slot[11];  /*Массив в котором хранятся числа, которые нужно
вывести через порт на индикатор, чтобы он показал цифру, равную номеру
  элемента массива. Числа зависят только от макросов.*/
 void Slot_init ()  /*Функция инициализации индикатора*/
 {
         Slot[0] = (a+b+c+d+e+f);
         Slot[1] = (b+c); 
         Slot[2] = (a+b+g+e+d);
         Slot[3] = (a+b+g+c+d); .
         Slot[4] = (f+g+b+c);  /*Имена макросов соответствуют именам сегментов индик*/
         Slot[5] = (a+f+g+c+d);
         Slot[6] = (a+f+g+c+d+e);
         Slot[7] = (a+b+c);
         Slot[8] = (a+b+c+d+e+f+g);
         Slot[9] = (a+b+c+d+f+g);
         Slot[10] = dp;   /*Точка*/
 }
/*В этих переменных хранятся цифры, которые нужно отобразить*/
 char Elem1, Elem2, Elem3; 
 /* Функция выделяет цифры из трехзначного числа Number*/
 void Display (float Number)
 {
        float N1, N2; /*Переменные для функции modf*/
        N1 = modf (Number, &N2); /*Разбиваем число на целую и дробную части,                                                                                                             N1  = дробной N2 = целой*/
        if (N1 != 0) /*Еслине равно нулю то присутствует дробь*/ 
       {
                  Number= Number*10; /*тогда умножаем число на 10, для обычного вывода                                                                                                  на  индикатор трехзначного дробного числа*/
                  w = 1; /* переменная индикатор которая используется в цикле ниже,                                                 чтобы  включать точку*/
        }
        short unsigned int Num1, Num2, Num3;
        Num1=Num2=0;
        while (Number >= 100) /*Сотни*/ 
       {
                Number -= 100;
                Num1++;
       }
       while (Number >= 10) /*Десятки*/ 
      {
               Number -= 10;
               Num2++;
      }
       Num3 = Number; /*Еденицы*/ 
       Elem1 = Slot[Num1];
       if (w == 1) /*Условие дя включения точки на втором элементе*/
       {
                Elem2 = (Slot[Num2]|0×04); /*логическое сложение с пином отвечающим за                                                                                                        точку*/
                w = 0;    /*Выключаем точку*/
        }
        else
                Elem2 = Slot[Num2];
        Elem3 = Slot[Num3];
       }

 

int main (void) /*начало основой программы*/
{
        DDRB = 0Xff;  /*все выводы порта B сконфигурировать как выходы*/
        DDRD = 0xff; /*все выводы порта D сконфигурировать как выходы*/
        PORTD = 0×00; /*Устанавливаем 0*/ 
        PORTB |= _BV (PB6);
        PORTB |= _BV (PB0);
        PORTB |= _BV (PB7);
        Slot_init (); 
        sei (); /*Разрешить общее прерыввание*/
        /*Инициализация таймера Т0*/
       TIMSK = (1<<TOIE0); /*Флаг разрешенияя по переполнению таймера счетчика Т0*/
       TCCR0 = (0<<CS02)|(1<<CS01)|(0<<CS00);/* 1000000/8 = 125000= 125000/256 =                                                                                                                                488,28 гц */
 while (1) /*Бесконечный цикл, выводим переменную на индикатор*/
       {
              _delay_ms (1000);
              i= i+0.1;
              if (i == 99.9)
              i = 0.0;
              Display (i);
         }     /*Закрывающая скобка бесконечного цикла*/
}              /*Закрывающая скобка основной программы*/

Следующим шагом мы добавим функцию прерывания, которая будет срабатывать  по специальному вектору TIMER0_OVF_vect, который отвечает за прерывания по переполнению Т0. Для этого мы используем аппаратный таймер/счетчик Т0. Выше, в программе мы с Вами прописали настройки таймера, там же посчитали частоту, с которой будет происходить динамическая индикация. Т.е. когда переполняется регистр счета в счетчике, останавливается общая программа и выполняется функция ниже, после выхода из нее пролжается выполнение основной программы. 

ISR (TIMER0_OVF_vect)
{
        PORTB &= 0x3e; //Очистка PB7, PB6, PB0
        _for (j = 0; j<=30; j++) { } // Задержка для выключения транзистора
        (k == 3) ? k = 0 : k++; /*Переменная, которая отвечает за очередность загорания трехэлементного индикатора, 0,1 и 2. При определенной цифре, на определенной ножке усатнавливается 1, далее открывается транзистор и загорается сегменты индикатора, соответсвующие переменной Elemn */
        switch (k)
        {
                case 0: PORTB |= (1 << PINB7); // Единицы
                PORTD = Elem3;
                break;
                case 1: PORTB |= (1 << PINB6); // Десятки
                PORTD = Elem2;
                break;
                case 2: PORTB |= (1 << PINB0); // Сотни
                PORTD = Elem1;
         }
}

Выше приведенная программа испытана в железе и в симуляторе. Ниже выложены рисунки соответственно. В железе я все спаял навесом, на быструю руку. Как видите три элемента индикатора соответственно три транзистора (обведено кружочком). В симуляторе(Proteus) транзисторы нам не нужны. Так же одно существенное отличие в программе, а именно в прерывании, где происходит задержка для выключения транзистора -  в симуляторе пропишите 50 тактов. Все должно работать.

Семи сегментный индикатор и AVRСеми сегментный индикатор и AVR в Proteus

По ходу публикации постов, программа для индикатора у нас немного изменилась, а именно добавился четвертый элемент, вывод на него знака минус, символов “H” и “C", формат вывода времени и  объединение всех режимов вывода: времени, температуры, влажности . Так что читайте, анализируйте и экспериментируйте.

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

Выводим переменную на индикатор. ( Скачали: 741 чел. ) 

На этом все. В следущей статье я опишу подключение датчиков температуры и выведем инфорацию на индикатор. До скорой встречи!

Просмотрено 23913 раз.

Я на Google+

Семи сегментный индикатор и AVR. Динамическая индикация. Программа на Си. Шаг №5: 22 комментария

  1. /* Эти макросы содержат числа, соответствующие двойке, возведенной в степень,  равной номеру «ножки» того порта, к которому подключен сегмент индикатора содноименным макросу названием.  */

    Для чего возводили в степень ?

    • Здравствуйте! Степень — это число, номер пина (ножки). Пинов на порте у нас 8. Порт это байт, максимальное значение которого 256. Вот и получается каждая ножка это 2 в n-ой степени (данного порта).

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

    • Все это не для универсальности, а для понятия основ. Ведь оптимизация и модификация это всегда усложненные схемы и программы. Люди которые начинают освоивать всегда ищут «основу». Тем не менее спасибо за критику.

  3. Я посмотрел как вот тут объясняется программирование, а можно как нибудь вот под это задание сделать: написать программу для отображения положения переключателей, подключенных к одному из портов на светодиодах подключенных к другому порту МК. На семисегментном индикаторе отобразить число переключений? А то я мучаюсь и не могу сообразить, это для AVR ATmega16

    • Здравствуйте. Попробую ответить, настолько насколько я понял. Первое — опрос положения перключателей, я так понимаю либо 0 либо 1, просто опарашиваем состояне PIN. Далее я так понял светодиоды визуально отображают положение переключателя, которые подключены к другому порту — просто в теле условия активизируем необходимую ногу контроллера PORTN |= _BV (PNn); , для зажигания светодиода, ну и прописываем счетчик и выводим на индикатор. Вот как то так.

      • а не могли бы написать для ассемблера именно тот участок где используется этот семи сегментный индикатор? ну мне так чтоб провериться

      • Здравствуйте, к сожелению с ассемблером знаком на ВЫ. Не было особой необходимости развивать эти знания.

  4. Уважаемый автор. Помогите разобраться с куском кода под названием Display. Всю плеш на голове расчесал. Все время на дисплее сначала загораются 8.8.8.8. с точками, а потом — . — . — . — .

    остальные куски работают идеально. Расскажите про дИсплей.

    Не понимаю я этот алгоритм.

    • И что такое &N2. Не понял этого значка — &

      Откуда это &N2 берется? Эта переменная равна 0?

      кто её считает и изменяет. Пробовал её изменять. Эффекта нет.

      • Здравствуйте kroko. данная функция приниает один параметр, это число с плавающей запятой. цель функции N1 = modf (Number, &N2), определиться число Number содержит ли дробную часть, которая заносится в N1, и если есть то дальше избавляемся от хвоста доножив число на десять (я вывожу здесь один знак после запятой — индикатор то не ризиновый, хотя дальше в проете я использовал уже два знака,но это другая история). И далее мы считаем сколько сотен, десятков и едениц в нашем числе.Каждый разряд приравняв своему элемнту индикатора. Ну и естественно во вотором элементе включаем точку Elem2 = (Slot[Num2]|0×04). &N2 — данный параметр ничего не выполняет, в него просто сохраняется целое значение. Знак амперсанд возвращает адресс элемента, т.е. номер яччейки памяти. данная функция станндартная... ну вот в принципе все.

    • ...char volatile Elem1, Elem2, Elem3, Elem4 — здесь мне не нравится volatile. Из — за него может быть проблемы и отбражение не понятных символов — ведь оно говорит что значение переменных поменяется в любое время. Это не тот случай когда его надо ставить. Попробуйте покрутить настройки таймера — частоту TCCR0.

  5. Я пока не пробовал ставить код с точкой. Разбирался с остальным, но потренируюсь как будет время. Пасиба...

    Кое что другое удалось сделать на этих выходных. Я использовал материал из двух статей. Получилось очень симпотно и коротко.

    Теперь задача прикрутить кнопки для управления, думаю с этим проблем не будет... У меня другая проблема. Как работает логика 60-ти минут? Вопрос заключается в переходе от секунд к минутам и потом к часам(обратный отсчет конечно же)=). Как из того, что есть слепить таймер 24:60:60?

    Не понятно пока, как логику и арифметику описать =) Есть какие нибудь предложения? Пока гуглю...

    • Наверное если лепить таймер то вида 00:00:00 , -1 секунда 23:59:59 ну и так далее. Чем тактируете часы ? схемой реального времени или контроллером?

      • Тактировать планирую контроллером. Вроде он справляется на 1МГце. Можно и на 8-ми. Тоже не плохо работает =) Это не трудно посчитать и не так трудно перенастроить.

        Таймер настраиваемый, конечно на 23:59:59 и -1 каждый дэлэй.

        Сейчас пробую варианты If else, тут думаю создать еще две переменные — для минут и часов, а «х» считать как прежде. Например если ММ меньше 0, то ЧЧ= ЧЧ ^ 1. как-то так. =) Пробую пока. Что получится, то в копилку.

        Ну а PORTB PB0,PB1,PB2 на кнопки. (подключил, но не прописал пока ).

      • Можно и так. только частота частота контроллера плавающая и зависит от окружающей среды: температуры и т.п.и т.д. так что на точность не расчитывайте.

  6. Да. Из-за глобального Elem1, Elem2, Elem3, Elem4 действительно был глюк с отображением. Потому пошел путем создания массива. Это удобно =)

  7. #include

    #include

    #include

    static flash unsigned char symbols[]={0x3F, 0×06, 0x5B, 0x4F,

    0×66, 0x6D, 0x7D, 0×07,

    0x7F, 0x6F};

    //--------------------------------------------------------------------------

    short unsigned int j, k = 0; /*переменные исп-ся в макросе прерывания*/

    float i = 0; /*Переменная для вывода на индикатор*/

    unsigned int w = 0; /*Переменная индикатор для включения точки*/

    //unsigned char Slot[11]; /*Массив в котором хранятся числа, которые нужно */

    char Elem1, Elem2, Elem3;

    //--------------------------------------------------------------------------

    //---------------------------------------------------------------------

    /* Функция выделяет цифры из трехзначного числа Number*/

    void Display (double Number)

    { unsigned int Num1, Num2, Num3 = 0;

    float N1, N2; /*Переменные для функции modf*/

    N1 = modf (Number,&N2); /*Разбиваем число на целую и дробную части, N1 = дробной N2 = целой*/

    if ((N1!= 0)&&(N2 = 100) /*Сотни*/

    {

    Number -= 100;

    Num1++;

    }

    while (Number >= 10) /*Десятки*/

    {

    Number -= 10;

    Num2++;

    }

    Num3 = Number; /*Еденицы*/

    Elem1 = symbols[Num1];

    if (w == 1)

    { Elem2= (symbols[Num2]|0×80);

    w = 0;

    }

    else

    Elem2 = symbols[Num2]; // Elem2 = 235

    Elem3 = symbols[Num3]; // Elem3 = 218

    }

    //-----------------------------------------------------------------------------

    interrupt [TIM0_OVF] void timer0_ovf_isr (void)

    { PORTD = 0×00;

    for (j = 0; j<=30; j++) { } ;

    if (k==3)

    k=0;

    else

    k++;

    switch (k)

    {

    case 0: PORTC = 0×38^0×08 ;// Единицы

    PORTD =Elem1;

    break;

    case 1: PORTC = 0×38 ^ 0×10;

    delay_us (100); // Десятки

    PORTD = Elem2;

    break;

    case 2: PORTC = 0×38 ^0×20; // Сотни

    PORTD = Elem3; break;

    }

    }

    unsigned int adc_data;

    #define ADC_VREF_TYPE 0×40

    // ADC interrupt service routine

    interrupt [ADC_INT] void adc_isr (void)

    {

    // Read the AD conversion result

    adc_data=ADCW;

    }

    // Read the AD conversion result

    // with noise canceling

    unsigned int read_adc (unsigned char adc_input)

    {

    ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);

    // Delay needed for the stabilization of the ADC input voltage

    delay_us (10);

    #asm

    in r30,mcucr

    cbr r30,__sm_mask

    sbr r30,__se_bit | __sm_adc_noise_red

    out mcucr,r30

    sleep

    cbr r30,__se_bit

    out mcucr,r30

    #endasm

    return adc_data;

    }

    // Declare your global variables here

    void main (void)

    { unsigned int value = 0;

    float buf;

    // Declare your local variables here

    // Input/Output Ports initialization

    // Port B initialization

    // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In

    // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T

    PORTB=0×00;

    DDRB=0×00;

    // Port C initialization

    // Func6=In Func5=Out Func4=Out Func3=Out Func2=In Func1=In Func0=In

    // State6=T State5=1 State4=1 State3=1 State2=T State1=T State0=T

    PORTC=0×38;

    DDRC=0×38;

    // Port D initialization

    // Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out

    // State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0

    PORTD=0×00;

    DDRD=0xFF;

    // Timer/Counter 0 initialization

    // Clock source: System Clock

    // Clock value: 1000,000 kHz

    TCCR0=0×02;

    TCNT0=0×00;

    // Timer (s)/Counter (s) Interrupt (s) initialization

    TIMSK=0×01;

    // Analog Comparator initialization

    // Analog Comparator: Off

    // Analog Comparator Input Capture by Timer/Counter 1: Off

    ACSR=0×80;

    SFIOR=0×00;

    // ADC initialization

    // ADC Clock frequency: 1000,000 kHz

    // ADC Voltage Reference: AREF pin

    ADMUX=ADC_VREF_TYPE & 0xff;

    ADCSRA=0x8B;

    #asm ("sei")

    while (1)

    {

    delay_ms (50);

    buf+=1.1;

    Display (buf);

    delay_ms (50);

    };

    }

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

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting