Pagine

venerdì 22 giugno 2012

Dalle QT alla DEMOJM, ovvero come spedire "velocemente" i nostri bit dal PC all'esterno. (parte terza)




Cari lettori di hwdebug.com eccoci giunti alla terza uscita della serie di articoli dedicati alla comunicazione seriale fra le librerie Nokia Qt e la demoboard Freescale DEMOJM. Nella puntata precedente abbiamo visto come configurare il microcontrollore MCF51JM128 per comunicare via seriale. In questa ci occuperemo di creare un progetto utilizzando l'IDE QT Creator e realizzare una piccola interfaccia grafica che possa servire da base per poter spedire i dati attraverso la seriale.

Prima di cominciare iniziamo con il dire che le librerie utilizzate nel nostro esempio sono le Qt 4.7.4 a 32 bit e l'ambiente di sviluppo è il Nokia Qt Creator 2.3.1. Ad oggi però, data di uscita dell'articolo, sono disponibili le Qt 4.8.2 con l'IDE Qt Creator giunto alla versione 2.5.0. Il sistema operativo su cui realizziamo lo sviluppo è Windows 7.

Facciamo partire il Nokia Qt Creator e dal menu "File" dell'applicativo scegliamo "New File or Project" ci troveremo davanti all'immagine seguente.



Scegliamo il template "Qt Widget Project" e selezionamo nella casella a destra "Qt Gui Application" come nell'immagine seguente.



Queste scelte ci permetteranno di creare un'applicativo con interfaccia grafica su cui andremo ad inserire un tasto, un'etichetta e un line edit per predisporli ad inviare i comandi tramite seriale. Premiamo "Choose" e andiamo avanti. A quel punto diamo un nome al progetto che non contenga spazi bianchi e scegliamo la directory in cui lo vogliamo creare. Nella schermata successiva andiamo avanti premendo "Next" e ci troveremo difronte alla seguente schermata.



Questa finestra permette di scegliere un nome per i file e per la classe base che costituiranno l'ossatura del progetto. Per chi non avesse molte nozioni di informatica, nell'ambito della programmazione ad oggetti, una classe è un modello che descrive un'entità costituita da un insieme di attributi (variabili) e un insieme di metodi (funzioni). Da ogni classe è possibile instanziare uno o più oggetti che vengono definiti appunto istanze della classe su cui poi è possibile settare un valore agli attributi ed utilizzarne i metodi. I metodi servono ad eseguire delle azioni sulla classe stessa o a partire da essa.La classe MainWindow sarà la classe base dell'applicazione e i file mainwindow.h e mainwindow.cpp costituiscono rispettivamente i contenitori per la dichiarazione e la definizione della classe, Qt li creerà automaticamente per noi.

Lasciamo  attiva la spunta su "Generate form" che questo permetterà di creare un Form in cui inserire graficamente gli elementi (pulsanti, etichette, linee di testo, etc) di cui abbiamo bisogno. Nell'ultima schermata lasciamo tutto com'è e premiamo su "Finish". La schermata che apparirà sarà la seguente.



La barra stretta a sinistra permette di navigare all'interno del Qt Creator. Permette, ad esempio, di accedere alle opzioni del progetto in "Projects" oppure alla documentazione tramite il tasto "Help". Nella parte bassa della barra, al di sotto del nome del progetto, troviamo in verde i pulsanti di avvio e debug del progetto mentre l'icona del martello rappresenta il tasto di compilazione.

La finestra verticale intermedia permette di visualizzare tutti i progetti aperti (in questo caso solo il progetto "SerialInterface") con tutto l'albero delle cartelle ad esso associate ed i file ad essi associati. In particolare nelle sottocartelle Headers e Sources contiene i file .h e .cpp associati al progetto. La cartella Forms ci permette di accedere all'editor visuale delle interfacce: la utilizzeremo per creare una semplice finestra per inviare i comandi via seriale. Nel finestrone grande a destra troviamo invece l'editor per il codice. Le quattro etichette in basso, numerate da 1 a 4, permettono di attivare una finestra ausiliaria in cui viene mostrato l'output di compilazione, debug, etc.

A questo punto siamo pronti per iniziare a creare l'interfaccia visuale. Andiamo nella cartella Forms e facciamo doppio click sul file "mainwindow.ui". Ci ritroviamo davanti l'interfaccia mostrata nella figura seguente.



