Главная » Статьи » Proton PICBasic

Динамическая индикация. Наглядный пример

Возьмем случай более сложный, нежели простое отображение часов и минут, мы будем отображать также и секунды и сотые доли секунды. Получаем секундомер. О! Пусть это будет секундомер. Кроме того, мы подключим индикаторы к порту микроконтроллера не как обычно, а произвольно. Для этого мы воспользуемся специальным инструментом. Таким образом, нам потребуется 8 ножек микроконтроллера для вывода цифр на индикаторы. Мы их запараллелим, поскольку индикация и называется динамической потому, что мы будем динамично переключать каналы управления, меняя только выводимую для каждого отдельного индикатора цифру в определенный момент. Также нам понадобятся еще 8 портов для управления общими анодами (или катодами) индикаторов. Уже 16! Кроме этого, нам требуются входы для кнопок, которыми мы будем запускать, останавливать и сбрасывать время. Сделаем их отдельными входами. Итого – 18 портов. Не будем заморачиваться и возьмем PIC16F877. Индикаторы – с общим анодом – значит зажигать сегменты будем нулем, подавая на общий анод единицу.

Итак, рисуем схему:



Обратите внимание? Как подключены сегменты. Все мы привыкли подключать индикатор к порту последовательно – сегмент A – к, например PORTB.0, сегмент B – к PORTB.1 и так далее. Будем считать, что нам необходимо для лучшей разводки платы подключить по другому. Например, как на схеме. Как у меня так получилось? Я воспользовался созданным мной ранее этим онлайн-инструментом. Там все просто, обратите внимание на рисунок:

Подключаем порты микроконтроллера, щелкая по названию соответствующего порта. Порты подключаются последовательно, то есть, сначала нам нужно подцепить какой-нибудь порт к сегменту A, затем следующий – к сегменту B и так далее. Думаю, все понятно.
Итак – программа:

Code
  
'-------------------------- Описание проекта ---------------------------------

'Проект : Динамическая индикация  

'Автор : Admin  

'  

'Версия : 1.0.0 от 21.10.2011  

'  

'-------------------------- Опции компилятора --------------------------------

Declare SHOW_SYSTEM_VARIABLES = OFF ' При симуляции в Proteus не показывать внутренние переменные

Declare FSR_CONTEXT_SAVE = On ' Заботиться о сохранении содержимого регистра FSR

Declare Reminders = OFF ' Выключить напоминания компилятора  

Declare Warnings = OFF ' Выключить предупреждения компилятора  

Declare Optimiser_Level 0 ' Выключить оптимизацию программы  

Declare Bootloader = OFF ' Выключить загрузчик  

;-------------------------- Общие настройки-----------------------------------

Device = 16F877 ' Используемый микроконтроллер

Xtal = 4 ' Частота осциллятора 4 МГц

'-------------------------- Конфигурация программирования --------------------

Config CP_OFF, DEBUG_OFF, CPD_OFF, LVP_OFF, BODEN_OFF, PWRTE_ON, WDT_OFF, XT_OSC  

'-------------------------- Настройки портов ---------------------------------

PortB_Pullups = OFF ' Выключить подтягивающие резисторы на PORTB

Declare All_Digital = On ' Установить все порты цифровыми входами/выходами

'-------------------------- Регистры специального назначения------------------

'-------------------------- INTCON -------------------------------------------

Symbol RBIF = INTCON.0 ' Флаг внешнего прерывания по PORTB.4-PORTB.7

Symbol INTF = INTCON.1 ' Флаг внешнего прерывания по PORTB.0(INT)

Symbol T0IF = INTCON.2 ' Флаг переполнения TMR0

Symbol RBIE = INTCON.3 ' Бит разрешения прерывания по PORTB.4-PORTB.7

Symbol INTE = INTCON.4 ' Бит разрешения прерывания по PORTB.0(INT)  

Symbol T0IE = INTCON.5 ' Бит разрешения прерывания по переполнению TMR0

Symbol PEIE = INTCON.6 ' Бит разрешения прерывания от периферийных устройств

Symbol GIE = INTCON.7 ' Бит глобального разрешения прерываний

'-------------------------- PIE1, PIR1 ---------------------------------------

