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

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

Comments   

# foxit 2010-04-28 03:34
Спасибо
Буду разбираться
# Guest 2010-04-28 03:52
Спасибо, очень рад очередной статье...
# alexandershahbazov 2010-04-28 08:01
На макетке у меня сейчас 2-х зазрядный
7-сегментник с ATmega8 и сдвиговым регистром 74HC164 . Так и не попробовал как
с ним работать . Есть в наличии также 74HC595 . Читал статью "Подключение lcd. Варианты схем" , но хотелось бы посмотреть здесь их применение .
Читал про однопроводное подключение . Но для начала попробовать бы 2-х проводное
подключение , применяя 74HC164 или 74HC595 .
# alexandershahbazov 2010-04-28 12:29
Еще мне непонятно там по схеме 1 ножка индикатора соединена с 6 . И поточнее что за транзистор лучше ставить .
# foxit 2010-04-28 16:26
В коде есть строчки
//это часть можно настроить под другой индикатор
Как ими пользоваться?
# Pashgan 2010-04-28 17:48
Quote:
На макетке у меня сейчас 2-х зазрядный 7-сегментник с ATmega8 и сдвиговым регистром 74HC164 . Так и не попробовал как с ним работать
Там ничего сложного - нужно записывать коды цифр в регистры с помощью аппаратного/программного SPI. Думаю, накатаю чего-нибудь.
Quote:
Еще мне непонятно там по схеме 1 ножка индикатора соединена с 6
Это два катода - наверное добавили лишний, чтобы четное число выводов было.
Quote:
И поточнее что за транзистор лучше ставить .
В данном случае используется BC547 - биполярный npn транзистор общего назначения, 45В 100мА. Транзистор лучше ставить тот, который ток выдержит;)
# Pashgan 2010-04-28 17:50
Quote:
В коде есть строчки //это часть можно настроить под другой индикатор Как ими пользоваться?
Вот так:
Code:
индикатор с общим катодом подключенный напрямую к микроконтроллеру
#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 2010-05-03 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 2010-05-04 06:48
Отлично !
# Pashgan 2010-05-04 15:18
Quote:
лучше не использовать деление,я делаю преобразование вычитанием.Так быстрее,так как МК не умеет делить и при компиляции будет большая и медленная подпрограмма
Ну да, будет выигрыш (по коду - единицы байт, по скорости ~ в два раза для двухразрядного десятичного числа). В данном случае это не принципиально, здесь же нет ограничений на размер и скорость выполнения кода. В разделе "библиотеки" я выкладывал два исходника для преобразования двоичных чисел таким методом.
# alexandershahbazov 2010-05-09 17:36
С Днем Победы !
# Pashgan 2010-05-09 19:07
Да, С Днем Победы! Светлая память нашим дедам.
# alexandershahbazov 2010-05-12 22:10
Сходу вышло 2-х проводное подключение с
применением 74HC164 на 2-х разрядном
7-сегментнике . Но без SPI , без транзисторов . Сэкономлено 5 ножек . С SPI
вас подожду как вы обещали .
Есть ли какое-то преимущество в применении 74HC595 по сравнению с 74HC164 .
Слышал про однопроводное подключение .
Надежно ли это .
Хотелось бы узнать ваше мнение .
# Вадег 2013-01-08 13:01
Quoting alexandershahbazov:

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

