Witamy, Gość. Zaloguj się lub zarejestruj.
Aktualności:
Forum Sterowniki PLC Inteligentny dom > Systemy zarządzania >

Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485

Strony: [1]
Drukuj
Autor Wątek: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485  (Przeczytany 18624 razy)
Radek
Nowy
*
Wiadomości: 4


Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
: Luty 26, 2010, 15:40:51 pm »

Witam, od dawna chodzi mi po głowie pomysł zrealizowania mini HMI na Atmega i LCD 2x20. Ze sterownikiem komunikacja odbywałaby się za pomocą modbus RS485. Można było by wyświetlać różne zmienne, a także zadawać parametry (4 klawisze funkcyjne + menu).
Tu pojawia się prośba do PJ o udostępnienie części hardware i software swojego rozwiązania komunikacji modbus.
Pozdrawiam

EDIT(pj): zmieniłem temat.
« Ostatnia zmiana: Luty 27, 2010, 08:54:45 am wysłane przez pj »

pj
Global Moderator
Sr. Member
*****
Wiadomości: 303


Odp: Mini HMI
#1 : Luty 27, 2010, 08:32:47 am »

Heh, dobrze się dogadaliśmy z pominięciem ważnego szczegółu.

Ja swoje zabawki tworze w oparciu o PIC i biblioteki CCS.
Ale to nic może wyciągniesz to co potrzebne.

(będę używał skrótu uC - który oznacza mikrokontroler, czyli procek)
1) Hardware.

Komunikacja uC z PLC wydaje się prosta jeśli będzie bazować na protokole MODBUS over RS-485. (załączniki na dole posta).
PLC jest masterem Modbus. Odpytuje i zapisuje do slave'ów rejestry (może także odpytywać i zapisywać binarnie wejścia i wyjścia) do swoich podwładnych.
Sama magistrala bazuje na 3 przewodach: 2 sygnałowe oraz GND.

Od strony układu uC terminuję te sygnały na kości MAX487. (załącznik do posta)
Datasheet tutaj: http://www.atmicroprog.com/download/ci/MAX_485.pdf

Magistralę przypinasz do A i B.

Od strony układu uC - używasz standardowo USART czyli RX/TX + dodatkowy pin na sterowanie RE/DE.
Piny na kości MAX487 DE RE wskazują kierunek transmisji - odbiór/nadawanie. Jako że jeden jest zanegowany - łączymy je razem i ciągniemy do procka do pinu (do portu uC który posiada "podciągnięcie" czyli może dawać prąd) który będzie wybierał nasłuch (0) / nadawanie (1).
W nadawanie przełączamy się wyłącznie w odpowiedzi na zadane pytanie mastera. Po odpowiedzi znów trzeba przejść w nasłuch.

To właściwie wszystko od strony Hardware.
W moim przypadku używam PIC18F2550, 18F2550, 16F627A - na wszystkich działą tak samo. Byleby był układ USART (czyli RS-...).

2) Software.

W przypadku PICy i bibliotek CCS wszystko jest już gotowe.
Załączam źródła. Całą obsługa RS-485 zamyka się w:
a) deklaracje:
Kod:
#use delay(clock=24000000)

#define MODBUS_TYPE MODBUS_TYPE_SLAVE
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 9600
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define MODBUS_SERIAL_ENABLE_PIN   PIN_D4   // Controls DE pin for RS485
#define MODBUS_SERIAL_RX_ENABLE    PIN_D4   // Controls RE pin for RS485
#define MODBUS_ADDRESS 0x05
#include "modbus.c"

signed int16 OWTemp[16];  // Rejestry zawierające temperaturę o które odpyta sterownik PLC

b) inicjalizacja
Kod:
void Init(void)
{
.....
     modbus_init();
}

c) umieszczenie w pętli głównej obsługi modbus:
Kod:
...
      while(!modbus_kbhit());
      MODBUS_respond();
....

