Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Pytania, kody i porady dotyczące nie tylko Bascom.
Zbyszek
Posty: 96
Rejestracja: 30 lis 2017, 17:12

Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: Zbyszek » 28 maja 2026, 21:47

Szukałem jakichś przykładów w Internecie ale nic nie znalazłem, AI też nie jest chętna do pomocy bo każda poprawka to coraz więcej błędów w kompilatorze. Chciałbym prosić o pomoc w napisaniu programu. Chciałem odczytać za pomocą Atmegi 8-ki 1 rejestr dwukierunkowego licznika energii DTSU666 , wpisać tą wartość do jakiejś zmiennej i wyświetlić ją na wyświetlaczu LCD. Konkretnie chodzi o rejestr 2012H , wg. rozpiski opis rejestru Data type : float , data lenght : Word 2. Licznik ma komunikację RS485 Modbus RTU. Ustawienia parametrów licznika : Szybkość transmisji : 9600bps / Adres urządzenia : 1 / Protokół komunikacyjny : brak parzystości , 1 bit stopu , n.1. Wartość odczytana z rejestru będzie na pewno miała wartość ujemną lub dodatnią bo jest to moc czynna oddawana/pobierana z sieci. Będzie to wartość 32-bitowa. Docelowo mnie głównie będzie interesować wartość ujemna, ( poniżej zera ) którą chciałem wpisać do PWM i docelowo atmega ma za pomocą wypełnienia PWM przedstawiać wartość mocy oddawanej do sieci a to sobie dalej będzie sterować resztą.
Coś tam w Bascomie programowałem ale jeżeli chodzi o modbus to niestety jestem zielony. Jeżeli mogę to bardzo proszę o pomoc, oczywiście wcale nie chcę za darmo bo wiem że wiedza kosztuje i ktoś na to musi poświęcić swój czas. Jesteśmy tylko ludźmi i zawsze możemy się dogadać. Schemat podłączenia Amegi z układem MAX485 i wyświetlaczem w załączniku. W załączniku są także instrukcja do licznika i opis jego rejestrów. Poniżej kod który wygenerowała AI, ale kompilator pokazuje błędy z którymi ani AI ani ja nie potrafimy sobie poradzić
  1. $regfile = "m8def.dat"
  2. $crystal = 16000000
  3. $baud = 9600
  4.  
  5. '========================
  6. ' LCD 2x16 konfiguracja
  7. '========================
  8. Config Lcd = 16 * 2
  9. Config Lcdpin = Pin , Rs = Portd.4 , E = Portc.5 , Db4 = Portc.4 , Db5 = Portc.3 , Db6 = Portc.2 , Db7 = Portc.1
  10. Cursor Off
  11. Cls
  12.  
  13. '========================
  14. ' RS485 kierunek transmisji
  15. ' PD2 -> RE/ + DE MAX485
  16. ' 0 = odbiór
  17. ' 1 = nadawanie
  18. '========================
  19. Config Portd.2 = Output
  20. Portd.2 = 0
  21.  
  22. '========================
  23. ' UART
  24. ' PD0 = RXD
  25. ' PD1 = TXD
  26. '========================
  27. Open "COMD.0:9600,8,N,1" For Binary As #1
  28.  
  29. '========================
  30. ' Zmienne
  31. '========================
  32. Dim Tx(8) As Byte
  33. Dim Rx(9) As Byte
  34. Dim I As Byte
  35. Dim Crc As Word
  36. Dim Crc_lo As Byte
  37. Dim Crc_hi As Byte
  38.  
  39. Dim Reg_hi As Word
  40. Dim Reg_lo As Word
  41. Dim P As Long
  42.  
  43. Dim Slave_id As Byte
  44. Slave_id = 1
  45.  
  46. '========================
  47. ' Deklaracje
  48. '========================
  49. Declare Function Modbus_crc(byval Crcdata() As Byte , Byval N As Byte) As Word
  50. Declare Sub Send_modbus_request()
  51. Declare Function Read_power() As Byte
  52.  
  53. '========================
  54. ' Start
  55. '========================
  56. Cls
  57. Locate 1 , 1
  58. Lcd "DTSU666 Modbus"
  59. Locate 2 , 1
  60. Lcd "Start..."
  61. Wait 2
  62.  
  63. Do
  64.  
  65.    If Read_power() = 1 Then
  66.       Cls
  67.       Locate 1 , 1
  68.       Lcd "Moc P:"
  69.       Locate 2 , 1
  70.       Lcd P
  71.    Else
  72.       Cls
  73.       Locate 1 , 1
  74.       Lcd "Blad odczytu"
  75.    End If
  76.  
  77.    Wait 1
  78.  
  79. Loop
  80. End
  81.  
  82. '====================================================
  83. ' Funkcja odczytu rejestru 2012H (2 rejestry = 32 bity)
  84. ' Zwraca 1 gdy OK, 0 gdy blad
  85. '====================================================
  86. Function Read_power() As Byte
  87.  
  88.    Local Timeout As Word
  89.    Local Bytecount As Byte
  90.    Local Calc_crc As Word
  91.    Local Rec_crc As Word
  92.  
  93.    '------------------------------------------
  94.    ' Ramka zapytania Modbus RTU:
  95.    ' [ID][03][20][12][00][02][CRCL][CRCH]
  96.    '------------------------------------------
  97.    Tx(1) = Slave_id
  98.    Tx(2) = &H03
  99.    Tx(3) = &H20
  100.    Tx(4) = &H12
  101.    Tx(5) = &H00
  102.    Tx(6) = &H02
  103.  
  104.    Crc = Modbus_crc(tx() , 6)                               'Crc = Modbus_crc(tx , 6)
  105.    Tx(7) = Low(crc)
  106.    Tx(8) = High(crc)
  107.  
  108.    '------------------------------------------
  109.    ' Wysylanie
  110.    '------------------------------------------
  111.    Portd.2 = 1
  112.    Waitms 2
  113.  
  114.    For I = 1 To 8
  115.       Put #1 , Tx(i)
  116.    Next I
  117.  
  118.    Waitms 10
  119.    Portd.2 = 0
  120.  
  121.    '------------------------------------------
  122.    ' Odbior odpowiedzi:
  123.    ' [ID][03][04][D1][D2][D3][D4][CRCL][CRCH]
  124.    '------------------------------------------
  125.    Timeout = 0
  126.    For I = 1 To 9
  127.  
  128.       Do
  129.          If Ischarwaiting(#1) <> 0 Then Exit Do
  130.          Waitms 1
  131.          Incr Timeout
  132.          If Timeout > 500 Then
  133.             Read_power = 0
  134.             Exit Function
  135.          End If
  136.       Loop
  137.  
  138.       Get #1 , Rx(i)
  139.    Next I
  140.  
  141.    ' Sprawdzenie podstawowe
  142.    If Rx(1) <> Slave_id Then
  143.       Read_power = 0
  144.       Exit Function
  145.    End If
  146.  
  147.    If Rx(2) <> &H03 Then
  148.       Read_power = 0
  149.       Exit Function
  150.    End If
  151.  
  152.    Bytecount = Rx(3)
  153.    If Bytecount <> 4 Then
  154.       Read_power = 0
  155.       Exit Function
  156.    End If
  157.  
  158.    ' CRC odpowiedzi
  159.    Calc_crc = Modbus_crc(rx() , 7)
  160.    Rec_crc = Makeint(rx(8) , Rx(9))
  161.  
  162.    If Calc_crc <> Rec_crc Then
  163.       Read_power = 0
  164.       Exit Function
  165.    End If
  166.  
  167.    ' Skladanie 32-bitowej wartosci:
  168.    ' Rx(4)=MSB high word
  169.    ' Rx(5)=LSB high word
  170.    ' Rx(6)=MSB low word
  171.    ' Rx(7)=LSB low word
  172.    Reg_hi = Makeint(rx(5) , Rx(4))
  173.    Reg_lo = Makeint(rx(7) , Rx(6))
  174.  
  175.    P = Reg_hi
  176.    Shift P , Left , 16
  177.    P = P + Reg_lo
  178.  
  179.    Read_power = 1
  180.  
  181. End Function
  182.  
  183. '====================================================
  184. ' CRC16 Modbus
  185. '====================================================
  186. Function Modbus_crc(byval Crcdata() As Byte , Byval N As Byte) As Word
  187.  
  188.    Local J As Byte
  189.    Local K As Byte
  190.    Local C As Word
  191.  
  192.    C = &HFFFF
  193.  
  194.    For J = 1 To N
  195.       C = C Xor Crcdata(j)
  196.       For K = 1 To 8
  197.          If C.0 = 1 Then
  198.             Shift C , Right , 1
  199.             C = C Xor &HA001
  200.          Else
  201.             Shift C , Right , 1
  202.          End If
  203.       Next K
  204.    Next J
  205.  
  206.    Modbus_crc = C
  207.  
  208. End Function
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Awatar użytkownika
niveasoft
Posty: 1313
Rejestracja: 17 sie 2015, 12:13
Kontakt:

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: niveasoft » 29 maja 2026, 6:01

Modbus nie jest skomplikowany jak się go zrozumie. Mam z nim wiele projektów.
Tam mimo iż w ramce na początku podawana jest ilośc bajtów która będzie transmitowana to jeszcze kontroluje się czy coś nie utknęło poprzez timeout z timerem.
To znaczy gdyby program założył że do odczytania ma 20 bajtów a coś by przerwało transmisję to Twój program leży i kwiczy :lol:
Dlatego na podstawie prędkości taktowania procesora oraz prędkości transmisji oblicza się ile zajmuje przesłanie jednego znaku.

Są przykłady dla Bascom bezpośrednio w folderze Bascom\Samples\Serial
Bascom ten timeout robi tak że jedna funkcja oblicza ten czas potrzebny na jeden bajt i jak przychodzi bajt to ustawia timeout w timer0. Przychodzi kolejny bajt to przedłuża timeout i tak aż przestaną przychodzić bajty. Nie ma nowych bajtów to timeout i analiza ramki.

Przyjrzę się temu Twojemu kodowi, ale po południu bo rano to mam mnóstwo dziś roboty a będę też za kółkiem poza domem.

Co do Ai to które testowałeś? Ja przez jakieś dwa miesiące używałem ChatGPT nawet płatnie podając numer karty, ale zrezygnowałem bo w sprawach programowania to w porównaniu do Copilot od Microsoft to to jest niemota (do kodów Bascom AI nie używam).
Copilota dostałem w Visual Studio za darmo, ale teraz od 1 czerwca będzie jednak płatny 10$ (to chyba to samo co ChatGPT 34.99PLN)
Mi się Copilot przydaje bo oszczedza klawiaturę. Często generuje mi bloki kodu który musiałbym klepać ręcznie. Pomysł na program musisz jednak zawsze mieć swój.

Miałem kiedyś zadanie dla ChatGPT. Miałem wygenerowaną przez inny program tablicę 176 wartości, linia po linii w ten deseń:
134
185
155
.... itd...
Chciałem żeby mi to Ai opakowało tak by to zrozumiał Bascom najszybciej bez zbędnego parsowania:
Buff(1) = 134
Buff(2) = 185
Buff(3) = 155
...itd...
Wytłumaczenie tego Ai zajęło mi prawie godzine ( a juz jakies doświadczenie mam) i w miedzyczasie wygenerował kilka tych tablic :lol: Szybciej bym to zrobił ręcznie a może nawet automatem w Notepad++
Miałem jednak świadomośc że tych tablic mogę miec więcej więc może raz Ai nauczę to się potem już przyda i pójdzie z górki więc ten czas poświęciłem :lol: ale to była droga przez mękę. Copilot wydaje się sprytniejszy.

Co do Twojego kodu to juz na pierwszy rzut oka (pije poranną kawę to mam chwilę) widać że ten ChatGPT to niemota w Bascom.
Jesli w Bascom napiszesz $baud = 9600 to piny PD0 i PD1 zostają zajęte przez sprzętowy USART/SERIAL i już tam nic innego być nie może a Ai Tobie próbuje otworzyć softowy COMD.0 :lol: "Open "COMD.0:9600,8,N,1" For Binary As #1"
To nie może zadziałać.
Pomogę później.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Zbyszek
Posty: 96
Rejestracja: 30 lis 2017, 17:12

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: Zbyszek » 29 maja 2026, 9:01

Jeżeli możesz to bardzo proszę o pomoc, ja sam będę nad tym siedział, może coś wymyślę ...
Zbyszek
Posty: 96
Rejestracja: 30 lis 2017, 17:12

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: Zbyszek » 30 maja 2026, 19:22

Niestety nie mam pliku rs485-MODBUS-slave.bas
Awatar użytkownika
niveasoft
Posty: 1313
Rejestracja: 17 sie 2015, 12:13
Kontakt:

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: niveasoft » 31 maja 2026, 7:16

Daj mi jeszcze 5 minut (weekend) ahahahahaha :lol:
Żeby się komunikować z licznikiem potrzebujesz być "Mastere`m"
Licznik jest Slave
Zbyszek
Posty: 96
Rejestracja: 30 lis 2017, 17:12

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: Zbyszek » 31 maja 2026, 15:03

To że piszę w niedzielę to wcale nie znaczy że cię popędzam. Jak będziesz miał czas. Ja mam weekendy przeważnie pracujące, ten mam na II zmianie. Generalnie to nie napisałem co ta Atmega ma tak dokładnie robić. Cały czas na bieżąco odczytywać rejestr 2012H, który to rejestr przechowuje wartość mocy czynnej oddawanej do lub pobieranej z sieci. Czynna oddawana będzie ( tak obstawiam ) na pewno wartością ujemną przedstawioną w watach, i teraz Atmega ma sterować za pomocą PWM fazowym regulatorem mocy ( trójfazowym ) który w zależności od nadwyżki wyprodukowanej energii będzie zwiększał lub zmniejszał obciążenie tak żeby utrzymać np. - 9,9 kW oddawanej. Piszę o tym dlatego, że modbus na pewno będzie potrzebował timera ale PWM potem też. Wyświetlacz jest potrzebny żeby podejrzeć co Atmega odczytuje z licznika i jaka jest wartość PWM. Jeżeli obsługa wyświetlacza będzie za bardzo spowalniać układ to może przejdę na ledowy sterowany TM1637 ( miałem już z nim do czynienia i nie było problemów ) albo jak wszystko będzie pracować tak jak ma pracować to całkowicie zrezygnuję z wyświetlacza.
Awatar użytkownika
niveasoft
Posty: 1313
Rejestracja: 17 sie 2015, 12:13
Kontakt:

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: niveasoft » 01 cze 2026, 8:18

Do obsługi LCD potrzebujesz najmniej 6 pinów. Tyle ma w tych 28 nóżkowych Atmegach PORTC i jesli nie potrzebujesz ADC to można LCD`kiem zająć cały ten port.
Programowanie jest na PORTB3 do PORTB5 i to bym sobie darował coś tam podłączać chyba żeby potrzebne było wyjście OC2.
Na Modbus potrzebne PORTD0 do PORTD2.
Timer0 nie ma chyba w tej Atmedze wyjścia "OC0" więc mozna go na Modbus poswięcić. Zostaje Timer1 z PWM na PORTB1 i PORTB2
Ogólnie strachu nie ma tylko ja bym proponował się przesiąść na Atmega328P. Ma mądrzejsze Timery, PCINT`y, wiekszą pamięć. Ogólnie bardziej rozwojowa i nie myślisz od początku że musisz się zmieścić w 8kB....

Osobiście jeśli chciałbym coś zrobić na Mega8 to bym najpierw użył Arduino UNO/NANO z Mega328P i na koniec jak bym widział że kod się w 8kB zmieści to dopiero rozważał przesiadkę.
Zbyszek
Posty: 96
Rejestracja: 30 lis 2017, 17:12

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: Zbyszek » 07 cze 2026, 14:59

Jako że długi weekend mam w pracy od 6:00 do 18:00 ( ... i żeby nie było że mi się nie chce ) i czekam na gotowca, to zmieniłem trochę schemat. Zmieniłem mikrokontroler na Atmega 328P U. Pod komunikację przeznaczyłem porty RO - PD0/RxD , DI - PD1/TxD, RE/DE - PD4. Porty PD2/INT0 i PD3/INT1 przeznaczyłem pod przyciski + i -. Atmega ma odczytywać rejestr po modbus RTU i przyciskami będzie się ustawiać próg do którego Atmega ma dążyć regulując PWM. Próg będzie regulowany w granicach od -2 do -10. Na razie ogarnąłem sobie część schematu związaną z regulacją fazową. Na wyjściu OC1A układ całkujący, za nim wtórnik emiterowy, źródło napięcia odniesienia i wzmacniacz odejmujący. Zasilane jest to symetrycznie bo wtedy przy PWM=0 na TP2 mam 0V a przy PWM=1023 na TP2 mam 5V. Układ jest o tyle ciekawy że bawiąc się elementami przy wzmacniaczu operacyjnym na wyjściu TP4 uzyskuję napięcie jakie chcę pomiędzy napięciami zasilania wzmacniacza operacyjnego. Najważniejsze jest to że zmienia się liniowo wraz z PWM-em czyli praktycznie od -12 do +12V. Uład regulacji fazowej który będzie podłączony do TP4 też ładnie chodzi. W załączniku wstawiłem nowy schemat i arkusz excela do obliczania tych elementów, zmieniasz tylko wartości w żółtych polach.
Przyszła więc pora na odczyt modbus RTU. Znalazłem jakimś cudem ( w końcu ) na elektrodzie temat ( .. notabene brałeś w nim udział ) https://www.elektroda.pl/rtvforum/topic3776105.html który był moim punktem zaczepienia i z dużą pomocą AI ( może miała lepszy dzień ) coś tam stworzyłem. Kompilator nie wyrzuca błędów. Wrzucam to jako załącznik a jutro będę próbował na żywym organiźmie :shock:
  1. $regfile = "m328pdef.dat"
  2. $crystal = 16000000
  3. $baud = 9600
  4. $lib "modbus.lbx"
  5.  
  6. Config Lcd = 16 * 2
  7. Config Lcdpin = Pin , Db4 = Portc.4 , Db5 = Portc.3 , Db6 = Portc.2 , Db7 = Portc.1 , E = Portc.5 , Rs = Portc.0
  8. Cursor Off
  9. Cls
  10.  
  11. Config Portd.4 = Output : Reset Portd.4 : Rs485dir Alias Portd.4
  12.  
  13. Config Print1 = Portd.4 , Mode = Set
  14.  
  15. Config Com1 = 9600 , Parity = None , Stopbits = 1 , Databits = 8
  16.  
  17. Open "COM1:" For Binary As #1
  18.  
  19. Dim Rx(9) As Byte
  20. Dim I As Byte
  21. Dim B1 As Byte , B2 As Byte , B3 As Byte , B4 As Byte
  22. Dim Value As Single
  23.  
  24. Locate 1 , 1
  25. Lcd "MODBUS 2012H"
  26.  
  27. Do
  28.  
  29.     Print #1 , Makemodbus(1 , 3 , &H2012 , 4)               ' wysyłam zapytanie Modbus: FC03, slave 1, start address 0x2012, 2 rejestry = 4 bajty
  30.  
  31.                                                               ' i czekam na odpowiedź (slave, func, bytecount, 4 dane, 2 CRC = 9 bajtów)
  32.     I = 1
  33.     Do
  34.         If Ischarwaiting(#1) > 0 Then
  35.             Rx(i) = Waitkey(#1)
  36.             I = I + 1
  37.         End If
  38.     Loop Until I > 9
  39.  
  40.                                                               ' Sprawdzenie poprawności odpowiedzi
  41.     If Rx(2) = 3 And Rx(3) = 4 Then
  42.         B1 = Rx(4)
  43.         B2 = Rx(5)
  44.         B3 = Rx(6)
  45.         B4 = Rx(7)
  46.  
  47.         Dim Tempsingle As Single
  48.         Poke Varptr(tempsingle) , B1
  49.         Poke Varptr(tempsingle) + 1 , B2
  50.         Poke Varptr(tempsingle) + 2 , B3
  51.         Poke Varptr(tempsingle) + 3 , B4
  52.  
  53.         Value = Tempsingle
  54.  
  55.         Cls
  56.         Locate 1 , 1
  57.         Lcd "Reg 2012H:"
  58.         Locate 2 , 1
  59.         Lcd Fusing(value , "0.00")
  60.     End If
  61.  
  62.     Waitms 500
  63.  
  64. Loop
  65. End
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Ostatnio zmieniony 13 cze 2026, 10:14 przez Zbyszek, łącznie zmieniany 2 razy.
Zbyszek
Posty: 96
Rejestracja: 30 lis 2017, 17:12

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: Zbyszek » 08 cze 2026, 7:52

No niestety, na wyświetlaczu cisza ...
Awatar użytkownika
niveasoft
Posty: 1313
Rejestracja: 17 sie 2015, 12:13
Kontakt:

Re: Atmega Bascom odczyt rejestru po RS485 Modbus RTU

Post autor: niveasoft » 08 cze 2026, 16:32

Przepraszam, ale byłem obłozony robotą. Może się trochę obrobiłem i pomogę też w wątku z INA :D

Zanim się za coś zabieram to najczęściej czytam dokumentację. Ściągnąłem sobie tę Twoją, ale ściągnałem też Twój ostatni kod i może Tobie to zadziała nawet bez mojego kodu jeśli sprzedam Wam następny hint :lol:

Ai jeszcze się musi trochę podszkolić. Config Print który automatycznie steruje kierunkiem transmisji konfiguruje się tak że dla COM1 czyli pierwszy i jedyny SERIAL z Mega328P to piszesz Config Print, albo Config Print0

Config Print1 jest dla COM2. Piszą o tym w Helpie, ale nie Ty pierwszy na tym poległeś. To jest taka nomenklatura Bascoma w połączeniu z VB. W mikrokontrolerach masz SERIAL0 i potem SERIAL1 a w komputerach masz COM1 i COM2 no i tu się niejeden pomylił w Bascom.

Tak więć możliwe, że Twój program nigdy żadnego zapytania do licznika nie wysłał. :lol:

Jest jeszcze jeden hint. Czasem te transceivery są lenive i Basom jest dla nich za szybki. Za szybko kończy nadawanie. Wtedy lepiej nie używać Config Print tylko zrobić to ręcznie tak :
  1. Set Rs485dir
  2. Waitms 3
  3.  Print #1 .....
  4. Waitms 3
  5. Reset Rs485dir
W nowym Bascom już się to konfiguruje tak że mozna podać czas.

Ten Twój protokół to bardzo okrojony. W takim przypadku to w tym miejscu bym sobie dodał zerowanie tych dwóch bajtów bo inaczej to będzie migał tym CLS co 500ms
  1. If Rx(2) = 3 And Rx(3) = 4 Then
  2.   Rx(2) = 0
  3.   Rx(3) = 0  
ODPOWIEDZ