Динамическая развертка 7-сегментного 3 элементного индикатора

display

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

Почему используют именно 7-сегметные дисплеи в то время, когда на рынке есть уже тонны дешевых китайский тачскринов с разрешением далеко за 1000 пикселей? Или почему не использовать хотя бы знакосинтезирующие winstar дисплеи с несколькими строчками?

Ответов на эти вопросы несколько. Во-первых, безусловно, цена. Сегментные индикаторы стоят несопоставимо дешевле. Во-вторых, управлять ими проще, чем тем же графическим дисплеем (который к слову, без драйвера не работает и в неоптовых закупках стоит очень дорого), хотя чуть проблемнее, чем знакосинтезирующими дисплеями. Еще важные достоинства 7-сегметных дисплеев - их яркость (видно издалека) и температурный диапазон эксплуатации - они почти все работают в индустриальном диапазоне температур. Если нет необходимость показывать что-то, кроме нескольких цифр (например, температуру или давление, обороты), то есть смысл использовать именно такие индикаторы.

Как работают 7-сегметные дисплеи и что такое динамическая развертка? 7-сегментные дисплеи  как ни странно состоят из 7 управляемых светодиодов, которые можно по очереди зажигать, таким образом формируя определенную цифру. Как правило катод или анод у них общий, а остальные управляются отдельно. И вот тут возникает несколько тонкостей: не к каждому микроконтроллеру удается подключить такие индикаторы, потому что ток через светодиоды может быть больше, чем позволяют ножки МК из-за высокого потребления светодиодов. В таком случае можно использовать либо сдвиговые регистры, либо микросхемы-ключи типа ULN2003. Вторая тонкость заключается в том, что если у вас, например, уже три сегментных индикатора, то проблемно на каждый давать 7*3 = 21 ножки контроллера, потому что не у всех они есть (например, далее мы как раз будем подключать atmega8a), а во-вторых опять же ток через контроллер растет из-за количества подключенных светодиодов. В этом случае удобно использовать динамическую развертку - у нас 7 сигналов на 3 индикатора объединены, а вот анод (или катод) для каждого сегмента у нас отдельно. Т.е. мы задаем код на 7-сегментный индикатор, далее подаем сигнал на включение первого сегмента. затем выключаем первый сегмент, включаем второй и т.д. Если мы будем делать так быстро, то получится динамическая развертка, как в телевизоре. Мы каждый сегмент зажигаем ненадолго с большой частотой и получаем видимое изображение, как-будто горят все три сегмента сразу.

Этот способ позволяет использовать всего 7+3 = 10 ножек, т.е. для 3-элементного индикатора экономим ножки почти в два раза. Но ничего не бывает бесплатно как известно. Данный способ имеет существенный недостаток: развертка должна проходить очень равномерно. соответственно большие требования к процессорному времени и ресурсам. К примеру, если мы "передержим" ненадолго включенным первый сегмент,а остальные два пройдем как обычно, то первый сегмент будет явно пересвечен на фоне других и выглядеть будет ужасно. Это аналогично ШИМ-жффекту для светодиодов, т.к. мы по сути ШИМуем светодиоды индикатора, а задержка - увеличение скважности, соответственно увеличение яркости. Еще одна тонкость - частота прохода по индикаторам, если сделать очень маленькую, экономя ресурсы процессора, то будет видно мерцание. Также важно сделать правильный алгоритм включения и выключения каждого элемента: в начале необходимо выставить код 7-сегментов, затем включить общий сигнал, дать "посветиться" четко заданное время, выключить общий сигнал, перейти к следующему индикатору. Если последовательность действий перепутать, то на сегментах будет проскакивать ненужные элементы, т.е. индикаторы будут слегка перекрывать друг друга.

Принципиальная схема:

segmentnik


Листинг программы управления:
uint8_t font[14] = {0x3f, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x40, 0x73, 0x39, 0x3E};
 
uint8_t data_pins_map[][2] =
{
  {DIG1},
  {DIG2},
  {DIG3},
  {DA},
  {DB},
  {DC},
  {DD},
  {DE},
  {DF},
  {DG},
  {DDP}
};
 
void set_seg(uint8_t value , uint8_t point, uint8_t seg_num)
{
    sys.dig.buffer[seg_num] = font[value] | (0x80 * point); 
}
 
/* 
 *     Send data of command to display
 *    if cmd != 0 - command is sending
 */
 
void send (uint8_t data, uint8_t dig)
{
     uint8_t i;
 
//    dig_clear();
 
    for (i = 0; i < 8; i++)     
    {
         if (data & (1<            pin_low(data_pins_map[i+3][0], data_pins_map[i+3][1]);
        else
            pin_high(data_pins_map[i+3][0], data_pins_map[i+3][1]);     
    }
 
    pin_low(data_pins_map[dig][0], data_pins_map[dig][1]);
//    for (i = 0; i < 10; i++);
//    pin_high(data_pins_map[dig][0], data_pins_map[dig][1]);

void dig_scan (uint8_t *buf, uint8_t dig_count, uint8_t freq_involute)
{
    static uint8_t dig_step = 0;
 
    if (timers.period_involute)
      return;             
 
    dig_clear();
 
    send(buf[dig_step], dig_step);
 
    dig_step++;
 
    if (dig_step >= dig_count)
      dig_step = 0;
 
    timers.period_involute = 1000 / freq_involute;         
}
 
 
 
 
void dig_clear(void)
{
    pin_high(DIG1);
    pin_high(DIG2);
    pin_high(DIG3);     
}
 
void init_oled_lcd(void)
{
    volatile uint16_t i;
 
    for (i = 0; i < 11; i++)
    {
        pin_dir(data_pins_map[i][0],data_pins_map[i][1],OUT);
        pin_high(data_pins_map[i][0],data_pins_map[i][1]);
    }     
}


В основном цикле вызываем следующие функции:

while (1)

{

........

if (display_value)

{

                set_seg((display_value/1 )%10 , 0, 2);
                set_seg((display_value/10)%10 , 0, 1);
                set_seg((display_value/100)%10, 1, 0);
 }
     dig_scan(sys.dig.buffer, 3, 244);

...........

}