Programmare SlimLine Raspberry in “C”

Il modulo SlimLine Raspberry oltre alla programmazione secondo la normativa IEC61131 in stile PLC, in ambiente LogicLab o CODESYS, può essere programmato in linguaggio “C” e/o “C++” potendo così accedere all’enorme quantità di software disponibile su Internet. Per chi utilizza l’ambiente LogicLab e vuole aggiungere nuove funzioni ed FB scritte in linguaggio “C” e/o “C++” può leggere questo articolo. Per lo sviluppo di applicazioni è possibile utilizzare qualsiasi compilatore, in questo articolo utilizzeremo il noto IDE Codelite.

Installazione toolchain

Per poter compilare programmi eseguibili su Raspberry occorre scaricare la toolchain di cross-compilazione di Raspbian da GitHub, scompattare il file in una cartella di sistema esempio /home/elsist/dev. Ora aggiungere la cartella nel PATH di sistema, editare il file etc/profile inserendo in fondo il link alla toolchain nella cartella in cui è stata trasferita, di seguito il link per i sistemi a 32 bits sulla riga superiore e 64 bits sulla riga inferiore.

export PATH=/home/elsist/dev/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH
export PATH=/home/elsist/dev/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH

Dopo aver modificato il file per renderlo attivo occorre da un terminale eseguire il comando source /etc/profile, è possibile verificare che il PATH sia attivo con il comando printenv. Se tutto è corretto è possibile compilare un semplice programma HelloWorld ed eseguirlo. Con un editor di testo creare il file main.c con il seguente contenuto :

#include <stdio.h>
int main()
{
    printf("Hello World!\r\n");
    return 0;
}

Compilare il programma con il comando arm-linux-gnueabihf-gcc main.c -g -o hello, verrà creato il file eseguibile hello che trasferito sulla Raspberry potrà essere eseguito. Anche se ora abbiamo tutto quello che serve per compilare un programma è preferibile affidarsi ad un IDE che permetta di gestire in modo più semplice lo sviluppo di applicazioni complesse, nel prosieguo utilizzeremo Codelite.

Installazione IDE

Codelite è un IDE open source gratuito disponibile per Windows, Linux e MacOS per lavorare con il linguaggio C, il programma è scaricabile dal sito ufficiale. Tralascio tutte le istruzioni per l’installazione che oltre ad essere molto semplici sono disponibili su una infinità di guide reperibili su Internet. Anche se il programma è disponibile per Windows, ne consiglio l’installazione su di una macchina Linux in quanto dovendo sviluppare applicazioni per Raspberry ci si trova già con tutte le librerie di base disponibili.

Ora creare un nuovo Workspace a cui aggiungere un nuovo progetto, nel progetto settare il compilatore della toolchain Raspberry, tasto destro sul nome progetto, scegliere Settings->General->Compiler ed impostare Cross GCC (arm-linux-gnueabihf). Ora compilare il progetto e nella cartella Debug o Release del progetto verrà generato il file eseguibile. che trasferito sul modulo Raspberry sarà possibile eseguirlo. Viene fornito per il download un programma dimostrativo PTP168 con alcuni progetti Codelite già pronti.

CodeLite Screenshot

Trasferire programma su Raspberry

Con il comando build viene compilato il programma che è salvato nella cartella Debug o Release (In base alla selezione di compilazione) creata all’interno della cartella di progetto. Ora utilizzando un SFTP occorre trasferirlo sul modulo Raspberry per l’esecuzione. Esistono molti client SFTP con interfaccia grafica adatti allo scopo il più noto è Filezilla, ma è più comodo agire semplicemente da una finestra terminale utilizzando il comando scp abbinandolo al comando sshpass (Se il comando sshpass non è presente occorre installarlo).

Per poter accedere al sistema occorre acquisire la chiave SHA256 di autenticazione, quindi da una finestra terminale eseguire il comando ssh <Username>@<Host> (Esempio ssh [email protected]) e confermare con yes. Verrà richiesta la password di accesso e la chiave sarà aggiunta ai known hosts (File /home/user.ssh/known_hosts). Nel caso il sistema host venga sostituito occorrerà eliminare la chiave con il comando ssh-keygen -R <Host> e generare una nuova chiave.

Ora ipotizzando che il file eseguibile HelloWorld compilato sia nella cartella /home/HelloWorld/Release che il modulo Raspberry abbia credenziali pi:raspberry ed indirizzo IP 192.168.0.180, volendo trasferire l’eseguibile nella cartella /home/pi occorre utilizzare il comando sshpass seguito dall password di accesso

sshpass -p 'raspberry' scp /home/HelloWorld/HelloWorld/Release/HelloWorld [email protected]:/home/pi

Codelite può automaticamente al termine della compilazione eseguire il comando scp trasferendo il file sul modulo Raspberry, per impostarlo occorre dal tasto destro sul nome progetto, scegliere menù Settings->Pre/Post Build Commands->Post Build ed inserire la riga di comando definita sopra. Nei progetti presenti nel dimostrativo occorre modificare il comando di post build per trasferire il programma sul proprio modulo Raspberry.

Progetto “HelloWorld”

Questo progetto esegue la stampa del classico messaggio Hello World! sulla console.

