Код
'###############################################################################
'
'
' Библиотека обработки приема данных с кодированием Manchester
'
' В библиотеке задействованы ресурсы MK:
' * Таймер T0 (для подсчета длительности импульсов мanchester сигнала)
' * Линия внешнего прерывания Int0 (вход сигнала)
'
' Ресурсы контроля и управления:
' * Transmit - бит флаг активности режима передачи пакета (1=идет передача)
' * Man_in_line - порт приема должен быть порт INT0 (Man_in_line Alias Pin_x.x)
' * Man_out_line - порт отправки (Man_out_line Alias Port_х.х)
' * Rcvdata_check (возвращает количество принятых байт) - функция проверки наличии данных
' * Transmit_start (количество отправляемых байт) - программа отправки данных из буффера Manbuffer ()
' * Receive_start - программа инициализации и старта приема данных, по приему пакета останавливается.
'
'
' Programmed By Yuriy.pv
'
'
'###############################################################################
'Const $crystal = 8000000 'Частота МК
Const Man_speed = 1000 '800-4000 бит\сек - частота Manchester cигнала
Const Man_period_len = 125 '$crystal / 64 / MAN_SPEED - длит периода MANCHESTER сигнала (в тиках таймера)
Const Period_1_2 = 194 'Period 1/2 = 256-(Man_period_len/2)
Const Period_2_3 = 83 'Period 2/3 = Man_period_len*2/3
Const Period_3_4 = 163 'Period 3/4 = 256-(Man_period_len*3/4)
Const Man_pilot_len = 1 '1-16 бит. Длина пилотного сигнала
Const Man_buf_length = 2 '1-255 байта, размер буфера данных
Const Man_identifier_length = 2 '1-16 байта, размер идентификатора пакета
Dim Man_identifier As String * Man_identifier_length
Dim Manidentifier(man_identifier_length) As Byte At Man_identifier Overlay 'идентификационный заголовок сообщения
Man_identifier = "sh" '1-16 латинских символов должно = Man_identifier_length. строковый - идентификатор пакета
Dim Manbuffer(man_buf_length) As Byte 'буфер для накопления принимаемых Manchester данных
Dim Checksumm_byte As Byte 'байт подсчета CRC
Dim Bit_counter As Byte 'счетчик количества принятых бит
Dim Byte_counter As Byte 'счетчик принимаемых байтов
Dim Byte_in_out As Byte 'накопитель принимаемого \ контейнер передаваемого - байта
Dim Data_length As Byte 'длина блока данных в принимаемой\передаваемой серии (может быть <= MAN_BUF_LENGTH)
Dim Manflags_temp As Byte
Dim Manflags As Byte 'байт флагов
Data_enbl Alias Manflags.0 'флаг наличия в буфере принятых данных
Tim0_ovf Alias Manflags.1 'флаг наличия переполнения Т0
Flag_pilot Alias Manflags.2 'флаг окончания пилотного сигнала
Header_rcv Alias Manflags.3 'флаг приема заголовка
Line_inv Alias Manflags.4 'флаг необходимости инверсии сигнала в линии
Transmit_stop Alias Manflags.5 'флаг завершения передачи
Bit_part_num Alias Manflags.6 'флаг младшей\старшей половины бита при передаче
Transmit Alias Manflags.7 'флаг активности режима передачи пакета
Dim Timer_val As Byte 'значение таймера
Dim Temp_lib As Byte
'Dim Byte_ As Byte
'######################### Функции и подпрограммы ##############################
Declare Sub Init_
Declare Sub Receive_start
Declare Sub Receive_stop
Declare Sub Checksumm(byval Data_ As Byte)
Declare Sub Transmit_start(byval Count As Byte)
Declare Function Rcvdata_check As Byte
Config Pind.2 = Input : Man_in_line Alias Pind.2 'порт приема (должен быть порт INT0)
Config Portd.3 = Output : Man_out_line Alias Portd.3
'########################### Главный цикл ####################################
Dim Temp As Byte
Dim Tempw As Word
Print "Test Manchester"
Call Init_
Call Receive_start
Do
Tempw = Rcvdata_check()
If Tempw > 0 Then
' Set Led
For Temp = 1 To Tempw
Print "Rcvdata_check() = 1 data out=" ; Manbuffer(temp)
Next
' Reset Led
Call Receive_start
End If
Loop
'###############################################################################
Sub Init_ 'Инициализация
Disable Interrupts
Config Timer0 = Timer , Prescale = 64 'предделитель на 64 (частота счета 8000000 / 64 = 125000 Hz)
On Timer0 Timer_ovf_vector: 'вектор прерывания при переполнении
Enable Timer0
Config Int0 = Change 'прерывание по любому изменению уровня INT0
On Int0 Int0_vector: 'вектор прерывания INT0
Enable Interrupts
End Sub
Sub Receive_start 'Инициализация и старт приема данных
Transmit = 0
Data_enbl = 0 'очистить флаг наличия данных
Tim0_ovf = 0 'и флаг переполнения
Header_rcv = 1 'включить режим приема заголовка
Byte_counter = 0 'начать прием с начала
Byte_in_out = 0 'очистить байт приемник
Start Timer0
Enable Timer0
Gifr.6 = 1 'сбросить возможно проскочившее прерывание
Enable Int0
End Sub
Sub Receive_stop 'Останов приема данных
Stop Timer0 'выключить прерывание при переполнении Т0
Disable Int0 'выключить внешнее прерывание от INT0
End Sub
Function Rcvdata_check 'Проверка наличия Manchester данных в буфере
If Data_enbl = 1 Then 'проверка наличия принятых данных если есть,
Data_enbl = 0 'очистить флаг наличия данных
Rcvdata_check = Data_length 'при наличии данных - возвращаем количество принятых байт
Else
Rcvdata_check = 0
End If
End Function
Sub Transmit_start(byval Count As Byte)
Call Receive_stop()
Checksumm_byte = 0
Call Checksumm(count) 'подсчет контрольной суммы пакета для передачи
For Temp_lib = 1 To Count
Call Checksumm(manbuffer(temp_lib)) 'подсчет контрольки пакета
Next
Byte_in_out = 128 'для передачи пилот сигнала
Byte_counter = 0 'до 0 - передача пилота, а от 0 до sizeof(MAN_IDENTIFIER) - передача идентификатора
Data_length = Count 'передать столько байт из буфера
Flag_pilot = 0
Bit_part_num = 0 : Transmit_stop = 0 'сбросить флаги
Transmit = 1 : Header_rcv = 1 'включить режим передачи заголовка
Start Timer0 'включить "прерывание при переполнении Т0"
End Sub
Sub Checksumm(byval Data_ As Byte)
'CheckSummByte - глобальная переменная контрольной суммы
'в начале обмена необходимо обнулить CheckSummByte
Local Temp As Byte
Local I As Byte
For I = 1 To 7
Temp = Data_
Temp = Temp Xor Checksumm_byte
If Temp.0 = 1 Then
Checksumm_byte = Checksumm_byte Xor 24
Temp = 128
Else
Temp = 0
End If
Shift Checksumm_byte , Right
Checksumm_byte = Checksumm_byte Or Temp
Shift Data_ , Right
Next
End Sub
Timer_ovf_vector: 'Обработка прерывания при переполнении TIMER0
'Byte_ = Byte_in_out
'Если включена передача
If Transmit = 1 Then
Timer0 = Period_1_2
'начало передачи первой чсти бита
If Bit_part_num = 0 Then
If Byte_in_out.7 = 1 Then
Man_out_line = 0
Else
Man_out_line = 1
End If
If Transmit_stop = 1 Then
Stop Timer0
Transmit = 0
End If
Else
'Начало передачи второй части бита
If Byte_in_out.7 = 1 Then
Man_out_line = 1
Else
Man_out_line = 0
End If
Shift Byte_in_out , Left 'сдвигаем биты в передаваемом байте
Incr Bit_counter
'Если это пилот или заголовок
If Header_rcv = 1 Then
'Если это пилот
If Byte_counter < Man_pilot_len And Flag_pilot = 0 Then
Byte_in_out = 128 'для передачи бита 1
Incr Byte_counter
If Byte_counter = Man_pilot_len Then
Byte_in_out = Manidentifier(1)
Bit_counter = 0 'счетчик переданных бит
Flag_pilot = 1
Byte_counter = 1
End If
'Если это заголовок
Elseif Bit_counter = 8 Then 'если переданы все биты байта
Bit_counter = 0
If Byte_counter = Man_identifier_length Then 'если ноль - значит конец строки идентификатора
Header_rcv = 0
Byte_counter = 0
Byte_in_out = Data_length
Else
Incr Byte_counter
Byte_in_out = Manidentifier(byte_counter)
End If
End If
'Если это передача блока данных
Elseif Bit_counter = 8 Then 'если переданы все биты байта
Bit_counter = 0
If Byte_counter < Data_length Then 'передаем основной блок данных
Incr Byte_counter
Byte_in_out = Manbuffer(byte_counter)
Elseif Byte_counter = Data_length Then 'передаем контрольную сумму
Byte_in_out = Checksumm_byte
Incr Byte_counter
Else 'завершение передачи
Transmit_stop = 1 'установить флаг завершения передачи
Byte_in_out = 128 '128 'для сброса линии в 0
End If
End If
End If
Toggle Bit_part_num 'меняем состояние флага младшей\старшей половины бита
Else
'Если включен прием
If Gicr.6 = 1 Then 'если ожидали внеш прерывания INT0 то
Tim0_ovf = 1 'отметить переполнение
Else 'если внеш прерывания INT0 были выключены то
Gifr.6 = 1 'сбросить возможно проскочившее прерывание
Enable Int0
End If
End If
'Byte_in_out = Byte_
Return
Int0_vector: 'Обработка внешнего прерывания INT0
Byte_in_out = Byte_in_out
Timer_val = Timer0
Timer0 = Period_3_4 'счетчик таймера настроить на 3/4 длины периода MANCHESTER бита данных
Disable Int0
Gifr.6 = 1 'сбросить возможно проскочившее прерывание
'задвигаем принятый бит
Shift Byte_in_out , Left 'сдвигаем байт перед записью бита
If Man_in_line <> 0 Then Byte_in_out.0 = 1
'проверка на корректность длительности замера (измеряли начиная с 1/4 бита до середина бита (момент перепада)
If Timer_val > Period_2_3 Or Tim0_ovf = 1 Then
Init:
Tim0_ovf = 0 'сбросить флаг переполнения
Header_rcv = 1 'ожидать прием заголовка
Byte_counter = 0 'начать прием с начала
Byte_in_out = 0 'очистить байт приемник
End If
'крутимся тут до окончания приема хедера
If Header_rcv = 1 Then
If Byte_counter = 0 Then 'крутимся тут пока не совпадет первый байт хедера
If Byte_in_out = Manidentifier(1) Then
Line_inv = 0 'сбросить флаг инверсии
Bit_counter = 0 'готовимся к приему следующих байтов хедера
Incr Byte_counter
Incr Byte_counter
End If
Toggle Byte_in_out
If Byte_in_out = Manidentifier(1) Then
Line_inv = 1 'инверсное совпадение
Bit_counter = 0 'готовимся к приему следующих байтов хедера
Incr Byte_counter
Incr Byte_counter
End If
Toggle Byte_in_out
Goto Return_
End If
'принимаем остальные байты хедера и байт длины пакета
Incr Bit_counter
If Bit_counter < 8 Then Goto Return_ 'ждем заполнения байта
If Line_inv = 1 Then Toggle Byte_in_out 'если сигнал инверсный
If Byte_counter <= Man_identifier_length Then 'если хедер еще не закончен
If Byte_in_out <> Manidentifier(byte_counter) Then Goto Init 'проверяем идентичность хедера
Bit_counter = 0 '
Incr Byte_counter 'ожидаем следующий байт хедера
Goto Return_
End If
'хедер закончился, значит принят байт длины блока данных
If Byte_in_out > Man_buf_length Then Goto Init 'размер блока данных превышает допустимый - рестарт
Data_length = Byte_in_out 'запомним длину пакета
Header_rcv = 0 'заголовок принят переходим к приему основного файла
Bit_counter = 0
Byte_counter = 0
Goto Return_
End If
'принимаем основной блок данных
Incr Bit_counter
If Bit_counter < 8 Then Goto Return_ 'ждем накопления байта
Bit_counter = 0
If Line_inv = 1 Then Toggle Byte_in_out 'необходима ли инверсия
If Byte_counter < Data_length Then 'если это еще байты пакета
Incr Byte_counter
Manbuffer(byte_counter) = Byte_in_out 'сохраняем принятый байт
Else 'если пакет закончен
Checksumm_byte = 0 'очистить байт контрольной суммы
Call Checksumm(data_length) 'подсчет контрольки длинны пакета
For Temp_lib = 1 To Data_length
Call Checksumm(manbuffer(temp_lib)) 'подсчет контрольки пакета
Next
If Checksumm_byte <> Byte_in_out Then Goto Init 'если контролька не верна (не 0) - рестарт 'Контролька правильная
Data_enbl = 1 'установить флаг наличия данных
Call Receive_stop() 'тормозим дальнейший прием
End If
Return_:
' Byte_in_out = Byte_
Return