Symbol TMR1IE = PIE1.0 ' Бит разрешения прерывания по переполнению TMR1

Symbol TMR1IF = PIR1.0 ' Флаг прерывания по переполнению TMR1

'-------------------------- T1CON --------------------------------------------

Symbol TMR1ON = T1CON.0 ' Бит включения модуля TMR1  

Symbol TMR1CS = T1CON.1 ' Выбор источника тактового сигнала для TMR1

Symbol NOT_T1SYNC = T1CON.2 ' Синхронизация внешнего тактового сигнала

Symbol T1INSYNC = T1CON.2 ' Синхронизация внешнего тактового сигнала

Symbol T1SYNC = T1CON.2 ' Синхронизация внешнего тактового сигнала

Symbol T1OSCEN = T1CON.3 ' Включение тактового генератора TMR1

Symbol T1CKPS0 = T1CON.4 ' Выбор коэффициента деления предделителя TMR1

Symbol T1CKPS1 = T1CON.5 ' Выбор коэффициента деления предделителя TMR1

'-------------------------- ADCON0 -------------------------------------------

Symbol ADON = ADCON0.0 ' Включение модуля АЦП

Symbol NOT_DONE = ADCON0.2 ' Статус модуля АЦП

Symbol GO_DONE = ADCON0.2 ' Статус модуля АЦП

Symbol CHS0 = ADCON0.3 ' Выбор аналогового канала

Symbol CHS1 = ADCON0.4 ' Выбор аналогового канала

Symbol CHS2 = ADCON0.5 ' Выбор аналогового канала

Symbol ADCS0 = ADCON0.6 ' Выбор источника тактового сигнала

Symbol ADCS1 = ADCON0.7 ' Выбор источника тактового сигнала

'-------------------------- ADCON1 -------------------------------------------

Symbol PCFG0 = ADCON1.0 ' Настройка каналов АЦП

Symbol PCFG1 = ADCON1.1 ' Настройка каналов АЦП

Symbol PCFG2 = ADCON1.2 ' Настройка каналов АЦП

Symbol PCFG3 = ADCON1.3 ' Настройка каналов АЦП

Symbol ADFM = ADCON1.7 ' Формат результата преобразования

'-------------------------- Определение переменных ---------------------------  

Dim MSEKED As Byte 'единицы миллисекунд

Dim MSEKDES As Byte 'десятки миллисекунд

Dim SEKED As Byte 'единицы секунд

Dim SEKDES As Byte 'десятки секунд

Dim MINED As Byte 'единицы минут

Dim MINDES As Byte 'десятки минут

Dim CHASED As Byte 'единицы часов

Dim CHASDES As Byte 'десятки часов

Dim INDEX As Byte 'индекс для отображения

Dim StartFlag As Bit 'Флаг, указывающий на то, запущен секундомер или нет

'-------------------------- Определение символов -----------------------------

Symbol RESET_ = PORTC.1 'Кнопка сброса

Symbol SF = PORTC.0 'Кнопка старта/останова

Symbol SEGMENTS = PORTB 'Назовем порт, отвечающий за вывод сегментов  

Symbol Anodes = PORTD  

Symbol DOT = PORTB.4  

'-------------------------- Начало -------------------------------------------

TRISA = %00000000 'Порт А - на выход

TRISB = %00000000 'Порт Б - на сегменты

TRISC = %00000011 'Порт С - на кнопки

TRISD = %00000000 'Порт Д - управление анодами

SEGMENTS = %00010001 'Зажгем все нули

Anodes = %11111111 'Включим все индикаторы

StartFlag = 0

'-------------------------- ADCON0, ADCON1 -----------------------------------

'Все каналы - цифровые порты ввода/вывода

ADCON0 = %0000000 ' Значение регистра ADCON0

ADCON1 = %10000111 ' Значение регистра ADCON1

'-------------------------- Настройка TMR1 для прерываний --------------------

TMR1L = $F0 ' Значение младшего регистра TMR1

TMR1H = $D8 ' Значение старшего регистра TMR1

T1CON = %00000000

TMR1IE = 1 ' Разрешение прерывания при переполнении TMR1 с частотой 10,000000 Гц

PEIE = 1 ' Разрешение прерываний от периферийных устройств

