Администрация форума не несёт ответственности за достоверность информации и оставляет за собой право редактировать или в особых случаях даже удалять посты без предупреждения. Спасибо за понимание.

Программирование ATMEL в BASCOM.

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Программирование ATMEL в BASCOM. » Вопросы - ответы » Modbus rtu на atmega328


Modbus rtu на atmega328

Сообщений 1 страница 3 из 3

1

Здравствуйте, прошу помощи...   Не могу запустить код modbus на Atmega328. Этот код хорошо работает на atmega162, atmega8.  Но на 328 даже если не отправлять запрос на устройство о чтении регистров, контроллер постоянно шлёт какой-то бред.
Код прилагаю, взят с вашего форума.   Может различия в таймере?

Код:
'modbus.lib contains the crcMB function
 $lib "modbus.lbx"

$regfile = "m328def.dat"                                    ' chip used
$crystal = 14745600                                         ' xtal used
$baud = 19200                                              ' baudrate N,8,1


 Dim Mbuf(255) As Byte , Modc As Byte , Modt As Byte , Modw As Word
 Dim Table_data(30) As Word                                 'таблица с данными (текущие значения измерений, слова входов выходов уставки, настройки )
 Dim Tmp As Word                                            'промежуточные переменные
 Dim Tmp_byte As Byte                                       'промежуточные переменные
 Dim Tmp_byte2 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 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 Md_t_start As Bit
 Config Timer0 = Timer , Prescale = 256                     'организация задержки отключения разрешения передачи, при передаче на порт 485             '4000000 0,065536 сек (15,25878906 в 1 секунду)          8000000  0,032768 30,51757813
 On Timer0 Modbus_timers

 ' **************** выходы*************
 'Config Portd.4 = Output
 Config Portc.4 = Output

   'Test_light Alias Portc.0
   Enable_tx Alias Portc.4                                  'разрешение на передачу по 485
