Доброго времени суток господа,
собрал небольшое устройство на атмеге8 :
измерение
3 канала АЦП
5 дискретных входов
сохранение состояния дискретных входов в небольшой журнал в EEPROM
и передача данных и журнала по rs485 по modbusRTU на ПК
Код:'modbus.lib contains the crcMB function $lib "modbus.lbx" $regfile = "m8def.dat" '$regfile = "m8adef.dat" Const _crystal = 8000000 ' chip used $crystal = _crystal ' xtal used 'Const Baudrate = 9600 'Const Baudrate = 19200 'Const Baudrate = 38400 'Const Baudrate = 57600 'Const Baudrate = 115200 $baud = 9600 $hwstack = 80 $swstack = 96 $framesize = 80 '=============================================================================== ' И С П О Л Ь З У Е М Ы Е П Е Р Е М Е Н Н Ы Е '=============================================================================== Config Com1 = Dummy, Synchrone = 0, Parity = None, Stopbits = 2, Databits = 8, Clockpol = 0 Config Serialin = Buffered, Size = 8, Bytematch = All Config Scl = Portc.5 ' I2c Scl Config Sda = Portc.4 ' I2c Sda 'Config I2cdelay = 1 ' Скорость работы шины I2c на программном уровне I2cinit Dim Год As Byte Dim Часы As Byte Dim Месяц As Byte Dim Минуты As Byte Dim Секунды As Byte Dim День_недели As Byte Dim Число_месяца As Byte Dim Флаг_опросить_ds1307 As Byte Dim Mbuf(65) As Byte, Modc As Byte, Modt As Byte, Modw As Word Dim Table_data(30) As Word 'таблица с данными (текущие значения измерений, слова входов выходов уставки, настройки ) Dim Tmp_table_data(14) As Word 'таблица с данными (текущие значения измерений, слова входов выходов уставки, настройки ) Dim Tmp As Word Dim Tmpl_input As Byte Dim Inputs As Byte 'промежуточные переменные Dim Tmp_byte_modbus As Byte Dim N_word As Byte 'количество слов в посылке Dim N_byte As Byte 'количество байт в посылке Dim Beg_adr As Byte 'начальный адрес данных Dim Beg_adr_w As Byte ' адрес данных при записи Dim I As Byte 'счетчик циклов в for Dim Cnt_print As Byte Dim Error_mod As Integer ' код ошибки Dim Frame_value As Byte 'значение паузы между фреймами загружаемое в счетчик Dim Serial_numb_l As Eram Byte Dim Serial_numb_h As Eram Byte ' параметры для записи в энергонезависимую память Dim W_to_device As Byte 'признак записи в устройство Dim Modbus_slave As Eram Byte 'адрес устройства в сети Modbus Dim Frame_value_dev As Eram Byte 'значение паузы между фреймами Dim Ust1_up As Eram Integer 'уставка1 верхняя Dim Ust1_dwn As Eram Integer 'уставка1 нижняя Dim Usart_speed As Eram Byte Const Maximum_input_registers = 30 ' максимальное количество регистров которое можно прочесть 0-30 Dim Load_timer As Word Dim Md_t_start As Bit Dim Tmp_byte As Byte 'промежуточные переменные Dim Tmp_byte2 As Byte Dim L_time(30) As Eram Word Dim L_id As Eram Integer At 69 Dim Id_adr As Eram Byte At 71 Dim Koef_r As Eram Word Dim Koef_i As Eram Word Dim Koef_u As Eram Word Dim Tport As Eram Byte Const Запись_ds1307 = &HD0 ' Адрес записи в ОЗУ Ds1307 Const Чтение_ds1307 = &HD1 ' Адрес чтения из ОЗУ Ds1307 Const Const_год = 6 ' Регистр год Ds1307 Const Const_часы = 2 ' Регистр часы Ds1307 Const Const_месяц = 5 ' Регистр месяц Ds1307 Const Const_минуты = 1 ' Регистр минуты Ds1307 Const Const_секунды = 0 ' Регистр секунды Ds1307 Const Const_день_недели = 3 ' Регистр день_недели Ds1307 Const Const_число_месяца = 4 ' Регистр число_месяца Ds1307 Const Const_настройка_ds1307 = 7 ' Регистр для настройки Ds1307 Const Const_stop_часы = &B10000000 ' Значение константы для остановки часов Const Const_start_часы = &B00000000 ' Значение константы для запуска часов Const Const_разрешение_sout_ds1307 = &H90 ' Значение константы для старта секундных импульсов '=============================================================================== ' И С П О Л Ь З У Е М Ы Е П О Д П Р О Г Р А М М Ы '=============================================================================== Declare Sub Запись_i2c(byval Адрес_чипа_запись As Byte , Byval Регистр_чипа As Byte , Byval Записываемая_переменная As Byte) Declare Sub Чтение_i2c(byval Адрес_чипа_чтение As Byte , Byval Адрес_чипа_запись As Byte , Byval Регистр_чипа As Byte , Считанная_переменная As Byte) Declare Sub Copy_inputs_to_table Declare Sub Modbus_exec Declare Sub Set_usart_speed Declare Sub Send_to_master 'подпрограмма передачи ответа мастеру Declare Sub Error_modbus 'подпрограмма формирования ошибки модбас Declare Sub Copy_data_to_table Declare Sub Copy_log_to_table Declare Sub Save_log Declare Sub Mbpacage ' **************** выходы************* 'Config Portc.0 = Output Config Portd.3 = Output Config Portd.4 = Output Config Portd.7 = Output Config Pinc.0 = Input Config Pinc.1 = Input Config Pinb.0 = Input Config Pinb.1 = Input Config Pinb.2 = Input Config Pind.5 = Input Config Pinb.3 = Input Config Pinb.4 = Input Set Pinb.3 Set Pinb.4 'Test_light Alias Portc.0 Enable_tx Alias Portd.3 'разрешение на передачу по 485 Enable_rx Alias Portd.4 'разрешение на прием в двух проводке 'T1 Alias Portb.0 T2 Alias Portd.7 Tmreset Alias Pind.5 Set Tmreset ' **************** 0 '*************************************************************** ' I/O configuration Input_1 Alias Pinc.0 ' Input 1 Input_2 Alias Pinb.2 ' Input 2 Input_3 Alias Pinc.1 ' Input 3 Input_4 Alias Pinb.1 ' Input 4 Input_5 Alias Pinb.0 ' Input 5 'Input_6 Alias Pina.5 ' Input 6 'Input_7 Alias Pina.6 ' Input 7 'input_8 Alias Pina.7 ' Input 8 Set Input_1 Set Input_2 Set Input_3 Set Input_4 Set Input_5 Set Portd.6 Config Pind.2 = Input ' Настраиваем вывод на вход Config Int0 = Rising ' Прерывание срабатывает по спаду уровня Rising (по переднему фронту) ; Falling (по заднему фронту) ; Low Level (по низкому уровню) On Int0 Прерывание_ds1307 Nosave ' После прерывания идем в подпрограмму обработки Config Timer0 = Timer, Prescale = 256 'организация задержки отключения разрешения передачи, при передаче на порт 485 '4000000 0,065536 сек (15,25878906 в 1 секунду) 8000000 0,032768 30,51757813 Set Portd.2 Enable_tx = 1 If Tport > 10 Then Tport = 9 If Tport = 0 Then Tport = 9 If Modbus_slave > 254 Then Modbus_slave = 1 If Modbus_slave = 0 Then Modbus_slave = 1 Illegal_function Alias 1 Illegal_data_address Alias 2 Illegal_data_value Alias 3 '---------------------- Настраиваем АЦП -------------------------------------------------- Config Adc = Single , Prescaler = 16 , Reference = Avcc ' Admux = &B00000110 ' |||||||| ' |||||||+--- 0 - MUX0 - выбор входа АЦП для измерения ' ||||||+---- 1 - MUX1 - выбор входа АЦП для измерения ' |||||+----- 2 - MUX2 - выбор входа АЦП для измерения ' ||||+------ 3 - MUX3 - выбор входа АЦП для измерения ' |||+------- 4 - резерв ' ||+-------- 5 - ADLAR - выравнивание результата измерения по левому краю ' |+--------- 6 - REFS0 - выбор источника опорного напряжения ' +---------- 7 - REFS1 - выбор источника опорного напряжения Start Adc Reset T2 Waitms 150 Set T2 Waitms 300 Reset T2 Waitms 150 Set T2 Waitms 300 Reset T2 Waitms 150 Set T2 Waitms 300 Reset T2 Waitms 150 Set T2 Waitms 300 Reset T2 Waitms 150 Set T2 Waitms 300 Reset T2 Waitms 500 '************************************************************ ' Program initialisation '************************************************************ ' Config Portc = Output 'настроим порт с как выходы Modc = 0 'инициализация W_to_device = 0 Error_mod = 0 Cnt_print = 0 Frame_value = Frame_value_dev Load_timer = &H6C If Frame_value = 255 Then Frame_value = 253 Call Set_usart_speed Call Copy_inputs_to_table Table_data(6) = Inputs Tmpl_input = Table_data(6) Load Timer0, Load_timer On Timer0 Modbus_timers ' On Urxc Urxc_isr 'прерывание по приему UART ' On Utxc Utxc_isr 'прерывание по передаче UART ' On Urxc Usart_0_input_data_asm Nosave ' Enable Urxc ' Enable Timer0 'Start Timer0 'Enable Interrupts Clear Serialin Enable Timer0 Start Timer0 '\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ '-------------- Разрешаем работу необходимых прерываний ---------------------- Enable Int0 '------------ Разрешаем работу всех включенных прерываний -------------------- Enable Interrupts '=============================================================================== 'Clear Serialin 'резерв '\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ' З А П У С К И П Р Е Д В А Р И Т Е Л Ь Н Ы Е Н А С Т Р О Й К И '\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ 'Читаем данные нулевого регистра Ds1307, если 7 бит равен 1, значит часы стоят 'Если часы стояли, запускаем I2cstart I2cwbyte &HD0 I2cwbyte Const_настройка_ds1307 I2cwbyte Const_разрешение_sout_ds1307 I2cstop 'Секунды = Read_ds1307(const_секунды) I2cstart I2cwbyte &HD0 I2cwbyte &H0 I2cstart I2cwbyte &HD1 I2crbyte Секунды, Ack I2crbyte Минуты, Ack I2crbyte Часы, Ack I2crbyte День_недели, Ack I2crbyte Число_месяца, Ack I2crbyte Месяц, Ack I2crbyte Год, Nack I2cstop 'Для симуляции в Протеусе всегда "0" 'If Tmreset = 0 Then 'Запускаем часы Waitms 100 ' T1 = 1 Секунды = 1 Минуты = 0 Часы = 1 День_недели = 3 Число_месяца = 1 Месяц = 12 Год = 15 Table_data(16) = Секунды Table_data(17) = Минуты Table_data(18) = Часы Table_data(19) = Число_месяца Table_data(20) = Месяц Table_data(21) = Год Год = Makebcd(Год) ' Год Часы = Makebcd(часы) ' Часы Месяц = Makebcd(Месяц) ' Месяц Минуты = Makebcd(Минуты) ' Минуты Секунды = Makebcd(Секунды) ' Секунды День_недели = Makebcd(День_недели) ' День недели Число_месяца = Makebcd(число_месяца) ' Число Call Запись_i2c(Запись_ds1307, Const_секунды, Секунды) Enable_rx = 1 '1- приемние включен , 0 - приемник выключен ' Г Л А В Н Ы Й Ц И К Л '=============================================================================== ' Главный_цикл: Do 'начало основного цикла If W_to_device = 1 Then 'при наличии признака записи в устройство (уставки, настройки, выходыи т.д.) Select Case Beg_adr_w 'при записи (функция 6) Beg_adr = индексу в массиве Table_data Case 1: 'адрес 4010 - номер устройства в сети модбас (читать писать) 'запись в энергонезависимую память Tmp_byte = Low(Tmp_table_data(Beg_adr_w)) If Tmp_byte > 0 And Tmp_byte < 255 Then Modbus_slave = Tmp_byte Table_data(12) = Tmp_byte End If Case 2: Tmp_byte = Low(Tmp_table_data(Beg_adr_w)) ' адрес 4003 - номер (читать писать) Serial_numb_l = Tmp_byte 'запись в энергонезависимую память Tmp_byte = High(Tmp_table_data(Beg_adr_w)) Serial_numb_h = Tmp_byte Table_data(3) = Tmp_table_data(Beg_adr_w) 'запись в таблицу Case 3 Секунды = Low(Tmp_table_data(Beg_adr_w)) Секунды = Makebcd(Секунды) Call Запись_i2c(Запись_ds1307, Const_секунды, Секунды) Case 4 Минуты = Low(Tmp_table_data(Beg_adr_w)) Минуты = Makebcd(Минуты) Call Запись_i2c(Запись_ds1307, Const_минуты, Минуты) Case 5 Часы = Low(Tmp_table_data(Beg_adr_w)) Часы = Makebcd(часы) Call Запись_i2c(Запись_ds1307, Const_часы, Часы) Case 6 Число_месяца = Low(Tmp_table_data(Beg_adr_w)) Число_месяца = Makebcd(число_месяца) Call Запись_i2c(Запись_ds1307, Const_число_месяца, Число_месяца) Case 7 Месяц = Low(Tmp_table_data(Beg_adr_w)) Месяц = Makebcd(Месяц) Call Запись_i2c(Запись_ds1307, Const_месяц, Месяц) Case 8 Год = Low(Tmp_table_data(Beg_adr_w)) Год = Makebcd(Год) Call Запись_i2c(Запись_ds1307, Const_год, Год) Case 10: Tmp_byte = Tmp_table_data(Beg_adr_w) If Tmp_byte > 0 And Tmp_byte < 6 Then Usart_speed = Tmp_byte Call Set_usart_speed End If Case 11: Tmp = Tmp_table_data(Beg_adr_w) If Tmp > 0 And Tmp < 20000 Then Koef_r = Tmp End If Case 12: Tmp = Tmp_table_data(Beg_adr_w) If Tmp > 0 And Tmp < 20000 Then Koef_i = Tmp End If Case 13: Tmp = Tmp_table_data(Beg_adr_w) If Tmp > 0 And Tmp < 20000 Then Koef_u = Tmp End If Case 14: Tmp = Tmp_table_data(Beg_adr_w) If Tmp > 0 And Tmp < 10 Then Tport = Tmp End If End Select W_to_device = 0 End If Call Copy_inputs_to_table If Флаг_опросить_ds1307 <> 0 Then T2 = Not T2 I2cstart I2cwbyte &HD0 I2cwbyte &H0 I2cstart I2cwbyte &HD1 I2crbyte Секунды, Ack I2crbyte Минуты, Ack I2crbyte Часы, Ack I2crbyte День_недели, Ack I2crbyte Число_месяца, Ack I2crbyte Месяц, Ack I2crbyte Год, Nack I2cstop Год = Makedec(Год) ' Год Часы = Makedec(часы) ' Часы Месяц = Makedec(Месяц) ' Месяц Минуты = Makedec(Минуты) ' Минуты Секунды = Makedec(Секунды) ' Секунды День_недели = Makedec(День_недели) ' День недели Число_месяца = Makedec(число_месяца) ' Число Флаг_опросить_ds1307 = 0 End If '=============================================================== Waitms 1 ' время последней сработавшей защиты If Tmpl_input <> Inputs Then Call Save_log End If '========================================================= Idle 'nop Loop '----------------------------------------------------------------------- '----------------------------------------------------------------------- Utxc_isr: 'Подпрограмма передачи данных If Cnt_print = 1 Then ' если последний байт передан Enable_tx = 1 ' (запрещаем передачу ) Enable_rx = 1 ' разрешаем прием Cnt_print = 0 ' обнуляем признак последнего байта передачи Disable Utxc ' запреаем прерывание. ' Enable Int0 End If Return '----------------------------------------------------------------------- Sub Send_to_master() ' Disable Int0 ' Disable Urxc Enable Utxc 'разрешаем прерывание по If Enable_tx = 1 Then Enable_tx = 0 ' разрешаем передачу ) Enable_rx = 0 'запрещаем прием For I = 1 To N_byte 'ответ мастеру. цикл для передачи буфера обмена с заданным кол. байт c заданного адреса Print Chr(Mbuf(I)); 'отправка буфера обмена в Нех Next ' Print N_byte ; Cnt_print = 1 End If ' Enable Urxc End Sub '----------------------------------------------------------------------- Sub Error_modbus() Mbuf(1) = Modbus_slave 'функция формирования пакета исключительного ответа мастеру по ошибке. Mbuf(2).7 = 1 ' 1дрес,2функц.код с 1(единицей),3код ошибки,4,5CRC всего 5 байт Mbuf(3) = Error_mod 'код ошибки (1- неправильный фунциональный код(неподдерживаемая функция); 2- неправильный адрес; 3- неправильные данные (CRC). ) Modw = Crcmb(Mbuf(1), 3) ' create checksum Mbuf(4) = Low(Modw) 'Checksumme LOWer Byte Mbuf(5) = High(Modw) 'Checksumme HIGHer Byte N_byte = 5 End Sub '----------------------------------------------------------------------- Modbus_timers: 'прерывание по таймеру. В нем организовано два счетчика. По одному счетчику обработка пакета принятых сообщений модбас, по другому задержка на отключение передатчика TX в 485. Stop Timer0 Call Modbus_exec 'останавливаем прерывание по таймеру тишины между фреймами ' Md_t_start = 1 'выставляем признак того, что в буфере принят фрейм Return '----------------------------------------------------------------------- Sub Copy_log_to_table() Disable Interrupts Local Tmprd As Word For I = 1 To 30 Tmprd = L_time(I) Table_data(I) = Tmprd Next ' = Число_месяца Enable Interrupts End Sub '------------------------------------------------------------------------- Sub Save_log() Local Tid As Byte Local Tmpid As Word Disable Interrupts Waitms 20 Tid = Id_adr If Tid > 29 Then Tid = 0 If Tid > 25 Then Tid = 0 End If Incr Tid Tmpid = Makeint(Секунды, Минуты) L_time(Tid) = Tmpid Incr Tid Tmpid = Makeint(часы, Число_месяца) L_time(Tid) = Tmpid Incr Tid ' = Секунды Tmpid = Makeint(Месяц, Год) L_time(Tid) = Tmpid Incr Tid ' = Минуты Tmpid = Inputs Tmpl_input = Inputs L_time(Tid) = Tmpid Incr Tid ' = Часы Tmpid = L_id Incr Tmpid If Tmpid > 65534 Then Tmpid = 0 L_time(Tid) = Tmpid L_id = Tmpid Id_adr = Tid ' = Число_месяца Enable Interrupts End Sub '----------------------------------------------------------------------------- Sub Copy_data_to_table() Tmp_byte = Modbus_slave ' нельзя писать ерам байт в инт. можно через промежуточную переменную Table_data(12) = Tmp_byte ' адрес 4010 - номер устройства в сети модбас (читать писать) Table_data(13) = Ust1_up ' адрес 4011 - уставка верх (читать писать) Table_data(14) = Ust1_dwn ' адрес 4012 - уставка низ (читать писать) Table_data(15) = Frame_value ' адрес 4015 - значение TIMER0 загружаемое при старте таймера TIMER0 (читать) Tmp_byte = Serial_numb_h Tmp_byte2 = Serial_numb_l 'серийный номер Table_data(3) = Makeint(Tmp_byte2, Tmp_byte) Table_data(4) = &H8 ' весия Прошивки ( 02) Table_data(5) = 660 't константа (660 или 2140) Table_data(7) = &HFFFF 'ток Imax амплитудное значение (нету - код FFFF) ' Table_data(8) = &HFFFF 'напряжение (нету - код FFFF) Table_data(9) = &HFFFF 'сопротивление (нету - код FFFF) Table_data(10) = &HFFFF 'наппряжение батарейки Tmp_byte = Tport Table_data(11) = Tmp_byte Table_data(16) = Секунды Table_data(17) = Минуты Table_data(18) = Часы Table_data(19) = Число_месяца Table_data(20) = Месяц Table_data(21) = Год Tmp = L_id Table_data(22) = Tmp Tmp = Koef_i If Tmp > 20000 Or Tmp = 0 Then Koef_i = 1000 Tmp = Koef_r If Tmp > 20000 Or Tmp = 0 Then Koef_r = 1000 Tmp = Koef_u If Tmp > 20000 Or Tmp = 0 Then Koef_u = 1078 Table_data(23) = Koef_r Table_data(24) = Koef_i Table_data(25) = Koef_u End Sub Sub Copy_inputs_to_table() Tmp_byte2 = Tport Table_data(26) = 0 Table_data(27) = 0 Table_data(28) = 0 Table_data(29) = 0 Table_data(30) = 0 For I = 0 To 9 If Input_1 = 0 Then Incr Table_data(26) If Input_2 = 0 Then Incr Table_data(27) If Input_3 = 0 Then Incr Table_data(28) If Input_4 = 0 Then Incr Table_data(29) If Input_5 = 0 Then Incr Table_data(30) Waitus 400 Next If Table_data(26) > Tmp_byte2 Then Inputs 0.0 = 0 Else Inputs 0.0 = 1 If Table_data(27) > Tmp_byte2 Then Inputs 0.1 = 0 Else Inputs 0.1 = 1 If Table_data(28) > Tmp_byte2 Then Inputs 0.2 = 0 Else Inputs 0.2 = 1 If Table_data(29) > Tmp_byte2 Then Inputs 0.3 = 0 Else Inputs 0.3 = 1 If Table_data(30) > Tmp_byte2 Then Inputs 0.4 = 0 Else Inputs 0.4 = 1 Table_data(26) = 0 Table_data(27) = 0 Table_data(28) = 0 Table_data(29) = 0 Table_data(30) = 0 Inputs 0.5 = 1 Inputs 0.6 = 1 Inputs 0.7 = 0 End Sub '----------------------------------------------------------------------- '=============================================================================== Sub Запись_i2c(ByVal Адрес_чипа_запись As Byte, ByVal Регистр_чипа As Byte, ByVal Записываемая_переменная As Byte) '------------------------------------------------------------------------------- I2cstart ' Подготовка к работа шины I2c I2cwbyte Адрес_чипа_запись ' Адресное обращение к используемому чипу i2c, для записи I2cwbyte Регистр_чипа ' Адрес регистра, в который будет производиться запись I2cwbyte Записываемая_переменная ' Записываемая переменная I2cstop ' Освобождаем шину I2c '------------------------------------------------------------------------------- Waitms 10 End Sub '=============================================================================== '=============================================================================== ' Подпрограмма для обработки прерывания инициируемого секундными импульсами с вывода "SQW/OUT" Ds1307 '=================<===<=<=<=<===<===<=<=<=<===================================== Прерывание_ds1307: ' с помощью ASM AVR '------------------------------------------------------------------------------- $asm push R16 ' Время выполнения инструкции, 2 такта ldi R16, 1 ' Загрузить константу, время выполнения инструкции, 1 такт sts {Флаг_опросить_ds1307} , R16 ' Загрузить значение регистра в переменную, время выполнения инструкции, 2 такта Pop R16 ' Время выполнения инструкции, 2 такта reti ' Время выполнения инструкции, 4 такта $end Asm '------------------------------------------------------------------------------- Return '=============================================================================== Sub Set_usart_speed() Local Tmpbyte As Byte Tmpbyte = Usart_speed Select Case Tmpbyte Case 1 Baud = 4800 Ubrr = 103 Ucsra.u2x = 0 Load_timer = &H36 '255-56=199 = 56=&H36 Case 2 Baud = 9600 Ubrr = 51 ' Ubrrh = 0 '; ' Ubrrl = 51 ';//9600 Ucsra.u2x = 0 Load_timer = &H9C '9c=156 255-156 = 99 Case 3 Baud = 19200 Ubrr = 25 Ucsra.u2x = 0 Load_timer = &HCD '255- 205=50 205=cd Case 4 Baud = 38400 Ubrr = 12 Ucsra.u2x = 0 Load_timer = &HE6 '255-230=25 =e6 Case Else Baud = 9600 ' Ubrr = 51 Ubrrh = 0 '; Ubrrl = 51 ';//9600 Ucsra.u2x = 0 Load_timer = &H9C End Select ' T1 = 1 T2 = 1 Waitms 100 'Config Com1 = Baudrate , Synchrone = 0 , Parity = None , Stopbits = 2 , Databits = 8 , Clockpol = 0 End Sub '------------------------------------------------------------------------------------------------------------------------------------------- Serial0bytereceived: Load Timer0, Load_timer Start Timer0 Return '------------------------------------------------------------------------------------------------------------------------------------------- Sub Modbus_exec() Local Tmpdates As Long Local Tmp_a As Word Local A As Byte Disable Int0 Modc = 0 M1: A = Ischarwaiting() If A = 1 Then Incr Modc Inputbin Modt Mbuf(Modc) = Modt If Modc > 16 Then GoTo M0 Else GoTo M0 End If GoTo M1 M0: Enable Int0 Clear Serialin '=======================обработка пакета============================= ' если время между принятыми байтами больше 3.5 символа, в буфере принят фрейм Error_mod = 0 ' обнуляем ошибку и начинаем разбирать принятый фрейм из буфера. 'T2 = 1 If Mbuf(1) = Modbus_slave And Modc > 0 Then 'первый байт это адрес устройства Or Mbuf(1) = 0 And Modc > 0 Tmp_byte = Modc - 1 ' вычисляем адреса для расчета CRC Tmp_byte2 = Modc ' вычисляем адреса для расчета CRC Modw = Makeint(Mbuf(Tmp_byte), Mbuf(Tmp_byte2)) ' считываем из принятого фрейма CRC Tmp_byte = Modc - 2 ' вычисляем адреса для расчета CRC Tmp = Crcmb(Mbuf(1), Tmp_byte) ' вычисляем CRC из фрейма принятых данных If Modw = Tmp Then ' сравниваем расчитанный CRC и принятый CRC 'справедливо для функций 3,4 и 5,6.Если будут другие функции кроме 3,4 или 5,6 то добавить сравнение с функциями Beg_adr = Makeint(Mbuf(4), Mbuf(3)) Incr Beg_adr 'адрес региста чтения (увеличиваем на 1 так как индекс массива начинается от 1, а адресация идет с 0 - 4000 адрес) 'второй байт это номер функции Select Case Mbuf(2) Case 3: ' функция 3- чтение регистров Call Copy_data_to_table 'копируем дискретные входа и информацию в таблицу Модбас для вывота Tmpdates = Getadc(2) Tmpdates = 1023 - Tmpdates Tmpdates = Tmpdates * 5000 Tmpdates = Tmpdates / 1024 Tmp = Koef_r Tmpdates = Tmpdates * Tmp Tmpdates = Tmpdates / 1000 Table_data(9) = Tmpdates ' I 10ma утечка Tmpdates = 0 For I = 0 To 19 Tmp_a = Getadc(6) Tmpdates = Tmpdates + Tmp_a Waitus 200 Next I Tmpdates = Tmpdates / 20 Tmpdates = Tmpdates * 5 Tmpdates = Tmpdates * 1000 Tmpdates = Tmpdates / 1023 Tmp = Koef_u Tmpdates = Tmpdates * Tmp Tmpdates = Tmpdates / 1000 Table_data(8) = Tmpdates ' Umax напряжение Питания (амплитудное) Tmpdates = 0 For I = 0 To 120 Tmp_a = Getadc(7) If Tmp_a > Tmpdates Then Tmpdates = Tmp_a If I > 100 And Tmp_a < 15 Then Exit For Waitus 100 Next I Tmpdates = Tmpdates * 5 Tmpdates = Tmpdates * 1000 Tmpdates = Tmpdates / 1024 'Tmpdates = Tmpdates * 100 ' Tmpdates = Tmpdates / 141 Tmp = Koef_i Tmpdates = Tmpdates * Tmp Tmpdates = Tmpdates / 1000 Table_data(7) = Tmpdates ' ток Imax амплитудное значение Call Copy_inputs_to_table Table_data(6) = Inputs Call Mbpacage Case 4: '4- функция чтение регистров журнала событий Call Copy_log_to_table 'формируем пакет Модбас Call Mbpacage Case 6: 'функция 6- запись одного регистра Tmp_table_data(Beg_adr) = Makeint(Mbuf(6), Mbuf(5)) 'записываем полученное значение W_to_device = 1 'формируем признак записи в устройство Beg_adr_w = Beg_adr 'номер слова для записи в память N_byte = 8 'длина посылки для функции 6 всегда =8 байт If Beg_adr_w > 14 Then Error_mod = Illegal_data_address 'неправильный адрес регистра ( нельзя писать) Case Else: Error_mod = Illegal_function 'неподдерживаемая функция End Select '----------------------------------обработка ошибок------------------------------------------------------ If Error_mod > 0 Then Call Error_modbus 'код ошибки неправильный функциональный код ' T1 = 1 'ответ мастеру ' Waitms 300 ' T1 = 0 End If Call Send_to_master 'ответ мастеру Else 'если неправильная контрольная сумма Error_mod = 3 'код ошибки Checksumme ' Call Error_modbus ' Call Send_to_master ' T1 = 1 'ответ мастеру ' Waitms 100 ' T1 = 0 ' T2 = 0 End If End If Modc = 0 Md_t_start = 0 ' обнуляем все признаки 'End If End Sub Sub Mbpacage() N_word = Makeint(Mbuf(6), Mbuf(5)) 'количество слов чтения If N_word > Maximum_input_registers Then 'я ограничил количество 27 словами N_word = 1 ' формируем ошибку если в запросе на чтение больше 27 слов Error_mod = Illegal_data_address 'если превышение размера буфера то формируем ошибку 2 - неправильный адрес End If N_byte = N_word + N_word 'количество слов переводим в кол. байт Mbuf(3) = N_byte 'кол считываемых байт byte count wieviele Bytes sollen gesendet werden Tmp_byte = 4 'начинаем считать с 4-го байта(1-ый байт данных) '************************ здесь был косяк For I = Beg_adr To N_word Tmp_byte2 = Beg_adr + N_word 'формируем индекс количества слов с учетом смещения от начального адресаа For I = Beg_adr To Tmp_byte2 'цикл записи в буфер обмена запрашиваемого количества слов Tmp_byte_modbus = High(Table_data(I)) 'разбираем по байтам на старший байт Mbuf(Tmp_byte) = Tmp_byte_modbus ' запись в буфер обмена с разбивкой на байты (таблица данных) Incr Tmp_byte ' Tmp_byte_modbus = Low(Table_data(I)) 'разбираем по байтам на младший байт Mbuf(Tmp_byte) = Tmp_byte_modbus 'запись в буфер обмена с разбивкой на байты (таблица данных) Incr Tmp_byte ' Next I N_byte = N_byte + 3 'номер последнего байта передаваемых в посылке слов с данными (таблица данных) 3-количество байт перед данными (номер устройства, функция, количество передаваемых слов ) Modw = Crcmb(Mbuf(1), N_byte) ' create checksum Incr N_byte 'номер предпоследнего байта во всей посылке () (CRC LOW) Mbuf(N_byte) = Low(Modw) 'Checksumme LOWer Byte Incr N_byte 'номер последнего байта во всей посылке (CRC High) Mbuf(N_byte) = High(Modw) 'Checksumme HIGHer Byte End Sub
в протеусе при эмуляции все замечательно работает
НО собрал в железяку и пошли глюки
при непрерывном обмене данными с ПК иногда происходит кратковременное "подвисание" контроллера- не отвечает на запрос и в этот момент происходит запись в ЕЕПРОМ произвольных данных с дискретных входов ( входа все посадил на землю а на них всеравно якобы есть 1)
тоесть
сначала читаю состояние портов в переменную Inputs
Sub Copy_inputs_to_table()
Tmp_byte2 = TportTable_data(26) = 0
Table_data(27) = 0
Table_data(28) = 0
Table_data(29) = 0
Table_data(30) = 0For I = 0 To 9
If Input_1 = 0 Then Incr Table_data(26)
If Input_2 = 0 Then Incr Table_data(27)
If Input_3 = 0 Then Incr Table_data(28)
If Input_4 = 0 Then Incr Table_data(29)
If Input_5 = 0 Then Incr Table_data(30)
Waitus 400
NextIf Table_data(26) > Tmp_byte2 Then Inputs.0 = 0 Else Inputs.0 = 1
If Table_data(27) > Tmp_byte2 Then Inputs.1 = 0 Else Inputs.1 = 1
If Table_data(28) > Tmp_byte2 Then Inputs.2 = 0 Else Inputs.2 = 1
If Table_data(29) > Tmp_byte2 Then Inputs.3 = 0 Else Inputs.3 = 1
If Table_data(30) > Tmp_byte2 Then Inputs.4 = 0 Else Inputs.4 = 1Table_data(26) = 0
Table_data(27) = 0
Table_data(28) = 0
Table_data(29) = 0
Table_data(30) = 0Inputs.5 = 1
Inputs.6 = 1
Inputs.7 = 0End Sub
затем сравниваю со старым состоянием порта
If Tmpl_input <> Inputs Then
и занисываю Call Save_log в ЕЕПРОМ
Sub Save_log()
Local Tid As Byte
Local Tmpid As Word
Disable Interrupts
Waitms 20
Tid = Id_adr
If Tid > 29 Then Tid = 0If Tid > 25 Then
Tid = 0
End If
Incr Tid
Tmpid = Makeint(Секунды, Минуты)
L_time(Tid) = Tmpid
Incr Tid
Tmpid = Makeint(часы, Число_месяца)
L_time(Tid) = Tmpid
Incr Tid ' = Секунды
Tmpid = Makeint(Месяц, Год)
L_time(Tid) = Tmpid
Incr Tid ' = Минуты
Tmpid = Inputs
Tmpl_input = Inputs
L_time(Tid) = Tmpid
Incr Tid
' = Часы
Tmpid = L_id
Incr Tmpid
If Tmpid > 65534 Then Tmpid = 0
L_time(Tid) = Tmpid
L_id = Tmpid
Id_adr = Tid ' = Число_месяца
Enable Interrupts
End Sub
и запоминаю Inputs во временную переменную Tmpl_input = Inputs
и в момент когда "подвисает" связь в переменных Tmpl_input и Inputs находится всякий бред
в результате чего даже при все время замкнутых входах на землю в лог пишутся разные значения
мозг взорвался думать уже нечем, но есть подозрения что набокопорил с
$hwstack = 80
$swstack = 96
$framesize = 80
и огромным количеством переменных
Отредактировано Penumbra (2016-07-12 23:48:56)