Учебный курс. Семисегментный индикатор. Динамическая индикация

27/04/2010 - 21:24

   Введение 

Для отображения цифровой информации в системах на базе микроконтроллеров  используются светодиодные семисегментные индикаторы. Они просты в управлении, имеет высокую яркость, широкий диапазон рабочих температур и низкую стоимость. К недостаткам светодиодных индикаторов относятся – высокое энергопотребление,  отсутствие управляющего контроллера и скудные возможности по выводу буквенной информации.
  Светодиодный семисегментный индикатор представляет собой группу светодиодов   расположенных в определенном порядке и объединенных конструктивно. Зажигая одновременно несколько светодиодов можно формировать на индикаторе символы цифр. Индикаторы различаются по типу соединения светодиодов – общий анод, общий катод, по количеству отображаемых разрядов – однораразрядные, двух разрядные и т.д. и по цвету –  красные, зеленые, желтые и т.д.



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



     Динамическое управление (динамическая индикация) подразумевает поочередное зажигание разрядов индикатора с частотой, не воспринимаемой человеческим глазом. Схема подключения индикатора в этом случае на порядок экономичнее благодаря тому, что одинаковые сегменты разрядов индикатора объединены.


Эксперименты с семисегментным индикатором

   Рассмотрим простейший случай управления индикатором – вывод одной цифры.  Схема для наших экспериментов приведена ниже. 

  Чтобы зажечь на индикаторе какую-то цифру нужно настроить порты, к которым подключен индикатор, в режим выхода, “открыть” транзистор (в данном случае подать на базу “единицу”) и установить в порту микроконтроллера её код.
  В зависимости от того, куда подключены сегменты индикатора – коды могут быть разные. Для нашего случая коды цифр будут выглядеть так.

unsigned char number[] =
{
  0x3f, //0
  0x06, //1
  0x5b, //2
  0x4f, //3  
  0x66, //4
  0x6d, //5
  0x7d, //6
  0x07, //7  
  0x7f, //8
  0x6f  //9   
};

   Используя десятичные цифры от 0 до 9 в качестве индекса массива, легко выводить в порт нужные коды.

Пример 1. Вывод цифр от 0 до 9

#include <ioavr.h>
#include <intrinsics.h>

unsigned char number[] =
{
  0x3f, //0
  0x06, //1
  0x5b, //2
  0x4f, //3  
  0x66, //4
  0x6d, //5
  0x7d, //6
  0x07, //7  
  0x7f, //8
  0x6f  //9   
};

unsigned char count = 0;
int main( void )
{
  //порт, к которому подкл. сегменты
  PORTB = 0xff;
  DDRB = 0xff;
 
  //вывод, к которому подкл. катод
  PORTD |= (1<<0);
  DDRD |= (1<<0);
 
  while(1){
    PORTB = number[count];
    count++;
    if (count == 10) count = 0;
    __delay_cycles(8000000);   
  }
  return 0;
}

Эта программа каждую секунду выводит значение переменной count на семисегментный индикатор. Индикация в данном случае - статическая.
 
   Идем дальше – вывод двух цифр. Настройка портов и коды цифр остаются без изменений, но теперь нам нужно добавить кусок кода, который будет зажигать разряды индикатора по очереди. Частота смены разрядов должна быть достаточно высокой, чтобы свечение индикатора воспринималось без мерцания.

Пример2. Вывод цифр от 0 до 99

#include <ioavr.h>
#include <intrinsics.h>

unsigned char number[] =
{
  0x3f, //0
  0x06, //1
  0x5b, //2
  0x4f, //3  
  0x66, //4
  0x6d, //5
  0x7d, //6
  0x07, //7  
  0x7f, //8
  0x6f  //9   
};

unsigned char
count = 0;

//числа для вывода на индикатор
unsigned char data1 = 2;
unsigned char data2 = 5;

int main( void )
{
  //порт, к которому подкл. сегменты
  PORTB = 0xff;
  DDRB = 0xff;
 
  //порт, к которому подкл. катод
  PORTD = 0;
  DDRD = (1<<1)|(1<<0);
 
  while(1){

    //гасим оба разряда
    PORTD &= ~((1<<1)| (1<<0));

    //выводим в порт код цифры
    //и зажигаем следующий разряд

    if (count == 0) {
      PORTB = number[data2];
      PORTD |= (1<<1);
    }
    if (count == 1) {
      PORTB = number[data1];
      PORTD |= (1<<0);
    }
   
    count++;
    if (count == 2) count = 0;

    //частота смены разрядов будет 100 Гц при кварце 8МГц

    __delay_cycles(800000);   
  } 
  return 0;
}

   Эта программа просто выводит любое поразрядно заданное число от 0 до 99.
   Частота смены разрядов семисегментного индикатора задается с помощью программной задержки __delay_cycles(). Это не самое удачное решение, потому что добавление каких-нибудь других задач в цикл while будет мешать выводу на индикатор. Давайте организуем смену разрядов индикатора с помощью аппаратного таймера/счетчика Т0

Пример3. Вывод цифр от 0 до 99. Смена разрядов выполняется в прерывании таймера

#include <ioavr.h>
#include <intrinsics.h>

unsigned char number[] =
{
  0x3f, //0
  0x06, //1
  0x5b, //2
  0x4f, //3  
  0x66, //4
  0x6d, //5
  0x7d, //6
  0x07, //7  
  0x7f, //8
  0x6f  //9   
};

