Skip to content

Instantly share code, notes, and snippets.

@dvtomas
Last active April 20, 2018 03:25
Show Gist options
  • Save dvtomas/552cc08cffb65d16d1e323abe7673a58 to your computer and use it in GitHub Desktop.
Save dvtomas/552cc08cffb65d16d1e323abe7673a58 to your computer and use it in GitHub Desktop.

Send command:

1B delka ( = CommandData.len() + 3)
1B Command code
... Command data
1B Chksum

Read response

1B delka ( = CommandData.len() + 3)
1B CommandCode | 0x80 (nejvyšší bit nastavený na 1)
1B ResponseCode
... Response Data
1B Status
1B Chksum

MsgPerif - příkaz pro některou z periferií

Periferií je zřejmě míněný nějaký piggyback na DMN kartě.

odesílá se jako Command s CommandCode = 0x0E. Command data jsou zpráva pro některou z periferií v následujícím formátu:

1B index periferie
1B délka příkazu pro periferii (včetně této délky)
zbytek je samotný příkaz pro periferii.

Příklad: Odeslaná zpráva 0A 0E 00 06 FD 00 00 03 03 21 obsahuje zprávu pro periferi 00 s délkou 6 bajtů, samotná zpráva pro tuto periferii je FD 00 00 03 03

odpověd je např. 06 8E 88 00 00 1C. Samotná data odpovědi na msgPerif jsou 88 00, kde "ACP = 0x88, // potrvzovaci zprava 'acknowledge parameter' s volnym poctem parametru podle typu dotazu, parametry jsou uvedenny u kazdeho cmd, pokud na nej tato odoved prichazi" a 00 je perif_reg, stavový registr periferie. Pokud je nejnižší bit nastaven na 1, značí to chybu periferie. Zdá se, že odpověď na MsgPerif je pro všechny MsgPerif stejná.

Co znamená konkrétně msgPerif 0A 0E 00 06 FD 00 00 03 03 21?

