Работа с чарлиплексинговым индикатором.

Все прочитали, вроде даже поняли, взяли паяльник - а нифига не получается? Скорее сюда! Поможем. Чем можем...
Ответить
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

Комрады, прошу помощи. Попался мне в лапы интересный дисплейчик от электронной сигареты. Очень кстати он подходит мне в проект термостата для портативного холодильника, но есть один нюанс - я с подобным никогда не работал, так что пытаюсь его освоить. Суть вот в чем: у дисплея сегменты подключены по методу чарлиплексинга и выведены на 6 выводов. Таблицу соответствия выводов и полярностей для каждого сегмента я сделал и все расписал, так что с этим вопросом не обращаюсь, мне нужен совет по подключению этой музыки к МК и реализации работы программы. Как вижу это я: каждый сегмент дисплея кодируется 6-ю байтами и заносится в массив, еще один массив такого же размера будет хранить 6-ти байтовую маску выводов порта, которые должны быть включены на выход во время индикации сегмента. Занимаем 1 порт контроллера под дисплей, индикация динамическая. Для отображения сегмента пишем в регистр порта нули, переводим весь порт на вход, чтобы погасить весь индикатор, затем пишем в в порт соответствующий нужному сегменту байт из массива и включаем нужные ножки порта на вывод записью соответствующей маски сегмента в регистр направления порта. После задержки продолжаем вывод следующего сегмента по вышеописанному алгоритму. Правильно ли такое решение, или я где-то ошибаюсь? Будет ли работать в таком режиме порт ввода-вывода. Работать будем с AVR.

UPD: не приложил фото дисплея, исправляюсь.

Морда:
Изображение


Со стороны хвоста:
Изображение
Не важно чем все начнется. Важно чем кончится!
Реклама
Друг Кота
Сообщения: 9595
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

подключаем согласно схеме...
Чарлиплексинг.jpg
(50.54 КБ) 144 скачивания
и быстро дёргаем ножками МК... ))

//DDRD.0=1; // лампа 0
//PORTD.0=0; // лампа 0
//DDRD.1=1; // лампа 1
//PORTD.1=0; // лампа 1
//DDRD.2=1; // лампа 2
//PORTD.2=0; // лампа 2
//DDRD.3=1; // лампа 3
//PORTD.3=0; // лампа 3
//DDRD.4=1; // лампа 4
//PORTD.4=0; // лампа 4
//DDRD.5=1; // лампа 5
//PORTD.5=0; // лампа 5
...
...
...
и т.д.

что сложного...
:))
Реклама
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

roman.com, ну во первых начнем с того, что ваш пример кода не заработает как надо, а начнется полная ахинея. Во вторых предложение подключить по указанной схеме меня немного смущает: вы мне предлагаете дисплей расковырять и переразвести его? Он, как бы уже собран на заводе и его схема конечна и неизменна.
В третьих, расписывать такую простыню после исправления для каждого кадра как минимум неэтично.
Не важно чем все начнется. Важно чем кончится!
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

Martian, К сожалению только размытыми фразами. Я же задал конкретный вопрос, расписав алгоритм работы так, как вижу его сам, с просьбой посмотреть и поправить, если я в чем то неправ.
Не важно чем все начнется. Важно чем кончится!
Реклама
Эиком - электронные компоненты и радиодетали
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

Martian, ну реализую, видно будет. Насчет яркости думаю проблем не будет, это индикатор все же, а не новогодняя елка. Меня больше смущает другой вопрос: при выводе каждого сегмента сначала порт настраивается на высокоимпендансное состояние, то бишь на вход. По моей логике я сначала пишу в регистр PORTx требуемое значение, а потом по маске включаю порт на выход записью в регистр DDRx той самой маски. Правильная ли очередность, или сначала надо порт на выход, а потом писать в него? Свой метод я применяю, отталкиваясь от логики работы той же 74hc595.
Не важно чем все начнется. Важно чем кончится!
Реклама
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

Таки дисплей я осилил этот, пока допиливаю код на полноценную работу с ним, но вывод информации проблем не составил. Яркость при частоте обновления 50 Гц вполне неплохая. И да, на свой же вопрос нашел ответ - разницы в очередности записи в регистры порта в принципе нет - работает все в любом порядке. А дисплей, вот он:
Изображение
Не важно чем все начнется. Важно чем кончится!
Реклама
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

