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

Obě strany předchozí revize Předchozí verze
Následující verze
Předchozí verze
2015:plc-st7580 [2016/01/16 03:58]
Ján Sláčik
2015:plc-st7580 [2016/01/20 16:52] (aktuální)
Ján Sláčik [Rozbor zadání]
Řádek 1: Řádek 1:
-====== ​Popis 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.1452913102.txt.gz · Poslední úprava: 2016/01/16 03:58 autor: Ján Sláčik