Реализация протокола Modbus
Дипломная работа - Компьютеры, программирование
Другие дипломы по предмету Компьютеры, программирование
>
.include
.dseg_id: .byte 1;1 байт для хранения id устройства_id: .byte 1;1 байт для хранения функции: .byte 2;2 байта для хранения регистра начального адреса
hold_regs: .byte 32;32 байта для хранения holding registers. 16 регистров по 2 байта: .byte 1;2 байта для хранения coils. 16 флагов по одному биту: .byte 2;2 байта для хранения crc, которая была передана по UART
.equ XTAL = 7372800;частота контроллера
.equ baudrate = 9600;необходимая скорость
.equ bauddivider = XTAL/(16*baudrate)-1;значение предделителя UART
.equslave_id = 1;адрес устройства
.def CRCH=R29;старший байт CRC, которая вычисляется для посылки
.def CRCL=R28;младший байт CRC, которая вычисляется для посылки
.def DataCH=R27;старший байт для числа данных в случае чтения, или самих данных в случае записи
.def DataCL=R26;младший байт для числа данных в случае чтения, или самих данных в случае записи
.def BYTENUM=R25;число байт в пакете, которые осталось передать
.def TMP=R24;временная переменная
.def COUNTER=R23;;счётчик. временная переменная, используется в функции getCRC
.def MES=R22;сообщение, которое было передано по UART
.cseg
.org 0x0Main
:;инициализиацияCRCH,0coils,CRCHCRCH,0xFFCRCL,0xFFBYTENUM,7R16,0x0FSPH,R16r16,(1<<RXEN1)|(1<<TXEN1)|(1<<RXCIE1)UCSR1B,r16r16,$06UCSR1C,r16R16,low(bauddivider)UBRR1L,r16R16,high(bauddivider)UBRR1H,r16_loop:
;ожидание прерыванияinf_loop
.org 0x3C
rjmp InterHandler
:MES,UDR1
cpi BYTENUM,7getID;если посылка только началась, то нужно сравнить IDBYTENUM,6getFID;получить ID функцииBYTENUM,2getDataInf; байты 2-5 - инфо о данныхBYTENUM,1getLoCRC; байт 1 - младшая часть CRC:; если управление здесь, значит происходит чтение 0-го байт. старшая часть CRC, выполнение операций
sts crc+1,MESperformBYTENUM,7;новая посылка
ldi CRCH,$FF;подготовка CRC к новой посылке
ldi CRCL,$FF_handle:
cpi BYTENUM,2; считаем CRC для битов 7-2
BRLO noCRCgetCRC:BYTENUM
:s_id,MESend_handle
:f_id,MESend_handle
:crc,MESend_handle
:_5:bytenum,5byte_4startreg+1,mesend_handle_4:bytenum,4byte_3startreg,mesend_handle_3:bytenum,3byte_2DataCH,mesend_handle_2:DataCL,mes end_handle
:
;процедура обработки запроса и отправки ответа
;свободны регистры 16-25
;R25 - код ошибки
;R21 - флаг broadcast
;R22 - MES, используется для передачиR16,s_id;считываем idR21,0;записываем 0 в флаг броадкаст. если R21<>0, значит посылка броадкастоваяR16,slave_id;сравниваем id с id устройстваunicast_request;если равны, значит посылка unicastR16,0;сравниваем id с нулём (broadcast)perform_end;если не равны, значит выходим из процедурыR21,0xFF;если равны, значит посылка broadcast, выставляем флаг
unicast_request:R16,crc;проверка crcR16,CRCL;младшая частьperform_endR16,crc+1;старшая часть R16,CRCHperform_end;если где-то не совпало, значит в пакете ошибкаR16,f_id;загружаем id функцииR16,7wrong_func;поддерживаются только первые 6 функций. для других случаев отправляем исключение
;в зависимости от функции выполняем действия
;чтобы заменить условные переходы на безусловные, вводим конструкцию:
ldi R17,1R17,R16no_1read_coils_1:R17R17,R16no_2read_coils_2:R17R17,R16no_3read_hold_regs_3:R17R17,R16no_4read_input_regs_4:R17R17,R16write_hold_regwrite_coil_hold_reg:
lds R16,startreg;считываем стартовый регистрR16;уменьшаем на 1, так как в modbus данные передаются начиная с 1R16,16;16 - кол-во данныхout_of_range;если считать нужно больше, чем есть, отправляем исключениеR16,R16;умножаем на 2, так как регистры 16-битные
ldi ZL,low(hold_regs)ZH,high(hold_regs)
add ZL,R16;загружаем в Z смещение запрашиваемых данныхR16,0ZH,R16;если произошёл перенос, необходимо это учестьZ,DATACH;данные хранятся в формате СтаршийБайт:МладшийZ,1;запись в следующий байтZ,DATACLR21,0xFF;если не стоит флаг броадкаст, посылаем ответ
breq perform_endsend_echo_end:
_func:
;отправить исключение с кодом 1. код хранится в r25
ldi r25,1exception
_of_range:
;отправить исключение с кодом 2. код хранится в r25
ldi r25,2exception
_value:
;отправить ексепшн с кодом 3. код хранится в r25
ldi r25,3exception
_coils:;функция для чтения coils или discrete input
cpi R21,0xFF;если не стоит флаг броадкаст, посылаем ответ
breq perform_end
R16,startregR16R16,DATACLR16,9;8 - кол-во данныхout_of_rangeR16,DATACL
MES,s_id;отправка s_id и f_idCRCL,$FFCRCH,$FFgetCRCsend_byteMES,f_idgetCRCsend_byteMES,1;посылаем 1 байтgetCRCsend_byteMES,f_idMES,1f_coilsMES,0get_status_regno_coils_coils:ZL,low(coils)ZH,high(coils)MES,Z_coils:R16,0no_lsr_loop:MESR16
cpi R16,0lsr_loop_lsr:;теперь нужно наложить маску и отправить DATA и CRC
rcall get_maskMES,R25getCRCsend_byteMES,CRCLsend_byteMES,CRCHsend_byteperform_end_value_copy:wrong_value
_of_range_copy:wrong_value
_end_copy:perform_end
read_hold_regs:R21,0xFF;если не стоит флаг броадкаст, посылаем ответ
breq perform_end_copyR16,startregR16R16,16out_of_range_copyR16,DATACLR16,17out_of_range_copyR16,DATACLR16,R16
ZL,low(hold_regs)ZH,high(hold_regs)ZL,R16R17,0ZH,R17
MES,s_idCRCL,$FFCRCH,$FFgetCRCsend_byteMES,f_idgetCRCsend_byteMES,DATACL
add MES,MES;отправляем удвоенное число байт
rcall getCRCsend_byte
R17,DATACL_hold_loop: MES,Z+;сначала старший байт.
ld R16,Z+;потом младшийsend_bytegetCRCMES,R16send_bytegetCRCR17R17,0send_hold_loopMES,CRCLsend_byteMES,CRCHsend_byteperform_end
_input_regs:
cpi R21,0xFF;если не стоит флаг броадкаст, посылаем ответ
breq perform_end_copyR16,startregR16R16,32out_of_range_copyR16,DATACLR16,33out_of_range_copyR16,DATACL
ZL,R16ZH,0
MES,s_idCRCL,$FFCRCH,$FFgetCRCsend_byteMES,f_idgetCRCsend_byteMES,DATACL
add MES,MES;отправляем удвоенное число байт
rcall getCRCsend_byte
R17,DATACL
send_input_loop:MES,0;сначала старший байт.
ld R16,Z+;потом младшийsend_bytegetCRCMES,R16send_bytegetCRCR17R17,0send_input_loopMES,CRCLsend_byteMES,CRCHsend_byteperform_end
_coil:R16,startregR16R16,8;8 - кол-во данныхout_of_range_copy2DATACL,0wrong_value_copy2DATACH,DATACLDATACH,0xFFwrong_value_copy2;флаг Z установится, если DATACH=DATACL=0, или если DATACH=0xFF, DATACL=0ZL,low(coils)ZH,high(coils)R17,Zbit_to_maskDATACH,0xFFset_bitR18R17,R18Z,R17send_echo_bit:R17,R18Z,R17
sbrc R21,0;если не стоит флаг броадкаст, посылаем ответ
rjmp perform_endsend_echo
_of_range_copy2:out_of_range_value_copy2:wrong_value
_echo:
lds MES,s_id
rcall send_byte
lds MES,f_id
rcall send_byte
lds MES,startreg+1
rcall send_byte
lds MES,startreg
rcall send_byte
mov MES,DATACH
rcall send_byte
mov MES,DATACL
rcall send_byte
mov MES,CRCL
rcall send_byte
mov MES,CRCH
rcall send_byteperform_end
_byte:TMP,UCSR1ATMP,UDRE1send_byteUDR1,MES;MES - буфер
ret
:R21,0;если не стоит флаг броадкаст, посылаем ответ
rjmp perform_endCRCH,$FFCRCL,$FFMES,s_idgetCRCsend_byteMES,f_idr17,$80MES,r17getCRCsend_byteMES,r25;r25 - код ошибкиgetCRCsend_byteMES,CRCLsend_byteMES,CRCHsend_byteperform_end
:
LDI TMP,0
EOR CRCH,TMP
EOR CRCL,MES