' ****************      0


 Declare Sub Send_to_master                                 'подпрограмма передачи ответа мастеру
 Declare Sub Error_modbus                                   'подпрограмма формирования ошибки модбас

 ' Config Portc = Output                                     'настроим порт с как выходы
  Modc = 0                                                  'инициализация
  W_to_device = 0
  Error_mod = 0
  Cnt_print = 0
  Frame_value = Frame_value_dev

  If Frame_value = 255 Then Frame_value = 253

                 'ВАЖНО! - После Перопрошивки Значение Modbus_slave = 255 !!!!!!!
  Tmp_byte = Modbus_slave                                   ' нельзя писать ерам байт в инт. можно через промежуточную переменную
  Table_data(11) = Tmp_byte                                 ' адрес 4010 - номер устройства в сети модбас (читать писать)
  Table_data(12) = Ust1_up                                  ' адрес 4011 - уставка верх  (читать писать)
  Table_data(13) = Ust1_dwn                                 ' адрес 4012 - уставка низ (читать писать)
  Table_data(16) = Frame_value                              ' адрес 4015 - значение TIMER0 загружаемое при старте таймера TIMER0 (читать)



 On Urxc Urxc_isr                                           'прерывание по приему UART
 Enable Urxc
 On Utxc Utxc_isr                                           'прерывание по передаче UART

 Enable Timer0
 Enable Interrupts

 For I = 0 To 9                                             'заполняем для примера
    Tmp_byte = I + 1
    Table_data(tmp_byte) = I
 Next

  Do                                                        'начало основного цикла
                                'Важно!!! не использовать wait !!!

 If Md_t_start = 1 Then                                     '  если время между принятыми байтами больше 3.5 символа, в буфере принят фрейм

      Md_t_start = 0                                        ' обнуляем все признаки
      Error_mod = 0                                         ' обнуляем ошибку и начинаем разбирать принятый фрейм из буфера.

    If Mbuf(1) = Modbus_slave Or Mbuf(1) = 0 Then           'первый байт это адрес устройства

      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- чтение регистров
                 N_word = Makeint(mbuf(6) , Mbuf(5))        'количество слов чтения
                 If N_word > 27 Then                        'я ограничил количество 27 словами
                    N_word = 1                              ' формируем ошибку если в запросе на чтение больше 27 слов
                    Error_mod = 2                           'если превышение размера буфера то формируем ошибку 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

                Case 6:                                     'функция 6- запись одного регистра
                      Table_data(beg_adr) = Makeint(mbuf(6) , Mbuf(5))       'записываем полученное значение
                      W_to_device = 1                       'формируем признак записи в устройство
                      Beg_adr_w = Beg_adr                   'номер слова для записи в память
                      N_byte = 8                            'длина посылки для функции 6 всегда =8 байт

               Case Else:
                      Error_mod = 1                         'неподдерживаемая функция

        End Select


           If Error_mod > 0 Then
              Call Error_modbus                             'код ошибки неправильный функциональный код
           End If

           Call Send_to_master                              'ответ мастеру

        Else                                                'если неправильная контрольная сумма
                Error_mod = 3                               'код ошибки Checksumme
                Call Error_modbus
                Call Send_to_master                         'ответ мастеру
      End If
    End If

  Modc = 0
  End If



      If W_to_device = 1 Then                               'при наличии признака записи в устройство (уставки, настройки, выходыи т.д.)
         Select Case Beg_adr_w                              'при записи (функция 6) Beg_adr = индексу в массиве  Table_data

            Case 1 :                                        'Portc = Low(table_data(beg_adr))       'запись в выходной порт  состояния 0 регистра

            Case 11 :                                       ' адрес 4010 - номер устройства в сети модбас (читать писать)
                       Modbus_slave = Table_data(beg_adr_w) 'запись в энергонезависимую память
            Case 12 :                                       ' адрес 4011 - уставка верх  (читать писать)
                       Ust1_up = Table_data(beg_adr_w)      'запись в энергонезависимую память
            Case 13 :                                       ' адрес 4012 - уставка низ (читать писать)
                       Ust1_dwn = Table_data(beg_adr_w)     'запись в энергонезависимую память

            Case 14:                                        ' Frame_value=делитель -(время в мс / заданное значение времени прерывания за один такт)    256-(время в мс/0.0032)
                       Tmp = Table_data(beg_adr_w) * 100
                       Tmp = Tmp \ 32
                                    Table_data(15) = Tmp
                                    Frame_value = 256 - Tmp
                                    Table_data(16) = Frame_value       'от 1 до 82мсек

                       Frame_value = Frame_value_dev

         End Select
         W_to_device = 0
      End If

 Loop

  Urxc_isr:                                                 '  прерывание по приему данных
   sbis usr,7                                               ' Wait for character
   rjmp urxc_isr                                            ' wait until we can read the byte
  Modt = Udr                                                ' get the byte
  Load Timer0 Frame_value                                   'устанавливаем значение таймера тишины между фреймами
  Start Timer0                                              'стартуем таймер фрейма
  Incr Modc                                                 'увеличиваем счетчик принятого байта
  Mbuf(modc) = Modt                                         ' пишем в буфер значение принятого байта

 Return

 Utxc_isr:
                                                    'Подпрограмма передачи данных
 If Cnt_print = 1 Then                                      ' если последний байт передан
   Enable_tx = 0                                            ' разрешаем прием (запрещаем передачу )
   Cnt_print = 0                                            ' обнуляем признак последнего байта передачи
   Disable Utxc                                             ' запреаем прерывание.
  End If

 Return


 Sub Send_to_master
    Enable Utxc                                             'разрешаем прерывание по
    If Enable_tx = 0 Then
     Enable_tx = 1
     For I = 1 To N_byte                                    'ответ мастеру.  цикл для передачи буфера обмена с заданным кол. байт c заданного адреса
     Print Chr(mbuf(i));                                    'отправка буфера обмена в Нех
     Next
     Cnt_print = 1
   End If
 End Sub
 Sub Error_modbus                                           'функция формирования пакета исключительного ответа мастеру по ошибке.
      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                                              'останавливаем прерывание по таймеру тишины между фреймами
   Md_t_start = 1                                           'выставляем признак того, что в буфере принят фрейм

 Return

0

2

Как вариант. У меня отлично работает с Modbus ОРС  Server