d) funkcji odpowiadającej na zapytanie
Kod:
void MODBUS_respond(void)
{
if((modbus_rx.address == MODBUS_ADDRESS) || modbus_rx.address == 0)
      {
         switch(modbus_rx.func)
         {
            case FUNC_READ_HOLDING_REGISTERS:
               if(modbus_rx.data[0] || modbus_rx.data[2] ||
                  modbus_rx.data[1] > 9 || modbus_rx.data[3]+modbus_rx.data[1] > 16)
                  modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
               else
                  modbus_read_holding_registers_rsp(MODBUS_ADDRESS,(modbus_rx.data[3]*2),OWTemp+modbus_rx.data[1]);
               break;
            case FUNC_READ_INPUT_REGISTERS:
            case FUNC_READ_COILS:    
            case FUNC_READ_DISCRETE_INPUT:    
            case FUNC_WRITE_SINGLE_COIL:      
            case FUNC_WRITE_SINGLE_REGISTER:
            case FUNC_WRITE_MULTIPLE_COILS:
            case FUNC_WRITE_MULTIPLE_REGISTERS:
            default:  
               modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_FUNCTION);
         }
      }

}

To właściwie tyle. To co umieścisz w tablicy OWTemp zostanie przekazane do sterownika PLC.

Na początku męczyłem się z ręcznym budowaniem ramek modbus i ręczną obsługą LCD, ale to strata czasu skoro ktoś napisał już profesjonalne biblioteki. To samo dotyczy wyświetlaczy LCD - obsługa ich to kilka linijek kodu. Po prostu bajka. Oczywiście developing nie trwa 2 minut, ale znacznie przyspiesza ten proces.

3) Obudowa
Sam zastanawiałem się nad takim samym rozwiązaniem, ale nie znajduję rozwiązania dot. "obudowania" takiego panelu.
Jestem estetą, więc wrzucenie tego w standardową obudowę raczej nie wchodzi w grę. Jak to opakować ?

Chciałbym żebyś rozwinął tutaj ideę czym i jak chcesz sterować - no i jak zasilać taki panel: 230 - potrzebna duża obudowa, 5V - potrzebny zew. zasilacz.
(Trochę nad tym już pracowałem i zrobiłem sobie zasilacz 5V na szynę DIN który umieściłbym w rozdzielni.)

Pozdrawiam,
PJ


* max487.png (68.06 KB, 1064x406 - wyświetlony 2096 razy.)

* uC-MAX487.png (18.73 KB, 1083x403 - wyświetlony 1804 razy.)

* zasilacz_1.jpg (39.13 KB, 400x300 - wyświetlony 1710 razy.)
« Ostatnia zmiana: Luty 27, 2010, 08:59:09 am wysłane przez pj »

Radek
Nowy
*
Wiadomości: 4


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#2 : Luty 27, 2010, 12:59:35 pm »

Witam,
Jako, że jestem automatykiem, wykonując różne projekty proste i co ważne o ograniczonych funduszach, bardzo często zachodzi potrzeba podglądania zmiennych jak i zadawania wartości procesowych. Zastosowanie gotowego produktu to koszt ok 1kPLN, ma to sens jeśli się wykorzystuje możliwości takich paneli ale nie zawsze jest to potrzebne.
Dlatego właśnie pomyślałem, że można to wykonać na uC i LCD + 4 przyciski. W instalacjach domu inteligentnego miało by to również ogromne zastosowanie, można byłoby np. wyświetlać temperatury, zadawać je itd. Umieszczając takie urządzonko przy wejściu np. do pokoju dziennego uzyskujemy możliwość sterowania bez konieczności zaglądania do SCADA na kompie.
Co do obudowy, tu zaczynają się schody ale mój pomysł jest taki:
- prostokąt wykonany z blachy nierdzewnej z wyciętym okienkiem na LCD.
- w okienku plexi odpowiednio wyfrezowana aby zewnętrzna pow był równa.
- pozostaje jeszcze kwestia przycisków do dopracowania.
- od spodu puszka, którą obsadza się w ścianie.