La barra a sinistra contiene tutti gli elementi che potremmo inserire nella nostra finestra di dialogo (grigia con i puntini neri) mostrata invece al centro schermo nella finestra di colore bianco. Nelle due finestre all'estrema sinistra troviamo in alto la gerarchia degli oggetti che costituiscono l'interfaccia grafica, mentre in basso le proprietà dell'oggetto correntemente selezionato. Attualmente nella MainWindow troviamo soltanto quattro oggetti: centralWidget, menuBar, mainToolBar e statusBar. Selezionando uno di essi la finestra sottostante mostrerà tutti gli attributi dell'oggetto selezionato.

Ad esempio, selezionando l'oggetto MainWindow, troveremo nella lista delle proprietà in basso a destra la proprietà windowTitle, tale proprietà rappresenta il titolo della finestra principale. Selezionandola con il mouse potremmo modificarla e inserire il titolo che più ci aggrada. Nel nostro caso lo modifichiamo in "Serial to Human Interface" come mostrato nell'immagine successiva.



Dalla lista di classi a sinistra trasciniamo nella finestra un "Vertical Layout" dal gruppo Layouts e lo mettiamo al centro della finestra. Vediamo che nella finestra delle gerarchie l'oggetto viene instanziato gerarchicamente sotto l'oggetto centralWidget. Il Vertical Layout servirà ad ordinare un insieme di elementi verticalmente.


All'interno del Vertical Layout trasciniamo una "Line Edit" dal gruppo Input Widgets e un "Push Button" dal gruppo Buttons. Entrambi gli oggetti sono stati instanziati al di sotto del Vertical Layout inserito in precedenza. In tal modo abbiamo una finestra con un box dove inserire del testo e un pulsante che ci permetterà di inviarlo via seriale alla DEMOJM.


Cambiamo l'etichetta dell'oggetto pushButton selezionandolo dalla finestra delle gerarchie e andando ad editare la proprietà text. Con molta fantasia il testo che scriviamo è: "Invia stringa". Per ora la nostra interfaccia spartana è pronta, salviamo e proviamo a visualizzarla in funzionamento premendo il tasto di compilazione. Se tutto va bene il nostro Qt Creator ci aprirà la finestra della nostra applicazione così come l'avevamo costruita. Se proviamo a premere il tasto "Invia stringa" al momento non succederà nulla perché non abbiamo ancora predisposto l'applicazione per reagire a tale evento.

Chiudiamo la nostra applicazione appena creata e torniamo sulla pagina dell'editor dell'interfaccia. Chiudiamo l'editor di interfaccia con la X in alto a sinistra nella barra grigia. Ora bisogna creare un oggetto della classe qextserialport per realizzare la comunicazione seriale. Per crearlo dobbiamo innanzitutto includere l'header delle librerie in cima al file mainwindow.h utilizzando la direttiva
#include "qextserialport.h"
Instanziamo poi un puntatore ad un oggetto di tipo qextserialport all'interno della classe MainWindow inserendolo tra i membri privati della classe, sotto la direttiva private:
private:
     Ui::MainWindow *ui;
     QextSerialPort *port;
in tal modo abbiamo creato un puntatore denominato port il quale punterà ad successivamente ad un'istanza della classe qextserialport. Tale puntatore sarà accessibile da qualsiasi metodo (funzione) appartenente alla classe ma non dall'esterno perché è un membro privato; se fosse stato un membro pubblico invece sarebbe stato accessibile anche dall'esterno.

Apriamo il file mainwindow.cpp e all'interno della funzione MainWindow::MainWindow(QWidget *parent) che costituisce il costruttore della classe MainWindow, cioè il metodo che viene chiamato all'avvio della classe per inizializzare l'istanza della classe e cioè l'oggetto che si sta creando, aggiungiamo le seguenti righe di codice
/* Setting Serial Port parameters */
port = new QextSerialPort();

if(port){
  port->setPortName("COM2");
  port->setBaudRate(BAUD115200);
  port->setDataBits(DATA_8);
  port->setParity(PAR_NONE);
  port->setFlowControl(FLOW_OFF);

  /* Try to open the port */
  port->open(QIODevice::ReadWrite);
  if(port->isOpen())
  {
     ui->lineEdit->setText("COM2 APERTA");
     
  }
  else
  {
     ui->lineEdit->setText("COM2 NON APERTA");
  }
} 
come si può notare nella prima parte andiamo ad instanziare l'oggetto dalla classe QextSerialPort con la funzione new, creando in pratica quella che sarà la nostra interfaccia seriale. A quel punto se l'instanziazione dell'oggetto è andata a buon fine si settano i parametri della porta, nell'ordine: nome, velocità, numero di bit, controllo di parità e di flusso. Per la descrizione dettagliata delle funzioni chiamate ci si può riferire alla guida delle librerie qextserialport che si può trovare qui. In buona sostanza si vanno a settare gli attributi dell'oggetto instanziato tramite le chiamate a dei metodi specifici. Il vantaggio di tale meccanismo sta nel fatto che l'inizializzazione di un attributo tramite metodo permette di realizzare un'inizializzazione intelligente, realizzando un controllo sui valori ed evitando ad esempio di attribuire dei valori sbagliati ad un attributo.

