Представленная в этой статье программа написана для микроконтроллера, управляющего генератором прямоугольных импульсов. Основные функции, выполняемые программой: 1) обмен данными с компьютером по последовательному интерфейсу; 2) настройка необходимым образом и включение встроенного в микроконтроллер модуля ШИМ; 3) работа с EEPROM.
Для обмена информацией между контроллером и ПК подходит любая терминалка, умеющая работать в hex-режиме, например, RH_Com — программа, специально разработанная нами для работы с COM-портом (прогу и исходники можно найти здесь).
Теперь по пунктам:
-
Конфигурационная информация, посылаемая с компьютера, состоит из трёх байт. Первый байт содержит командные биты, биты, определяющие коэффициент деления предделителя TMR2, а также 2 младших бита длительности импульса. Он имеет следующий формат: старший бит — стирание EEPROM (если старший бит установлен в 1, то будет выполнено стирание информации из EEPROM), 6-й бит — запись в EEPROM (если этот бит установлен в 1, то будет выполнена запись принятой конфигурационной информации в EEPROM), 5-й и 4-й биты — младшие биты длительности импульса, 3-й и 2-й биты — биты, определяющие коэффициент деления предделителя TMR2, 1-й и 0-й биты не используются и могут быть любыми.
Второй байт содержит 8 бит, определяющих период ШИМ.
Третий байт содержит старшие 8 бит длительности импульса.
Кроме того, управляющая программа посылает назад в терминалку диагностические сообщения о выполнении некоторых действий, например, сразу после старта она посылает сообщение "Start", при записи полученных данных — "Save" и т.д.
-
Настройка ШИМ. Настройка ШИМ выполняется следующим образом:
а) В регистр PR2 (банк 1, смещение 12h) загружается период импульса;
б) В регистр CCPR1L (банк 0, смещение 15h) загружаются старшие 8 бит длительности импульса, а в регистр CCP1CON (банк 0, смещение 17h) загружаются младшие 2 бита длительности импульса {5-й и 4-й биты};
в) В регистр T2CON (банк 0, смещение 12h) загружаются настройки предделителя {1-й и 0-й биты}, и там же включается сам таймер {бит 2 устанавливается в 1}.
Коэффициент предделителя TMR2 в зависимости от настроек: 00 — 1:1, 01 — 1:4, 1x — 1:16.
г) 3-й и 2-й биты регистра CCP1CON (банк 0, смещение 17h) устанавливаются в 1.
Кроме этого, для работы вывода RB3/CCP1 в качестве выхода ШИМ, — бит направления TRISB{3} должен быть сброшен в ноль.
Период и длительность импульса генератора определяются по следующим формулам:
T=(PR2+1)*4*Tosc*коэффиц.преддел.TMR2
tи=CCPR1L:CCP1CON{5,4}*Tosc*коэффиц.преддел.TMR2,
где Tosc — тактовая частота контроллера.
-
Работа с EEPROM заключается в следующем: а) при включении питания, контроллер загружает конфигурацию из EEPROM (если она там есть); б) запись/стирание EEPROM в зависимости от состояния в принятой конфигурации командных битов.
Для того, чтобы не мучится с вычислениями, на традиционном уже C++ Builder 6 была написана прога, вычисляющая искомые три байта конфигурации контроллера по введённым параметрам генератора. Опять же традиционно, отдаю с исходниками: прога + исходники.
Итак, алгоритм работы управляющей программы:
![]() ![]() Здесь представлены только алгоритмы основной программы и подпрограммы обработки прерывания, поскольку алгоритм настройки и включения ШИМ достаточно подробно описан выше, а алгоритмы работы с EEPROM и UART стандартны и ранее уже не раз описывались на сайте. |
list p = 16f628a __config 2110h ;*** Переменные ****************************************** CBLOCK 0x20 ; Начальный адрес блока пользовательской памяти Rbyte ; Принятый байт Byte_Counter ; Счётчик ENDC ;----------------------------- CBLOCK 0x30 ; Блок принятых данных Regim ; режим работы 30h ; 7 - стир-е EEPROM, 6 - запись в EEPROM, ; 5,4 - младшие биты длит-ти, 3,2 - преддел. Period ; период 31h Dlit_imp ; длит-ть импульса 32h , старшие 8 бит длительности ENDC ;*** Константы / Адреса регистров ************************* Baudrate equ .12 ; скорость 19200 ;--------------------------------------------------------- STATUS equ 03h ; Регистр выбора банка PORTA equ 05h ; Порт А PORTB equ 06h ; Порт В TRISA equ 05h ; Конфигурация порта А (банк 1) TRISB equ 06h ; Конфигурация порта В (банк 1) CMCON equ 1Fh ; Управление компараторами INTCON equ 0Bh ; Управление прерываниями ;--------------------------------------------------------- PR2 equ 12h ; период Т2 (период ШИМ) (банк 1) CCPR1L equ 15h ; старшие 8 бит длительности импульса CCP1CON equ 17h ; биты 5,4 - младшие биты длит.имп-са, биты 3,2=1 - ШИМ режим T2CON equ 12h ; настр-ка предд-ля и вкл-е TMR2 ;--------------------------------------------------------- FSR equ 04h ; регистр адреса при косвенной адресации INDF equ 0h ; регистр косвенной адресации EEADR equ 1Bh ; адрес EEPROM EECON1 equ 1Ch ; управляющий регистр для работы c EEPROM EECON2 equ 1Dh ; управляющий регистр для работы c EEPROM EEDATA equ 1Ah ; регистр данных из/в EEPROM ;-------Регистры передатчика и приемника---------------------- TXREG equ 19h ; буфер передатчика RCREG equ 1Ah ; буфер приемника PIR1 equ 0Ch ; регистр флагов передатчика PIE1 equ 0Ch ; регистр разрешения/запрета прерываний передатчика TXSTA equ 18h ; конфигурация передатчика (1-й банк) RCSTA equ 18h ; конфигурация приемника SPBRG equ 19h ; настройка скорости ;******************************************************** org 0 goto start ;******************************************************** ;*** ОБРАБОТКА ПРЕРЫВАНИЯ ******************************* org 4 prog btfss PIR1,5 ; если 5-й бит PIR1 = 1, - в буфер пришли данные goto exit ;--- Принимаем данные -------------------- call zagruzka2 ;--- Проверяем, надо ли стирать EEPROM --- btfsc Regim,7 ; если 7-й бит Regim=1, то надо стирать EEPROM call clear_data ;------ Проверяем, надо ли записывать принятую инфу в EEPROM --- btfsc Regim,6 ; если 6-й бит Regim=1, то надо писать в EEPROM call save_data call start_shim ; загружаем новые данные в ШИМ exit retfie ;***** Конец обработчика прерывания ********************* ;******************************************************** ;******** НАЧАЛЬНАЯ ИНИЦИАЛИЗАЦИЯ *********************** ;--- Настройка портов -------------------- start movlw .7 movwf CMCON ; выключить компараторы clrf PORTA ; инициализация защелок порта А clrf PORTB ; инициализация защелок порта В bsf STATUS,5 ; Перейти в 1-й банк movlw .32 ; Записать конф-ю порта A в аккум-р (W). .32=00100000 movwf TRISA ; Скопировать конф-ю порта А из W в регистр TrisA movlw .6 ; Записать конф-ю порта В в аккум-р (W). .6=00000110 movwf TRISB ; Скопировать конф-ю порта B из W в регистр TrisB. bcf STATUS,5 ; Перейти в 0-й банк ;--- Настройка приемо-передатчика -------- bsf STATUS,5 ; перейти в первый банк movlw Baudrate ; загрузить устанавливаемую скорость в аккумулятор movwf SPBRG ; установить скорость movlw b'00100100' ; 8-разрядные данные, включить передачу, movwf TXSTA ; высокоскоростной асинхронный режим bcf PIE1, 4 ; запретить прерывания от передатчика USART (TXIE=0) bsf PIE1, 5 ; разрешить прерывания от приемника USART (RCIE=1) bcf STATUS,5 ; перейти в нулевой банк movlw b'10000000' ; 8-разрядные данные, выключить прием, movwf RCSTA ; включить модуль USART ;--- Загрузка данных из EEPROM в ОЗУ ----- call zagruzka ;--- Включение ШИМ, если есть данные ----- movlw 0FFh xorwf Regim,0 btfss STATUS,2 ; если Regim равно FF - пропустить call start_shim ; запускаем ШИМ ;--- Включить приёмник и разрешить прерывания от него --- bsf RCSTA,4 ; включаем приемник movlw b'11000000' ; GIE, PEIE - прерывания от периферии и глобальные iorwf INTCON,1 ;*** РАБОТА ********************************************* cikl nop goto cikl ;******************************************************** ;*** ПРОЦЕДУРЫ ****************************************** ;*** ЗАГРУЗКА ИЗ EEPROM в ОЗУ *************************** ;--- Загружаем данные zagruzka bsf STATUS,5 ; переходим в первый банк movlw .0 movwf EEADR ; читаем нулевой байт из EEPROM bsf EECON1,0 ; чтение movf EEDATA,0 ; запись прочитанного байта в аккумулятор bcf STATUS,5 ; Перейти в 0-й банк movwf Regim ; сохранение прочитанного байта в Regim bsf STATUS,5 ; переходим в первый банк incf EEADR,1 ; читаем первый байт из EEPROM bsf EECON1,0 ; чтение movf EEDATA,0 ; запись прочитанного байта в аккумулятор bcf STATUS,5 ; Перейти в 0-й банк movwf Period ; сохранение прочитанного байта в Period bsf STATUS,5 ; переходим в первый банк incf EEADR,1 ; читаем первый байт из EEPROM bsf EECON1,0 ; чтение movf EEDATA,0 ; запись прочитанного байта в аккумулятор bcf STATUS,5 ; Перейти в 0-й банк movwf Dlit_imp ; сохранение прочитанного байта в Dlit_imp ;--- Пишем "Load " movlw 4Ch ; шлём "L" call send_byte movlw 6Fh ; шлём "o" call send_byte movlw 61h ; шлём "a" call send_byte movlw 64h ; шлём "d" call send_byte movlw 20h ; шлём " " call send_byte return ;*** ПОСЫЛКА БАЙТА В ПОРТ ********************* send_byte movwf TXREG ; шлём байт из аккумулятора nop wait_tr btfss PIR1,4 ; ждём пока данные уйдут в порт (сразу после загрузки goto wait_tr ; TXREG бит проверять нельзя - может не успеть устан-ся) return ;*** Приём данных в ОЗУ *********************** zagruzka2 movf RCREG,0 ; читаем буфер приемника в аккумулятор movwf Regim ; и сохраняем в Size wait_msg1 btfss PIR1,5 goto wait_msg1 ; ждём приёма второго байта movf RCREG,0 ; читаем принятый байт в аккумулятор movwf Period ; и сохраняем в Period wait_msg2 btfss PIR1,5 goto wait_msg2 ; ждём приёма третьего байта movf RCREG,0 ; читаем принятый байт в аккумулятор movwf Dlit_imp ; и сохраняем в Dlit_imp ;--- пишем "Read" --- movlw 52h ; шлём "R" call send_byte movlw 65h ; шлём "e" call send_byte movlw 61h ; шлём "a" call send_byte movlw 64h ; шлём "d" call send_byte movlw 20h ; шлём " " call send_byte return ;*** Сохранение данных в EEPROM *************** ;--- Сохраняем данные --- save_data movlw Regim ; адрес начала записываемого буфера movwf FSR movlw .3 ; кол-во записываемых байт movwf Byte_Counter bsf STATUS,5 ; переходим в первый банк clrf EEADR ; установить адрес для записи в EEPROM bcf STATUS,5 zapis movf INDF,0 ; читаем байт из буфера bsf STATUS,5 movwf EEDATA ; помещаем его на запись bsf EECON1, 2 ; WREN=1 - разрешаем запись movlw 55h movwf EECON2 movlw 0AAh movwf EECON2 bsf EECON1,1 ; WR=1 - пишем bcf STATUS,5 wait_end btfss PIR1,7 ; если EEIF=1 - запись окончена goto wait_end bcf PIR1,7 ; сбросить флаг bsf STATUS,5 ; в первый банк incf EEADR,1 ; увеличить адрес EEPROM bcf STATUS,5 ; в нулевой банк incf FSR,1 ; увеличить адрес буфера decfsz Byte_Counter,1 ; если записали все байты - проп.след.команду goto zapis ;--- Пишем "Save " --- movlw 53h ; шлём "S" call send_byte movlw 61h ; шлём "a" call send_byte movlw 76h ; шлём "v" call send_byte movlw 65h ; шлём "e" call send_byte movlw 20h ; шлём " " call send_byte return ;*** Стирание EEPROM ********************************** ;--- Пишем в стираемые ячейки FF --- clear_data movlw .3 movwf Byte_Counter ; кол-во стираемых байт bsf STATUS,5 ; переходим в первый банк clrf EEADR ; установить адрес для записи в EEPROM bcf STATUS,5 clear bsf STATUS,5 movlw 0FFh movwf EEDATA ; помещаем на запись FFh bsf EECON1, 2 ; WREN=1 - разрешаем запись movlw 55h movwf EECON2 movlw 0AAh movwf EECON2 bsf EECON1,1 ; WR=1 - пишем incf EEADR,1 ; увеличить адрес EEPROM bcf STATUS,5 wait_clear btfss PIR1,7 ; если EEIF=1 - запись окончена goto wait_clear bcf PIR1,7 ; сбросить флаг decfsz Byte_Counter,1 ; если записали все байты - проп.след.команду goto clear ;--- Пишем "Clear " --- movlw 43h ; шлём "C" call send_byte movlw 6Ch ; шлём "l" call send_byte movlw 65h ; шлём "e" call send_byte movlw 61h ; шлём "a" call send_byte movlw 72h ; шлём "r" call send_byte movlw 20h ; шлём " " call send_byte return ;*** Включение ШИМ ************************************ start_shim movf Period,0 ; прочитать период ШИМ в аккумулятор bsf STATUS,5 ; перейти в первый банк movwf PR2 ; загрузить период в PR2 bcf STATUS,5 ; вернуться в нулевой банк movf Dlit_imp,0 movwf CCPR1L ; загрузить старшие 8 бит movf Regim,0 andlw b'00110000' ; выделить 5-й и 4-й биты длительности импульса iorwf CCP1CON,1 ; загрузить их в CCP1CON movf Regim,0 andlw b'00001100' ; выделить коэфф. предд-ля (3,2-й биты рег.Regim) movwf Byte_Counter ; загрузить полученное значение в Byte_counter rrf Byte_Counter,1 ; сдвинуть вправо на 1 бит bsf Byte_Counter,3 ; поднять 3-й бит rrf Byte_Counter,0 ; сдвинуть ещё на один бит вправо и загрузить в W movwf T2CON ; настроить предделитель и включить таймер movlw b'00001100' iorwf CCP1CON,1 ; включить ШИМ ;--- Пишем "Enable "--- movlw 45h ; шлём "E" call send_byte movlw 6Eh ; шлём "n" call send_byte movlw 61h ; шлём "a" call send_byte movlw 62h ; шлём "b" call send_byte movlw 6Ch ; шлём "l" call send_byte movlw 65h ; шлём "e" call send_byte movlw 20h ; шлём " " call send_byte return end ;--------------------------------------------------------- |
Данная программа — это простейший пример, в котором отсутствуют какие-либо возможности по управлению передачей данных, а также напрочь отсутствует такое понятие при работе с последовательным портом, как тайминги. То есть, получив первый байт, программа до упора будет ждать конца посылки, что не есть хорошо, но для примера вполне приемлемо.