Ostatnio w firmie zaprojektowaliśmy podobną obudowę do wyświetlacza LCD do windy ( wyświetlacz nad drzwiami wejściowymi). Muszę przyznać, że całkiem nieźle się to prezentuje, myślę, że tu też można byłoby coś podobnego stworzyć tylko oczywiście mniejsze.
Co do kwestii zasilania, jak dla mnie podstawą jest 24VDC i na takie napięcie miało by to działać. Przeprowadzałem testy i LM7805 z malutkim radiatorkiem spokojnie załatwi na sprawę zasilania (podpięta Atmega32 + LCD 2x16).
Oczywiście dziękuję ci za podzielenie się swoim projektem Chichot. Jednak nie zrozumiałem jednej rzeczy, mianowicie z modbusa korzystam bardzo często i jeśli PLC jest masterem to muszę w nim podać adresy rejestrów z jakich ma odczytywać z slava, jakie to są adresy w twoim rozwiązaniu.
Pozdrawiam i mam nadzieje, że uda się coś takiego stworzyć.
 

Radek
Nowy
*
Wiadomości: 4


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#3 : Luty 27, 2010, 15:08:37 pm »

Witam, po przeszukaniu informacji w internecie zorientowałem się, że na Atmege modbus jest zdecydowanie trudniejszy.
W związku z tym może opisałbyś szerzej jakie środowisko i kompilator używasz do tego PICa. Osobiście jakoś nie miałem nigdy z nimi do czynienia. Nie będzie to rozbudowana aplikacja więc myślę, że spokojnie sobie poradzi.
Pozdrawiam i z góry dzięki za pomoc 

pj
Global Moderator
Sr. Member
*****
Wiadomości: 303


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#4 : Marzec 02, 2010, 15:59:13 pm »

Cytat: Radek  Luty 27, 2010, 12:59:35 pm
Dlatego właśnie pomyślałem, że można to wykonać na uC i LCD + 4 przyciski. W instalacjach domu inteligentnego miało by to również ogromne zastosowanie, można byłoby np. wyświetlać temperatury, zadawać je itd. Umieszczając takie urządzonko przy wejściu np. do pokoju dziennego uzyskujemy możliwość sterowania bez konieczności zaglądania do SCADA na kompie.

No tutaj właśnie brakuje mi trochę pomysłu i wyobraźni co można by dokładnie tam umieścić.
Temperatura. No niby okej, ale:
1) od czego mamy sterownik pieca - dobrze wyregulowane ogrzewanie utrzyma dobrą temp. we wszystkich pomieszczeniach.
2) jak często potrzebuję sprawdzić / zmieniać temperaturę
3) temperatury planuję mieć "w telewizorze"

Inne parametry, które wchodzą w grę:
- wentylacja / monitoring wentylacji
- alarm ? (mamy przecież panel alarmu)
- poziom szamba - no tutaj widzę zastosowanie - ale to też będzie w telewizorze
- operowanie światłami.. hmmm

Cytat: Radek  Luty 27, 2010, 12:59:35 pm
Co do obudowy, tu zaczynają się schody ale mój pomysł jest taki:
- prostokąt wykonany z blachy nierdzewnej z wyciętym okienkiem na LCD.
- w okienku plexi odpowiednio wyfrezowana aby zewnętrzna pow był równa.
- pozostaje jeszcze kwestia przycisków do dopracowania.
- od spodu puszka, którą obsadza się w ścianie.

No bardzo dobry początek. Hmm... wydaje mi się, że aluminium byłoby lepsze.
Masz jakieś zdjęcia ?

Cytat: Radek  Luty 27, 2010, 12:59:35 pm
Co do kwestii zasilania, jak dla mnie podstawą jest 24VDC i na takie napięcie miało by to działać. Przeprowadzałem testy i LM7805 z malutkim radiatorkiem spokojnie załatwi na sprawę zasilania (podpięta Atmega32 + LCD 2x16).

Jaki prąd może pobierać taki układ - powiedzmy 50mA (30mA to podświetlenie LCD)
Moc strat na stablilzatorze: (24-5)/0,05 ~= 0,95W
Faktycznie - nie potrzeba nawet zew. radiatora.

Cytat: Radek  Luty 27, 2010, 12:59:35 pm
Oczywiście dziękuję ci za podzielenie się swoim projektem Chichot. Jednak nie zrozumiałem jednej rzeczy, mianowicie z modbusa korzystam bardzo często i jeśli PLC jest masterem to muszę w nim podać adresy rejestrów z jakich ma odczytywać z slava, jakie to są adresy w twoim rozwiązaniu.