В общем я таки добил этот дисплей, работает неплохо. Писал в Arduino IDE - никак с нее слезть проклятой не могу. Замечания к коду и битье тапком по морде приветствуются, кто в программировании разбирается, прошу глянуть и натыкать мордой в глупости. Описание работы программы в шапке кода подробно разжевано.

Кино про дисплей:
Спойлер
Код:
Спойлер

Код: Выделить всё

/*
Программа написана в среде Arduino IDE. Использован МК AtMega328 c внешним кварцем 16МГц. 
                        _______________
Подключение индикатора: |   1   |     2    |    3    |    4    |    5     |    6    |
                        |  PD0  |    PD1   |   PD2   |   PD3   |   PD4    |   PD5   |
                        -------------------------------------------------------------
Сегменты по банкам:
1) A, B, C, D, E, F, G;
0) A, B, C, D, E, F, G;

Символы в массиве:

0 - Капля красная, 1 - Капля зеленая, 2 - Капля белая, 3 - Знак %, 4 - Молния красная, 5 - Молния белая;

Логика работы программы следующая:
Для работы используется прерывание таймера 1. Контроль работы а так же порядок выполнения операций осуществляется посредством счетчика на инкрементируемой переменной dyncounter. Частота прерываний - 10КГц.
Для вывода данных на индикатор в данный момент используется 16-ти байтный буфер buff[16], представляющий из себя одномерный массив содержащий
информацию для каждого из 16 сегментов в виде [A2,B2,A1,B1,C1,D1,E1,F1,G1,A0,B0,C0,D0,E0,F0,G0]. При выводе на индикатор в соответствии со значением
 dyncounter в регистр данных порта PORTx записываются данные сегмента из ячейки буфера, а в регистр направления порта DDRx соответствующая
маска сегмента из массива segmentmask[16].

Функция buffops() занимается подготовкой выходных данных. Во время ее работы вывод информации на дисплей запрещен
чтобы отображаемые данные были достоверными. Исходное число val в десятичном формате разбивается на разряды функцией digdisass() и помещает его в переменную vals[2]. Всвязи с особенностью дисплея отображать 
в старшем разряде только единицу? отображение этого разряда происходит по признаку val>=100. Если условие верное, показываем единичку, если нет, молчим в тряпочку. Она проверяет и в случае необходимости включает
 старший разряд индикатора, по очереди берет исходное число, разбитое на десятки и единицы  из массива vals[2] и по битовой маске числа, соответствующей необходимым сегментам на дисплее, заполняет буфер вывода по следующей
системе: переменной outseg1 присваивается значение битовой маски из массива segments1, где записаны битовые маски для чисел от 0 до 9.
путем сдвига на 1 влево обрезается ненужный старший бит, так как каждый символ кодируется только 7-ю битами.
в цикле каждый бит переменной segments1 сравнивается с маской digitmask, и  пишет в выходной буфер данные для каждого из сегментов. В случае совпадения в соответствующую
 ячейку буфера будет записано значение из массива segments1, где содержатся битовые маски для отображения сегментов первого разряда, в случае несовпадения будет записан
 0, означающий, что текущий сегмент зажигать не надо. Здесь же присутствует операция гашения незначащего разряда, если исходное число меньше 10. Заполнение осуществляется 
 от сегмента A1 до сегмента G1. Далее таким же способом заполняется буфер для сегментов A0-G0, после чего снимается запрет на вывод дисплей и останавливается работа с буфером до полного обновления индикатора.

В программе присутствует переменная timecontr. Она нужна для автоинкремента исходного числа val в прерывании. Данная функция нужна для демонстрации работы программы.

Отображение символов пока не реализовано, оно ничем не отличается от отображения символов, необходимо увеличить обьем выходного буфера на 6 байт и дописать в функцию работы с буфером обработку символов.
мне, как истинному коту пока лень ))))

*/
byte digitmask = 0x80; 
volatile byte outseg0 = 0;
volatile byte outseg1 = 0;
volatile unsigned int timecontr = 0;
volatile byte dyncounter = 0;
volatile bool buffupdate = 0;
volatile byte val = 0;
volatile byte vals[2] = { 0, 0 };
byte segments1[7] = { 0x08, 0x10, 0x20, 0x01, 0x04, 0x08, 0x10 };
byte segments0[7] = { 0x20, 0x01, 0x02, 0x08, 0x10, 0x20, 0x01 };
byte symbols[6] = { 0x10, 0x08, 0x02, 0x08, 0x20, 0x20 };
byte segmentsmask[16] = { 0x03, 0x05, 0x09, 0x11, 0x21, 0x03, 0x06, 0x0a, 0x12, 0x22, 0x05, 0x06, 0x0c, 0x14, 0x24, 0x09 };
byte cifraseg[10] = { 0x7e, 0x30, 0x6d, 0x79, 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b };
volatile byte outbuff[16];
void digdisass() {
  if (val >= 100) {
    vals[1] = (val - 100) / 10;
    vals[0] = val % 10;
  }
  if (val < 100) {
    vals[1] = val / 10;
    vals[0] = val % 10;
  }
}
void buffops() {
   if (buffupdate == 1) {
digdisass();
    if (val >= 100) {
      outbuff[0] = 0x02;
      outbuff[1] = 0x04;
    }
    if (val < 100) {
      outbuff[0] = 0x00;
      outbuff[1] = 0x00;
    }
    if (val < 10) {
      outseg1 = 0x00;
    } else {
      outseg1 = cifraseg[vals[1]];
    }
    outseg1 = outseg1 << 1;
    for (int i = 0; i <= 6; i++) {
      if ((outseg1 & digitmask) == digitmask) {
        outbuff[(i + 2)] = segments1[i];
      }
      if ((outseg1 & digitmask) != digitmask) {
        outbuff[i + 2] = 0x00;
      }
      outseg1 = outseg1 << 1;
    }
    outseg0 = cifraseg[vals[0]];
    outseg0 = outseg0 << 1;
    for (int i = 0; i <= 6; i++) {
      if ((outseg0 & digitmask) == digitmask) {
        outbuff[i + 9] = segments0[i];
      }
      if ((outseg0 & digitmask)!= digitmask) {
        outbuff[i + 9] = 0x00;
      }
      outseg0 = outseg0 << 1;
    }
    buffupdate = 0;
  }
}
void displayout() {
  PORTD = outbuff[dyncounter];
  DDRD = segmentsmask[dyncounter];
}
void setup() {
  // Заводим таймер1 в режим СТС на 10 КГц
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 24;
  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << CS11) | (1 << CS10);
  TIMSK1 |= (1 << OCIE1A);
  sei();
}
ISR(TIMER1_COMPA_vect) {
  if (buffupdate == 0) {
    displayout();
     }

  dyncounter += 1;
  if (dyncounter > 15) {
    buffupdate = 1;
    buffops();
    dyncounter = 0;
  }
  timecontr += 1;
  if (timecontr >= 1500) {
    val += 1;
    timecontr = 0;
  }
  if (val >= 200) {
    val = 0;
  }
}
void loop() {
}
Не важно чем все начнется. Важно чем кончится!
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