GIE = 1 ' Разрешение глобального прерывания

On_Interrupt GoTo ISR_Service

TMR1ON = 1 'Запустим таймер

GoTo MainProgram

'-------------------------- Прерывания--------------------------

ISR_Service:

Context Save ' Этой строкой компилятор автоматически сохранит регистры Status и W

  ' во временные переменные

Nop ' Подстроим ход часов

TMR1L = $ED ' Зададим значение младшего регистра TMR1, с учетом сохранения контекста

TMR1H = $D8 ' Значение старшего регистра TMR1

If StartFlag = 1 Then 'Если разрешен старт,

  Inc MSEKED 'то инкрементируем десятки миллисекунд  

EndIf

If MSEKED = 10 Then 'Если десятки миллисекунд дошли до сотни(10 раз по 10 мсек), то  

  MSEKED = 0 'обнуляем их

  Inc MSEKDES 'и увеличиваем сотни миллисекунд

  If MSEKDES = 10 Then ' Если прошла 1000 миллисекунд (10 раз по 100 мсек)

  MSEKDES = 0 ' обнуляем их

  Inc SEKED ' и прибавляем секунды

  If SEKED = 10 Then 'Если прошло десять секунд,

  SEKED = 0 'то сбрасываем их  

  Inc SEKDES 'и прибавляем один десяток секунд

  If SEKDES = 6 Then 'Если прошло 60 секунд,

  SEKDES = 0 'то обнуляем  

  Inc MINED 'Инкрементируем единицы минут

  If MINED = 10 Then 'Если прошло 10 минут,

  MINED = 0 'то обнуляем

  Inc MINDES 'Прибавляем десятки минут

  If MINDES = 6 Then 'Если прошло 60 минут,

  MINDES = 0 'обнуляем

  Inc CHASED 'Увеличиваем на 1 единицы часов

  If CHASED = 10 Then 'Если прошло 10 часов,

  CHASED = 0 'обнуляем

  Inc CHASDES 'и прибавляем десятки часов

  If CHASDES = 9 Then 'Если прошло 90 часов,

  CHASDES = 0 'обнуляем

  'Думаю, до такого времени у секундомера никто не будет сидеть <img src="http://s8.ucoz.net/sm/24/smile.gif" border="0" align="absmiddle" alt="smile" />

  'Здесь можно придумать что-нибудь свое

  'это вам домашнее задание

  EndIf

  EndIf

  EndIf

  EndIf

  EndIf

  EndIf

  EndIf

EndIf  

TMR1IF = 0 'Сбросим флаг, вызвавший прерывание  

Context Restore ' Возврат из обработчика прерывания и восстановление

  'регистров Status и W

'-------------------------- Главная программа -------------------------

MainProgram:  

Anodes = %10000000 'Зажигаем анод первого индикатора

INDEX = MSEKED 'Загружаем значение десятков миллисек

GoSub OutNum 'Выставляем в порт

GoSub Dlay 'Ждем для того, чтобы различить цифру  

Anodes = %01000000 'Зажигаем анод второго индикатора

INDEX = MSEKDES 'Сотни миллисекунд

GoSub OutNum

GoSub Dlay

Anodes = %00100000 'Третий индикатор

INDEX = SEKED 'Единицы секунд

GoSub OutNum

DOT = 0 'Зажгем точку, чтобы визуально отделить секунды от миллисекунд

GoSub Dlay

Anodes = %00010000 'Четвертый индикатор  

INDEX = SEKDES 'Десятки секунд

GoSub OutNum

GoSub Dlay

Anodes = %00001000 'Зажигаем пятый индикатор

INDEX = MINED 'Загружаем единицы минут

GoSub OutNum

DOT = 0 'Зажигаем точку, чтобы отделить минуты от секунд

GoSub Dlay

Anodes = %00000100 'Шестой индикатор

INDEX = MINDES 'Десятки минут

GoSub OutNum

GoSub Dlay

Anodes = %00000010 'Седьмой индикатор

INDEX = CHASED 'Единицы часов

GoSub OutNum

DOT = 0 'Зажгем точку, чтобы отделить часы от минут

GoSub Dlay

Anodes = %00000001 'Восьмой индикатор

INDEX = CHASDES 'и десятки часов