Jeśli chodzi o kod powyżej to rejestry zaczynające się od zera czyli 400001.


Środowisko darmowe: Microchip MPLAB IDE
Do tego (komercyjne, ale "do znalezienia w sieci") biblioteki: CCS PCWHD
Po instalacji otwierasz projekt w MPLABie i wybierasz środowisko kompilacji CCS.

Zajrzyj do katalogu CCS po instalacji: Examples - dużo ciekawych przykładów wykorzystania biblioteki i fajny help.

Pozdrawiam,
PJ

zylek
Jr. Member
**
Wiadomości: 19


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#5 : Marzec 11, 2010, 13:51:43 pm »

Cytat: pj  Marzec 02, 2010, 15:59:13 pm
Środowisko darmowe: Microchip MPLAB IDE
Do tego (komercyjne, ale "do znalezienia w sieci") biblioteki: CCS PCWHD
Po instalacji otwierasz projekt w MPLABie i wybierasz środowisko kompilacji CCS.

Zajrzyj do katalogu CCS po instalacji: Examples - dużo ciekawych przykładów wykorzystania biblioteki i fajny help.

Na ATMEGA subiektywnie, jak dla kogoś kto nie ma pojęcia o elektronice i nigdy nie programował w C, "MODBUS" nie jest trudny, trzeba znaleźć tylko odpowiednie biblioteki.
Ja skorzystałem z Arduino modbus rtu (robiłem tylko slave a zasadniczo to nie biblioteka ale gotowiec.   ) - http://sites.google.com/site/jpmzometa/arduino-mbrt
+ zestawy biblotek Arduino + Sanguino.
Całość jest OpenSource.

pozdrawiam,
Andrzej

Radek
Nowy
*
Wiadomości: 4


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#6 : Marzec 11, 2010, 18:24:33 pm »

Witam,
kolego Zylek mógłbyś się podzielić gotowym przykładem modbusa na Atmegę . Chwilowo projekt został odłożony na bok ze względów zawodowych (ogrom delegacji) ale jak wrócę to wrzucę zdjęcia wspomnianej wcześniej obudowy i może już prototyp mojej.
Pozdrawiam

zylek
Jr. Member
**
Wiadomości: 19


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#7 : Marzec 18, 2010, 23:20:00 pm »

Cytat: Radek  Marzec 11, 2010, 18:24:33 pm
...kolego Zylek mógłbyś się podzielić gotowym przykładem modbusa na Atmegę .
No gotowy jest pod podanym adresem który podałem  Musisz tylko ustawić adresy pin'ów. Wymagane jest zastosowanie bibliotek z http://www.arduino.cc.  Przydługie ale co tam, u mnie wygląda to tak:
Kod:
void init_mb_slave(long baud, char parity, char txenpin);
int start_mb_slave(unsigned char slave, int *regs, unsigned int regs_size);

/* Modbus RTU common parameters, the Master MUST use the same parameters */
enum {
        MB_SLAVE = 4, /* modbus slave id */
};
  /* slave registers */
  enum {       
          MB_TMP0,        /* Temps regs for Dallas Semicondutors temperature mesurments. You can add more then 24, by putting new lines*/
          MB_TMP1,
          MB_TMP2,
          MB_TMP3,
          MB_TMP4,
          MB_TMP5,
          MB_TMP6,
          MB_TMP7,
          MB_TMP8,
          MB_TMP9,
          MB_TMP10,
          MB_TMP11,
          MB_TMP12,
          MB_TMP13,
          MB_TMP14,
          MB_TMP15,
          MB_TMP16,
          MB_TMP17,
          MB_TMP18,
          MB_TMP19,
          MB_TMP20,
          MB_TMP21,
          MB_TMP22,
          MB_TMP23,
          MB_TMP24,
          MB_TMP25,
          MB_TMP26,
          MB_TMP27,
          MB_TMP28,
          MB_TMP29,
          MB_TMP30,
          MB_TMP31,
          MB_TMP32,
          MB_TMP33,
          MB_TMP34,
          MB_TMP35,
          MB_TMP36,
          MB_TMP37,
          MB_TMP38,
          MB_TMP39,
          MB_TMP40,
          MB_TMP41,
          MB_TMP42,
          MB_TMP43,
          MB_TMP44,
          MB_TMP45,
          MB_TMP46,
          MB_TMP47,
          MB_TMP48,
          MB_TMP49,
          MB_DSBN,
          MB_DSDN,
          MB_DSIDX,
          MB_DSAD0,
          MB_DSAD1,
          MB_DSAD2,
          MB_DSAD3,
          MB_DSAD4,
          MB_DSAD5,
          MB_DSAD6,
          MB_DSAD7,         
          MB_IRC,
          MB_STH1TEM,
          MB_STH1HUM,
          MB_STH1DEW,
          MB_SETDSFLG, // Registers MB_SETDS* are used for DS sort
          MB_SETDSIDX, // If you add some rows before (above) this point
          MB_SETDSAD0, // you have to change constant regsRowWriteNum
          MB_SETDSAD1,
          MB_SETDSAD2,
          MB_SETDSAD3,
          MB_SETDSAD4,
          MB_SETDSAD5,
          MB_SETDSAD6,
          MB_SETDSAD7,
          MB_REGS, /* total number of registers on slave */
  };
