7 seg динамическая индикация

Март 15, 2010

Как и обещал - в этом уроке будет рассмотрен один пример динамической индикации на 4 разряда без дополнительных деталей.
Плюсы этого метода в том, что можно сократить количество выводов у модуля индикации. Минусы - постоянно занят 1 таймер, схема генерирует помехи, целый порт уходит на сегменты. Использовать динамическую индикацию выгодно начиная с 3-4х разрядов.

 

Для динамической индикации одноименные сегменты всех индикаторов соединяются между собой и через один резистор подключаются к порту. Вместе с точкой как раз 8 бит. Общий анод или катод подключается через транзистор к плюсу или минусу питания. База транзистора подключается к выводом другого порта через резистор в 1к. Сопротивление резистора высчитывается следущим образом.

R=(U-Ud+0.7v)/Id, где d обозначает ток и напряжение падения на диоде. Ток обычно 10ма. Падение 2.1в. 0.7в - падение на транзисторе.

Существут целые модули, где все соединения сегментов уже выполнены внутри корпуса. Такие модули могут быть по 4-8 разрядов.

Работает динамическая индикация очень просто.
В первый момент в порт сегментов выводится 1 байт кода символа. Например для зажигания нуля надо послать в порт 0x3F.
PORT_SEG = 0x3F;

Теперь надо подать 1 на базу транзистора, чтобы он открылся и ток потек через индикатор.

PORT_CAT &=~(1<<n); , где n - номер пина от 0 до 7.

Потом в порт снова записывается код цифры второго разряда, а n увеличивается на единицу, зажигая второй индикатор, а первый гаснет

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

Для переключения разрядов надо использовать таймер. Глаз почти не замечает мерцания при частоте 50гц. При 100 уже точно не заметит. Чем больше индикаторов, тем выше должна быть частота. Если для каждого индикатора брать по 50гц, то на 4 индикатора надо 200гц.

Однако тут не все так просто. При быстром переключении яркость светодиодов будет падать, т.к. речь идет о средней яркости, а не о пиковой. Поэтому формула остается, но резистор придется увеличить экспериментально.

 

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

При частоте 1мгц таймер посчитает 256 раз. Это значит 1 000 000гц/256раз = 3906гц или 3.9кгц, что довольно таки много и будет бесполезно загружать мк.
Поэтому таймер можно сконфигурировать при помощи 2х параметров в разных регистрах. Самый простой параметр - делитель или prescaler. Это еще один счетчик, который может не пропускать сигнал от тактового генератора, может пропускать 1:1 (без изменений), 1:8, 1:64, 1:256 и 1:1024.

Т.е. формула будет выглядеть так: F = Fosc/256/presc . Если мы подберем делитель 64, то получим 61гц
Поделим эти 61гц на 4 индикатора и тогда увидим, что это всего 15гц.
Выходит одним делителем не обойтись. Поэтому есть еще один регистр для начального конфигурирования счетчика. Задает число, от которого тот начнет считать до 255.

TCNT0 = 1 000 000гц/64раз/75гц . В результате мы получим какое-то нецелое число типа 205.59. В регистр такое число не записать, поэтому мы выбираем или 205 или 206, что в данном случае без разницы.
За начальное конфигурирование таймера отвечает регистр TCNT0, в который можно записать число не более 255.
За конфигурирование делителя отвечает регистр TCCR0 и 3 бита CS0n , где n от 0 до 2. Подробнее в даташите на странице 85 для меги16.

Чтобы не заморачиваться с вычислениями, добрые люди создали программы и конфигураторы в компиляторах. Но конфигураторы пишут конкретное число, а не побитно. А калькуляторы не дают кода. Поэтому каждый выбирает сам для себя. http://www.b9.com/elect/avr/kavrcalc/

timer0
Работает это так: выбираем частоту генератора. Выбираем единицы отсчета (время или частоту) и крутим делитель, пока в поле error не будет 0 или близкое к нему. В поле TCNT Base будет начальное значение.

Т.к. мы используем прерывания, то надо подключить заголовок #include <avr/interrupt.h>, в нем определяются вектора прерываний.
Для включения счетчика и прочей периферии есть специальные регистры. В частности нам надо вообще включить прерывания при переполнении и включить это от таймера0.
Timer/Counter Interrupt Mask Register – TIMSK у него есть бит Bit 0 – TOIE0: Timer/Counter0 Overflow Interrupt Enable.
Значит включаем его TIMSK |= (1<<TOIE0);
В данном случае тут написано overflow, потому как есть еще другой тип прерывания compare, но в данном случае это не важно.
Второй регистр Timer/Counter Interrupt Flag Register – TIFR . В этом регистре установлены выключатели счетчиков. В меге16 счетчиков 3. Мы используем первый (timer0). Поэтому нам его надо включить при помощи бита Bit 0 – TOV0: Timer/Counter0 Overflow Flag.
TIFR |= (1<<TOV0);

В конечном счете нам надо разрешить все прерывания разом при помощи макроса sei();
Этот макрос вызывается только после того, как вся периферия с портами будет настроена. Потом идет бесконечный цикл while(1);.

 

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

