Спойлер
Код: Выделить всё
list p=16f629
__config _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_ON & _PWRTE_ON & _WDTE_OFF & _FOSC_INTRCIO
include p12f629.inc
#define RX_ 2
#define TX_ 1
#define RX GPIO,RX_ ; В качестве приемника может быть только эта ножка
#define TX GPIO,TX_ ; А вот в качестве передатчика можем назначить любую, даже ту же
#define BITS_DELAY .30 ; Задержка длинной 1 бит - 104 мкс при 9600 бод и частоте 4 МГц
#define RX_Complete 0 ; Флаг завершения приема
; В прерывании только прием байта (побитно), и выставление флага окончания приема, который уже анализируется в основном цикле
;======================
Soft_UART udata_shr
w_temp res 1
status_temp res 1
TX_byte res 1
RX_byte res 1
UART_counter res 1
delay_counter res 1
Flags res 1
;======================
push macro
; movwf w_temp
; swapf STATUS,w
; movwf status_temp
movwf w_temp ; save off current W register contents
movfw STATUS ; move status register into W register
movwf status_temp ; save off contents of STATUS register
endm
pop macro
; swapf status_temp,w
; movwf STATUS
; swapf w_temp,f
; swapf w_temp,w
movfw status_temp ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
endm
;======================
RESET_VECTOR code 0h
goto Ini
;======================
ISR code 4h ; Вектор прерывания
Interrupt_Heandler
push ; Сохраним контекст
btfsc INTCON,INTF
goto Start_bit ; Если есть флаг внешнего прерывания - пришел старт-бит
btfsc INTCON,T0IF
goto Receive_bit ; Если флаг таймера, значит принимаем очередной бит посылки
End_Interrupt_Heandler
pop ; Восстановим контекст
retfie
;======================
Start_bit
bcf INTCON,INTF ; Сбросим флаг внешнего прерывания
movlw .191 ; Перезапишем таймер для периода переполнения в 156 мкс (середина 1 бита данных)
movwf TMR0
movlw .8 ; Загрузим количество принимаемых битов
movwf UART_counter
clrf RX_byte ; Очистим приемник
bcf INTCON,INTE ; Запрещаем внешнее прерывание (4 бит)
bsf INTCON,T0IE ; Разрешаем прерывание по переполнению Таймера 0 (5 бит)
bcf INTCON,T0IF ; Сбросим флаг переполнения Таймера 0, он почти наверняка переполнился, пока мы ждали старт-бит
goto End_Interrupt_Heandler
;======================
Receive_bit
movlw .211 ; Перезапишем таймер для периода переполнения в 104 мкс
movwf TMR0
bcf INTCON,T0IF ; Сбросим флаг переполнения Таймера 0
bcf STATUS,C ; Сбросим бит переноса
rrf RX_byte ; Задвигаем сброшеный бит переноса в приемный регистр
btfsc RX ; А теперь проверяем, что на самом деле надо было туда задвинуть
bsf RX_byte,7 ; Если 1, надо срочно подкорректировать, ведь мы задвинули 0
decfsz UART_counter ; Весь байт принят ?
goto End_Interrupt_Heandler ; Нет, идем принимать следующий бит
bsf Flags,RX_Complete ; Поставим флажок окончания приема байта
bcf INTCON,T0IE ; Запрещаем прерывание по переполнению Таймера 0 (5 бит)
bsf INTCON,INTE ; Разрешаем внешнее прерывание (4 бит) для приема следующего старт-бита
goto End_Interrupt_Heandler ; На выход
;======================
Ini
clrf Flags ; Сбросим все флаги
movlw b'111111' ; Заранее установим необходимые уровни на выходах, чтобы в момент переключения с входов на выходы му уже имели на них нужные значения
movwf GPIO ; Init GPIO
banksel OSCCAL ; Bank 1
call 3FFh
movwf OSCCAL
if RX_ != TX_
bcf TX ; Настроим TX на выход
endif
movlw b'11000000' ; Подтягивающие резисторы включены, внешнее прерывание по переднему фронту, тактирование Таймера 0 от внутреннего генератора, пределитель 1 к 2, что дает 512 мкс на частоте 4 Мгц
movwf OPTION_REG
banksel GPIO ; Bank 0
movlw b'00000111'
movwf CMCON ; Выключим аналоговый компаратор
movlw b'10010000' ; Разрешаем прерывания вообще (7 бит) и внешнее прерывание (4 бит)
movwf INTCON
;======================
; movlw 'A'
; call Send_TX_byte
Main
; goto Main
btfss Flags,RX_Complete ; Анализируем флажок приема байта
goto Main ; Нет флажка - нет принятого байта
movfw RX_byte ; Засунем принятый байт в рабочий регистр
call Send_TX_byte ; И отправим назад
bcf Flags,RX_Complete ; Скинем флажок принятого байта
goto Main
;======================
Send_TX_byte ; Подпрограмма передачи 1 байта
bcf INTCON,GIE ; Запрещаем прерывания
if RX_ == TX_
banksel TRISIO ; Bank 1
bcf TX ; Настроим TX на выход
banksel GPIO ; Bank 0
endif
;-----------------------------------------------------------------------------------------------
bcf TX ; Шлем старт бит
movwf TX_byte ; Загрузим передавемый байт
movlw .8 ; Загрузим количесвто передаваемых битов
movwf UART_counter
nop
Transmit_Next_Bit
nop
call Delay_1bit ; Длинной 1 бит
rrf TX_byte ; Продвигаем в бит переноса все биты, начиная с младшего
btfsc STATUS,C ; Проверяем, что выдавилось в бит переноса
bsf TX ; Если 1, выставляем 1 на ТХ
btfss STATUS,C
bcf TX ; Если 0, выставляем 0 на ТХ
decfsz UART_counter ; Весь байт отослан ?
goto Transmit_Next_Bit ; Нет, идем слать следующий бит
call Delay_1bit ; Задержка длинной 1 бит
nop
nop
nop
nop
nop
bsf TX ; Шлем стоп-бит
call Delay_1bit ; Задержка длинной 1 бит
;-----------------------------------------------------------------------------------------------
if RX_ == TX_
banksel TRISIO ; Bank 1
bsf TX ; Настроим ножку на вход
banksel GPIO ; Bank 0
endif
bcf INTCON,T0IF ; Сбросим флаг переполнения Таймера 0
bcf INTCON,INTF ; Сбросим флаг внешнего прерывания
bsf INTCON,GIE ; Разрешаем прерывания
return
;======================
Delay_1bit
movlw BITS_DELAY ; 104 мкс для 9600 бод
movwf delay_counter
Loop_Delay
decfsz delay_counter
goto Loop_Delay
return
end