//числа для вывода на индикатор
volatile unsigned char data1 = 0;
volatile unsigned char data2 = 0;

int main( void )
{
  //порт, к которому подкл. сегменты
  PORTB = 0xff;
  DDRB = 0xff;
 
  //порт, к которому подкл. катод
  PORTD = 0;
  DDRD |= (1<<1)|(1<<0);
 
  //инициализация таймера Т0
  //частота прерываний 100Гц при кварце 8МГц

  TIMSK = (1<<TOIE0);
  TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00);
  TCNT0 =  0xb2;
   
  __enable_interrupt();
  while(1){
    //программный счетчик секунд
    data1++;
    if (data1 == 10) {
        data1 = 0;
        data2++;
        if (data2 == 10) data2 = 0;
    }
    __delay_cycles(8000000);   
  } 
  return 0;
}

//прерывания таймера Т0 - вывод на индикатор
#pragma vector = TIMER0_OVF_vect
__interrupt void Timer0_Ovf(void)
{
  static unsigned char count = 0;
  TCNT0 = 0xb2;
 
   //гасим оба разряда
   PORTD &= ~((1<<1)|(1<<0));

    //выводим в порт код цифры
    //и зажигаем следующий разряд

   if (count == 0) {
      PORTB = number[data2];
      PORTD |= (1<<1);
   }
   if (count == 1) {
      PORTB = number[data1];
      PORTD |= (1<<0);
   }
   
    count++;
    if (count == 2) count = 0;
}

   Переменные data1, data2  объявлены с ключевым словом volatile, потому что они используются и в основном коде и в прерывании.  В проекте  под GCC я забыл поставить его поставить, и компилятор выкинул обе переменные, посчитав их ненужными!

   Прерывания таймера происходят параллельно выполнению цикла while. Это позволяет выполнять в цикле какую-нибудь полезную задачу. В данном примере с помощью двух переменных  в цикле организован программный счетчик от 0 до 99.
   Использовать две восьмиразрядные переменные для организации счетчика от 0 до 99 неудобно и расточительно, ведь такой счетчик можно сделать и на одной переменной типа unsigned char. Хорошо, счетчик мы сделаем, а как вывести его значение на семисегментный индикатор? Нужен код “разбивающий” десятичное число на отдельные разряды и вот как он выглядит:

    //программный счетчик
    unsigned char counterProg = 35;
   
    //”разбиваем” значение счетчика на отдельные разряды
    data1 = counterProg % 10;
    data2 = counterProg/10;

data1 = counterProg % 10 – это операция деления по модулю 10 (деление с остатком). Результатом этого выражения будет остаток от деления переменной counterProg на 10, то есть для нашего случая  5.

counterProg/10 – это целочисленное деление на 10. Результатом этого выражения будет число 3.

Таким образом, в переменные data2, data1 будут записаны числа 3 и 5 соответственно, значение счетчика counterProg при этом не изменится.

Пример 4. Вывод цифр от 0 до 99.
Преобразование двоичных чисел в двоично-десятичные (BCD)


#include <ioavr.h>
#include <intrinsics.h>

unsigned char number[] =
{
  0x3f, //0
  0x06, //1
  0x5b, //2
  0x4f, //3  
  0x66, //4
  0x6d, //5
  0x7d, //6
  0x07, //7  
  0x7f, //8
  0x6f  //9   
};

//числа для вывода на индикатор
volatile  unsigned char data1 = 0;
volatile  unsigned char data2 = 0;

//программный счетчик секунд
unsigned char counterProg = 0;

int main( void )
{
  //порт, к которому подкл. сегменты
  PORTB = 0xff;
  DDRB = 0xff;
 
  //порт, к которому подкл. катод
  PORTD = 0;
  DDRD |= (1<<1)|(1<<0);
 
  //инициализация таймера Т0
  TIMSK = (1<<TOIE0);
  TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00);
  TCNT0 =  0xb2;
   
  __enable_interrupt();
  while(1){
    //программный счетчик секунд
    counterProg++;
    if (counterProg == 100) counterProg = 0;
    data1 = counterProg % 10;
    data2 = counterProg/10;
    __delay_cycles(8000000);   
  } 
  return 0;
}

//прерывания таймера Т0 - вывод на индикатор
#pragma vector = TIMER0_OVF_vect
__interrupt void Timer0_Ovf(void)
{
  static unsigned char count = 0;
  TCNT0 = 0xb2;
 
   //гасим оба разряда
   PORTD &= ~((1<<1)|(1<<0));
  
   //выводим код цифры в порт
   //и зажигаем следующий разряд

   if (count == 0) {
      PORTB = number[data2];
      PORTD |= (1<<1);
   }
   if (count == 1) {
      PORTB = number[data1];
      PORTD |= (1<<0);
   }
   
    count++;
    if (count == 2) count = 0;
}

   Следующий этап работы над программой – выделение кода обслуживающего светодиодный семисегментный индикатор в отдельные функции. Какой минимальный набор функций нам необходим? Функция инициализации, функция вывода на индикатор и функция преобразования чисел и записи их в буфер.

Функция инициализации

#define PORT_IND PORTB
#define DDR_IND DDRB
#define PORT_K PORTD
#define DDR_K DDRD
#define KAT1 0
#define KAT2 1

volatile  unsigned char data[2];