Есть, 595 с защелкой и управляя оной можно не бояться за засвечивание негорящих сегментов. На мой взгляд такая индикация выглядит солидно и профессионально . В книге В.Трамперта "Измерение, управление и регулирование с помощью AVR-микроконтро ллеров." я считаю самая правильная реализация однопроводного подключения в случае со сдвиговыми регистрами, стоит ли оно того, решать Вам. Остальные поделки, которые мне встречались содержат простую RC-цепь, а это не надежно.
У меня без SPI потребовалось три ноги МК для 8 разрядной "панели". Со SPI надо 4, четвертый как раз и есть эта защелка.
# Pashgan 2010-05-13 20:45
Quote:
Есть ли какое-то преимущество в применении 74HC595 по сравнению с 74HC164
по-моему нет.
Quote:
Слышал про однопроводное подключение . Надежно ли это .
Eсть два варианта такого подключения. Первый - с использованием RC цепочки, второй - с использованием двух одновибраторов. Во втором варианте деталей побольше, но мне он внушает больше доверия.
# Guest 2010-05-15 13:54
Pashgan
а как быть если у меня 9разрядный индикатор с общим катодом?
# Guest 2010-05-15 13:59
и подключается он напрямую к атмеге8
# Pashgan 2010-05-15 20:49
Ну как быть.. код подправить под этот индикатор. Посмотри как организован вывод на 4-ех разрядный индикатор в статье про вольтметр.
# Guest 2010-05-17 17:39
Pashgan.напиши плиз на мыло.есть пару вопросов
auag27 собака mail.ru
# ivan 2017-11-20 18:44
как мне вывести результат на семисегментный индикатор?
# Guest 2010-05-25 08:37
Вы применяете поразрядную индикацию. Но есть еще способ посегментной развертки. Его можно использовать для индикаторов с общим анодом (тогда аноды подключаются к выводам порта, а на транзисторы (7шт) подключаются сегменты - катоды). Десятичную точку можно включить без транзистора - она, как правило, включается только в одном знакоместе. Алгоритм, соответственно, изменяется - перебираем сегменты и зажигаем те знакоместа, в которых этот сегмент должен гореть. Такой способ индикации хорош для большого количества знакомест - транисторов будет 7, а скважность будет 8 независимо от количества знакомест, а значит, яркость и импульсный ток будут одинаковы что для 2, что для 20 знакомест. Увеличение количества транзисторов не страшно - 7 транзисторов с базовыми резисторами - это одна микросхема ULN2003, ее стоимость и занимаемое место на плате сравнимы со стоимостью и местом пары транзисторов и их резисторов.
# Pashgan 2010-05-26 14:48
САБ, вы просто кладезь информации
# Guest 2010-08-19 07:23
наконец то очень понятно и полезно. большое спасибо автору. и просьба - сможете выложить какой нибудь тестовый пример для ML1001. это жк 7 сегментный по двух проводной линии. пытаюсь тут его заставить работать... пока не очень
# Pashgan 2010-08-28 18:24
У меня нет в наличии такого индикатора, поэтому выложить пока ничего не могу..
# Олег 2010-10-27 11:15
а как эти коды составляютя?
0x3f, //0
0x06, //1
0x5b, //2
0x4f, //3 И.Т.Д. я так понял они означают те выводы определённого порта которые нужно включить что бы высветилась определённая цифра. например для единички это будет 15 и 16 ножка то есть порт В1, В2. и катод подключеный к С7. в 16ричной системе сумма этих портов будет записываться как 0x3f.
так вот сама технология перевода портов в 16 систему меня интересует. помогите пожалуйста разобраться! :-)
# Pashgan 2010-10-31 20:23
Можно пойти другим путем и он намного удобнее. Раздел "библиотеки" - драйвер семисегментного индикатора 7seg-driver.rar . Посмотри как там определяются коды.
# Иван 2012-03-26 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 2010-11-01 16:18
попробуйте скомпилить исходники под cvavr - и ничего у вас не получится, потому что cvavr не знает ничего про TOIE0, CS02, CS01, CS00 и т.д.
# JoJo 2010-11-07 00:04
В конце статьи есть проект для CodeVision
# enclis 2010-11-10 13:07
сами то пробовали?
# JoJo 2010-12-08 20:50
Да, в CodeVision 2.04. Это в старых версиях CodeVision нет описания битов регистров микроконтроллер а AVR.
# Lion_A 2010-12-10 20:15
Написал и тут-же разобрался :-) . Вот что делает копи паст. Просьба модератора удалить предыдущий пост. Первую программу написал пять дней назад так, что не ругайте :-)
# Pashgan 2010-12-10 20:41
Удалил. Ничего страшного.
# aurysto 2011-03-04 19:03
С удовольствием читаю ваши статьи.Очень доходчиво...А вот как быть если сегменты раскиданы по портам, напр. А-PORTB.7,B-PORTB.5,C-PORTB.4,D-PORTB.2,E-PORTD.4,F-PORTD.5,G-PORTD.6
Знаете, бывает, что удобнее так развестись :-)
# Pashgan 2011-03-05 07:02
Определить дефайном каждый сегмент и переписать код вывода цифры в порт в виде отдельной функции.
Например, так
Code:
#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 2011-03-06 17:33
Спасибо,Pashgan!ОЧЕНЬ!
Code:#define SEG0 3
#define PORT_SEG0 PORTB
#define SEG1 4
#define PORT_SEG0 PORTC