Dopo aver settato i parametri si prova ad aprire la porta con il metodo open() chiamato direttamente all'interno del costrutto if(). Se l'istruzione torna un valore non nullo e l'apertura della porta va a buon fine l'istruzione
ui->lineEdit->setText("COM2 APERTA");
modifica il testo del lineEdit che avevamo creato dicendo che la porta è aperta. Si vede che l'accesso ai membri dell'interfaccia grafica si fa tramite il puntatore "ui" che rappresenta la user interface. In caso contrario il messaggio nega l'apertura della porta avendo subito un feedback visuale della cosa.

A questo punto bisognerà collegare la pressione del pulsante all'invio della stringa di dati contenuta nel lineEdit che abbiamo inserito in precedenza. Prima di far questo conviene fare una piccola digressione su come le Qt gestiscono gli eventi. Un evento all'interno di un'applicazione viene generato all'accadere di qualcosa nell'applicazione. Ad esempio, alla pressione di un pulsante viene generato un evento che segnala che quel pulsante è stato premuto. Tale evento viene comunicato all'applicazione e può essere gestito per realizzare una sequenza di istruzioni che realizzano una specifica funzionalità. Nel nostro caso ad esempio alla pressione del tasto si vorrà inviare la stringa contenuta nel lineEdit attraverso l'interfaccia seriale.

Nelle Qt la gestione degli eventi avviene tramite i signal e gli slot. Un signal, in italiano segnale, viene emesso quando avviene un particolare evento; l'applicazione segnala l'evento appunto. Ad esempio, per i tasti, viene dichiarato nella classe del tasto un segnale che segnala la pressione del tasto, oppure un segnel che ne segnala il rilascio, etc. Lo slot costituisce invece la funzione che dovrà gestire tale evento. Collegando fra loro un segnale ad uno slot si ha che lo slot verrà processato ogni volta che verrà emesso un segnale.

 Il signal relativo alla pressione del pulsante è dichiarato  all'interno della classe che descrive il pulsante. Lo slot invece va dichiarato nella classe MainWindow al di sotto delle dichiarazioni di tipo private nel seguente modo
private:
     Ui::MainWindow *ui;
     QextSerialPort *port;
private slots:
    void sendSerial();
mentre la sua definizione si fa all'interno del file mainwindow.cpp nel seguente modo
void MainWindow::sendSerial()
{
  QString command;
  command = ui->lineEdit->text();
  if(port)
  {
    port->write(command.toAscii().data()):
  }
}
Si vede chiaramente che il nostro slot, che risponderà alla pressione del tasto, inizialmente dichiara una stringa denominata command di tipo QString che utilizziamo per prendere il testo presente nella lineEdit tramite il metodo text().  Il comando port->write(command.toAscii().data()); permette di inviare i dati contenuti nella variabile command attraverso la porta seriale.

Per poter attivare lo slot sendSerial() alla pressione del tasto bisognerà realizzare la connessione fra il signal e lo slot all'interno del costruttore della classe MainWindow con l'istruzione
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(sendSerial()));
Tale instruzione la si potrà collocare all'interno del metodo costruttore in un qualsiasi punto e farà in modo che ad ogni pressione del tasto "Invia stringa" venga inviata la stringa presente nella linea di testa alla seriale.

A tal punto premendo il tasto di compilazione si dovrebbe essere in grado di far partire l'applicazione.
Prima però di poter comunicare con la nostra DEMOJM dovremo realizzare il ponte fra le COM2 e la porta COM virtuale come descritto nell'articolo di markocont qui. Gestendo opportunamente la stringa dei dati inviati alla DEMOJM potremmo ad esempio far realizzare alla scheda dei comandi, come ad esempio, l'accensione di alcuni led oppure chi più ne ha più ne metta. Nel nostro caso ad esempio inviava dei comandi che modificavano il duty cycle di un segnale PWM generato dai Timer su 3 canali diversi.

Bene la serie di articoli con argomento la comunicazione seriale è terminata. Spero siano stati abbastanza chiari. In ogni caso per ogni dubbio c'è lo strumento dei commenti a disposizione.
Alla prossima!!!

Le puntate precedenti sono qua:

Nessun commento:

Posta un commento