GoSub OutNum

GoSub Dlay

If StartFlag = 0 Then 'Если секундомер остановлен, то  

If RESET_ = 0 Then 'Если нажата кнопка сброса, обнуляем все цифры

MSEKED = 0

MSEKDES = 0

SEKED = 0

SEKDES = 0

MINED = 0

MINDES = 0

CHASED = 0

CHASDES = 0

INDEX = 0  

EndIf

EndIf

If SF = 0 Then 'Если нажата кнопка Старт/стоп, то  

  StartFlag = ~StartFlag 'выставляем флаг либо старта, либо стопа  

  While SF = 0 'Ждем отпускания кнопки

  SEGMENTS = %11111110 'На время нажатия показываем заставку

  Anodes = %11111111  

  Wend

EndIf  

GoTo MainProgram ' Возврат на начало основной программы

OutNum:

SEGMENTS = LookUp INDEX,[$11, $9F, $58, $1C, $96, $34, $30, $1F, $10, $14]

'Здесь по порядковому номеру INDEX мы загружаем в порт число из квадратных скобок

Dlay:

  DelayMS 2 'Задержка для отображения

Return  


Работает она так – a фоновом режиме запущен таймер TMR1, который настроен на переполнение через каждые 10 миллисекунд. Каждое переполнение таймера вызывает прерывание (за обработку прерываний отвечает подпрограмма ISR_Service), в котором мы загружаем таймер новым значением, чтобы он опять отсчитывал новые 10 миллисекунд. В обработчике прерываний мы инкрементируем наш секундомер, если он запущен, каждые 10 миллисекунд,. Обратите внимание на значения, которые загружаются в таймер (TMR1H и TMR1L), а также на команду Nop, которая сама по себе ничего не делает, но дает задержку в один машинный цикл. Эти значения таймера, а также команда Nop подобраны экспериментальным путем с помощью отладки в MPLAB.
К статье прилагаю архив, содержащий все материалы. Вы можете скачать его и запустить проект MPLAB. Как писать программы с помощью MPLAB на Basic? Я писал раньше – в этой статье.


Задавайте вопросы, если не понятно, комментируйте.


Похожие материалы: Часы на МК PIC16F628A с двумя таймерамиЧАСЫ НА PIC16F628 С ДАТОЙ И ДНЕМ НЕДЕЛИ




Категория: Proton PICBasic | Добавил: ADMIN (21.10.2011)
Просмотров: 10424 | Комментарии: 7 | Теги: прерывания, MPLAB, таймер, PROTON, динамическая индикация | Рейтинг: 5.0/2
Всего комментариев: 7
1 vanish   (21.10.2011 21:06)
Мне про таймеры очень понравилось. Доходчиво. А для индикации я использую сдвиговые регистры 595 или 164 - цена смешная и очень удобно.

2 DAlexV   (23.10.2011 21:32)
Осталось только перевести и индикацию в прерывание разгрузив основное тело для расширения dry

3 ADMIN   (24.10.2011 11:43)
Да думал об этом. но лучше последовательно. Позже отработаю код да дополню статью

4 vanish   (24.10.2011 21:23)
И, если можно, пожалуйста , напишите статью про использование ds1307! ,буду очень благодарен!

5 Matrix252005   (25.10.2011 10:04)
vanish, но на форуме много об ds1307 писалось! Что именно Вас интересует? И кстати, можно так же использовать 4094!!! Она по управлению схожа с 595, только управляющий сигнал "ОЕ" инверсный, по отношению к 595

С уважением, Николай.

6 vanish   (25.10.2011 20:06)
Дело в том что я с другой страны и у нас этот сайт не открывается. А через анонимайзер на этом сайте много что не открывается.И к тому же жутко медленно.Часто выскакивает - " Невозможно отобразить страницу". Вот я урывками информацию и накапливаю. А сайт отличный слов нет.
Мне интересна элементарная программа часы на ds1307 на протоне с комментариями.

7 Matrix252005   (26.10.2011 09:16)
vanish, хорошо, я Вам помогу с ds1307. Только напишите, что именно Вас интересует!!! Только, чтобы не засорять тут, напишите, либо на форуме либо в личку, но лучше на форуме!!! ОК!!!

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]