Код:
$regfile = "m328pdef.dat"
$crystal = 11059200
$hwstack = 40
$swstack = 16
$framesize = 32
$lib "modbus.lbx"
$baud = 19200
'*******************************************************************************
'       конфигурация устройств
'-----------------------------V
Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0       'Parity = Even
'
'*******************************************************************************
'        инициализация переменных
'----------------------------V
Dim Adres As Byte                                           ' адрес устройства в RS485
Dim Bufer_rs485(8) As Byte                                  ' буфер принимаемых и отправляемых байт
Dim S4et_mb As Byte                                         ' счётчик принятых байт
Dim Byte_mb As Byte                                         ' временная переменная принятого байта
Dim Crc_in As Word                                          ' два байта контрольной суммы запроса
Dim Crc_x As Word                                           ' два байта для сравнения контрольной суммы
'*******************************************************************************
On Urxc Mbus
Enable Urxc
Enable Interrupts
'
'*******************************************************************************
'*****                     Основной цикл программы                         *****
'*******************************************************************************
Osnova:
Do
'****
'**** цикл программы
'****
'*******************************************************************************
'  Интерфейс общения (MODBUS)   формируем ответ на запрос по RS485
      If S4et_mb >= 8 Then
         S4et_mb = 0
         Crc_x = Crcmb(bufer_rs485(1) , 6)                  ' считаем контрольную сумму принятого запрса
         Crc_in = Makeint(bufer_rs485(7) , Bufer_rs485(8))  ' собираем в одну переменную два байта принятой контрольной суммы
         If Crc_x = Crc_in Then                             ' сравниваем принятую и вычесленную контрольную сумму
            If Bufer_rs485(2) = 3 Then                      ' функция чтения значения манометра (MODBUS)
      '--------------------------
               If Bufer_rs485(3) = &H0F And Bufer_rs485(4) = &HA1 Then       ' читаем с 4001-го регистра
                  Bufer_rs485(3) = 2                        ' кол-во отправляемых байт
            '----------------------------------------------------------
                  Bufer_rs485(4) = High(x)                  ' Старший разряд регистра значения
                  Bufer_rs485(5) = Low(x)                   ' Младший разряд регистра значения
            '------------------------
                  Crc_x = Crcmb(bufer_rs485(1) , 5)
                  Bufer_rs485(6) = Low(crc_x)
                  Bufer_rs485(7) = High(crc_x)
                  Waitms 1
                  Disable Urxc
                  Tx_mb = 1
                  Print Chr(bufer_rs485(1)) ; Chr(bufer_rs485(2)) ; Chr(bufer_rs485(3)) ; Chr(bufer_rs485(4)) ; Chr(bufer_rs485(5)) ; Chr(bufer_rs485(6)) ; ; Chr(bufer_rs485(7))
                  Tx_mb = 0
                  Enable Urxc
               End If
            End If
         End If
      End If
'----------------
Loop
End
'*******************************************************************************
'*******************************************************************************
'*******************************************************************************
'*****    Подпрограмма прерывания по приёму байта по UART                  *****
'*******************************************************************************
Mbus:
$asm
sbis usr,7
rjmp mbus
$end Asm
'
Byte_mb = Udr                                               ' считываем регистр принятого байта
  If S4et_mb = 0 Then
      If Byte_mb = Adres Then
         S4et_mb = S4et_mb + 1
         Bufer_rs485(1) = Byte_mb
      Else
         S4et_mb = 0
         Fl_rs485_er = 1
      End If
  Elseif S4et_mb = 1 Then
      If Byte_mb = 6 Or Byte_mb = 3 Then
         S4et_mb = S4et_mb + 1
         Bufer_rs485(2) = Byte_mb
      Else
         S4et_mb = 0
      End If
  ''''''''''''''''''''''''''''''
  ' адрес и код функции приняты и проверяны
'
  Elseif S4et_mb = 2 Then
      S4et_mb = S4et_mb + 1
      Bufer_rs485(3) = Byte_mb
'///////////////////////////
   Elseif S4et_mb = 3 Then
      S4et_mb = S4et_mb + 1
      Bufer_rs485(4) = Byte_mb
'///////////////////////////
    Elseif S4et_mb = 4 Then
      S4et_mb = S4et_mb + 1
      Bufer_rs485(5) = Byte_mb
'///////////////////////////
    Elseif S4et_mb = 5 Then
      S4et_mb = S4et_mb + 1
      Bufer_rs485(6) = Byte_mb
'///////////////////////////
   Elseif S4et_mb = 6 Then
      S4et_mb = S4et_mb + 1
      Bufer_rs485(7) = Byte_mb
'///////////////////////////
   Elseif S4et_mb = 7 Then
      S4et_mb = S4et_mb + 1
      Bufer_rs485(8) = Byte_mb
End If
'
Return
'*******************************************************************************

+1

3

Большое спасибо, завтра буду пробовать.

0


Вы здесь » Программирование ATMEL в BASCOM. » Вопросы - ответы » Modbus rtu на atmega328