# define regsRowWriteNum 67
 
int regs[MB_REGS]; /* this is the slave's modbus data map */
unsigned long wdog = 0; /* watchdog */

void setup() {
   // Startup MODBUS lib
   /* Modbus setup, the master must use the same COM parameters */
   /* 115200 bps, 8N1, two-device network */
   lcdsetcur("INIT:ModBUS Lib ");
   init_mb_slave(19200, 'n', 4);
   delay(1000);
}
void loop() {
  // ***********************************************************
  /* This is all for the Modbus slave */
  // ***********************************************************
  start_mb_slave(MB_SLAVE, regs, MB_REGS);
}


/****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 * BEGIN MODBUS RTU SLAVE FUNCTIONS
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************/

/* global variables */
unsigned int Txenpin = 0;        /* Enable transmission pin, used on RS485 networks */

/* supported functions. If you implement a new one, put its function code into this array! */
const unsigned char fsupported[] = {
        0x03, 0x10 };

/* constants */
enum {
        MAX_READ_REGS = 0x7D,
        MAX_WRITE_REGS = 0x7B,
        MAX_MESSAGE_LENGTH = 256
};


enum {
        RESPONSE_SIZE = 6,
        EXCEPTION_SIZE = 3,
        CHECKSUM_SIZE = 2
};

/* exceptions code */
enum {
        NO_REPLY = -1,
        EXC_FUNC_CODE = 1,
        EXC_ADDR_RANGE = 2,
        EXC_REGS_QUANT = 3,
        EXC_EXECUTE = 4
};

/* positions inside the query/response array */
enum {
        SLAVE = 0,
        FUNC,
        START_H,
        START_L,
        REGS_H,
        REGS_L,
        BYTE_CNT
};


/*
CRC
 
 INPUTS:
  buf   ->  Array containing message to be sent to controller.           
  start ->  Start of loop in crc counter, usually 0.
  cnt   ->  Amount of bytes in message being sent to controller/
 OUTPUTS:
  temp  ->  Returns crc byte for message.
 COMMENTS:
  This routine calculates the crc high and low byte of a message.
  Note that this crc is only used for Modbus, not Modbus+ etc.
 ****************************************************************************/

unsigned int crc(unsigned char *buf, unsigned char start,
unsigned char cnt)
{
        unsigned char i, j;
        unsigned temp, temp2, flag;

        temp = 0xFFFF;

        for (i = start; i < cnt; i++) {
                temp = temp ^ buf[i];

                for (j = 1; j <= 8; j++) {
                        flag = temp & 0x0001;
                        temp = temp >> 1;
                        if (flag)
                                temp = temp ^ 0xA001;
                }
        }

        /* Reverse byte order. */
        temp2 = temp >> 8;
        temp = (temp << 8) | temp2;
        temp &= 0xFFFF;

        return (temp);
}




/***********************************************************************
 *
 * The following functions construct the required query into
 * a modbus query packet.
 *
 ***********************************************************************/

/*
 * Start of the packet of a read_holding_register response
 */
