Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2015:plc-st7580

Rozdíly

Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.

Odkaz na výstup diff

Následující verze
Předchozí verze
2015:plc-st7580 [2016/01/16 03:08]
Ján Sláčik vytvořeno
2015:plc-st7580 [2016/01/20 16:52] (aktuální)
Ján Sláčik [Rozbor zadání]
Řádek 1: Řádek 1:
-====== ​Zadání ​======+====== ​Rozbor zadání ​====== 
 +Úkolem tohoto projektu je demonstrovat datový tok po silovém vedení a to ve standardní napájecí síti 230 VAC/50 Hz. Zvolený komunikační kmitočet spadá do kategorie úzkopásmových technologií,​ tedy frekvenční rozsah je 3-500 kHz. Pro realizaci byl vybrán vhodný integrovaný obvod ST7580 v pozici PLC modemu, s programovatelnou nosnou frekvencí do 250 kHz. Obvod mimo jiné odpovídá Evropské normě CENELEC (EN 50065), tedy tento obvod je možné nasadit i do veřejných napájecích sítí. ​ Čip vyžaduje obsluhu externím hostitelem přes rozhraní UART. Do této pozice byl vybrán mikrokontrolér architektury ARM, typu STM32 řady F0. V rámci projektu byl vytvořen obslužný software základních funkcí obvodu ST7580, které umožňují funkční přenos po uvedeném silovém vedení. 
 +\\ 
 + ​====== Princip řešení ====== 
 +Je vytvořená jednoduchá komunikační síť s dvěma uzly, které tvoří jednotky master a slave, podle obrázku níže.  
 +{{ :​2015:​plc-st7580:​blockdiag.png?​750 |}} 
 +Konkrétní obvodové zapojení jednotky master je tvořeno vývojovým kitem EVALKITST7580-1 jako PLC modem a kitem STM32F0Discovery jako jednotka hostitele. Toto zapojení je totožné i pro slave. 
 +Obslužný software je tedy implementován v hostitelských MCU, který realizuje jak komunikaci s modemem, tak komunikaci po elektrickém vedení. Vytvořený software tvoří jednotlivé funkce, které je pak v rámci aplikace možné použít jako knihovní. ​ V textu jsou popsány jen klíčové funkční části, bez nastavení procesoru, periferií nebo některých deklarací a definicí. Zběžně je přiblížen princip komunikace s modemem a vytvořené řešení. 
 +Výstupem projektu je řízení aktoru (RGB dioda), který je součást jednotky slave, a výpisu jeho stavu na LCD displej. Ovládání řeší uživatelské tlačítko na jednotce master. 
 +\\  
 +\\  
 +{{ :​2015:​plc-st7580:​connection.jpg |}} 
 +\\  
 + 
 +Vytvořený software realizuje tyto funkce:  
 +  * lokální komunikace 
 +  * konfigurace modemu 
 +  * vytvoření korektní zprávy pro PLC modem 
 +  * odeslání dat do elektrické sítě 
 +  * příjem dat z elektrické sítě 
 +  * rozpoznání a zpracování přijatých dat 
 +  * předání potřebných dat externí funkci pro řízení periferií 
 +\\  
 + 
 +===== Lokální komunikace (hostitel – PLC modem) ===== 
 +Lokální komunikaci master zahájí funkcí ''​_ST7580sendCommand'',​ splňující požadavky lokálního rámce PLC modemu. Na straně jednotky slave je to funkce ''​ST7580poll''​ 
 +Pro celé řízení systému jsou zásadní příkazové kódy, které definují typ zprávy, např. žádost o konfiguraci modemu, odesílání dat do el. sítě, indikace přijatých zpráv, nebo chybová hlášení. 
 +{{ :​2015:​plc-st7580:​localframe.png?​700 |}}\\  
 +Pole data určené právě pro přenos uživatelských dat, je implementuje rovněž vytvořený aplikační protokol pro řízení výstupů hostitelského MCU na jednotce slave. Lokální a aplikační rámec je zobrazen na obrázku níže. 
 + 
 +{{ :​2015:​plc-st7580:​appframe.png?​700 |}}\\  
 +Argumenty funkce ''​_ST7580sendCommand''​ jsou právě hodnoty polí lokálního rámce.  
 +<code c> 
 +static int _ST7580sendCommand(ST7580_STX_Typedef stx, 
 +                             ​uint8_t len, 
 +                             ​ST7580_CmdCode_Typedef cmdCode, 
 +                             ​uint8_t* dataIn, 
 +                             ​uint8_t ​ maxOutLen,​ 
 +                             ​uint8_t* dataOut, 
 +                             ​uint8_t* lenOut) 
 +
 +    int i; 
 +    uint16_t ​ checksum = 0; 
 +    uint8_t ​  ​ackNack;​ 
 +    uint8_t ​  ​isReady[2] = {0, 0}; 
 +    uint8_t ​  ​respStx = 0, respLen = 0, respCmd = 0; 
 +    uint16_t ​ respChecksum = 0; 
 +    uint8_t ​  ​dataReqConfig = 0x24; 
 + 
 +    if(!dataIn) return ST7580_ERR;​ 
 +    if((!dataOut)&&​(maxOutLen!=0)) return ST7580_ERR;​ 
 + 
 +    RingBuffer_flush((RingBuffer*)ST7580_RB_U1);​ 
 + 
 +    HAL_GPIO_WritePin(GPIOA,​ GPIO_PIN_11,​ GPIO_PIN_RESET);​ 
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)isReady,​ 2); 
 +    HAL_GPIO_WritePin(GPIOA,​ GPIO_PIN_11,​ GPIO_PIN_SET);​ 
 + 
 +    if((isReady[0] != ST7580_READY_B0) || (isReady[1] != ST7580_READY_B1)) 
 +    { 
 +        goto ERROR; 
 +    } 
 +     
 +    /* checksum consists of len, CMD, payload */ 
 +    for(i = 0; i < len; i++) 
 +    { 
 +        checksum += dataIn[i];​ 
 +    } 
 +    checksum += cmdCode; 
 + 
 +    switch(cmdCode) 
 +    { 
 +        default: 
 +            checksum += len; 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​stx,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​len,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​cmdCode,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)dataIn,​ len, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​checksum,​ 2, ST7580_ACK_TIMEOUT);​ 
 +            break; 
 +        case DL_DataRequest:​ 
 +            checksum += dataReqConfig;​ 
 +            len++; 
 +            checksum += len; 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​stx,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​len,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​cmdCode,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​dataReqConfig,​ 1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)dataIn,​ len-1, ST7580_ACK_TIMEOUT);​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​checksum,​ 2, ST7580_ACK_TIMEOUT);​ 
 +            break; 
 +    } 
 + 
 +    /* receive the ACK */ 
 +            RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​ackNack,​ 1); 
 + 
 +    if(ackNack != ST7580_ACK) 
 +    { 
 +             goto ERROR; 
 +    } 
 +     
 +    /* receive the STX and Lenght */  
 +            RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​respStx,​ 1); 
 +         
 +    if(respStx != stx) 
 +    { 
 +        goto ERROR; 
 +    } 
 +         
 +            RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​respLen,​ 1); 
 +         
 +    if(respLen != maxOutLen) 
 +    { 
 +        goto ERROR; 
 +    } 
 + 
 +    if(lenOut != NULL) 
 +    { 
 +        *lenOut = respLen;  
 +    } 
 +         
 +     
 +    /* receive the CMD code */ 
 +            RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​respCmd,​ 1); 
 +         
 + 
 +    if(respCmd != (cmdCode+1)) 
 +    { 
 +            goto ERROR; 
 +    } 
 + 
 +    /* receive the payload */ 
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)dataOut,​ respLen); 
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​respChecksum,​ 2); 
 + 
 +    checksum = 0; 
 + 
 +    for(i = 0; i < respLen; i++) 
 +    { 
 +            checksum += dataOut[i];​ 
 +    } 
 +    checksum += respLen; 
 +    checksum += respCmd; 
 + 
 +    if(checksum == respChecksum) 
 +    { 
 +        /* send the ACK */ 
 +        ackNack = ST7580_ACK;​ 
 +        HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​ackNack,​ 1, ST7580_ACK_TIMEOUT);​  
 +        return ST7580_OK;​ 
 +    } 
 +ERROR: 
 + /* send the NACK */ 
 +ackNack = ST7580_NACK;​ 
 +HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​ackNack,​ 1, ST7580_ACK_TIMEOUT);​ 
 +return ST7580_ERR;  
 +
 +</​code>​ 
 +Po skončení komunikace s hostitel-modem,​ funkce vrací stavy ''​ST7580_OK''​ nebo ''​ST7580_ERR''​. 
 +\\  
 +\\ 
 + 
 +===== Konfigurace modemu ===== 
 +Princip konfigurace modemu je definován několika deklarovanými sktrukturami,​ které definují potřebnou konfiguraci. 
 +<code c> 
 +typedef struct 
 +
 +#define ST7580_MODEM_CONFIG_BASE (0x00) 
 +#define ST7580_MODEM_CONFIG_LEN ​ (0x02) 
 +    uint8_t modemConfigIndex; ​     /*Index of the MIB object (0x00)*/ 
 +    uint8_t modemConfig; ​        /​*Actual value of the MIB object*/ 
 +}ST7580modemConfigTypedef;​ 
 + 
 +typedef struct 
 +
 +#define ST7580_PHY_CONFIG_BASE (0x01) 
 +#define ST7580_PHY_CONFIG_LEN ​ (0x0F) 
 +    uint8_t phyConfigIndex; ​     /* Index of the MIB object (0x01) */ 
 +    uint8_t DlFreqPair[6]; ​      /* Frequency Pair Byte 0 - 5*/ 
 +    uint8_t DlRXControl; ​        /*RX Control Byte 6 */ 
 +    uint8_t Gain;                /* Gain Byte 7*/ 
 +    uint8_t DlZeroCrossing[2]; ​  /​*Zero Crossing Byte 8-9*/  
 +    uint8_t D1PSKPreamLen; ​      /*PSK Preamble Length Byte 10*/ 
 +    uint8_t DlFSKConf; ​          /*FSK Conf Byte 11*/ 
 +    uint8_t FSKunicWord1; ​       /*FSK Unic Word 1*/ 
 +    uint8_t FSKunicWord2; ​       /*FSK Unic Word 2*/ 
 +}ST7580phyConfigTypedef;​ 
 +</​code>​ 
 + 
 +Po resetu MCU proběhne výchozí konfigurace voláním funkce ''​_ST7580sendInitSequence'',​ v rámci inicializace. 
 +<code c> 
 +void _ST7580sendInitSequence(void) 
 +
 +    ST7580phyConfigTypedef phyCfg =  
 +    { 
 +        .phyConfigIndex = ST7580_PHY_CONFIG_BASE,​ 
 +        .DlFreqPair = {0x01, 0x4F, 0xF0, 0x01, 0x19, 0x40}, 
 +        .DlRXControl = 0x0E, 
 +        .Gain = 0x15, 
 +        .DlZeroCrossing = {0x00, 0x00}, 
 +        .D1PSKPreamLen = 0x02, 
 +        .DlFSKConf = 0x35, 
 +        .FSKunicWord1 = 0x9B, 
 +        .FSKunicWord2 = 0x58, 
 +    }; 
 + 
 +    ST7580phyConfigTypedef rxPhyCfg; 
 + 
 +    ST7580modemConfigTypedef modemCfg =  
 +    { 
 +        .modemConfigIndex = ST7580_MODEM_CONFIG_BASE,​ 
 +        .modemConfig = 0x15, 
 +    }; 
 + 
 +    uint8_t readAddr; 
 + 
 +    _ST7580sendCommand(STX_local,​ 
 +                       ​ST7580_MODEM_CONFIG_LEN,​ 
 +                       ​MIB_WriteRequest,​ 
 +                       ​(uint8_t*)&​modemCfg,​ 
 +                       0, 
 +                       ​NULL,​ 
 +                       ​NULL);​ 
 +   
 +    _ST7580sendCommand(STX_local,​ 
 +                       ​ST7580_PHY_CONFIG_LEN,​ 
 +                       ​MIB_WriteRequest,​ 
 +                       ​(uint8_t*)&​phyCfg,​ 
 +                       0, 
 +                       ​NULL,​ 
 +                       ​NULL);​ 
 + 
 +    readAddr = ST7580_PHY_CONFIG_BASE;​ 
 +    _ST7580sendCommand(STX_local,​ 
 +                       1, 
 +                       ​MIB_ReadRequest,​ 
 +                       &​readAddr,​ 
 +                       ​ST7580_PHY_CONFIG_LEN-1, ​                       
 +                       &​rxPhyCfg.DlFreqPair[0],​  
 +                       ​NULL);​ 
 +    rxPhyCfg.phyConfigIndex = ST7580_PHY_CONFIG_BASE;​ 
 +
 +</​code>​ 
 + 
 +Tímto proběhne kompletní nastavení PLC, např. výběr linkové komunikační vrstvy, možnosti výpočtu CRC a další. Pro přehlednost jsou deklarovány výčtové typy enum, definující všechny možné příkazové kódy pro lokální komunikaci. Stejně jsou pak definovány pomocné stavy a další prvky nutné pro komunikaci hostitel-modem. Příklad níže. 
 +<code c> 
 +/* STX */ 
 +typedef enum 
 +
 +    STX_local ​         = 0x02, 
 +    STX_retransmit ​    = 0x03, 
 +}ST7580_STX_Typedef;​  
 + 
 +typedef enum 
 +
 +    /*** Request commands ***/ 
 +    BIO_ResetRequest ​   = 0x3C, 
 +    MIB_WriteRequest ​   = 0x08, 
 +    MIB_ReadRequest ​    = 0x0C, 
 +    MIB_EraseRequest ​   = 0x10, 
 +    PingRequest ​        = 0x2C, 
 +    PHY_DataRequest ​    = 0x24, 
 +    DL_DataRequest ​     = 0x50, 
 +    SS_DataRequest ​     = 0x54, 
 +         
 +    /*** Confirm commands ***/ 
 +    BIO_ResetConfirm ​   = 0x3D,  
 +    MIB_WriteConfirm ​   = 0x09, 
 +    MIB_ReadConfirm ​    = 0x0D, 
 +    MIB_EraseConfirm ​   = 0x11, 
 +    PingConfirm ​        = 0x2D, 
 +    PHY_DataConfirm ​    = 0x25, 
 +    DL_DataConfirm ​     = 0x51, 
 +    SS_DataConfirm ​     = 0x55, 
 + 
 +    /*** Error commands ***/ 
 +    BIO_ResetError ​     = 0x3F, 
 +    MIB_WriteError ​     = 0x0B, 
 +    MIB_ReadError ​      = 0x0F, 
 +    MIB_EraseError ​     = 0x13,          
 +    PHY_DataError ​      = 0x27, 
 +    DL_DataError ​       = 0x53, 
 +    SS_DataError ​       = 0x57, 
 +    CMD_SyntaxError ​    = 0x36, 
 + 
 +    /*** Indication commands ***/ 
 +    BIO_ResetIndication = 0x3E, 
 +    PHY_DataIndication ​ = 0x26, 
 +    DL_DataIndication ​  = 0x52, 
 +    DL_SnifferIndication= 0x5A, 
 +    SS_DataIndication ​  = 0x56, 
 +    SS_SnifferIndication= 0x5E, 
 + 
 +    CMD_END = 0xFF, 
 +}ST7580_CmdCode_Typedef;​ 
 +</​code>​ 
 +\\  
 +\\ 
 + 
 +===== Odeslání dat do elektrické sítě masterem ===== 
 + 
 +Pro vysílání dat do elektrické sítě (na linkové vrstvě) je nutné zaslat žádost použitím příkazového kódu ''​DL_DataRequest''​. K tomu je určená funkce ''​ST7580sendPayload'',​ která má jako argumenty užitečná dat (aplikační rámec) a jeho délku. 
 + 
 +<code c> 
 +int ST7580sendPayload(uint8_t* payload, uint8_t len) 
 +
 +    uint8_t temperatures[5];​ 
 +    return _ST7580sendCommand(STX_local,​ 
 +                              len, 
 +                              DL_DataRequest,​ 
 +                              payload, 
 +                              5, 
 +                              temperatures,​ 
 +                              NULL); 
 +
 +</​code>​ 
 + 
 +Tímto se provede další lokální komunikace s žádostí o vysílání dat to elektrické sítě. Po úspěšném zpracování,​ se vytvoří datový rámec (v tomto případě na linkové vrstvě), který se vyšle do sítě. Tuto proceduru samostatně obstarává PLC modem. Data jsou přenášena na nosném kmitočtu 148 kHz s modulací BPSK. 
 +\\  
 +\\ 
 + 
 +===== Příjem dat z elektrické sítě slavem ===== 
 + 
 +Pokud má jednotka slave nakonfigurované klíčové parametry totožně jako master, např. nosná frekvence a další, slave příjme a zpracuje datový rámec do podoby lokálního rámce, který je odeslán modemem k hostiteli. Lokání rámec je definován příkazovým kódem ''​DL_DataIndication'',​ který v poli data obsahuje poslední přenesená data. Lokální rámec se pak člení s pomocí kruhového bufferu. 
 +Pro příjem a rozpoznání dat slouží kontinuální volání funkce ''​ST7580poll''​. 
 + 
 +<code c> 
 +int ST7580poll(void) 
 +
 +    int i; 
 +    uint8_t ​  ​ackNack;​ 
 +    uint8_t ​  stx = 0, len = 0, cmd = 0; 
 +    uint16_t ​ checksum = 0, calcChecksum = 0; 
 +    uint8_t payload[256];​ 
 +  
 +            while(!RingBuffer_empty((RingBuffer*)ST7580_RB_U1)) 
 +            { 
 +            /* receive the STX and Lenght */  
 +            RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​stx,​ 1); 
 +       
 +             ​if(stx != STX_local) 
 +            { 
 +            RingBuffer_flush((RingBuffer*)ST7580_RB_U1);​ 
 +            return ST7580_ERR;​ 
 +            } 
 +             
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​len,​ 1);   
 +       
 +    /* receive the CMD code */ 
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​cmd,​ 1); 
 + 
 + 
 +    /* receive the payload and checksum */ 
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)payload,​ len); 
 +    RingBufferReceive((RingBuffer*)ST7580_RB_U1,​ (uint8_t*)&​checksum,​ 2); 
 + 
 +    calcChecksum = 0; 
 + 
 +    for(i = 0; i < len; i++) 
 +    { 
 +            calcChecksum += payload[i];​ 
 +    } 
 +    calcChecksum += len; 
 +    calcChecksum += cmd; 
 + 
 + if(checksum == calcChecksum) 
 +
 +            /* send the ACK */ 
 +            ackNack = ST7580_ACK;​ 
 +            HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​ackNack,​ 1, ST7580_ACK_TIMEOUT);​  
 +            RingBuffer_flush((RingBuffer*)ST7580_RB_U1);​ 
 + 
 +            for(i = 0; ST7580rxTable[i].code != CMD_END; i++) 
 +            { 
 +               ​ if(cmd == ST7580rxTable[i].code) 
 +                { 
 +               ​  ​  ​ if(ST7580rxTable[i].pfunc != NULL) 
 +               ​  ​  ​ ST7580rxTable[i].pfunc(payload,​ len); 
 +               ​  ​  ​ break;​ 
 +                } 
 +            } 
 +
 + else 
 +
 + /* send the NACK */ 
 + ackNack = ST7580_NACK;​ 
 + HAL_UART_Transmit(&​huart1,​ (uint8_t*)&​ackNack,​ 1, ST7580_ACK_TIMEOUT);​ 
 + RingBuffer_flush((RingBuffer*)ST7580_RB_U1);​ 
 + return ST7580_ERR;  
 +
 +
 +    return ST7580_OK;​ 
 +
 +</​code>​ 
 + 
 +Tímto jsou přijatá data zpracována a také je identifikován typ zprávy. Jedná se tedy o přijata data s příkazovým kódem ''​DL_DataIndication''​. Dále je zjištěna existence funkce pro tento příkazový kód. V tomto případě se jedná o funkci ''​DL_DataIndicationFunc''​. 
 +<code c> 
 +void DL_DataIndicationFunc(uint8_t* payload, uint8_t len) 
 +
 +    AppCmdParser(payload + 4, len - 4);  
 +
 +</​code>​ 
 +Jedná se pouze o předání osamostatněného pole ''​payload''​ (aplikační rámec) ke zpracování požadovaných funkcí aplikačním protokolem a tvoří pak s délkou zprávy argument funkce ''​AppCmdParser''​. Zároveň je určen počátek zprávy, tedy pole ''​payload''​ je indexováno od 0. 
 + 
 +Funkce je koncipována podobně jako funkce ''​ST7580poll'',​ ale je přizpůsobena pro strukturu aplikačního rámce. V rámci zpracování se určí, jestli je zpráva určená pro daný slave. Dále se ověří hodnota adresy registru, která tvoří identifikátor povelu v poli struktur, které obsahují ukazatele na možné funkce dané jednotky slave. Požadované funkci (povelu) je pak argument obsah pole ''​AppData''​ (níže uveden příklad funkce ''​AppLedStateChanged''​). 
 + 
 +<code c> 
 +void AppCmdParser(uint8_t* payload, uint8_t len) 
 +
 +    uint8_t i, j, chs = 0, temp; 
 +    int remaining;​ 
 +    if((APP_EXTRACT_ADDRESS(payload[0]) == AppCmdSlaveAddress) || 
 +        (APP_EXTRACT_ADDRESS(payload[0]) == APP_ADDRESS_BROADCAST)) 
 +    { 
 +        /* calculate the checksum */ 
 +        for(i = 0; i < (len-1); i++) 
 +        { 
 +            chs += payload[i];​ 
 +        } 
 + 
 +        if(chs == payload[len-1]) 
 +        { 
 +            switch(APP_EXTRACT_REQUEST(payload[0])) // masked FF to 0x80 
 +            { 
 +                case APP_READ_REQUEST:​ 
 +                for(i = payload[1], remaining = payload[2], j = 0; sensitivityTable[j].addr != 0xFF; j++) 
 +                { 
 +                    if(sensitivityTable[j].addr == i) 
 +                    { 
 +                        if(sensitivityTable[j].pfuncRead != NULL) 
 +                        { 
 +                            temp = sensitivityTable[j].pfuncRead(i,​ remaining);​ 
 +                            if(!temp) temp = 1; 
 +                            i += temp; 
 +                            remaining -= temp; 
 +                        } 
 +                        if(!remaining) break; 
 +                    }    
 +                }            
 +                break; 
 + 
 +                case APP_WRITE_REQUEST:​ 
 +                memcpy(AppCmdRegisters + payload[1], &​payload[2],​ len-3); 
 +                for(i = payload[1], remaining = len-3, j = 0; sensitivityTable[j].addr != 0xFF; j++) 
 +                { 
 +                    if(sensitivityTable[j].addr == i) 
 +                    { 
 +                        if(sensitivityTable[j].pfuncWrite != NULL) 
 +                        { 
 +                            temp = sensitivityTable[j].pfuncWrite(AppCmdRegisters + i, remaining);​ 
 +                            if(!temp) temp = 1; 
 +                            i += temp; 
 +                            remaining -= temp; 
 +                        } 
 +                        if(!remaining) break; 
 +                    }        
 +                }  
 +                break; 
 +            } 
 +        } 
 +    } 
 +
 +</​code>​ 
 + 
 + 
 +Příklad možností povelu dané jednotky slave: 
 +<code c> 
 +AppCmdParserRecordTypedef sensitivityTable[] =  
 +
 +/* {cmd identifier, pointer to a write function, pointer to a read function} */ 
 +    {APP_ADDR_LED,​ AppLedStateChanged,​ AppLedReadRequested},​ 
 +    {0xFF, NULL, NULL}, 
 +}; 
 +</​code>​ 
 +\\  
 +\\ 
 + 
 +===== Předání přijatých dat externí funkci pro řízení periferií ===== 
 +Funkce vykonaná po přijetí ukázkové zprávy pro demonstraci komunikace po napájecím vedení: 
 +<code c> 
 +uint8_t AppLedStateChanged(void* newValue, uint8_t remLen) 
 +
 +   ​uint8_t val = *((uint8_t*)newValue);​ 
 +  
 +   ​HAL_GPIO_WritePin(GPIOC,​GPIO_PIN_9,​ val);  
 +   ​HAL_GPIO_WritePin(GPIOC,​GPIO_PIN_6,​ !val);  
 +   ​HAL_GPIO_WritePin(GPIOC,​GPIO_PIN_7,​ !val);  
 +  
 +   if (val == 1) 
 +   { 
 + LCD_Clear();​ 
 + LCD_Puts(0,​0,​(uint8_t*)"​Stav aktoru:"​);​ 
 + LCD_Puts(0,​1,​(uint8_t*)"​ZAPNUTO"​);​ 
 +   } 
 +   if (val == 0) 
 +   { 
 + LCD_Clear();​ 
 + LCD_Puts(0,​0,​(uint8_t*)"​Stav aktoru:"​);​ 
 + LCD_Puts(0,​1,​(uint8_t*)"​VYPNUTO"​);​  
 +   } 
 +   if ((val != 0) && (val != 1)) 
 +   { 
 + LCD_Clear();​ 
 + LCD_Puts(0,​0,​(uint8_t*)"​Stav aktoru:"​);​ 
 + LCD_Puts(0,​1,​(uint8_t*)"​WRONG DATA"​);​ 
 + return 0;  
 +   } 
 + 
 +   ​return 1; 
 +
 +</​code>​ 
 + 
 +Ukázková aplikace odesílání dat masterem v nekonečné smyčce hlavního souboru. 
 +<code c> 
 +  while (1) 
 +  { 
 + ST7580poll();​ 
 + //​ST7580getFirmwareVersion();​ 
 + 
 +      if(HAL_GPIO_ReadPin(GPIOA,​ GPIO_PIN_0)) 
 +      { 
 +        request[2] = !request[2];​ 
 +        request[3] = 0xFF & (request[0] + request[1] + request[2]);​ 
 +        ST7580sendPayload(request,​ 4); 
 +        HAL_Delay(600);​ 
 +      } 
 +
 +</​code>​ 
 + 
 +Obsah hlavní smyčky jednotky slave tvoří pouze funkce ''​ST7580poll'',​ která plně obsluhuje lokální komunikaci. 
 + 
 +\\  
 + 
 +====== Logická analýza lokální komunikace ====== 
 + 
 +Uvedené průběhy na sebe horizontálně navazují, první na druhý. Zachycují lokální komunikaci jednotek master (1-PLC) a slave (2-PLC). Jedná se o žádost mastera k odeslání zprávy do elektrické sítě, úspěšné provedení, a příjem lokálního rámce obsahující přenášená data pro daný slave. 
 + 
 +{{ :​2015:​plc-st7580:​analysis1.png |}} 
 +\\  
 +{{ :​2015:​plc-st7580:​analysis2.png |}} 
 +\\  
 +\\  
 +====== Videoukázka ====== 
 + 
 +V následujícím videu je zachyceno řízení výstupního členu jednotky slave, uživatelským tlačítkem na Discovery kitu jednotky master. 
 + 
 +{{ youtube>​PO7Exa0Xi2o?​900x500 |}} 
 +\\  
 +\\ 
 + 
 +====== Závěr ====== 
 + 
 +Pro vývoj tohoto software bylo použito aplikace STM32CubeMX v kooperaci s HAL knihovnami ve free verzi vývojového prostředí MDK.  
 +Navržený software implementovaný v hostitelském MCU STM32F0 pro obsluhu PLC modemu ST7580, demonstruje funkční úzkopásmovou komunikaci po napájecím vedení. Pro ukázku je zvolen k jednotce slave aktor, v podobě RGB diody a LCD displeje, který vypisuje stav. Podle dosažených výsledku, je zadání projektu splněno. 
 + 
 +autor --- //​[[slacik.jan@gmail.com|Ján Sláčik]] 2016/01/16 23:01//
  
2015/plc-st7580.1452910114.txt.gz · Poslední úprava: 2016/01/16 03:08 autor: Ján Sláčik