Семисегментный индикатор и динамическая индикация на AVR микроконтроллере ATmega8

В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:
В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:
семисегментный светодиодный индикатор
Светодиоды внутри имеют общий анод (ОА) или общий катод (ОК). То есть, для управления одной цифрой нужно 8 выводов микроконтроллера. А что же делать, когда нужно управлять, например, четырьмя цифрами? Использовать микроконтроллер с 4*8=32 выводами? Не экономично.

Для такого случая придумали динамическую индикацию. Для этого соединяем выводы, которые отвечают за включение сегментов в общую шину, а общими анодами (катодами) будем управлять через транзисторы. В отдельный момент времени горит только одна цифра. Таким образом быстро перебирая цифры на дисплее (как кадры в фильме) мы получим эффект постоянно горящего изображения. В замедленном варианте, как это происходит, можно посмотреть на картинке:
анимация динамической индикации
А вот ускоренная в 25 раз картинка, уже начинают вырисовываться контуры «12.34»:
анимация динамической индикации
Используя принцип динамической индикации мы сможем управлять четырьмя цифрами при помощи 8+4=12 выводов. Использование же 2-х сдвиговых регистров HC595 может сократить это число до 4. Рассмотрим схему подключения к микроконтроллеру:

Управлять же индикатором будем с помощью микроконтроллера ATmega8. Резисторы R5-R13 – ограничительные на 470 Ом. R1-R4 – по 1кОм. Транзисторы Q1-Q4 – любые PNP типа, я использовал BC807 в планарном исполнении. Конденсаторы С5,С7 – электролиты по 100 и 200мкф соответственно, С4,С6 – керамика по 0,1мкф. Так как индикатор с общим анодом, то соответственно включение разряда/сегмента производиться низким уровнем.

Для индикаторов с общим катодом схема аналогична, только транзисторы следует взять NPN структуры, и управляться индикатор будет высоким уровнем.
Продемонстрируем сказанное, напишем программу, которая будет перебирать числа от 0 до 9999 и выводить их на семисегментный индикатор.

  1. #include <avr/io.h>
  2. #include <avr/interrupt.h>
  3. // 0 1 2 3 4 5 6 7 8 9
  4. const unsigned char codes[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
  5.  
  6. unsigned char data[4]={0x00,0x00,0x00,0x00};
  7. unsigned char counter=0;
  8.  
  9. void pause (unsigned int a)
  10. { unsigned int i; for (i=a;i>0;i--); }
  11.  
  12. void init_timer (void)
  13. { TIMSK=(1<<TOIE0); //Enable timer overflow interrupt
  14. TCCR0=(0<<CS00)|(1<<CS01)|(0<<CS02); //Prescaller = /1
  15. }
  16.  
  17. void convert_data (unsigned int x)
  18. {unsigned int temp,res;
  19. temp=x;
  20. res=temp/1000; //Calculate 1000-s
  21. data[3]=codes[res];
  22. temp=temp-res*1000;
  23.  
  24. res=temp/100; //Calculate 100-s
  25. data[2]=codes[res];
  26. temp=temp-res*100;
  27.  
  28. res=temp/10; //Calculaate 10-s
  29. data[1]=codes[res];
  30. temp=temp-res*10;
  31.  
  32. data[0]=codes[temp]; //Calculate 1-s
  33. }
  34.  
  35. ISR (TIMER0_OVF_vect)
  36. {PORTD=0xff;
  37. PORTB=~_BV(counter); //Enable digit
  38. PORTD=~data[counter]; //Write code
  39. counter=(counter+1)%4; //Increment digit counter
  40.  
  41. TCNT0=0x00; //Clear timer
  42. }
  43.  
  44. int main(void)
  45. { unsigned int x=0;
  46. DDRD=0xff;
  47. PORTD=0x00;
  48. DDRB=0x0f;
  49. PORTB=0x0f;
  50.  
  51. pause(1000); //Settle pause
  52. init_timer(); //Init timer
  53. sei(); //Interrupt enable
  54. while(1)
  55. {convert_data(x); //Conver data to codes
  56. if (x<9999) x=x+1; //Increment data
  57. else x=0;
  58. pause(30000);
  59. }
  60. return 1;
  61. }

Код очень простой. В массиве codes находятся коды, которые следует выводить на порт, чтобы получить желаемую цифру. Смена активной цифры производиться по прерыванию от переполнения таймера 0. А функция convert_data(int x) раскладывает число х по разрядам, и записывает соответствующие коды в массив data, данные из которого выводятся непосредственно на индикатор, при срабатывание прерывания.
Что из этого вышло, можно глянуть на рисунках:
фото динамической индикации
фото динамической индикации

Исходный код можно скачать в виде проекта под AVR Studio 4
Также, может кому пригодиться, платка для моего табло в формате .lay для программы Sprint Layout 5

Пример: Точка1 - 10 Точка2 -

Пример:
Точка1 - 10
Точка2 - 50
Точка3 - 3

Считем:
Точка1 - 10
Точка1а - 30
Точка2 - 50
Точка2а - 27,5
Точка3 - 3

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

Искуственная инерционность 7сегментного индикатора!!!

Можно ли на АТМЕГА8 создать такой адаптер который будет подключатся к устройству (спидометр) в разрез между МК "дешифратором" и 7сег.индикатором на 3цифры с общ-анодом. и в realtime перехватывать сигналы идущие на индикатор перерабатывая их, добавляя искусственную инерционность. Пример - на 7 сег-индикаторе отображается скорость 20км\ч и через секунду скачкообразно 29км\ч , а в этот момент адаптер подключенный в разрез должен обработать данную информацию таким образом чтобы на индикаторе было плавное изменение скорости, то есть так (20-21-22-23-24-25,,,,км\ч,,,,,,и т.д)
Заранее БЛАГОДАРЮ!!!

Делать плавное изменение

Делать плавное изменение цифровых данных с помощью мк достаточно проблематично....
А как на счет перевода цифрового сигнала идущего на индикатор в аналоговый (ЦАП)... Потом аналоговый сигнал трансформируем в плавные изменения и обратно в цифру (АЦП)... Грубо говоря просто подключаем (вольтметр) к спидометру и на выходе получаем (FX inertial) инерционность... Как такая идейка??? Думаю плавность изменения в реальном времени получится...(add-sodomora)

плавное изменение алгоритм

как вариант можно писать значения показаний скорости в массив, потом при появлении нового показания сдвигать массив. Что бы реализовать плавность я бы сделал бы так:
проверку двух пред идущих значений. Например скорость изменяется так: 0км/ч - 15км/ч - 45км/ч.
Необходимо во первых уменьшить частоту вывода на экран что позволит добавить так нужные нам избыточные точки плавного изменения что даст возможность из 3х значений сделать 6 а так же сделать оценку значения 1 с значением 2 перед выводом значения 3 выводить среднее арифметическое из двух величин 1 и 2. Идея я думаю понятна.

Сильно мудрено как-то, проще

Сильно мудрено как-то, проще сделать среднее арифметическое скорости за какой-то промежуток. Т.е. за 1с делается 10 выборок, все суммируется и делится на 10. В итоге получаем требуемую инерционность. Чем больше промежуток времени и чем больше выборок - тем большая инерционность.