Nevím :( Ze zdrojáků nevyčtu víc než toto:

const unsigned char msgPerif_muxMode[] = {
    0xFD, //INT_MUX_MODE
    0x00, /*Single ended */ 0x00, 0x3, 0x3
};

používá se v tomto kontextu (setMuxMode):

    void
    Svawin2DmnDevice::getReadyForMeasuring(size_t samplingFrequency) {
        unsigned requestedBaudrate = serialHandle.baudRate;
        try {
            findBaudrate();
            stop();
            setSettings(requestedBaudrate, samplingFrequency);
        } catch (misc::ChainedException &e) {
            serialHandle.close();
            serialHandle.open(serialHandle.portNumber, requestedBaudrate);
            THROW_CHAINED_EXCEPTION(dmnlib::DmnCommunicationError, std::string("getReadyForMeasuring: setSettings on device failed."), e);
        }
        setMuxMode();
        muxSetting();
        resetIrc();
        xMotOn();
    }

Širší kontext je v Svawin2DmnDevice.cpp. Pro zajímavost, další povel muxSetting nastavuje, které AD/IRC/DIG kanály se mají v jakém pořadí posílat, a to pro tu samou periferii 0. Jde tedy o dost důležitý povel.

    void
    Svawin2DmnDevice::muxSetting() {
        unsigned char msgPerif_muxSetting[100];
        size_t offset = 0;

        msgPerif_muxSetting[offset++] = 0xFE; //INT_MUX_SETTING
        msgPerif_muxSetting[offset++] = 0x00; // SCHEMA_1
        msgPerif_muxSetting[offset++] = channelsDescriptor.numberOfADChannels;
        msgPerif_muxSetting[offset++] = channelsDescriptor.numberOfIRCChannels;
        msgPerif_muxSetting[offset++] = channelsDescriptor.numberOfDIGChannels;
        msgPerif_muxSetting[offset++] = 0; // This makes sense for cards with PWM outputs, Libor knows more
        for (size_t i = 0; i < channelsDescriptor.numberOfADChannels; i++)
            msgPerif_muxSetting[offset++] = i;
        for (size_t i = 0; i < channelsDescriptor.numberOfIRCChannels; i++)
            msgPerif_muxSetting[offset++] = i;
        for (size_t i = 0; i < channelsDescriptor.numberOfDIGChannels; i++)
            msgPerif_muxSetting[offset++] = i;
        try {
            msgPerif(0, msgPerif_muxSetting, offset);
        } catch (misc::ChainedException &e) {
            THROW_CHAINED_EXCEPTION(dmnlib::DmnCommunicationError, std::string("muxSetting"), e);
        }
    }

Vyčítání měřených dat

Vyčítání dat je vidět z high-level v ThreadedDmnDataReader.hpp. V zásadě se periodicky volá dmnDevice.getNextPacket implementované v DmnDevice.cpp. getNextPacket je DMN příkaz GET_NEXT_BLOCK = 0x11, // Pozadavek na cely blok dat od naposled vyslane polohy. Odpověď není úplně jednoduchá....

na odpověď je timeout s následujícím vzorcem:

/* Hope this reflects real world demands. Wait at least 5 more times than should be necessary for a new packet, but no less than 500 milliseconds. 
 * TODO Maybe I should also count for the RS232 communication delay?
 */
size_t tooLittleDataTimeout = 5 * 1000 * measurementsPerPacket / samplingFrequency;
if (tooLittleDataTimeout < 500)
   tooLittleDataTimeout = 500;

Takto vypadá zpracování odpovědi GET_NEXT_BLOCK:

void
DmnDevice::getNextPacket(unsigned char *buffer) {
    try {
        /* Hope this reflects real world demands. Wait at least 5 more times than should be necessary for a new packet, but no less than 500 milliseconds. 
         * TODO Maybe I should also count for the RS232 communication delay?
         */
        size_t tooLittleDataTimeout = 5 * 1000 * measurementsPerPacket / samplingFrequency;
        if (tooLittleDataTimeout < 500)
            tooLittleDataTimeout = 500;

        DBG_COMMAND;

        uint32_t startMs = misc::getTickCount();
        while (true) {
            DmnCommandResponse response;
            sendCommandAndReadResponse(CommandCode::GET_NEXT_BLOCK, response);
            if (response.responseCode == ResponseCode::ACK)
                break;
            else if (response.responseCode == ResponseCode::TOL) {
                if (misc::ticksDiff(startMs, misc::getTickCount()) > tooLittleDataTimeout)
                    THROW_EXCEPTION(DmnCommunicationError, std::string(_("Timeout while waiting for data (dmnGetNext)")));
            }
        }

        uint8_t status;
        uint16_t receivedCrc;
        uint16_t expectedCrc;
        size_t packetSize = getPacketSize();

        readData(buffer, packetSize);

        currentReadOffset += packetSize;
        totalDataReadSinceLastStart += packetSize;
        while (currentReadOffset >= offsetSize)
            currentReadOffset -= offsetSize;

        readData((unsigned char *) &status, 1);
        readData((unsigned char *) &receivedCrc, 2);
        expectedCrc = getCRC(buffer, packetSize);
        expectedCrc = crc_ccitt_update(expectedCrc, status);
        expectedCrc ^= currentReadOffset;

        if (receivedCrc != expectedCrc) {
            unsigned char dbgbuf[300];
            memcpy(dbgbuf, buffer, packetSize);
            memcpy(dbgbuf + packetSize, &status, 1);
            memcpy(dbgbuf + packetSize + 1, &receivedCrc, 2);
            THROW_EXCEPTION(DmnCommunicationError,
                    std::string(_("getNextPacket: CRC failed. Current read offset: ")) + misc::intAsString(currentReadOffset) +
                    ", offset size: " + misc::intAsString(offsetSize) +
                    ", total data read since last start: " + misc::intAsString(totalDataReadSinceLastStart) +
                    ", measured packet data dump (including status and CRC): " + misc::dumpBufferAsString("", dbgbuf, packetSize + 3)
                    );
        }
    } catch (misc::ChainedException &e) {
        THROW_CHAINED_EXCEPTION(DmnCommunicationError, std::string("getNextPacket"), e);
    }
}

Podrobněji o odpovědi

Zde je jedna ukázková odpověď na GET_NEXT_BLOCK (03 11 14):

05 91 82 00 18 20 B5 C2 91 AE FF A4 A5 A0 90 9C 02 9B 00 00 EA 9A 3C 9A 67 9A B8 AA BC AA 88 92 60 92 00 00 00 20 C5 86 BE 80 31 80 CE 81 7A 84 E9 87 00 00 76 8E BF 90 BB 93 B3 AA B8 AA 86 92 5F 92 00 00 00 20 FC 86 2B 81 C1 80 6C 82 11 85 6E 88 00 00 D9 8E 0C 91 F3 93 B2 AA BA AA 86 92 5F 92 00 00 00 20 1D 87 72 81 23 81 E2 82 92 85 F1 88 00 00 5E 8F 8B 91 5E 94 B3 AA BB AA 89 92 5F 92 00 00 00 00 7F 08

Rozepsáno: Nejprve je standardní odpověď, teprve poté je blok měřených dat.

Standardní odpověď:

05 délka odpovědi
91 GET_NEXT_BLOCK response
82 ACK - data jsou ready (další častá odpověd je 84, TOL - too little, data zatím nejsou)
00 - status registr
18 - CRC

Blok měřených dat:

V tomto případě obsahuje 4 sady měření. Počet sad měření pro kraba a podbíječku se nastavuje v Svawin2ChannelsDescriptor.cpp. Velikost bloku dat, který má DMN karta posílat, se nastavuje v DmnDevice::setSetting ve fieldu packetSize.

Každá sada měření začíná délkou (zde 0x20 = 32 bajtů), pak následují kanály (nejprve AD kanály, pak IRC, pak DIG), a nakonec je perif_reg

20 B5 C2 91 AE FF A4 A5 A0 90 9C 02 9B 00 00 EA 9A 3C 9A 67 9A B8 AA BC AA 88 92 60 92 00 00 00 
20 C5 86 BE 80 31 80 CE 81 7A 84 E9 87 00 00 76 8E BF 90 BB 93 B3 AA B8 AA 86 92 5F 92 00 00 00 
20 FC 86 2B 81 C1 80 6C 82 11 85 6E 88 00 00 D9 8E 0C 91 F3 93 B2 AA BA AA 86 92 5F 92 00 00 00 
20 1D 87 72 81 23 81 E2 82 92 85 F1 88 00 00 5E 8F 8B 91 5E 94 B3 AA BB AA 89 92 5F 92 00 00 00 

Úplně nakonec se posílá stavový registr (1B) a CRC (2B). CRC pro data se počítá jinak než checksum pro odpovědi! Podrobnosti o CRC níže.

00 7F 08

Počítání CRC: CRC se počítá z bajtů měřených dat včetně délek a perif_reg. Dále se do CRC započte bajt stavového registru (hned po měřených datech), a bajt currentReadOffset (níže). CRC se počítá funkcí getCRC:

#define _LOBYTE(w) ((uint8_t)(w))
#define _HIBYTE(w) ((uint8_t)(((uint16_t)(w) >> 8) & 0xFF))

static uint16_t
crc_ccitt_update(uint16_t crc, uint8_t data) {
    data ^= _LOBYTE(crc);
    data ^= data << 4;

    return ((((uint16_t) data << 8) | _HIBYTE(crc)) ^ (uint8_t) (data >> 4) ^ ((uint16_t) data << 3));
}

static uint16_t
getCRC(uint8_t *data, size_t length) {
    uint16_t crc = 0xffff;
    for (size_t i = 0; i < length; i++) {
        crc = crc_ccitt_update(crc, data[i]);
    }
    return crc;
}

currentReadOffset Aby to celé nebylo zbytečně jednoduché (ノಠ益ಠ)ノ, jedním z bajtů v CRC je currentReadOffset (viz výše). Ten se před každým počítáním CRC updatne takto:

currentReadOffset += packetSize;
while (currentReadOffset >= offsetSize)
    currentReadOffset -= offsetSize;

kde

  • packetSize je velikost měřených dat (v našem případě 4*32 = 128)
  • offsetSize je interní parametr DMN karty (velikost interního bufferu), který se zjišťuje příkazem GET_BUF_PAR.

Ukázková komunikace pro zjištění offsetSize:

 WRITE 03 16 19
 READ: 08 96 88 00 80 10 00 B6

offsetSize je v tomto případě 0x0080, tedy 32768 bajtů. Je v kódu asi špatně pojmenovaný, spíše by se měl jmenovat bufferSize, protože jde o velikost interního bufferu DMN karty (???)

Statusový registr a indikace baterie

Statusový registr se posílá v odpovědi na příkazy jako předposlední byte (poslední je CRC). Obsahuje následující flagy včetně indikace vybité baterie. Ohledně baterie u svawin2 - opravdu se tam baterie nějak řeší? Nemohu to ve zdrojácích najít, není možné že se podpětí vůbec neřeší, nebo řeší jinde než na úrovni tohoto SW?

enum StatusReg {
    cmd_err = 0x01, //chybny obsah prikazu
    //nastavuje se behem dekodovani cmd
    //maze se vyslanim odpovedi na cmd
    read_err = 0x02, //chybne prijat prikaz:
    //  chybne cc
    //  chyba z radice seriove linky  (frame error, apod.
    //  prikaz se nevejde do bufferu
    //  prikaz nema predepsanou delku
    //nastavuje se na konci cteni prikazu
    //maze se vyslanim odpovedi na cmd
    internal_err = 0x04, //vnitrni programova chyba
    //Maze se po prikazu GET_STATUS i s jeho priznakovym bajtem 'internal_code'.
    //Pri jejim vyskytu vzdy volat GetStatus a ulozit cely jeho obsah.
    tick_err = 0x08, //Nestihl se ulozit buffer behem TIME1,
    //Nastavuje se v obsluze TIME. Vzorek se cely dokonci,
    //ale za nim muze byt dira.
    //Maze se zapsanim statusu do buf_data.
    empty = 0x10, //Nejsou platna data v 'buf_data'.
    //Nastavuje se pri inicializaci bufferu nebo
    //kdyz cteci ukazatel dobehl zapisovaci ukazatel.
    //Maze se vzdy pri zapisu do 'buf_data'.
    //Seek ho neovlivnuje!
    full = 0x20, //Neni kam zapisovat.
    //Nastavuje se, kdyz zapisovaci ukazatel dobehne ukazatel
    //zacatku bloku paketu.
    //Seek ho neovlivnuje!
    //Maze se pri inicalizaci bufferu.
    //'full' a 'empty' muze byt soucane, pak plati 'full'.
    bat = 0x40, // varuje pred vybitou baterii
    // nastavi se pri zjisteni podpeti
    // stahne se zapsanim status do buf_data nebo vyslanim odpovedi na cmd.
    eep_data = 0x80 // Rezim prace s daty z eeprom. Vsechny prikazy GetData atd..
    // budou cist data z eeprom v rezimu, ktery nastavilo EEPActive.
    // Nastavi se prikazem EEPActive., shodi se RAMActive
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment