Zpět na domů

CLI přes CAN: Ladění vestavěných systémů s ISO-TP a CANshell

Hloubkové ponoření do implementace CLI pro vestavěné systémy přes CAN sběrnici s použitím protokolu ISO-TP a nástroje CANshell. Efektivní řešení pro ladění bez UART.

CLI přes CAN: Ladění vestavěných systémů s ISO-TP a CANshell
Advertisement 728x90

Správa a ladění vestavěných systémů: CLI přes CAN s ISO-TP a nástrojem CANshell

Vývoj vestavěných systémů se často potýká s problémem omezených ladicích rozhraní. V situacích, kdy tradiční UART není k dispozici, ale jsou přítomny CAN sběrnice, vzniká potřeba alternativních metod vzdálené interakce. Tento článek popisuje přístup k implementaci rozhraní příkazového řádku (CLI) přes CAN sběrnici s využitím protokolu ISO-TP a specializovaného nástroje CANshell, který poskytuje plnohodnotné ladicí prostředí pro mikrokontroléry.

Výzva ladění bez UART

Klasické ladění vestavěných systémů se tradičně opírá o sériová rozhraní, jako je UART. V reálných projektech, zejména při vývoji specializovaných elektronických desek, však může UART buď chybět, nebo být vyhrazen pro jiné účely. V takových scénářích, kde je k dispozici mnoho CAN portů, vyvstává úkol využít CAN sběrnici pro přenos ladicích informací a řídicích příkazů.

Hlavní problémy spočívají v následujícím:

Google AdInline article slot
  • Omezená velikost CAN paketů: Standardní CAN paket pojme pouze 8 bajtů dat. To je naprosto nedostatečné pro přenos dlouhých CLI příkazů, které mohou dosahovat 150 bajtů i více, stejně jako pro příjem objemných odpovědí a logů. Řešení tohoto problému vyžaduje použití protokolu schopného fragmentovat a skládat data z několika CAN paketů.
  • Oddělení datových toků: Při vzdáleném ladění je často nutné současně pracovat s vlastním CLI ladicího nástroje a CLI vzdáleného zařízení (ECU). Je nezbytné efektivně oddělovat a zobrazovat tyto toky na jednom terminálu.
  • Přesměrování logů: Vestavěný software obvykle vypisuje logy do UART nebo SWO. Pro použití CLI přes CAN je nutné zajistit mechanismus přesměrování těchto logů do vybraného CAN rozhraní přes ISO-TP.

Pro řešení těchto úkolů se navrhuje použít protokol ISO-TP (ISO 15765-2), který umožňuje přenášet zprávy libovolné délky přes CAN sběrnici, a vyvinout specializovaný konzolový nástroj CANshell pro PC.

Architektura řešení: ISO-TP a CANshell

Navržené řešení je založeno na kombinaci protokolu ISO-TP a nástroje CANshell. ISO-TP zajišťuje spolehlivý přenos velkých objemů dat přes CAN, zatímco CANshell funguje jako most mezi TCP klientem (například PuTTY nebo TeraTerm) a CAN sběrnicí.

Princip fungování:

Google AdInline article slot
  • Na straně PC: Nástroj CANshell spustí TCP server a otevře TCP socket. Tento socket je na jedné straně spojen s protokolem ISO-TP a na druhé straně s TCP klientem. Parametry připojení (ISO-TP adresy, COM port, rychlost CAN) jsou nástroji předány prostřednictvím argumentů příkazového řádku, což umožňuje automatizovat proces pomocí skriptů.
  • Na straně mikrokontroléru (ECU): Je implementován ISO-TP zásobník, který přijímá data z CAN sběrnice a předává je do parseru CLI příkazů. Pro výstup logů a odpovědí se používá mechanismus přesměrování, který umožňuje odesílat tato data zpět přes ISO-TP.
  • Interakce: Když TCP klient odešle příkaz, CANshell jej zachytí, zapouzdří do ISO-TP paketů a odešle přes USB-CAN převodník do CAN sběrnice. Na mikrokontroléru ISO-TP zásobník pakety shromáždí a příkaz se dostane do CLI parseru. Odpovědi a logy z mikrokontroléru jsou obdobně zapouzdřeny do ISO-TP, přeneseny přes CAN sběrnici do CANshell, a poté do TCP klienta.

Realizace komponent a příklady kódu

Pro praktickou implementaci byl vybrán USB-CAN převodník USB2CANFD_V1 díky jeho dostupnosti a jednoduchosti interakce přes sériový COM port pomocí protokolu SLCAN. Jako cílová platforma byla použita vývojová deska JZ-F407VET6 se dvěma CAN porty.

TCP server na straně PC je implementován v čistém C s použitím funkcí WinSock2. To umožňuje maximální znovupoužitelnost komponent protokolu ISO-TP a FIFO front jak ve firmwaru mikrokontroléru, tak v aplikaci na PC.