Очевидно в 4-й строке PORT_SEG1?
# Pashgan 2011-03-06 21:36
Да, описался. Исправил.
# aurysto 2011-03-08 01:17
Спасибо.Есть над чем подумать... :-)
Наваяю - можно выложить код?
# Pashgan 2011-03-08 07:05
Выкладывай на форуме.
# гость 2011-04-26 12:25
Спасибо за отличную статью. Хочу прикрутить attiny2313 4-x разрядный индикатор. IAR Выдает ошибки:
Code: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 нет. Простите начинающего,про сто хочу разобраться. Как быть или где почитать?
# Сережа 2011-03-18 23:27
если такое совмещать с чтением 1wire - какие тонкости?
у меня не получается увидеть устройство... :-(
# Ратмир 2011-05-09 18:25
Pashgan, доброй ночи. попробывал ваш первый пример через CodeVision, индикатор просто моргает. Может надо мне фьюзы установить? И еще вопросик: на отладочной плате у меня перед индикатором установлена 74HC595, сигналы на нее не подаю, может она мешает?
# Ратмир 2011-05-10 13:14
по ходу у меня открытый анод поэтому не работает, значит нужно менять код
# 1100.1010.1011.1010 2011-06-20 10:33
Pashgan,поясни пожалуйста:
Для частоты прерываний 100Гц при кварце 8МГц и делителе на 1024 по подсчету нужно 78,125 тактов. У тебя указано 77 (ff-b2).где 78й выполняется?

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



и

Code:
//***************************************
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 2011-11-22 14:26
Помоги пожалуйста.
В примере 4, если использовать индикатор с общим анадом, что нужно исправить в коде?
P.S. желательно исправленный код.
# ALEX789 2011-11-30 21:32
Здравствуйте Павел.
Мне надо подправить драйвер семисегментного индикатора так, чтобы на индикаторе выводить переменную со знаком (signed). Как это правильно сделать. Эта переменная рассчитывается постоянно в программе, и ее со знаком (+ или -)надо выводить на семисегментник.
# BlkDem 2011-12-12 18:09
Толковая статья. Избавило от излишней траты времени на освоение 7seg. Правда у меня не только цифры, но и некоторые буквы. Что, впрочем, ничего не меняет :)
Спасибо.
# Катя 2011-12-20 13:57
Помогите,пожалу йста,написать программу для отображения на семисегментном индикаторе количества нажатий (в шестнадцатеричн ой форме) на кнопки. Каждое нажатие должно сопровождаться звуковым сигналом.
# Triaton 2012-12-12 20:23
Извините за глупый вопрос. Скачал для CVAVR, стоит ver.2.05, компилирует с ошибками - "не могу открыть mega8.h", не понимает регистры портов (PORTB, DDRB, PORTD, DDRD). Понимаю, что проблема на поверхности - ткните носом.
# Triaton 2012-12-12 20:34
Добавлю, что оба файла в папке inс, отобдажаются в дереве проекта и на соотв. вкладках виден их код.
# Pashgan 2012-12-14 13:02
У меня версия CodeVision 2.04.4a. А подобные проблемы у меня были со старой версией.
# Maksim 2013-01-08 09:18
Дорогие,уважаем ые программисты,по могите пожалуйста написать программу на Assemblere,нужн о для лабораторной работы в университете,ес ли завтра не сдам зачета не будет и следовательно отчислят...пров еряем ее в AVR Studio ATmega 16.Задание такое,написать программу для циклического отображения на семисегментном индикаторе цифр по возрастанию от 0 до F.Каждый счет должен сопровождаться звуковым сигналом. Прошу помоч,так как думаю что для вас это не составит труда,спасибо огромное,вязать ся можно по контакту http://vk.com/id99022848 и майлу max_kop_rus@mail.ru
# Вадег 2013-01-08 12:44
Вот тут подробно все разжевано:
http://alex.starspirals.net/2010/02/07/управление-семисегментным-индикатор/
# maksim 2013-01-08 13:00
ну помоги пожалуйста, просто я с Дальнего Востока(Хабаров ск)времени вообще нету,через 8 часов сдавать работу, а щас 12 ночи....очень прошу(
# Pashgan 2013-02-20 20:55
bchserg, да так и есть. Задавая частоту обновления разрядов нужно учитывать их количество.
Твой комментарий удалился, потому что я потер комментарии maksim
# Юра 2013-04-17 05:40
Вот инициализация таймера происходит здесь
//инициализация таймера Т0
TIMSK = (1
# Юра 2013-04-17 05:44
Вот инициализация таймера происходит здесь
Code:
//инициализация таймера Т0
TIMSK = (1<<TOIE0);
TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00);
TCNT0 = 0xb2;


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

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


С работой таймера разобрался за пару часов со статьей про прерывания и с даташитом.
Большое спасибо за ссыылочку :) Вот про "магические числа" Вы правы))) Путаюсь я в этом пока, что и как считать, а в Вашем примере всё просто и понятно, только поменять в дефайн и всё. Ничего высчитывать не нужно.
# Юра 2013-04-17 09:58
Но всё же как высчитывать вручную тоже, я думаю, нужно знать )) Это также не лишние знания, но в проектах буду юзать метод по ссылке выше от САБа
# Pashgan 2013-04-18 08:34
Quote:
за магические цифры надо бить по рукам!
Есть некоторые недостатки и они накапливаются, но до исправлений не доходят руки.
# Марат 2013-05-15 13:50
Помогите пожалуйста! Написать контрольную. Девушки одной помочь надо было. Вот тема:
Подключение трех разрядного семисегметного индикатора к МК. Использовать индикаторы с общим анодом с динамической способом подключения
Вот E-Mail: skyvip@bk.ru
# Юра 2013-05-15 16:25
инфы в интернете много :) напиши сам, труда особого не составит, на 10 листов этой тематики уйдет полчаса-час, самому лень?
# Марат 2013-05-16 03:38
нет не лень, если знал бы. Я в гуманитарий учусь:( это не моё. Юра помоги?
# Gennady 2014-02-18 06:39
Подскажите: Меняю программу на обратный отсчёт. Выставляю прерывание по совпадению для Т1 (1 сек реального времени). Всё настраиваю под Т1. НО! зажигание сегментов идёт по реальному времени, а подсчёт так работает по какому-то своему отсчёту (counterProg) и не подчиняется _delay_(1000)ко торая не равна 1с при 8МГц
# Gennady 2014-02-18 09:21
Неправильно сформулировал пост выше. В протеусе во время симуляции счётчик отсчитывает не 1 с и цифры бегут очень быстро. Где косяк понять уже второй день не могу. Исходник брал для WinAVR, 4.
# Дитрий 2014-03-20 21:59
У Вас в прерываниях count увеличиваться не будет, т.к. это локальная переменная для прерывания и вы её обнуляете при инициализации! Исправьте пожалуйста это.
# JoJo 2014-03-20 22:56
Переменная статическая. Она сохраняет свое значение между вызовами.
# Дитрий 2014-03-21 10:08
Прочитал про статическую переменную, разобрался. Спасибо
# Maryanne 2014-08-02 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 2014-10-07 17:34
А почему нельзя сделать так. просто при трассировке. Достаточно много времени оба индикатора включены.
Code: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 знакоместа уже в примере не работоспособны. Собственно и эта конструкция неудачна достаточно большое время индикаторы погашены.
# Толян 2015-04-07 18:53
Как вывести результаты работы суммирующего счетчика на двойной 7-сегментный индикатор на стенде LESO 2.1 (http://www.labfor.ru/devices/leso2) ? Спасибо!
# Boorstysound 2016-03-27 11:40
Как переделать на счетчик обратного отсчета например от 99 до 0 ?
# ujin 2016-03-28 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 2016-10-01 08:59
А как называются индикаторы, которые используются в мультиметрах, калькуляторах и т.д. И как их можно подключить к МК, где об этом можно почитать?
# ivan 2017-11-20 18:46
как мне вывести результат ацп на индикатор

У вас недостаточно прав для комментирования.