void IND_Init(void)
{
  //порт к которому подкл. сегменты
  PORT_IND = 0xff;
  DDR_IND = 0xff;
 
  //порт, к которому подкл. катоды
  PORT_K &= ~((1<<KAT2)|(1<<KAT1));
  DDR_K |= (1<<KAT2)|(1<<KAT1);

   //очистка буфера
   data[0] = 0;
   data[1] = 0;
}

Порты, к которым подключен семисегментный индикатор, определены с помощью директивы #define – в будущем это позволит быстро править код. Вместо двух переменных data1, data2 удобнее использовать массив unsigned char data[2].

Функция преобразования


void IND_Conv(unsigned char value)
{
  unsigned char tmp;
  tmp = value % 10;
  data[0] =  number[tmp];
  tmp = value/10;
  data[1] =  number[tmp];
}

Процедура преобразования чисел аналогична описанной выше. Единственное отличие – в буфере (data[]) мы теперь сохраняем не результат преобразования, а коды цифр. Зачем делать в прерывании то, что можно сделать в основном цикле программы?

Функция вывода на индикатор

void IND_Update(void)
{
   static unsigned char count = 0;
  
   //гасим оба разряда
   PORT_K &= ~((1<<KAT2)|(1<<KAT1));

   //выводим в порт код цифры
   PORT_IND = data[count];  
  
   //зажигаем нужный разряд
   if (count == 0) PORT_K |= (1<<KAT1);
   if (count == 1) PORT_K |= (1<<KAT2);
     
    count++;
    if (count == 2) count = 0;
}

Эта функция будет вызываться в прерывании таймера. В принципе, для экономии ресурсов ее можно было бы сделать встраиваемой.

Пример 5. Код программы с использованием этих функций
//программный счетчик секунд
unsigned char counterProg = 0;

int main( void )
{
  IND_Init();
 
  //инициализация таймера Т0
  TIMSK = (1<<TOIE0);
  TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00);
  TCNT0 =  0xb2;
   
  __enable_interrupt();
  while(1){
    counterProg++;
    if (counterProg == 100) counterProg = 0;
    IND_Conv(counterProg);
    __delay_cycles(8000000);   
  } 
  return 0;
}

//прерывания таймера Т0 – вывод на индикатор
#pragma vector = TIMER0_OVF_vect
__interrupt void Timer0_Ovf(void)
{
  TCNT0 = 0xb2;
  IND_Update();
}

И, наконец – разбиение программы на модули.

Создаем два файла – indicator.h и indicator.c. Сохраняем их в папке проекта.
В хидер файле у нас будут директивы условной компиляции, макроопределения и прототипы функций.

#ifndef INDICATOR_H
#define INDICATOR_H

#include <ioavr.h>

#define PORT_IND PORTB
#define DDR_IND DDRB
#define PORT_K PORTD
#define DDR_K DDRD
#define KAT1 0
#define KAT2 1

void
IND_Init(void);
void IND_Conv(unsigned char value);
void IND_Update(void);

#endif //INDICATOR_H

В сишном файле – #include “indicator.h”,  объявления переменных  и реализация трех функций. Функции описаны выше, поэтому здесь их не привожу.

Подключаем файл indicator.c к проекту.
В файл main.c инклюдим заголовочный файл нашего драйвера семисегментного индикатора  - #include “indicator.h”

Все. Теперь мы можем использовать функции для работы с индикатором в файле main.
Полный код драйвера смотрите в проектах.

Продолжение следует… Скоро будет дополнение к этой статье -  описание вольтметра на микроконтроллере – в проекте используется 4-ех разрядный семисегментный индикатор.

Файлы

