Разбирайся...
(«Телесистемы»: Конференция «Микроконтроллеры и их применение»)

миниатюрный аудио-видеорекордер mAVR

Отправлено Сергей Борщ 28 декабря 2004 г. 22:54
В ответ на: для 16 пика подскажите программный UART по прерыванию на асме отправлено rq 28 декабря 2004 г. 20:32


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 ; Фиктивная переменная, равна количеству байтов в блоке
ENDC

TxHandler MACRO Module, Port, Pin, ActiveLevel ; Макрос обработчика прерывания для передатчика
; Параметры: Module - начало блока данных в ОЗУ
; Port, Pin, ActivityLevel - Относится к ножке передатчика,
; см. макросы ON, OFF, SkipIfOn, SkipIfOff

LOCAL 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 ; Сдвинуть буфер. Очередной бит попадет в флаг _C

BTFSS _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 PCL

JumpTable
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 Done

RxWaitStart ; Ожидание стартового бита.
SkipIfOff Port, Pin, ActiveLevel ; Если линия в неактивном состоянии, пропустить след. команду
GOTO Done ; Линия все еще в состоянии "пауза", ждать дальше
NextState
INCF Module + State ; Переключить конечный автомат в следующее состояние
GOTO Done

RxBit ; Прием бита

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, блок данных Rx

ENDC


org 0x0000
GOTO start

org 0x0004

Interupt:
; ** Сохранение регистров, которые нужны в прерывании **
MOVWF WSave
SWAPF WSave ; SWAPF нужен, чтобы не испортить флаг _Z при
; восстановлении значений при выходе из прерывания
SWAPF STATUS,W
SETBANK 0
MOVWF StatusSave
MOVFW PCLATH
MOVWF PCLATHSave
MOVFW FSR
MOVWF FSRSave

CLRWDT
; Поскольку используется только одно прерывание - от таймера,
; то проверка флагов и битов разрешения прерываний не требуется.

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
RETFIE

start ; Собственно, начало программы

; Очистка ОЗУ
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, использовать блок данных Rx

putchar ; функция чтения принятого байта
SendByte Tx ; Положить байт в буфер UART, использовать блок данных Tx

; ----------------------------------------------- Конец, дальше проверки на этапе компиляции

CBLOCK
check_ram ; присвоить check_ram очередной адрес в ОЗУ
ENDC

#if check_ram >= RAMEND ; проверить, не попала ли check_ram за пределы ОЗУ
ERROR "Too many variables" ; "Звиняйте, памяти не хватило"
#endif

END


Составить ответ  |||  Конференция  |||  Архив

Ответы



Перейти к списку ответов  |||  Конференция  |||  Архив  |||  Главная страница  |||  Содержание  |||  Без кадра

E-mail: info@telesys.ru