void build_read_packet(unsigned char slave, unsigned char function,
unsigned char count, unsigned char *packet)
{
        packet[SLAVE] = slave;
        packet[FUNC] = function;
        packet[2] = count * 2;
}

/*
 * Start of the packet of a preset_multiple_register response
 */
void build_write_packet(unsigned char slave, unsigned char function,
unsigned int start_addr,
unsigned char count,
unsigned char *packet)
{
        packet[SLAVE] = slave;
        packet[FUNC] = function;
        packet[START_H] = start_addr >> 8;
        packet[START_L] = start_addr & 0x00ff;
        packet[REGS_H] = 0x00;
        packet[REGS_L] = count;
}



/*
 * Start of the packet of an exception response
 */
void build_error_packet(unsigned char slave, unsigned char function,
unsigned char exception, unsigned char *packet)
{
        packet[SLAVE] = slave;
        packet[FUNC] = function + 0x80;
        packet[2] = exception;
}


/*************************************************************************
 *
 * modbus_query( packet, length)
 *
 * Function to add a checksum to the end of a packet.
 * Please note that the packet array must be at least 2 fields longer than
 * string_length.
 **************************************************************************/

void modbus_reply(unsigned char *packet, unsigned char string_length)
{
        int temp_crc;

        temp_crc = crc(packet, 0, string_length);
        packet[string_length] = temp_crc >> 8;
        string_length++;
        packet[string_length] = temp_crc & 0x00FF;
}



/***********************************************************************
 *
 * send_reply( query_string, query_length )
 *
 * Function to send a reply to a modbus master.
 * Returns: total number of characters sent
 ************************************************************************/

int send_reply(unsigned char *query, unsigned char string_length)
{
        unsigned char i;

        if (Txenpin > 1) { // set MAX485 to speak mode
                UCSR0A=UCSR0A |(1 << TXC0);
                digitalWrite( Txenpin, HIGH);
                delay(1);
        }

        modbus_reply(query, string_length);
        string_length += 2;

        for (i = 0; i < string_length; i++) {
                Serial.print(query[i], BYTE);
        }

        if (Txenpin > 1) {// set MAX485 to listen mode
                while (!(UCSR0A & (1 << TXC0)));
                digitalWrite( Txenpin, LOW);
        }

        return i; /* it does not mean that the write was succesful, though */
}

/***********************************************************************
 *
 * receive_request( array_for_data )
 *
 * Function to monitor for a request from the modbus master.
 *
 * Returns: Total number of characters received if OK
 * 0 if there is no request
 * A negative error code on failure
 ***********************************************************************/

int receive_request(unsigned char *received_string)
{
        int bytes_received = 0;

        /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */
        while (Serial.available()) {
                received_string[bytes_received] = Serial.read();
                bytes_received++;
                if (bytes_received >= MAX_MESSAGE_LENGTH)
                        return NO_REPLY; /* port error */
        }

        return (bytes_received);
}


/*********************************************************************
 *
 * modbus_request(slave_id, request_data_array)
 *
 * Function to the correct request is returned and that the checksum
 * is correct.
 *
 * Returns: string_length if OK
 * 0 if failed
 * Less than 0 for exception errors
 *
 * Note: All functions used for sending or receiving data via
 *       modbus return these return values.
 *
 **********************************************************************/

int modbus_request(unsigned char slave, unsigned char *data)
{
        int response_length;
        unsigned int crc_calc = 0;
        unsigned int crc_received = 0;
        unsigned char recv_crc_hi;
        unsigned char recv_crc_lo;

        response_length = receive_request(data);

        if (response_length > 0) {
                crc_calc = crc(data, 0, response_length - 2);
                recv_crc_hi = (unsigned) data[response_length - 2];
                recv_crc_lo = (unsigned) data[response_length - 1];
                crc_received = data[response_length - 2];
                crc_received = (unsigned) crc_received << 8;
                crc_received =
                        crc_received | (unsigned) data[response_length - 1];

                /*********** check CRC of response ************/
                if (crc_calc != crc_received) {
                        return NO_REPLY;
                }

                /* check for slave id */
                if (slave != data[SLAVE]) {
                        return NO_REPLY;
                }
        }
        return (response_length);
}