HelloWorld (Ptp168)
// #include <stdio.h>

int main(int argc, char **argv)
{
    printf("hello world\n");
    return 0;
}

// [End of file]

Progetto “CPhrAccess”

Il programma attiva il watchdog (Vedi articolo) e gestisce un modulo di estensione I/O digitali connesso al bus. Per lo sviluppo di applicazioni che accedono all’hardware del sistema ed ai moduli di estensione viene fornita la libreria libeS8CoreMng. Per includere la libreria nel progetto con il tasto destro sul nome progetto nel menù Settings->Compiler->Include Paths definire la cartella “./Lib/eS8CoreMng/Include” in cui si trova la libreria. La libreria usa la gestione dei threads quindi per poter compilare il progetto occorre definire dal tasto destro sul nome progetto nel menù Settings->Linker->Linker Options i parametri -pthread -lrt.

CPhrAccess (Ptp168)
// *****************************************************************************
// PROGRAM "CPhrAccess"
// *****************************************************************************
// A simple programs that shows how to access to peripheral modules attached to
// the SlimLine extension BUS.
// -----------------------------------------------------------------------------

#include <stdio.h>
#include <PhrFcts.h>
#include <Library.h>
#include <WatchDog.h>
#include <PhrI2cBus.h>
#include <SystemTime.h>
#include <LastError.h>
using namespace Elsist; //Defines namespace

// -----------------------------------------------------------------------------
// MAIN PROGRAM
// -----------------------------------------------------------------------------

int main(int argc, char **argv)
{
    // Define variables.
    
    int8_t Result; //Function result
    uint8_t PModules; //Peripheral modules
    uint32_t DInputs; //Digital inputs
    static uint32_t DOutputs; //Digital outputs
    static uint32_t TimeBf; //Time buffer (uS)

    // -------------------------------------------------------------------------
    // SYSTEM INITIALIZATION
    // -------------------------------------------------------------------------
    // A welcome message is displayed.

    printf("Elsist PTP168A000, CPhrAccess, Library:%s\n", eGetLibVersion());

    // Initialize the library, this must be done before use any function.
    // Function returns 0 if Ok or the error code.

    if ((Result=eLibInit()) != 0) {printf("eLibInit error %d\n", Result); return(0);}

    // Defines wich I2C device has been to used to manage the extension bus,
    // and initializes it. Function returns 0 if Ok or the error code.
    
    Result=ePhrI2cBusInit((char_t*)"/dev/i2c-4");
    switch(Result)
    {
        // Error on bus initializing there's some hardware problem.

        case -1: printf("Bus init error: %d\n", Result); return(0);
        
        // System has been restarted by a watchdog intervention.
        // "Result" returns the number of subsequent watchdog interventions.

        default:
        if (Result == 0) printf("System power up\n");
        if (Result != 0) printf("System has been rebooted for %d times\n", Result);
        
        // After a defined number of system reboots by watchdog interventions,
        // a decision must be taken. In this example the program is stopped.
        
        if (Result > 2)
        {
            printf("Too wdog reboot\n");
            eResetWDog(); //Watchdog reset, it reinits the reboot counter
            return(0);
        }
    }

    // Set the peripheral bus ready, this activates all the attached modules.
    // Set also the wdog timeout to 2 seconds. Time range (1 to 60000 mS).
    
    while (!ePhrI2cBusReady(true, 2000)); //Set ready signal on peripheral bus

    // -------------------------------------------------------------------------
    // PROGRAM LOOP
    // -------------------------------------------------------------------------
    // This is the main program loop, here all the program tasks has to be done
    // in this example the 8 digital outputs of an extension module are managed.

    while(true)
    {
        // The watchdog control has been set so it must be refreshed. If it's
        // not refreshed in the time set the system is rebooted.

        eWDogRefresh(); //Refresh the watchdog circuit

        // Read how many modules are attached to the bus, if no modules ends.

        PModules=eGetModulesOnPhrI2cBus(); //Peripheral modules
        if (PModules == 0) return(0);

        // Supposing to have a logic I/O module (With address 0) attached to the bus
        // are reading the inputs and managed the outputs.

        if (!eIsPhrI2cBusExtModuleAv(0)) return(0); //Exit if module not present
        if (!eGetPhrDI(0, DI_MODE::DI_8_LL, &DInputs))
            {printf("eGetPhrDi error: %u\n", eGetLastError()); return(0);}

        // Copy Di00 input status on Do00 output.

        if (DInputs&eBITPATTERN(0)) eBITSET(DOutputs, 0); else eBITRESET(DOutputs, 0);

        // Blink the Do01 outputs.

        if (TimeBf == 0) TimeBf=eGetSysTime(); //Initialize the reference time
        if ((eGetSysTime()-TimeBf) > eSEC(1))
        {
            TimeBf=eGetSysTime(); //Time buffer (uS)
            if (DOutputs&eBITPATTERN(1)) eBITRESET(DOutputs, 1); else eBITSET(DOutputs, 1);
        }

        // Transfer the value on the extension module digital outputs.

        if (!eSetPhrDO(0, DO_MODE::DO_8_LL, &DOutputs))
            {printf("eSetPhrDO error: %u\n", eGetLastError()); return(0);}
    }
}

// [End of file]
Was this article helpful?