Семисегментный индикатор. Проекты для IARа
Семисегментный индикатор. Проекты для WinAvr
Семисегментный индикатор. Проекты для CodeVision
Отладочный проект для Proteus`a

Комментарии   

# foxit 28.04.2010 03:34
Спасибо
Буду разбираться
Ответить | Ответить с цитатой | Цитировать
# Guest 28.04.2010 03:52
Спасибо, очень рад очередной статье...
Ответить | Ответить с цитатой | Цитировать
# alexandershahbazov 28.04.2010 08:01
На макетке у меня сейчас 2-х зазрядный
7-сегментник с ATmega8 и сдвиговым регистром 74HC164 . Так и не попробовал как
с ним работать . Есть в наличии также 74HC595 . Читал статью "Подключение lcd. Варианты схем" , но хотелось бы посмотреть здесь их применение .
Читал про однопроводное подключение . Но для начала попробовать бы 2-х проводное
подключение , применяя 74HC164 или 74HC595 .
Ответить | Ответить с цитатой | Цитировать
# alexandershahbazov 28.04.2010 12:29
Еще мне непонятно там по схеме 1 ножка индикатора соединена с 6 . И поточнее что за транзистор лучше ставить .
Ответить | Ответить с цитатой | Цитировать
# foxit 28.04.2010 16:26
В коде есть строчки
//это часть можно настроить под другой индикатор
Как ими пользоваться?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 28.04.2010 17:48
Цитата:
На макетке у меня сейчас 2-х зазрядный 7-сегментник с ATmega8 и сдвиговым регистром 74HC164 . Так и не попробовал как с ним работать
Там ничего сложного - нужно записывать коды цифр в регистры с помощью аппаратного/программного SPI. Думаю, накатаю чего-нибудь.
Цитата:
Еще мне непонятно там по схеме 1 ножка индикатора соединена с 6
Это два катода - наверное добавили лишний, чтобы четное число выводов было.
Цитата:
И поточнее что за транзистор лучше ставить .
В данном случае используется BC547 - биполярный npn транзистор общего назначения, 45В 100мА. Транзистор лучше ставить тот, который ток выдержит;)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 28.04.2010 17:50
Цитата:
В коде есть строчки //это часть можно настроить под другой индикатор Как ими пользоваться?
Вот так:
Код:
индикатор с общим катодом подключенный напрямую к микроконтроллеру
#define LightOutAll() PORT_TR |= (1<<NUM1)|(1<<NUM2)|(1<<NUM3)|(1<<NUM4)
#define BurnDigit(port, digit) port &= ~(1<<digit)
#define ValueBuf() buf[count]

индикатор с общим катодом подключенный к микроконтроллеру с помощью транзисторов
#define LightOutAll() PORT_TR &= ~((1<<NUM1)|(1<<NUM2)|(1<<NUM3)|(1<<NUM4))
#define BurnDigit(port, digit) port |= (1<<digit)
#define ValueBuf() buf[count]

индикатор с общим анодом подключенный напрямую к микроконтроллеру
#define LightOutAll() PORT_TR &= ~((1<<NUM1)|(1<<NUM2)|(1<<NUM3)|(1<<NUM4))
#define BurnDigit(port, digit) port |= (1<<digit)
#define ValueBuf() ~buf[count]

индикатор с общим анодом подключенный к микроконтроллеру с помощью транзисторов
#define LightOutAll() PORT_TR |= (1<<NUM1)|(1<<NUM2)|(1<<NUM3)|(1<<NUM4)
#define BurnDigit(port, digit) port &= ~(1<<digit)
#define ValueBuf() ~buf[count]

Ответить | Ответить с цитатой | Цитировать
# Guest 03.05.2010 19:50
В функции преобразования:
IND_Conv()

лучше не использовать деление,я делаю преобразование вычитанием.Так быстрее,так как МК не умеет делить и при компиляции будет большая и медленная подпрограмма. Я делаю так:

void Decoder(unsigne d int DataForLed)
{
unsigned char Num1=0, Num2=0, Num3=0;
while (DataForLed >= 100)
{
DataForLed -= 100;
Num1++;
}
while (DataForLed >= 10)
{
DataForLed -= 10;
Num2++;
}
Num3 = DataForLed ;

ByDisplay[2] =Num1;
ByDisplay[1] =Num2;
ByDisplay[0] =Num3;
}
Ответить | Ответить с цитатой | Цитировать
# alexandershahbazov 04.05.2010 06:48
Отлично !
Ответить | Ответить с цитатой | Цитировать
# Pashgan 04.05.2010 15:18
Цитата:
лучше не использовать деление,я делаю преобразование вычитанием.Так быстрее,так как МК не умеет делить и при компиляции будет большая и медленная подпрограмма
Ну да, будет выигрыш (по коду - единицы байт, по скорости ~ в два раза для двухразрядного десятичного числа). В данном случае это не принципиально, здесь же нет ограничений на размер и скорость выполнения кода. В разделе "библиотеки" я выкладывал два исходника для преобразования двоичных чисел таким методом.
Ответить | Ответить с цитатой | Цитировать
# alexandershahbazov 09.05.2010 17:36
С Днем Победы !
Ответить | Ответить с цитатой | Цитировать
# Pashgan 09.05.2010 19:07
Да, С Днем Победы! Светлая память нашим дедам.
Ответить | Ответить с цитатой | Цитировать
# alexandershahbazov 12.05.2010 22:10
Сходу вышло 2-х проводное подключение с
применением 74HC164 на 2-х разрядном
7-сегментнике . Но без SPI , без транзисторов . Сэкономлено 5 ножек . С SPI
вас подожду как вы обещали .
Есть ли какое-то преимущество в применении 74HC595 по сравнению с 74HC164 .
Слышал про однопроводное подключение .
Надежно ли это .
Хотелось бы узнать ваше мнение .
Ответить | Ответить с цитатой | Цитировать
# Вадег 08.01.2013 13:01
Цитирую alexandershahbazov:

Есть ли какое-то преимущество в применении 74HC595 по сравнению с 74HC164 .
Слышал про однопроводное подключение .
Надежно ли это .
Хотелось бы узнать ваше мнение .

Есть, 595 с защелкой и управляя оной можно не бояться за засвечивание негорящих сегментов. На мой взгляд такая индикация выглядит солидно и профессионально . В книге В.Трамперта "Измерение, управление и регулирование с помощью AVR-микроконтро ллеров." я считаю самая правильная реализация однопроводного подключения в случае со сдвиговыми регистрами, стоит ли оно того, решать Вам. Остальные поделки, которые мне встречались содержат простую RC-цепь, а это не надежно.
У меня без SPI потребовалось три ноги МК для 8 разрядной "панели". Со SPI надо 4, четвертый как раз и есть эта защелка.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 13.05.2010 20:45
Цитата:
Есть ли какое-то преимущество в применении 74HC595 по сравнению с 74HC164
по-моему нет.
Цитата:
Слышал про однопроводное подключение . Надежно ли это .
Eсть два варианта такого подключения. Первый - с использованием RC цепочки, второй - с использованием двух одновибраторов. Во втором варианте деталей побольше, но мне он внушает больше доверия.
Ответить | Ответить с цитатой | Цитировать
# Guest 15.05.2010 13:54
Pashgan
а как быть если у меня 9разрядный индикатор с общим катодом?
Ответить | Ответить с цитатой | Цитировать
# Guest 15.05.2010 13:59
и подключается он напрямую к атмеге8
Ответить | Ответить с цитатой | Цитировать
# Pashgan 15.05.2010 20:49
Ну как быть.. код подправить под этот индикатор. Посмотри как организован вывод на 4-ех разрядный индикатор в статье про вольтметр.
Ответить | Ответить с цитатой | Цитировать
# Guest 17.05.2010 17:39
Pashgan.напиши плиз на мыло.есть пару вопросов
auag27 собака mail.ru
Ответить | Ответить с цитатой | Цитировать
# ivan 20.11.2017 18:44
как мне вывести результат на семисегментный индикатор?
Ответить | Ответить с цитатой | Цитировать
# Guest 25.05.2010 08:37
Вы применяете поразрядную индикацию. Но есть еще способ посегментной развертки. Его можно использовать для индикаторов с общим анодом (тогда аноды подключаются к выводам порта, а на транзисторы (7шт) подключаются сегменты - катоды). Десятичную точку можно включить без транзистора - она, как правило, включается только в одном знакоместе. Алгоритм, соответственно, изменяется - перебираем сегменты и зажигаем те знакоместа, в которых этот сегмент должен гореть. Такой способ индикации хорош для большого количества знакомест - транисторов будет 7, а скважность будет 8 независимо от количества знакомест, а значит, яркость и импульсный ток будут одинаковы что для 2, что для 20 знакомест. Увеличение количества транзисторов не страшно - 7 транзисторов с базовыми резисторами - это одна микросхема ULN2003, ее стоимость и занимаемое место на плате сравнимы со стоимостью и местом пары транзисторов и их резисторов.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 26.05.2010 14:48
САБ, вы просто кладезь информации
Ответить | Ответить с цитатой | Цитировать
# Guest 19.08.2010 07:23
наконец то очень понятно и полезно. большое спасибо автору. и просьба - сможете выложить какой нибудь тестовый пример для ML1001. это жк 7 сегментный по двух проводной линии. пытаюсь тут его заставить работать... пока не очень
Ответить | Ответить с цитатой | Цитировать
# Pashgan 28.08.2010 18:24
У меня нет в наличии такого индикатора, поэтому выложить пока ничего не могу..
Ответить | Ответить с цитатой | Цитировать
# Олег 27.10.2010 11:15
а как эти коды составляютя?
0x3f, //0
0x06, //1
0x5b, //2
0x4f, //3 И.Т.Д. я так понял они означают те выводы определённого порта которые нужно включить что бы высветилась определённая цифра. например для единички это будет 15 и 16 ножка то есть порт В1, В2. и катод подключеный к С7. в 16ричной системе сумма этих портов будет записываться как 0x3f.
так вот сама технология перевода портов в 16 систему меня интересует. помогите пожалуйста разобраться! :-)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 31.10.2010 20:23
Можно пойти другим путем и он намного удобнее. Раздел "библиотеки" - драйвер семисегментного индикатора 7seg-driver.rar . Посмотри как там определяются коды.
Ответить | Ответить с цитатой | Цитировать
# Иван 26.03.2012 10:29
Код составляется следующим образом.
В зависимости от цифры нужно включать биты(ножки мк)
Например чтобы вкл. цифру 8 необходимо зажечь все сегменты, кроме точки. Тогда:
PORTX = 0b11111110;
Теперь переводим это дело в 16-ную .
Для этого разбиваем наши циферки на две группы по четыре цифры
1111 1110 и заглядываем в таблицу соответствия:
Десятичная

1)Десятичная система
2)Двоичная система
3)Шестнадцатеричная система

0
0
0

1
1
1

2
10
2

3
11
3

4
100
4

5
101
5

6
110
6

7
111
7

8
1000
8

9
1001
9

10
1010
A

11
1011
B

12
1100
C

13
1101
D

14
1110
E

15
1111
F

16
10000
10

Получаем 0xEF;
Ответить | Ответить с цитатой | Цитировать
# enclis 01.11.2010 16:18
попробуйте скомпилить исходники под cvavr - и ничего у вас не получится, потому что cvavr не знает ничего про TOIE0, CS02, CS01, CS00 и т.д.
Ответить | Ответить с цитатой | Цитировать
# JoJo 07.11.2010 00:04
В конце статьи есть проект для CodeVision
Ответить | Ответить с цитатой | Цитировать
# enclis 10.11.2010 13:07
сами то пробовали?
Ответить | Ответить с цитатой | Цитировать
# JoJo 08.12.2010 20:50
Да, в CodeVision 2.04. Это в старых версиях CodeVision нет описания битов регистров микроконтроллер а AVR.
Ответить | Ответить с цитатой | Цитировать
# Lion_A 10.12.2010 20:15
Написал и тут-же разобрался :-) . Вот что делает копи паст. Просьба модератора удалить предыдущий пост. Первую программу написал пять дней назад так, что не ругайте :-)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 10.12.2010 20:41
Удалил. Ничего страшного.
Ответить | Ответить с цитатой | Цитировать
# aurysto 04.03.2011 19:03
С удовольствием читаю ваши статьи.Очень доходчиво...А вот как быть если сегменты раскиданы по портам, напр. А-PORTB.7,B-PORTB.5,C-PORTB.4,D-PORTB.2,E-PORTD.4,F-PORTD.5,G-PORTD.6
Знаете, бывает, что удобнее так развестись :-)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 05.03.2011 07:02
Определить дефайном каждый сегмент и переписать код вывода цифры в порт в виде отдельной функции.
Например, так
Код:
#define SEG0 3
#define PORT_SEG0 PORTB
#define SEG1 4
#define PORT_SEG1 PORTC
....
unsigned char data;
....
void OutData(unsigned char data)
{
if (data&(1<<0)) SetBit(PORT_SEG0, SEG0);
else ClearBit(PORT_SEG0, SEG0);

if (data&(1<<1)) SetBit(PORT_SEG1, SEG1);
else ClearBit(PORT_SEG1, SEG1);
...
}

Если посидеть подумать, может быть удастся найти более красивое решение.
Ответить | Ответить с цитатой | Цитировать
# aurysto 06.03.2011 17:33
Спасибо,Pashgan!ОЧЕНЬ!
Код:#define SEG0 3
#define PORT_SEG0 PORTB
#define SEG1 4
#define PORT_SEG0 PORTC

Очевидно в 4-й строке PORT_SEG1?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 06.03.2011 21:36
Да, описался. Исправил.
Ответить | Ответить с цитатой | Цитировать
# aurysto 08.03.2011 01:17
Спасибо.Есть над чем подумать... :-)
Наваяю - можно выложить код?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 08.03.2011 07:05
Выкладывай на форуме.
Ответить | Ответить с цитатой | Цитировать
# гость 26.04.2011 12:25
Спасибо за отличную статью. Хочу прикрутить attiny2313 4-x разрядный индикатор. IAR Выдает ошибки:
Код:Error[Pe020]: identifier "TOIE1" is undefined C:\project\main.c 37
Error[Pe020]: identifier "TCCR0" is undefined C:\project\main.c 38
Error[Pe020]: identifier "CS02" is undefined C:\project\main.c 38
Error[Pe020]: identifier "CS01" is undefined C:\project\main.c 38
Error[Pe020]: identifier "CS00" is undefined C:\project\main.c 38

и т.п. По даташиту таймеры там есть и буковки типа TOIE1 есть, а в библиотеке iotiny2313.h нет. Простите начинающего,про сто хочу разобраться. Как быть или где почитать?
Ответить | Ответить с цитатой | Цитировать
# Сережа 18.03.2011 23:27
если такое совмещать с чтением 1wire - какие тонкости?
у меня не получается увидеть устройство... :-(
Ответить | Ответить с цитатой | Цитировать
# Ратмир 09.05.2011 18:25
Pashgan, доброй ночи. попробывал ваш первый пример через CodeVision, индикатор просто моргает. Может надо мне фьюзы установить? И еще вопросик: на отладочной плате у меня перед индикатором установлена 74HC595, сигналы на нее не подаю, может она мешает?
Ответить | Ответить с цитатой | Цитировать
# Ратмир 10.05.2011 13:14
по ходу у меня открытый анод поэтому не работает, значит нужно менять код
Ответить | Ответить с цитатой | Цитировать
# 1100.1010.1011.1010 20.06.2011 10:33
Pashgan,поясни пожалуйста:
Для частоты прерываний 100Гц при кварце 8МГц и делителе на 1024 по подсчету нужно 78,125 тактов. У тебя указано 77 (ff-b2).где 78й выполняется?

77й TCNT0 == 0xFF
78й TCNT0 == 0x00
прерывание по переполнению
Последовательно сть такая?
Ответить | Ответить с цитатой | Цитировать
# FIN 29.08.2011 07:42
Благодарю за великолепный сайт!
Вопрос не в тему:
Кокой программой пользуетесь для рисования схем на этом сайте?
Ответить | Ответить с цитатой | Цитировать
# Anzor 14.09.2011 11:50
Здравствуйте. Скажите, почему в одном из примеов динамической индикации в codevision с применением таймера в строках инициализации TCCR0 = (1
Ответить | Ответить с цитатой | Цитировать
# Silvercrab 04.10.2011 10:56
Спасибо за прекрасный сайт и статьи.
Пишу в этой ветке вот почему, используя Ваши библиотеки 7-seg столкнулся с проблемкой, нужно было оперативно менять кол-во выводимых разрядов, т.е. гасить старшие, оставляя в работе младшие и наоборот. Пришлось немного подправить Вашу библиотеку :) добавив в функцию void IND_Output ещё одну переменную. теперь это выглядит так:
Код:
/******************************************
* Function name : IND_Output
* Returns : нет
* Parameters : value - число для преобразования, comma - позиция точки на индикаторе
digN - количество разрядов выводимых на индикатор справа на лево
* Purpose : преобразует 16-ти разрядное число, записывает его в буфер индикатора
******************************************/
void IND_Output(unsigned int value, unsigned char comma, unsigned char digN);



и

Код:
//***************************************
void IND_Output(unsigned int value, unsigned char comma, unsigned char digN)
{
unsigned char i;
unsigned char tmp;
for(i = 0; i < digN; i++){
tmp = value % 10;
buf = number[tmp];
value = value/10;
}

if (comma < digN) {
buf[comma] |= 1<<(SEG_DP);
}
}
//**************************************
Ответить | Ответить с цитатой | Цитировать
# fredi 22.11.2011 14:26
Помоги пожалуйста.
В примере 4, если использовать индикатор с общим анадом, что нужно исправить в коде?
P.S. желательно исправленный код.
Ответить | Ответить с цитатой | Цитировать
# ALEX789 30.11.2011 21:32
Здравствуйте Павел.
Мне надо подправить драйвер семисегментного индикатора так, чтобы на индикаторе выводить переменную со знаком (signed). Как это правильно сделать. Эта переменная рассчитывается постоянно в программе, и ее со знаком (+ или -)надо выводить на семисегментник.
Ответить | Ответить с цитатой | Цитировать
# BlkDem 12.12.2011 18:09
Толковая статья. Избавило от излишней траты времени на освоение 7seg. Правда у меня не только цифры, но и некоторые буквы. Что, впрочем, ничего не меняет :)
Спасибо.
Ответить | Ответить с цитатой | Цитировать
# Катя 20.12.2011 13:57
Помогите,пожалу йста,написать программу для отображения на семисегментном индикаторе количества нажатий (в шестнадцатеричн ой форме) на кнопки. Каждое нажатие должно сопровождаться звуковым сигналом.
Ответить | Ответить с цитатой | Цитировать
# Triaton 12.12.2012 20:23
Извините за глупый вопрос. Скачал для CVAVR, стоит ver.2.05, компилирует с ошибками - "не могу открыть mega8.h", не понимает регистры портов (PORTB, DDRB, PORTD, DDRD). Понимаю, что проблема на поверхности - ткните носом.
Ответить | Ответить с цитатой | Цитировать
# Triaton 12.12.2012 20:34
Добавлю, что оба файла в папке inс, отобдажаются в дереве проекта и на соотв. вкладках виден их код.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 14.12.2012 13:02
У меня версия CodeVision 2.04.4a. А подобные проблемы у меня были со старой версией.
Ответить | Ответить с цитатой | Цитировать
# Maksim 08.01.2013 09:18
Дорогие,уважаем ые программисты,по могите пожалуйста написать программу на Assemblere,нужн о для лабораторной работы в университете,ес ли завтра не сдам зачета не будет и следовательно отчислят...пров еряем ее в AVR Studio ATmega 16.Задание такое,написать программу для циклического отображения на семисегментном индикаторе цифр по возрастанию от 0 до F.Каждый счет должен сопровождаться звуковым сигналом. Прошу помоч,так как думаю что для вас это не составит труда,спасибо огромное,вязать ся можно по контакту http://vk.com/id99022848 и майлу max_kop_rus@mail.ru
Ответить | Ответить с цитатой | Цитировать
# Вадег 08.01.2013 12:44
Вот тут подробно все разжевано:
http://alex.starspirals.net/2010/02/07/управление-семисегментным-индикатор/
Ответить | Ответить с цитатой | Цитировать
# maksim 08.01.2013 13:00
ну помоги пожалуйста, просто я с Дальнего Востока(Хабаров ск)времени вообще нету,через 8 часов сдавать работу, а щас 12 ночи....очень прошу(
Ответить | Ответить с цитатой | Цитировать
# Pashgan 20.02.2013 20:55
bchserg, да так и есть. Задавая частоту обновления разрядов нужно учитывать их количество.
Твой комментарий удалился, потому что я потер комментарии maksim
Ответить | Ответить с цитатой | Цитировать
# Юра 17.04.2013 05:40
Вот инициализация таймера происходит здесь
//инициализация таймера Т0
TIMSK = (1
Ответить | Ответить с цитатой | Цитировать
# Юра 17.04.2013 05:44
Вот инициализация таймера происходит здесь
Код:
//инициализация таймера Т0
TIMSK = (1<<TOIE0);
TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00);
TCNT0 = 0xb2;


Почему не указан регистр OCR0A ? В него же записывается значение с которым сравнивается значение TCNT0 и происходит прерывание? В прерывание нужно обнулить TCNT0, и т.д. Поправьте если я не прав.

Индикация работает, но не могу понять почему)) Как работает таймер в Вашем случае?
Заранее благодарен :)
Ответить | Ответить с цитатой | Цитировать
# САБ 17.04.2013 09:40
Цитирую Юра:
Почему не указан регистр OCR0A ?
Потому что у меги8 такого регистра нет.
Цитирую Юра:
Как работает таймер в Вашем случае?
Таймер считает от 0xb2 (за магические цифры надо бить по рукам!) до переполения, генерит прерывание, в прерывании в него снова записывается 0xb2 (хотя надо прибавлять 0xb2, см. тут )
Ответить | Ответить с цитатой | Цитировать
# Юра 17.04.2013 09:57
Цитирую САБ:
Цитирую Юра:
Почему не указан регистр OCR0A ?
Потому что у меги8 такого регистра нет.
Цитирую Юра:
Как работает таймер в Вашем случае?
Таймер считает от 0xb2 (за магические цифры надо бить по рукам!) до переполения, генерит прерывание, в прерывании в него снова записывается 0xb2 (хотя надо прибавлять 0xb2, см. тут )


С работой таймера разобрался за пару часов со статьей про прерывания и с даташитом.
Большое спасибо за ссыылочку :) Вот про "магические числа" Вы правы))) Путаюсь я в этом пока, что и как считать, а в Вашем примере всё просто и понятно, только поменять в дефайн и всё. Ничего высчитывать не нужно.
Ответить | Ответить с цитатой | Цитировать
# Юра 17.04.2013 09:58
Но всё же как высчитывать вручную тоже, я думаю, нужно знать )) Это также не лишние знания, но в проектах буду юзать метод по ссылке выше от САБа
Ответить | Ответить с цитатой | Цитировать
# Pashgan 18.04.2013 08:34
Цитата:
за магические цифры надо бить по рукам!
Есть некоторые недостатки и они накапливаются, но до исправлений не доходят руки.
Ответить | Ответить с цитатой | Цитировать
# Марат 15.05.2013 13:50
Помогите пожалуйста! Написать контрольную. Девушки одной помочь надо было. Вот тема:
Подключение трех разрядного семисегметного индикатора к МК. Использовать индикаторы с общим анодом с динамической способом подключения
Вот E-Mail: skyvip@bk.ru
Ответить | Ответить с цитатой | Цитировать
# Юра 15.05.2013 16:25
инфы в интернете много :) напиши сам, труда особого не составит, на 10 листов этой тематики уйдет полчаса-час, самому лень?
Ответить | Ответить с цитатой | Цитировать
# Марат 16.05.2013 03:38
нет не лень, если знал бы. Я в гуманитарий учусь:( это не моё. Юра помоги?
Ответить | Ответить с цитатой | Цитировать
# Gennady 18.02.2014 06:39
Подскажите: Меняю программу на обратный отсчёт. Выставляю прерывание по совпадению для Т1 (1 сек реального времени). Всё настраиваю под Т1. НО! зажигание сегментов идёт по реальному времени, а подсчёт так работает по какому-то своему отсчёту (counterProg) и не подчиняется _delay_(1000)ко торая не равна 1с при 8МГц
Ответить | Ответить с цитатой | Цитировать
# Gennady 18.02.2014 09:21
Неправильно сформулировал пост выше. В протеусе во время симуляции счётчик отсчитывает не 1 с и цифры бегут очень быстро. Где косяк понять уже второй день не могу. Исходник брал для WinAVR, 4.
Ответить | Ответить с цитатой | Цитировать
# Дитрий 20.03.2014 21:59
У Вас в прерываниях count увеличиваться не будет, т.к. это локальная переменная для прерывания и вы её обнуляете при инициализации! Исправьте пожалуйста это.
Ответить | Ответить с цитатой | Цитировать
# JoJo 20.03.2014 22:56
Переменная статическая. Она сохраняет свое значение между вызовами.
Ответить | Ответить с цитатой | Цитировать
# Дитрий 21.03.2014 10:08
Прочитал про статическую переменную, разобрался. Спасибо
Ответить | Ответить с цитатой | Цитировать
# Maryanne 02.08.2014 07:15
Heya, this is certainly such a great subject to know about.


Also visit my blog: shonaad71.webno de.com (Maryanne: http://shonaad71.webno de.com/news/having-the-best-40th-celebration/)
Ответить | Ответить с цитатой | Цитировать
# asad 07.10.2014 17:34
А почему нельзя сделать так. просто при трассировке. Достаточно много времени оба индикатора включены.
Код:PORTD|=((1<<1)|(1<<0));
if (count==0)
{
PORTB=number[data2];
PORTD&=~(1<<1);
}
if (count==1)
{
PORTB=number[data1];
PORTD&=~(1<<0);
}

count++;
if (count==2)count=0;

В приведенных примерах незадействованн ые сегменты индикатора тоже включены но не светятся только из за того что время их включения меньше если уменьшить время сканирования видна засветка на индикаторах. И боюсь что 3 знакоместа уже в примере не работоспособны. Собственно и эта конструкция неудачна достаточно большое время индикаторы погашены.
Ответить | Ответить с цитатой | Цитировать
# Толян 07.04.2015 18:53
Как вывести результаты работы суммирующего счетчика на двойной 7-сегментный индикатор на стенде LESO 2.1 (http://www.labfor.ru/devices/leso2) ? Спасибо!
Ответить | Ответить с цитатой | Цитировать
# Boorstysound 27.03.2016 11:40
Как переделать на счетчик обратного отсчета например от 99 до 0 ?
Ответить | Ответить с цитатой | Цитировать
# ujin 28.03.2016 14:21
здравствуйте...
Boorstysound посмотрите пожалуйста на исходники автора уважаемого тов Pashgan...
программы предельно просты и прозрачны...
А именно Пример 4. Вывод цифр от 0 до 99.
Преобразование двоичных чисел в двоично-десятич ные (BCD).
По вашим критериям программу можно изменить всего в нескольких строках а именно в строке инициализации переменной
//программный счетчик секунд
unsigned char counterProg = 0; установите вместо цифры 0 цифру 99.
далее немного изменить основной цикл программы:
while(1){
data1 = counterProg % 10;
data2 = counterProg/10;
//программный счетчик секунд
if (counterProg ==0) counterProg = 99 else counterProg--;
__delay_cycles( 8000000);
}
Ответить | Ответить с цитатой | Цитировать
# Максим232 01.10.2016 08:59
А как называются индикаторы, которые используются в мультиметрах, калькуляторах и т.д. И как их можно подключить к МК, где об этом можно почитать?
Ответить | Ответить с цитатой | Цитировать
# ivan 20.11.2017 18:46
как мне вывести результат ацп на индикатор
Ответить | Ответить с цитатой | Цитировать

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

Защитный код
Обновить