/*********************************************************************
 *
 * validate_request(request_data_array, request_length, available_regs)
 *
 * Function to check that the request can be processed by the slave.
 *
 * Returns: 0 if OK
 * A negative exception code on error
 *
 **********************************************************************/

int validate_request(unsigned char *data, unsigned char length,
unsigned int regs_size)
{
        int i, fcnt = 0;
        unsigned int regs_num = 0;
        unsigned int start_addr = 0;
        unsigned char max_regs_num;

        /* check function code */
        for (i = 0; i < sizeof(fsupported); i++) {
                if (fsupported[i] == data[FUNC]) {
                        fcnt = 1;
                        break;
                }
        }
        if (0 == fcnt)
                return EXC_FUNC_CODE;

        /* check quantity of registers */
        if (0x03 == data[FUNC])
                max_regs_num = MAX_READ_REGS;
        else if (0x10 == data[FUNC])
                max_regs_num = MAX_WRITE_REGS;

        regs_num = ((int) data[REGS_H] << 8) + (int) data[REGS_L];

        if ((regs_num < 1) || (regs_num > max_regs_num))
                return EXC_REGS_QUANT;

        /* check registers range, start address is 0 */
        start_addr = ((int) data[START_H] << 8) + (int) data[START_L];
        if ((start_addr + regs_num) > regs_size)
                return EXC_ADDR_RANGE;

        return 0; /* OK, no exception */
}



/************************************************************************
 *
 * write_regs(first_register, data_array, registers_array)
 *
 * writes into the slave's holding registers the data in query,
 * starting at start_addr.
 *
 * Returns:   the number of registers written
 ************************************************************************/

int write_regs(unsigned int start_addr, unsigned char *query, int *regs)
{
        int temp;
        unsigned int i;

        for (i = 0; i < query[REGS_L]; i++) {
                /* shift reg hi_byte to temp */
                temp = (int) query[(BYTE_CNT + 1) + i * 2] << 8;
                /* OR with lo_byte           */
                temp = temp | (int) query[(BYTE_CNT + 2) + i * 2];

                regs[start_addr + i] = temp;
        }
        return i;
}

/************************************************************************
 *
 * preset_multiple_registers(slave_id, first_register, number_of_registers,
 * data_array, registers_array)
 *
 * Write the data from an array into the holding registers of the slave.
 *
 *************************************************************************/

int preset_multiple_registers(unsigned char slave,
unsigned int start_addr,
unsigned char count,
unsigned char *query,
int *regs)
{
        unsigned char function = 0x10; /* Function: Preset Multiple Registers */
        int status = 0;
        unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE];

        build_write_packet(slave, function, start_addr, count, packet);

        if (write_regs(start_addr, query, regs)) {
                status = send_reply(packet, RESPONSE_SIZE);
        }

        return (status);
}

/************************************************************************
 *
 * read_holding_registers(slave_id, first_register, number_of_registers,
 * registers_array)
 *
 * reads the slave's holdings registers and sends them to the Modbus master
 *
 *************************************************************************/

int read_holding_registers(unsigned char slave, unsigned int start_addr,

unsigned char reg_count, int *regs)
{
        unsigned char function = 0x03; /* Function 03: Read Holding Registers */
        int packet_size = 3;
        int status;
        unsigned int i;
        unsigned char packet[MAX_MESSAGE_LENGTH];

        build_read_packet(slave, function, reg_count, packet);

        for (i = start_addr; i < (start_addr + (unsigned int) reg_count);
       i++) {
                        packet[packet_size] = regs[i] >> 8;
                packet_size++;
                packet[packet_size] = regs[i] & 0x00FF;
                packet_size++;
        }

        status = send_reply(packet, packet_size);

        return (status);
}