Příjem dat na mikrokontroléru přes ISO-TP a jejich předání do CLI parseru vypadá následovně:

Google AdInline article slot
void iso_tp1_rx_done(n_indn_t* in_done){
    LOG_DEBUG(ISO_TP,"ISO_TP1,%s",Iso15765_n_indn_ToStr(in_done));
    if(N_OK==in_done->rslt){
#ifdef HAS_CLI
        IsoTpHandle_t* Node=IsoTpGetNode(1);
        if(Node) {
            Node->target_id = in_done->n_ai.n_sa;
            cli_process_data(Node->cli_num, in_done->msg, in_done->msg_sz);
            memset(in_done->msg,0,I15765_MSG_SIZE);
        }
#endif
    }
}

Tento callback iso_tp1_rx_done je volán po dokončení příjmu kompletní ISO-TP zprávy. Extrahovaná data předává funkci cli_process_data k dalšímu zpracování.

Pro přesměrování logů z mikrokontroléru do ISO-TP se používá speciální příkaz tpw 1. Po jeho provedení začnou všechny logovací funkce (LOG_ERROR, LOG_INFO, LOG_DEBUG) zapisovat data do fronty TxFIFO, která je asociována s první instancí ISO-TP.

void iso_tp1_puts(void* stream_ptr, const char* str, int32_t len) {
    IsoTpHandle_t* Node = IsoTpGetNode(1);
    if(Node) {
        if(str) {
            if(len) {
                bool res = fifo_push_array(&Node->TxFifo, (uint8_t*)str, (uint32_t)len);
                if(!res) {
                    Node->error_cnt++;
                }
            }
        }
    }
}

void iso_tp1_putc(void* stream_ptr, char ch) {
    IsoTpHandle_t* Node = IsoTpGetNode(1);
    if(Node) {
        bool res = fifo_push(&Node->TxFifo, (uint8_t)ch);
        if(!res) {
            Node->error_cnt++;
        }
    }
}

Funkce iso_tp1_puts a iso_tp1_putc umisťují znaky nebo řetězce do bufferu TxFifo, odkud budou odeslány přes ISO-TP.

Správa přenosu dat a synchronizace

Úloha IsoTpTx na mikrokontroléru je zodpovědná za extrakci dat z TxFifo a jejich odesílání přes ISO-TP, jakmile je automat protokolu připraven.

static bool iso15765_tx_next(IsoTpHandle_t* const Node) {
    bool res = false;
    res = iso15765_is_idle( &Node->instance);
    if(  res) {
        uint32_t count = fifo_get_count(&Node->TxFifo);
        if(0 < count) {
            uint32_t txLen = 0;
            n_req_t isoTpFrame={0};
            isoTpFrame.fr_fmt=CBUS_FR_FRM_STD;
            res = iso_tp_node_to_address_info( Node, &isoTpFrame.n_ai);
            res = iso_tp_node_to_proto_ctrl_info(  Node, &isoTpFrame.n_pci);
            res = fifo_pull_array(&Node->TxFifo, isoTpFrame.msg, sizeof(isoTpFrame.msg), &txLen);
            if(res) {
                isoTpFrame.msg_sz = txLen;
                n_rslt ret = iso15765_send(&Node->instance, &isoTpFrame);
                res = iso15765_ret_to_res(ret);
            }
        }
    }
    return res;
}

bool iso_tp_tx_proc_one(uint8_t num) {
    bool res = false;
    IsoTpHandle_t* Node = IsoTpGetNode(num);
    if(Node) {
        LOG_PARN(ISO_TP, "IsoTp%u,ProcTx", num);
        res = iso15765_tx_next(Node);
        Node->spin++;
    }
    return res;
}

Tyto úryvky kódu ukazují logiku odesílání dat. Funkce iso15765_tx_next kontroluje, zda je ISO-TP transceiver volný, a pokud ano, extrahuje data z TxFifo a iniciuje odeslání.

Na straně PC, v nástroji CANshell, jsou data přijatá přes ISO-TP také umístěna do fronty, odkud jsou následně odeslána TCP klientovi.

bool can_shell_iso_tp_rx_data(uint8_t num,
                              uint8_t iso_num,
                              const  uint8_t* const data,
                              uint16_t msg_sz) {
    bool res = false;
    if(data) {
        if(msg_sz) {
            SocketHandle_t * Socket = SocketGetNode(1);
            if(Socket) {
                res = fifo_push_array(&Socket->TxFifo, data, (uint32_t) msg_sz);
                log_debug_res(CAN_SHELL, res, "SocketTxFiFoPush");
            }
        }
    }
    return res;
}