Martian, Огромное спасибо за замечания. buffupdate можно вообще убрать, а вывод на индикатор ограничить значением счетчика dyncounter - надо было сразу так сделать, хорошо, что подсказали. Способ записи и проверки инкремента тоже утащил в уголок, буду теперь так делать. Со сдвигом битов я пока хреново дружу - сказывается обучение программированию с помощью ардуино - сейчас активно с него пытаюсь слезть. Я неправильно записывал выражения и они у меня просто не работали, поэтому пришлось извращаться. Например, вместо outseg0 <<=1; чукча писал outseg0 << 1; Внезапно это не работало :shock: , поэтому старым добрым железобетонным методом писал outseg0 = outseg0<<1. Это то работает. :))

Связка if()...else() у меня иногда выдает проблемы - неоднократно нарывался на неадекватно работающий код. Я же пока только учусь, вот уже и от delay и millis отучился вроде. :))

Добавлено after 13 minutes:
В моем решении есть еще один недочет, который надо будет исправить - я для управления 6-ти выводным индикатором полностью занимаю весь 8-ми битный порт целиком. Надо будет перекошачить немного код, добавить запись в порт по маске требуемых выводов, чтобы остальные не трогать. Будет время, переделаю, а то 2 пина вникуда. То есть на вход все время :))

Добавлено after 20 minutes 2 seconds:
Может это в виде статьи оформить и выложить тут же на сайте? Сам же информацию подробную недавно искал про такие индикаторы.
Не важно чем все начнется. Важно чем кончится!
Электрический кот
Аватара пользователя
Сообщения: 1050
Зарегистрирован: Вс мар 01, 2009 19:47:16
Откуда: Ростов-на-Дону

Сообщение Viper_Snake »

Martian, тады вечерком добавлю управление символами и забузолю в статью. Спасибо за помощь, при написании статьи буду ссылаться на того, от кого получил знания. :beer:
Не важно чем все начнется. Важно чем кончится!
Ответить

Вернуться в «Практика»