Реализация протокола 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