void init_mb_slave(long baud, char parity, char txenpin)
{
        Serial.begin(baud);

        switch (parity) {
        case 'e': // 8E1
                UCSR0C |= ((1<<UPM01) | (1<<UCSZ01) | (1<<UCSZ00));
                //      UCSR0C &= ~((1<<UPM00) | (1<<UCSZ02) | (1<<USBS0));
                break;
        case 'o': // 8O1
                UCSR0C |= ((1<<UPM01) | (1<<UPM00) | (1<<UCSZ01) | (1<<UCSZ00));
                //      UCSR0C &= ~((1<<UCSZ02) | (1<<USBS0));
                break;
        case 'n': // 8N1
                UCSR0C |= ((1<<UCSZ01) | (1<<UCSZ00));
                //      UCSR0C &= ~((1<<UPM01) | (1<<UPM00) | (1<<UCSZ02) | (1<<USBS0));
                break;               
        default:
                break;
        }

        if (txenpin > 1) { // pin 0 & pin 1 are reserved for RX/TX
                Txenpin = txenpin; /* set global variable */
                pinMode(Txenpin, OUTPUT);
                digitalWrite(Txenpin, LOW);
        }

        return;
}   

/*
 * start_mb_slave(slave_id, holding_regs_array, number_of_regs)
 *
 * checks if there is any valid request from the modbus master. If there is,
 * performs the action requested
 */

unsigned long Nowdt = 0;
unsigned int lastBytesReceived;
const unsigned long T35 = 5;

int start_mb_slave(unsigned char slave, int *regs,
unsigned int regs_size)
{
        unsigned char query[MAX_MESSAGE_LENGTH];
        unsigned char errpacket[EXCEPTION_SIZE + CHECKSUM_SIZE];
        unsigned int start_addr;
        int exception;
        int length = Serial.available();
        unsigned long now = millis();

        if (length == 0) {
                lastBytesReceived = 0;
                return 0;
        }

        if (lastBytesReceived != length) {
                lastBytesReceived = length;
                Nowdt = now + T35;
                return 0;
        }
        if (now < Nowdt)
                return 0;

        lastBytesReceived = 0;

        length = modbus_request(slave, query);
        if (length < 1) {
                return length;
        }
        else {
                exception = validate_request(query, length, regs_size);
                if (exception) {
                        build_error_packet(slave, query[FUNC], exception,
                        errpacket);
                        send_reply(errpacket, EXCEPTION_SIZE);
                        return (exception);
                }
                else {
                        start_addr =
                                ((int) query[START_H] << 8) +
                                (int) query[START_L];
                        if (0x03 == query[FUNC]) {
                                return read_holding_registers(slave, start_addr,
                                query[REGS_L],
                                regs);

                        }

                        if (0x10 == query[FUNC])
                                return preset_multiple_registers(slave,
                                start_addr,
                                query
                                        [REGS_L],
                                query,
                                regs);
                }
        }
}



fuul
Full Member
***
Wiadomości: 22


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#8 : Wrzesień 03, 2010, 21:42:42 pm »

Wymagane jest zastosowanie bibliotek z http://www.arduino.cc.

Mam problemy z zainstalowaniem bibliotek arduino (wiring).
Proszę o krótką instrukcję.

zylek
Jr. Member
**
Wiadomości: 19


Odp: Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#9 : Wrzesień 09, 2010, 08:59:47 am »

Cytat: fuul  Wrzesień 03, 2010, 21:42:42 pm
Mam problemy z zainstalowaniem bibliotek arduino (wiring).
Proszę o krótką instrukcję.

Z czym konkretnie masz problem? Napisz co robisz a czego zrobić nie możesz wtedy coś wspólnie wymóżdżymy.

Zylek

ojciec10
Nowy
*
Wiadomości: 1


Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#10 : Kwiecień 05, 2012, 16:52:17 pm »

Witam. Jestem tutaj nowy.
Jestem kompletnie zielony w cc i ardunio.
Czy mogłby mi ktos podać dokładny schemat połączeń i najlepiej wsad albo program (bascom) do zaprogramowania procesorka?
Dziękuję i pozdrawiam.

Mario0658
Sr. Member
****
Wiadomości: 214


Mini HMI - komunikacja układu uC ze sterownikiem PLC po MODBUS RS-485
#11 : Listopad 06, 2014, 16:59:04 pm »

witam 

mam urzadzonko juz gotowe - zlozone  wlasnie z atmega   
rs485  wkomponowany 
trzeba tylko soft   napisac  ....

http://plc-home.pl/forum/empty-t360.0.html

GG:4010578

Strony: [1]
Drukuj
Skocz do: