Dot Led Matrix Display: controllo da PC via USB

Ho preso un Kingbringht Dot Led Matrix Display (datasheet Kingbright Electronic 157524, 1 datasheet pdf): una matrice di LED 7x5.

Per l'occasione ho scritto sull'arduino questo semplice firmware: un parser dei comandi che gli invio sulla serial.

// array that will hold the  bytes of the incoming string.
#define inBUF 14
char inBuffer[inBUF];

// read a string from the serial and store it in an array
// you must supply the array variable
void readSerialString (char *strArray)
{
  int i=0;
  while (true)
  {
    if (Serial.available())
    {
      while (Serial.available())
      {
        strArray[i] = Serial.read();
        i++;
      }
      if (strArray[i-1] == '

Quindi sul lato PC ho realizzato un semplice programmino in C# con mono.

using System;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace BlikALed
{
      class MainClass
      {
            
            public static void Main(string[] args)
            {
                  
                  // Init Porta seriale RS-232
                  SerialPort rs232 = new SerialPort("/dev/ttyUSB0", 115200, Parity.None, 8, StopBits.One);
 
                  // Apertura della porta
                  try
                  {
                        rs232.Open();
                        // È necessario aspettare un tempo minimo di 1.5 s prima di inviare
                      // qualsiasi comando alla seriale, pena il non funzionamento di
                        // ogni comando.
                        Thread.Sleep(2000);
                  }
                  catch (System.IO.IOException ioe)
                  {
                        Console.Write(ioe.Message);
                        Environment.Exit(1);
                  }
                  
                  // Setup pins
                  rs232.Write("SOOOOOOOOOOOO$");
                  //rs232.Write("DLLLLLLLLLLLL$");
                  Thread.Sleep(200);
                  
                  // mappatura pin 7x5
                  //int C = {2, 3, 4, 5, 6};
                  //int R = {7, 8, 9, 10, 11, 12, 13};
                  
                  // acensione colonne in sequenza
                  rs232.Write("DLLLLLHHHHHHH$");
                  Thread.Sleep(150);
                  for (int i=0; i<100;i++)
                  {
                        rs232.Write("DHLHHHHHHHHHH$");
                        Thread.Sleep(11);
                        rs232.Write("DHHLHHHHHHHHH$");
                        Thread.Sleep(11);
                        rs232.Write("DHHHLHHHHHHHH$");
                        Thread.Sleep(11);
                        rs232.Write("DHHHHLHHHHHHH$");
                        Thread.Sleep(11);
                        rs232.Write("DHHHHHLHHHHHH$");
                        Thread.Sleep(11);
                        rs232.Write("DLHHHHHHHHHHH$");
                  }
                  Thread.Sleep(500);
                  rs232.Write("DLLLLLLLLLLLL$");

                  // Chiusura porta
                  rs232.Close();
            }
      }
}

Il programma a basse velocità funziona bene, non appena riduco i delay le cose non vanno troppo bene. Che salti qualche colonna a volte me lo sarei anche aspettato (ma 115200 bps non è una velocità di trasferimento sufficientemente alta per evitarlo??), quello che non capisco è perchè a volte nella scansione mi accende solo metà colonna o anche solo un led solo, questo proprio non riesco a capirlo. Una volta che sono entrato nella routin di accensione dei led non centra più nulla la seriale e sono dentro all'arduino, quindi non ne trovo un senso.

Come posso migliorare la situazione? Cioè, posso andare a codificare in qualche modo la stringa di comandi che invio sulla seriale riducendo quindi il numero di byte trasmessi oppure mi devo mettere il cuore in pace che anche codificando non guadagnerei abbastanza in velocità? Oppure pensare ad ottimizzare il firmware, invece del pinmode accedere direttamente in assembler alle porte?

Qualche suggerimento?

Ciao
Albano)
     {
       return;
     }
   }  
 }
}

//utility function to know wither an array is empty or not
boolean isStringEmpty (char *strArray)
{
   if (strArray[0] == 0)
   {
       return true;
   }
   else
   {
       return false;
   }
}

void setup()
{
   Serial.begin(115200);
   Serial.println("PaceyIV v0.1");
}

void loop ()
{
   
   // read the serial port and create a string out of what you read
   readSerialString(inBuffer);
   
   if (isStringEmpty(inBuffer)==false)
   {
     // echo cmd
     Serial.println(inBuffer);

//------------
       // "S" = code to setup pins 2-13 as input I or output O
       // eg. SIOIOIOIOIOOO
       if (inBuffer[0]=='S')
       {
           for (int i=1; i<=12; i++)
           {
               if (inBuffer[i]=='I')
               {
                   pinMode(i+1, INPUT);
               }
               else
               {
                   pinMode(i+1, OUTPUT);
               }
           }
       }
       
       //--------
       // "D" = code for digital out to arduino looks for D as first
       // character in the input string
       // then looks for H & L letters
       // for example computer sends DHLHHLLHHHLH
       if (inBuffer[0]=='D')
       {
           for (int i=1; i<=12; i++)
           {
               if (inBuffer[i]=='H')
               {
                   digitalWrite(i+1, HIGH);
               }
               else
               {
                   digitalWrite(i+1, LOW);
               }
           }
       }
   }
   
   // clean the buffer
   for (int i=0; i<=inBUF; i++)
   {
       inBuffer[i]=0;
   }
   
}


Quindi sul lato PC ho realizzato un semplice programmino in C# con mono.

§DISCOURSE_HOISTED_CODE_1§


Il programma a basse velocità funziona bene, non appena riduco i delay le cose non vanno troppo bene. Che salti qualche colonna a volte me lo sarei anche aspettato (ma 115200 bps non è una velocità di trasferimento sufficientemente alta per evitarlo??), quello che non capisco è perchè a volte nella scansione mi accende solo metà colonna o anche solo un led solo, questo proprio non riesco a capirlo. Una volta che sono entrato nella routin di accensione dei led non centra più nulla la seriale e sono dentro all'arduino, quindi non ne trovo un senso.

Come posso migliorare la situazione? Cioè, posso andare a codificare in qualche modo la stringa di comandi che invio sulla seriale riducendo quindi il numero di byte trasmessi oppure mi devo mettere il cuore in pace che anche codificando non guadagnerei abbastanza in velocità? Oppure pensare ad ottimizzare il firmware, invece del pinmode accedere direttamente in assembler alle porte?

Qualche suggerimento?

Ciao
Albano

Ho riscritto il firmware inserendogli dentro anche un print sulla seriale di debug

// 
int Opins[13]; int NOpins=0;
int Ipins[13]; int NIpins=0;

//
char chr;

// read a char from the serial
char readSerialChar ()
{
  while (true)
  {
    if (Serial.available())
    {
      return Serial.read();
    }
  }
}

void setup()
{
    Serial.begin(115200);
    Serial.println("PaceyIV v0.1");
}


void loop ()
{

  chr = readSerialChar();
  
  switch (chr)
  {
    case 'S':
      // routin to setup pins 2-13 as input I or output 0
      NOpins = 0; NIpins = 0;
      Serial.println("S");
      for (int i=2; i<=13; i++)
      {
        chr = readSerialChar();
        if (chr == 'O')
        {
          pinMode(i, OUTPUT);
          Opins[NOpins] = i;
          NOpins++;
        }
        else
        {
          pinMode(i, INPUT);
          Ipins[NIpins] = i;
          NIpins++;
        }
        Serial.print(i);
        Serial.println(chr);
      }
      
      break;
    
    case 'D':
      // routin to set digital output
      for (int i=0; i<NOpins; i++)
      {
        chr = readSerialChar();
        if (chr == 'H')
        {
          digitalWrite(Opins[i], HIGH);
          Serial.print(Opins[i]);
          Serial.println("H");
        }
        else
        {
          digitalWrite(Opins[i], LOW);
          Serial.print(Opins[i]);
          Serial.println("L");
        }
      }
      
      break;
  }
}

Ho mandato in sequenza questi comandi con un delay tra un comando e l'altro di ben 3 secondi.

SOOOOOOOOOOOO // setto tutti i pins come uscite
DLLLLLHHHHHHH // accendo tutti i led della matrice (tutte le righe H e le colonne L: i led sn girati così!)
DLHHHHHHHHHHH // accendo una colonna dopo l'altra in sequenza
DHLHHHHHHHHHH
DHHLHHHHHHHHH
DHHHLHHHHHHHH
DHHHHLHHHHHHH
DLLLLLLLLLLLL // spengo tutto

Questo quello che ricevo

PaceyIV v0.1
S
2O
3O
4O
5O2L
32H
3L
4L
5L
6L
7L
2H
3H
4L
5H
6H
7H
8H
9H
10H
112L
3L
4L5L
6L
7L
8L
9L
10L
11L
12L
13L

Mi perde byte. La seconda colonna non me l'ha accesa per niente (solo quando ho acceso tutto all'inizio x verificare che non sia un problema di cablaggio), la 4 colonna l'ha accesa solo per metà.
Dov'è ke si può inceppare???? :cry:

Ciao

Per una serie di ragioni abbastanza lunghe da spiegare il driver che permette ad arduino di essere visto come una seriale ha dei problemi a 115200.

fino a 57600 usa dei buffer da 64 byte mentre dopo passa a 4k
se non mandi 4k di dati allora lui aspetta di andare in timeout e diventata tutto meno fluido.

Inoltre il buffer di RX di Arduino è di circa 63 caratteri perciò devi cercare di ottimizzare gli invii di dati.

Queste righe non ti danno il massimo dell'affidabilità:
if (Serial.available())
{
while (Serial.available())
{

processa tutto un carattere alla volta tipo
if ((Serial.available() > 0)) {
in_byte = Serial.read();

e metti in un buffer tuo... quando ricevi il terminatore parsi il buffer.

per usare arduino come "terminale di IO" per il computer è meglio usare lo standard Firmata che trovi sul playground. E' molto piu ottimizzato e debuggato

massimo

nella seconda versione del mio firmware ho ottimizzato infatti la cosa, e sono andato a scansionare e processare contemporaneamente carattere per carattere, in questo modo non ho nemmeno più bisogno del terminatore di comando.

Questa sera riguarderò la librerie Firmdata, ma mi pare di averla scartata perchè era troppo poco personalizzabile rispetto a quello che voglio fare io, cmq posso eventualmente sempre prendere spunto da li!

Dunque, come prima cosa, grazie mille Massimo!
Riducendo la velocità della seriale a 57600 con la mia seconda versione del firmware il programma ha iniziato a funzionare perfettamente. Riesco ad arrivare tranquillamente a delay di 1ms sul pc tra un comando e l'altro. Ho provato anche frazioni di ms, ma così ogni tanto ho un leggero rallentamento ma visto che il PC è multithread direi che oltre ai ms non posso andare!

Ho guardato la libreria Firmdata. Però non ho capito molto e nemmeno poco :S
Si inizializza la libreria, si attaccano i segnali che si intende osservare/comandi ai quali si vuole reagire, quindi si entra nel suo loop di parsering del comando. E già qui iniziano i problemi. Perchè dovrei attaccarci dei segnali? Non basta il parser che analizza il comando e quindi fa quello ke gli ho kiesto da pc?
Poi anche gli esempi inclusi nella libreria: AnalogFirmata prima avvia il parser tramite la libreria e poi immediatamente dopo si mette a stampare sulla seriale letture dei pin analogici?! Ma sto firmware non serve x uccidere il cervello dell'arduino e fargli fare da automa tt quello che gli comanda il pc tramite il parsering dei comandi? e allora perchè si mette a stampare sulla seriale da solo, senza che glielo chiedo?

Immagino di non aver capito una mazza!

Se il tuo programma funziona , continua ad usare quello :slight_smile:

visto che sai programmare, Firmata non ti aggiunge molto

massimo

Ok.... :-?

grazie