|
PROCESSOR 16F84A
__CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC
#INCLUDE
;**********************************************************
; Проверка таблицы из RETLW, не попадает ли она на границу 256 слов.
; См. описание регистров PCL, PCLATH и особенности мат. операций с регистром PCL
CheckTable MACRO TableStart
IF HIGH(TableStart) != high($)
ERROR "TABLE ON PAGE BOUNDARY"
ENDIF
ENDM
;**********************************************************
; Короткая запись флагов регистра STATUS, чтоб не наделать лишних ошибок
#define _Z STATUS,Z
#define _C STATUS,C
;**********************************************************
; Макросы работы с битами регистров, в том числе и ножками портов
; Например, если ножка описана как #define TEST PORTA,0,ActiveHigh
; то макрос ON TEST развернется в команду BSF PORTA,0
; а если ножка описана как #define TEST PORTA,0,ActiveLow
; то макрос ON TEST развернется в команду BСF PORTA,0#define ActiveLow 0
#define ActiveHigh 1
;----------------------------------
; "Включить", установить в активное состояие
ON MACRO Port,Bit,Level
#IF Level == ActiveLow
BCF Port,Bit
#else
BSF Port,Bit
#endif
ENDM
;----------------------------------
; "Выключить", установить в неактивное состояие
OFF MACRO Port,Bit,Level
#IF Level == ActiveLow
BSF Port,Bit
#else
BCF Port,Bit
#endif
ENDM
;----------------------------------
; "Переключить", установить в противоположное состояие
CPL MACRO Port,Bit,Level
MOVLW (1<XORWF Port
ENDM
;----------------------------------
; Пропустить следующую команду, если вывод в активном состояии
SkipIfOn MACRO Port,Bit,Level
#IF Level == ActiveLow
BTFSC Port,Bit
#else
BTFSS Port,Bit
#endif
ENDM
;----------------------------------
; Пропустить следующую команду, если вывод в неактивном состояии
SkipIfOff MACRO Port,Bit,Level
#IF Level == ActiveLow
BTFSS Port,Bit
#else
BTFSC Port,Bit
#endif
ENDM
;**********************************************************
;**********************************************************
;********* UART Macros ************************************
;**********************************************************
;**********************************************************
; UART data block
CBLOCK 0 ; Присвоить следующим символическим именам номера подряд
; начиная с 0. Это будут смещения переменных относительно
; начала блока памяти в ОЗУState ; Состояние конечного автомата
Buffer ; буфер передаваемого/принимаемого символа
QPtrHead ; указатель на начало очереди.
QPtrTail ; указатель на конец очереди.
Queue:QueueSize ; Очередь, QueueSize байтов
UARTDataBlock ; Фиктивная переменная, равна количеству байтов в блоке
ENDCTxHandler MACRO Module, Port, Pin, ActiveLevel ; Макрос обработчика прерывания для передатчика
; Параметры: Module - начало блока данных в ОЗУ
; Port, Pin, ActivityLevel - Относится к ножке передатчика,
; см. макросы ON, OFF, SkipIfOn, SkipIfOffLOCAL NextState,JumpTable ; Локальные метки в макросе
LOCAL TxCheckBuffer,TxBit,TxStop ; -- // --
LOCAL NextState,Done ; -- // --
BTFSC Module + State,0 ; Если номер состояния конечного автомата нечетный,
GOTO NextState ; ничего не делать - это середина передаваемого бита.
; В противном случае обработчик соответствующего состоянияMOVLW HIGH(JumpTable) ; Загрузка старшего байта адреса таблицы переходов
MOVWF PCLATH ; в PCLATH
RRF Module + State,W ; W = State / 2, Вычисление смещения в таблице переходов
ANDLW 0xF ; На всякий случай сбросить незначащие старшие биты -
; в таблице 16 перехов, значит используются биты 3..0
ADDWF PCL ; Прыжок в нужное место таблицы переходовJumpTable ; Таблица переходов. Эта метка используется дальше для
; проверки, см. описание макроса CheckTable
GOTO TxCheckBuffer ; Состояние 0, ожидание, проверить, нет ли байтов в очереди на передачу,
; если есть, начать передачу.
GOTO TxBit ; Состояние 2, начать передачу бита 0 (младшего)
GOTO TxBit ; Состояние 4, начать передачу бита 1
GOTO TxBit ; Состояние 6, начать передачу бита 2
GOTO TxBit ; Состояние 8, начать передачу бита 3
GOTO TxBit ; Состояние 10, начать передачу бита 4
GOTO TxBit ; Состояние 12, начать передачу бита 5
GOTO TxBit ; Состояние 14, начать передачу бита 6
GOTO TxBit ; Состояние 16, начать передачу бита 7
GOTO TxStop ; Состояние 18, начать передачу стопового бита
CLRF Module + State ; Состояние 20, окончена передача стопового бита. Сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 22, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 24, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 26, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 28, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 30, но если уж в него попали, сбросить конечный автомат
CheckTable JumpTable ; Проверка, не попала ли таблица переходов на границу 256 словTxCheckBuffer ; Проверка, нет ли байтов в очереди на передачу
; Если есть, начать передачу.MOVFW Module + QPtrHead ; Загрузить указатель на первый байт в очереди
XORWF Module + QPtrTail,W ; Сравнить с указателем на последний байт
ANDLW QueueSize-1 ; Обнулить незначащие биты (вот для этой команды требуется,
; чтобы размер очереди был степенью двойки)
BTFSC _Z ; Если они равны, очередь пуста
GOTO Done ; и значит передавать нечегоMOVFW Module + QPtrTail ; Взять указатель на конец очереди (самый старый байт)
ANDLW QueueSize-1 ; Обнулить незначащие биты
ADDLW Module + Queue ; Добавить указатель к началу очереди, получить указатель на байтMOVWF FSR ; Указатель - в регистр косвенной адресации
MOVFW INDF ; Взять байт из очереди
MOVWF Module+Buffer ; и положит его в буфер передачи
INCF Module + QPtrTail ; Увеличить указатель на конец очереди; Начинаем передачу стартового бита
OFF Port, Pin, ActiveLevel ; Перевести ножку передатчика в неактивное состояние
; (Стартовый бит = 0)
GOTO NextState ; Перевести конечный автомат в следующее состояниеTxBit ; Начать передачу очередного информационного бита
RRF Module + Buffer ; Сдвинуть буфер. Очередной бит попадет в флаг _CBTFSS _C ; Если передаваемый бит не единица
OFF Port, Pin, ActiveLevel ; Перевести ножку передатчика в неактивное состояние
BTFSC _C ; Если передаваемый бит не ноль
TxStop ; Стоповый бит равен единице
ON Port, Pin, ActiveLevel ; Перевести ножку передатчика в активное состояние
NextState
INCF Module + State ; Перевести конечный автомат в следующее состояние
Done
ENDM;/*******************************************************************/
RxHandler MACRO Module, Port, Pin, ActiveLevel ; Макрос обработчика прерывания для приемника
; Параметры такие же как и для макроса обработчика передатчикаLOCAL NextState,JumpTable ; Локальные метки в макросе
LOCAL RxIdle,RxBit,RxRestart ; -- // --
LOCAL NextState,Done ; -- // --
BTFSC Module+State,0 ; Если номер состояния конечного автомата нечетный,
GOTO NextState ; ничего не делать - это где-то около перехода одного
; принимаемого бита в другой.
; В противном случае обработчик соответствующего состояния
MOVLW HIGH(JumpTable) ; см. описание макроса обработчика передатчика.
MOVWF PCLATH
RRF Module + State,W ; W = State / 2
ANDLW 0x0F
ADDWF PCLJumpTable
GOTO RxCheckIdle ; Состояние 0. Убедиться, что сейчас пауза между байтами
; (или стоп-бит предыдущего байта)
GOTO RxWaitStart ; Состояние 2: Ожидание стартового бита
GOTO RxBit ; Состояние 4: Прием бита 0 (младшего)
GOTO RxBit ; Состояние 6: Прием бита 1
GOTO RxBit ; Состояние 8: Прием бита 2
GOTO RxBit ; Состояние 10: Прием бита 3
GOTO RxBit ; Состояние 12: Прием бита 4
GOTO RxBit ; Состояние 14: Прием бита 5
GOTO RxBit ; Состояние 16: Прием бита 6
GOTO RxBit ; Состояние 18: Прием бита 7
GOTO RxStop ; Состояние 20: Проверка стопового бита
CLRF Module + State ; Невозможное состояние 22, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 24, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 26, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 28, но если уж в него попали, сбросить конечный автомат
CLRF Module + State ; Невозможное состояние 30, но если уж в него попали, сбросить конечный автомат
CheckTable JumpTable ; Проверка, не попала ли таблица переходов на границу 256 слов
RxCheckIdle
MOVLW 2 ; Если все получится, то попасть сразу в состояние 2
; (пропустив нечетное 1)
SkipIfOn Port, Pin, ActiveLevel ; Если линия в активном состоянии (т.е. пауза или стоп-бит)
ADDWF Module + State ; То переключить автомат в состояние 2
GOTO DoneRxWaitStart ; Ожидание стартового бита.
SkipIfOff Port, Pin, ActiveLevel ; Если линия в неактивном состоянии, пропустить след. команду
GOTO Done ; Линия все еще в состоянии "пауза", ждать дальше
NextState
INCF Module + State ; Переключить конечный автомат в следующее состояние
GOTO DoneRxBit ; Прием бита
BCF _C ; Сбросить флаг _С
SkipIfOff Port, Pin, ActiveLevel ; Если линия в неактивном состоянии, _С не изменится
BSF _C ; В противном случае взвести _СRRF Module + Buffer ; Задвинуть флаг _С в регистр приема.
GOTO NextState ; Переключить конечный автомат в следующее состояниеRxStop
SkipIfOn Port, Pin, ActiveLevel ; Проверить, действительно ли Стоповый бит принят как единица
GOTO RxRestart ; Ошибка приема, принятый байт отброситьMOVFW Module + QPtrHead ; Взять указатель на первый байт очереди
ANDLW QueueSize-1 ; Обнулить незначащие биты
ADDLW Module + Queue ; Добавить указатель к началу очереди, получить указатель на байтMOVWF FSR ; Указатель - в регистр косвенной адресации
MOVFW Module + Buffer ; Взять байт из приемного буфера
MOVWF INDF ; И положить его в начало очереди
INCF Module + QPtrHead ; Увеличить указатель на конец очередиRxRestart ; Что-то случилось неправильное, сбросить конечный автомат
CLRF Module + State ; Сбросить конечный автомат
Done
ENDM;/*******************************************************************/
SendByte MACRO Module ; Кладет байт из W в очереть UART
; Параметры: Module - начало блока данных в ОЗУLOCAL WaitQueue ; Локальная метка макроса
MOVWF FSR ; Временно положить байт в FSR
WaitQueue
MOVFW Module + QPtrHead ; взять указатель на начало очереди
ADDLW 1 ; Увеличить указатель
XORWF Module + QPtrTail,W ; Сравнить с указателем на конец очереди
ANDLW QueueSize-1 ; сбросить в результате сравнения незначащие биты
BTFSC _Z ; Если указатели совпали, выставится флаг _Z
GOTO WaitQueue ; Значит очередь заполнена, надо ждатьMOVFW Module + QPtrHead ; взять указатель на начало очереди
ANDLW QueueSize-1 ; Обнулить незначащие биты
ADDLW Module + Queue ; Добавить указатель к началу очереди, получить указатель на
; очередную свободную ячейку в очередиxcgwf FSR ; Обменять значения в W и FSR
; В результате в W оказался байт, а в FSR - адрес ячейки в очередиMOVWF INDF ; Положить байт в очередь
INCF Module + QPtrHead ; Увеличить указатель на начало очереди
RETURN ; Вернуться.
ENDM
;/*******************************************************************/
GetByte MACRO Module ; Берет байт из очереди UART. Если очередь пуста, возвращает
; флаг _Z
; Параметры: Module - начало блока данных в ОЗУMOVFW Module + QPtrHead ; Загрузить указатель на первый байт в очереди
XORWF Module + QPtrTail,W ; Сравнить с указателем на последний байт
BTFSC _Z ; Если они равны, флаг _Z выставлен, очередь пуста
RETURN ; ВернутьсяMOVFW Module + QPtrTail ; Взять указатель на конец очереди (самый старый байт)
ANDLW QueueSize-1 ; Обнулить незначащие биты
ADDLW Module + Queue ; Добавить указатель к началу очереди, получить указатель на байтMOVWF FSR ; Указатель - в регистр косвенной адресации
MOVFW INDF ; Взять байт из очереди
MOVWF Module+Buffer ; и положит его в буфер передачи
INCF Module + QPtrTail ; Увеличить указатель на конец очереди
BCF _Z ; Сбросить _Z - возвращаем байт
RETURN ; Вернуться
ENDM
;****************************************************
;******СОБСТВЕННО ПРОГА *****************************
;****************************************************
#define OSC 16000000 ; Частота кварца
#define BaudRate 9600 ; Скорость RS-232
#define CLK (OSC/4) ; Тактовая частота процессора (1 команда за 4 цикла)
#define TimerPeriod (CLK/(BaudRate*2)) ; Период вызова прерывания таймера - 2 прерывания за бит
#define QueueSize 4 ; Длина буферов приема и передачи (байт). Должна быть степенью
; двойки для упрощения программы; Описание выводов для одного варианта платы
RAMSTART equ 0x0C ; Начало ОЗУ PIC16F84A. Начиная с этого адреса будут размещаться
; переменные.
RAMEND equ 0x50 ; Конец ОЗУ PIC16F84A. Используется при проверке "Хватило ли
; памяти"#define _Tx 3 ; Номер бита ножки передатчика UART
#define _Rx 2 ; Номер бита ножки приемника UART#define TxPin PORTB, _Tx, ActiveHigh ; Передатчик будет на порту B, единица соответствует высокому
; уровню
#define RxPin PORTB, _Rx, ActiveHigh ; То же самое для приемника#define PORTA_Init 0 ; Выдавать 0 на все ножки
#define TRISA_Init 0 ; Все ножки прота А настроить на вывод
#define PORTB_Init (1<<_Tx) | (0<<_Rx) ; Выдавать 1 на выход передачи UART - уровень паузы в передачах
#define TRISB_Init (0<<_Tx) | (1<<_Rx) ; Ножку приема настроить на ввод, остальные - на выводCBLOCK RAMSTART ; Разместить переменные с адреса начала ОЗУ
WSave, StatusSave, PCLATHSave, FSRSave ; Сюда сохраняются важные регистры при входе в прерываниеTx:UARTDataBlock ; Зарезервировать UARTDataBlock байтов для передатчика UART, блок данных Tx
Rx:UARTDataBlock ; Зарезервировать UARTDataBlock байтов для приемника UART, блок данных RxENDC
org 0x0000
GOTO startorg 0x0004
Interupt:
; ** Сохранение регистров, которые нужны в прерывании **
MOVWF WSave
SWAPF WSave ; SWAPF нужен, чтобы не испортить флаг _Z при
; восстановлении значений при выходе из прерывания
SWAPF STATUS,W
SETBANK 0
MOVWF StatusSave
MOVFW PCLATH
MOVWF PCLATHSave
MOVFW FSR
MOVWF FSRSaveCLRWDT
; Поскольку используется только одно прерывание - от таймера,
; то проверка флагов и битов разрешения прерываний не требуется.BCF INTCON,T0IF ; Сбросить флаг переполнения таймера, чтобы не зациклиться в прерывании
MOVLW LOW(-(TimerPeriod)) ; Добавить к таймеру (256 - TimerPeriod), чтобы он досчитал до 256 за
; TimerPeriod тактов
ADDWF TMR0;*********** Tx routine ***********
TxHandler Tx, TxPin ; Обработчик прерывания передатчика UART, использовать блок данных Tx и
; ножку TxPin
;*********** Rx routine ***********
RxHandler Rx, RxPin ; Обработчик прерывания приемника UART, использовать блок данных Rx и
; ножку RxPin
CPL TESTPin ; Переключить ножку TEST. По ней можно осциллографом наблюдать,
; что прерывание вызывается
IntExit: ; * Восстановление регистров, которые сохранены перед входом впрерывание *
MOVFW FSRSave
MOVWF FSR
MOVFW PCLATHSave
MOVWF PCLATH
SWAPF StatusSave,W
MOVWF STATUS
SWAPF WSave,W
RETFIEstart ; Собственно, начало программы
; Очистка ОЗУ
MOVLW RAMSTART
MOVWF FSR
RAMClearLoop
CLRF INDF
INCF FSR
MOVFW FSR
SUBLW RAMEND
BTFSS _Z
GOTO RAMClearLoop; Настройка портов и внутренней периферии
MOVLW PORTB_Init
MOVWF PORTB
MOVLW PORTA_Init
MOVWF PORTA
SETBANK 1
ERRORLEVEL -302 ; Отключить предупреждение, что идет обращение к регистрам в банке 1
; Я уверен, потому что только что включил нужный банк.
MOVLW TRISA_Init
MOVWF TRISA
MOVLW TRISB_Init
MOVWF TRISB
MOVLW (0<MOVWF OPTION_REG ERRORLEVEL +302 ; Включить обратно предупреждение о обращении к банку 1
SETBANK 0
MOVLW (1<MOVWF INTCON ; Послать маленькое приветствие
putc "П" ; см. описание макроса putc
putc "р"
putc "и"
putc "в"
putc "е"
putc "т"
putc " "
putc "и"
putc "з"
putc " "
putc "Р"
putc "и"
putc "г"
putc "и"
putc "!"
putc_CR ; см. описание макроса putc_CR
MainLoop
CALL getchar ; Считать принятый байт в W
BTFSS _Z ; Если _Z, то пока ничего не принято
CALL putchar ; А если было принято, то отослать принятый байт обратно.GOTO MainLoop ; И повторять до бесконечности.
getchar ; функция чтения принятого байта
GetByte Rx ; Прочитать байт из буфера UART, использовать блок данных Rxputchar ; функция чтения принятого байта
SendByte Tx ; Положить байт в буфер UART, использовать блок данных Tx; ----------------------------------------------- Конец, дальше проверки на этапе компиляции
CBLOCK
check_ram ; присвоить check_ram очередной адрес в ОЗУ
ENDC#if check_ram >= RAMEND ; проверить, не попала ли check_ram за пределы ОЗУ
ERROR "Too many variables" ; "Звиняйте, памяти не хватило"
#endifEND
E-mail: info@telesys.ru