static bool iso_tp_rx_done(uint8_t iso_num, n_indn_t* in_done) {
    bool res = false;
    if(in_done) {
        LOG_INFO(ISO_TP, "ISO_TP_%u,MoveDone:%s", iso_num, Iso15765_n_indn_ToStr(in_done));
        res = iso15765_ret_to_res(in_done->rslt);
        if(res) {
            IsoTpHandle_t *Node = IsoTpGetNode(iso_num);
            if(Node) {
                Node->target_id = in_done->n_ai.n_sa;
                Node->rx_done = true;
#ifdef HAS_CAN_SHELL
                res = can_shell_iso_tp_rx_data(1, iso_num, in_done->msg, in_done->msg_sz);
#endif
            }
        }else{
            LOG_ERROR(ISO_TP, "MoveErr,Ret,%u=%s", in_done->rslt,
                      Iso15765retToStr(in_done->rslt));
        }
    }
    return res;
}

Tyto funkce jsou zodpovědné za příjem ISO-TP dat na straně PC a jejich přenos do bufferu určeného pro TCP socket.

Závěrečná fáze – přenos dat z TCP serveru k připojenému TCP klientovi (PuTTY, TeraTerm):

static bool socket_server_connected_tx_proc(SocketHandle_t *const Node) {
    bool res = false;
    uint32_t tx_len = fifo_get_count(&Node->TxFifo );
    if(tx_len) {
        uint8_t TxPart[150] = {0};
        uint32_t tx_size = 0 ;
        res= fifo_pull_array(&Node->TxFifo , TxPart, sizeof(TxPart), &tx_size);
        if(res) {
            int tx_done_cnt = send(Node->socket_remote, (char*) TxPart, (int) tx_size, 0 );
            if(tx_done_cnt==tx_size) {
                res = true;
            } else {
                res = false ;
                int ret = 0;
                ret = WSAGetLastError();
                LOG_ERROR(LG_SOCKET_SERVER, 
                          "SendFailedWithErrorCode,ErrCode:%d=%s", 
                          ret, WSAErrorToStr(ret));
            }
        }
    }
    return res;
}

Tento kód demonstruje, jak jsou data z bufferu socketu odesílána klientovi pomocí funkce send z WinSock2.

Důležitým aspektem je synchronizace ISO-TP relací. Byly pozorovány situace, kdy se CANshell "zahltil" proudem dat, což vedlo ke ztrátě zpráv. Řešení spočívalo v časovém rozložení ISO-TP relací, aby obě instance protokolu měly čas se obnovit a zpracovat data.

Použití nástroje CANshell

Pro spuštění nástroje CANshell je nutné zadat řadu pozičních argumentů:

  • COM port: Číslo COM portu, ke kterému je připojen USB-CAN převodník (například COM3).
  • Přenosová rychlost CAN: Rychlost CAN sběrnice v kbit/s (například 500000 pro 500 kbit/s).
  • ISO-TP adresa PC: Vlastní ISO-TP adresa nástroje CANshell v síti (například 0xC).
  • ISO-TP adresa cílového zařízení: ISO-TP adresa mikrokontroléru (ECU), se kterým je třeba navázat komunikaci (například 0xA).
  • TCP port: Číslo TCP portu, který CANshell otevře pro připojení klienta (například 50003).

Příklad spuštění nástroje:

CANshell.exe COM3 500000 0xC 0xA 50003

Po spuštění nástroj CANshell otevře dva terminály: jeden pro vlastní CLI a druhý pro CLI vzdáleného zařízení, ke kterému se lze připojit přes TCP klienta (PuTTY/TeraTerm) na zadaném TCP portu. Tímto způsobem získá ladič plnohodnotný přístup ke vzdálenému systému, simulující přímé UART připojení.

Co je důležité

  • Řešení problému absence UART: Navržená metoda umožňuje organizovat plnohodnotné CLI pro ladění vestavěných systémů, i když na desce není tradiční UART, a to s využitím dostupných CAN sběrnic.
  • Využití ISO-TP pro velká data: Protokol ISO-TP je kriticky důležitý pro přenos CLI příkazů a logů, protože umožňuje obejít omezení 8 bajtů na CAN paket tím, že fragmentuje a skládá zprávy.
  • Nástroj CANshell jako most: Specializovaný nástroj CANshell na PC funguje jako efektivní prostředník mezi standardními TCP klienty (PuTTY, TeraTerm) a CAN sběrnicí, zajišťující transparentní přenos dat.
  • Obousměrná komunikace a logování: Systém podporuje jak odesílání příkazů na ECU, tak příjem asynchronních logů a odpovědí, s možností přesměrování logů přímo do ISO-TP.
  • Univerzálnost a škálovatelnost: Přístup je založen na standardních protokolech a široce dostupném hardwaru, což jej činí použitelným pro širokou škálu vestavěných projektů.

— Editorial Team

Advertisement 728x90

Číst dál