А в дефайнах определим порты для сегментов и для катодов. Так же определим количество разрядов  - 4.

   1: #define PORT_SEG    PORTC
   2: #define PORT_CAT    PORTB
   3: #define DDR_SEG     DDRC
   4: #define DDR_CAT     DDRB
   5:  
   6: #define MAX_DIGS    4

Теперь идут глобальные переменные.

   1: const char cathode[10]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; // katode
   2: char current_dig, cat_shift=1, digs[MAX_DIGS];
   3: int out=0;

С первой строкой мы уже знакомы из прошлых уроков.

char current_dig - там храниться цифра из числа. Например число 1234 и делим его на 10 с остатком - 4. Подставив эту цифру в массив cathode[], мы получим код цифры 4, который будет выведен в порт.

cat_shift=1 - тут хранится счетчик состояний пинов для катодов. Эта переменная служит для зажигания разрядов. Ее состояние меняется при помощи сдвига влево на едницу, обеспечивая зажигание индикаторов справа налево.

digs[MAX_DIGS] - массив с количеством разрядов. В этом массиве будут храниться коды цифр, которые получены из массива cathode[].

int out=0 - это уже 2 байта для хранения и изменения изначального числа.

Функция disp_update() - 95% схем не нуждаются в постоянном обновлении информации. Например для следущего урока по управлению синтезатором частоты фм приемника не нужно постоянно менять данные на индикаторе, а значит не надо вызывать сложную процедуру раскладки числа на цифры. Поэтому вся эта работа вынесена в отдельную функцию, коотрая вызывается только при нажатии кнопки или каком-то другом событии. Функция просто изменит состояние в массиве digs[MAX_DIGS], а дальше уже прерывание каждые 300гц будет читать от туда данные.

Достаточно в переменную out записать новое значение и вызвать disp_update() один раз.

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

   1: if(cat_shift==2) // если переменная имеет значение 0b0000 0010
   2:  PORT_SEG = digs[current_dig]|0x80; //в код цифры последний бит H выставить в 1 и зажечь точку
   3:  else
   4:  PORT_SEG = digs[current_dig]; // или не зажигать, если не тот разряд
   5:  
   6: PORT_CAT &=0xf0;
   7: PORT_CAT |= cat_shift;
   8:  
   9:  cat_shift<<=1;

Изначально планировалось написать универсальную функцию, но с универсальностью стал расти код. Поэтому универсальность сократилась и придется некоторые части править руками.

Как уже говорилось, в cat_shift хранится только один бит, который указывает на включенный разряд.

Переключение идет справа налево, т.к. при делении с отстатоком 1234%10 первая цифра будет 4. Потом значение это сдвигается влево на 1 бит и принимает вид 2, потом 4 и потом 8, так же двигаясь права налево.

Точка нам нужна для следущей статьи по управлению цифровым синтезатором и индикации частоты, где будут десятие доли мегагерцев. Точку зажигаем в разряде десятков. Так же берем из памяти в массиве digs[] нужный нам код и выводим в порт.

Всего готово? Зажигаем весь разряд PORT_CAT = cat_shift; и сдвигаем бит разряда влево.

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

Поэтому используется небольшой трюк с маскированием и защитой тех бит, а портим только 4 первых, которые наши. Зато потом в 7й строке мы запишем только один бит. Если наши разрядные биты не стирать, то они так там и остануться и при следущей записи в порт будут включены уже 2 бита разрядов и на индикаторах все испортится.

Вот и весь алгоритм. Далее идут только проверки и обнуления.

   1: if(cat_shift>8){     cat_shift=1;}
   2:  
   3: current_dig++;
   4: if(current_dig>3) {current_dig = 0;}

Цифра 8 означает 4й разряд. Если у вас больше разрядов, то 16 будет для 5го, 32 для 6, и т.д.

Это запись в десятиричном виде. Можно записывать и в двоичном 0b0001000. Следует обратить внимание, что тут нужен именно знак больше, а не равно, т.к. сброс произойдет моментально и будут гореть только 3 разряда.

Осталась самая последняя строчка TCNT0 = 0xcc;.

Тут мы снова записываем изначальное значение счетчика, от которого он будет считать нам до 255 и вызывать прерывание. Если этого не сделать, то он будет считать с нуля и время срабатывания таймера будет нето. Прерывание срабатывает 1 раз и регистр TCNT очищается. Это сделано для того, чтобы в следущий раз можно было записать другое значение и таймер бы сгенерировал прерывание не через 1мс, а через 5. Таким образом можно генерировать импульсы разной длины и кодировать 1 и 0 длительностью пауз.

Вобщем не стоит забывать снова инициализировать этот регистр в теле прерывания счетчиков.

Протеус почему-то неправильно симулирует код и выводит на индикатор неверные показания, но все равно проект прикладывается к исходнику.

Tags: ,

категория: учим мк avr

ответить

Авторизация только через loginza.

Yandex Google Вконтакте Mail.ru Twitter Loginza MyOpenID OpenID