SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Placez ici vos trucs et astuces, étalez sans retenue votre savoir-faire et votre science qui va nous permettre de redonner une apparence neuve et fonctionnelle à nos bouzes.

Modérateurs : Papy.G, fneck, Carl

Daniel
Messages : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Daniel »

Chacun fabrique le sien. Il suffit d'enficher l'écran sur l'Arduino, il n'y a pas de soudure sauf pour la LED (facultative).
Le câble est différent pour chaque modèle d'ordinateur, c'est à chacun de le réaliser en fonction de son matériel.
Une commande groupée ne réduirait presque pas le coût des composants et ajouterai des frais de port disproportionnés par rapport au coût total.
(Environ 4 euros pour l'arduino uno et 5 euros pour l'écran 2,4" port inclus)
Daniel
L'obstacle augmente mon ardeur.
Vladimir Kr Can
Messages : 33
Inscription : 24 déc. 2020 00:04
Localisation : Quebec

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Vladimir Kr Can »

Pour l'Arduino chinois, il s'agit d'un Wavgat Uno, qui physiquement était assez sympa, puisque les connecteurs Rx/Tx étaient dupliqués, mais le code ne compilait pas, vraiment pas. J'ai fait quelques recherches, et il s'est avéré que les Wavgat ne sont pas 100% compatibles

Bref, plutôt que de chercher à faire marcher la plaque, ce que je pourrais faire, peut-être plus tard, j'ai préféré réduire le nombre d'inconnues et opté pour un original, qui lui, compile.

Voici le code en son état actuel et les modifications apportées :
  • ligne 137 pour la définition de la broche
  • lignes 167 à 184 pour la partie écran
  • lignes 330 à 347 pour la partie SD

Les modifs ne sont pas très propres pour le moment, puisque pas encore définitives et que je tâtonne.

Code : Tout sélectionner


/**************************************************\
*               S D L E P  -  T F T                * 
*            (c) 2019 - Daniel Coulom              *  
*                                                  *
*              http://dcmoto.free.fr/              *
*           http://forum.system-cfg.com/           *
*                                                  *
*       2.8" TFT version by Andrea Ottaviani       * 
*   Minor tweaks & improvements by C. Catalanotto  *
*                                                  *
*--------------------------------------------------*
* SDLEP-TFT+ emule un lecteur de cassette Thomson  *
* TO ou MO.  Les images de cassette au format .lep *
* doivent être stockees à la racine d'une carte SD *
* (SD/SDHC) format?e en FAT16 / FAT32.             *
*--------------------------------------------------*
* Ce code est distribue gratuitement dans l'espoir *
* qu'il sera utile, mais sans aucune  garantie  et *
* sans  engager  la  responsabilité  de  l'auteur. *
* Vous  pouvez  l' utiliser,  le  modifier  et  le *
* diffuser librement, en conservant cette  licence *
* et les références de l'auteur dans   toutes  les *
* copies. L'exploitation commerciale est interdite.*
\**************************************************/


/***************************************************
*             HISTORY (SDLEP-TFT+)                 *
****************************************************

V0.1 :
      o Some minor refactoring here and there.
      o Switched to FastGPIO library to hopefully spare a few tens of cycles here and there.
      o Removed second screen (filename + filesize + motor status).
      o Replaced second screen with very light motor state indicator and progress bar at the bottom of first screen
      o Tried to measure and compensate overhead of 1/ SD Block change 2/ Progress bar display
      o Added version number display
      o Added some debug modes to help code testing & execution time profiling 
*/


/***************************************************
*                   TODO LIST                      *
****************************************************

      o [TODO] Get rid of MCUDEV_kbv library
      o [TODO] Clean handling of X/Y inverted touchscreens
      o [TODO] Check whether or not SdFat & Gfx libs use fast GPIO accesses
      o [TODO] LEP write : is implementation even possible with Arduino and no extra HW ? Creating a .LEP
               file from TO7/70 output would require decoding bit subcarriers (6.3KHz & 4.6KHz) and writing
               to SD on the fly. Is it even worth the work ? Need some time to think about it...
*/

/***************************************************
* Wiring instructions                              *
****************************************************

SD Card (if not integrated to TFT shield) :
  o GND --> GND
  o D13 --> SCK
  o D12 --> MISO
  o D11 --> MOSI
  o D10 --> CS 
  o +5V --> VCC (5V)

TO / MO LEP (DIN) :
  o GND --> DIN 2
  o D0  --> DIN 1  (MOTOR)
  o D1  --> DIN 4  (READ DATA) 
   
Power Arduino from lightpen DIN (MO only) :
  o Vin --> DIN 5  (+5V)
   
SDLEP Activity LED
  o A5 --> LED to ground through ~1k resistor

*/

/***************************************************
* NOTES                                            *
****************************************************

  o SD card management is adapted from SimpleSDAudio library.
    Visit SimpleSDAudio website for more information:
    http://www.hackerspace-ffm.de/wiki/index.php?title=SimpleSDAudio

  o The following libs shall be installed
      . Adafruit_GFX
      . SdFat
      . TouchScreen
      . MCUFRIEND_kbv
*/

/***************************************************
*             HISTORY (SDLEP-TFT)                  *
****************************************************
  
2019.03.27 MOTOROFF: sortie haute immediate pour
           eviter l'erreur de detection du LEP 
2019.03.24 parametres touchscreen dans fichier .h
2019.03.20 ajout ecran 2.8" smart electronics HX8347
2019.03.14 ajout ecran 2.8" mcufriend ILI9325
2019.03.06 utilisation bibliotheque MCUFRIEND_kvb
2019.03.04 reset en cas d'echec de sd.begin()
2019.02.24 ameliorations de details by DC
2019.02.23 adaptation ? un ?cran 2.4" different
2019.02.10 added TFT support by AO
2017.04.16 optimisations des acces a la carte SD
2017.03.13 integration des acces a la carte SD
2017.01.17 bit data a 1 pendant l'arret moteur
2016.12.30 unite du delai definie en parametre
2016.12.29 noms de fichiers .lep sur 7 caracteres
2016.12.19 quelques optimisations mineures
2016.12.18 clignotement LED si fichier non trouve
2016.12.18 nom de fichier choisi par switches
2016.12.17 choix de broches different pour les I/O
2016.12.17 delai en dixiemes de ms dans sd.lep
2016.12.16 ajout LED d'activite
2016.12.16 reste de count/127 ecrit avant les zeros
2016.12.14 premiere version du programme
*/

#define DEBUG
//#define DEBUG_IGNORE_MOTOR_ON                             // Runs as soon as file is selected
//#define DEBUG_FIXED_DELAY                     50          // Constant delay between byte reads in us (for easier UI testing UI or execution time profiling)
//#define DEBUG_ALWAYS_TOGGLE                               // Toggle DIGITAL OUT every time a new byte is read (for easier execution time profiling)


#include <avr/wdt.h>                                      // HW Watchdog (CCA : shall not work without optiboot or decently recent stock bootloader) 
#include <FastGPIO.h>                                     // Faster GPIO handling
#include <SdFat.h>                                        // Access to FAT/FAT32 formatted SD/SDHC
#include <Adafruit_GFX.h>                                 // Core graphics library

#define SDLEP_PLUS_VERSION                    "v0.1"

#define SD_CS_PIN                             4           // remplace 10, car le lecteur SD sur la carte Adafruit 2.8 V2 nécessite la broche 4          // Set the SD chip select line to whatever pin you use (10 doesn't conflict with the library)


#define SDLEP_MOTOR_ON_PIN                    0           // Set this to whatever pin you use for MOTOR ON input signal (Generally D0 is used)
#define SDLEP_DIGITAL_DATA_PIN                1           // Set this to whatever pin you use for DIGITAL DATA output signal (Generally D1 is used)
#define SDLEP_ACTIVITY_LED_PIN                A5          // Set this to whatever pin you use for SDLEP activity LED (A5 is generally the only one left free)

#define DELAY_UNIT_US                           50          // Delay unit in us (50us as per LEP file format spec)
#define DELAY_COMPENSATE_PROGRESS_DISPLAY_US    50          // Define this to compensate progress bar update execution time. Value in us (need code profiling or a scope to obtain this value !). Comment if not needed.
#define DELAY_COMPENSATE_BLOCK_CHANGE_US        80          // Define this to compensate SD block chage execution time. Value in us (need code profiling or a scope to obtain this value !). Comment if not needed.

#define PROGRESSBAR_Y                           230
#define PROGRESSBAR_WIDTH                       280
#define PROGRESSBAR_GAP                         2
#define PROGRESSBAR_THICKNESS                   2

#define PROGRESSBAR_FRAME_X                     ((320 - PROGRESSBAR_WIDTH) / 2)
#define PROGRESSBAR_FRAME_Y                     (PROGRESSBAR_Y)
#define PROGRESSBAR_FRAME_WIDTH                 (PROGRESSBAR_WIDTH)
#define PROGRESSBAR_FRAME_HEIGHT                (1 + PROGRESSBAR_GAP + PROGRESSBAR_THICKNESS + PROGRESSBAR_GAP + 1)

#define PROGRESSBAR_BAR_X                       (PROGRESSBAR_FRAME_X + PROGRESSBAR_GAP + 1)
#define PROGRESSBAR_BAR_Y                       (PROGRESSBAR_FRAME_Y + PROGRESSBAR_GAP + 1)
#define PROGRESSBAR_BAR_HEIGHT                  (PROGRESSBAR_THICKNESS)
#define PROGRESSBAR_BAR_MAX_WIDTH               (PROGRESSBAR_WIDTH - 1 - PROGRESSBAR_GAP - PROGRESSBAR_GAP - 1)

SdFat     sd;
SdFile    root;
SdFile    file;

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/*
#include <MCUFRIEND_kbv.h>            
MCUFRIEND_kbv tft;
*/

// Déclaration pour Adafruit 2.8 v2, tirée de graphicTest de la bibliothèque
#include "Adafruit_ILI9341.h"
// For the Adafruit shield, these are the default.
#define TFT_DC 9
#define TFT_CS 10

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

// Touchscreen : update touchscreen-param.h depending on your own setup (some sample files are provided).
#include "TouchScreen.h"
#include "touchscreen-param.h" 
TouchScreen ts = TouchScreen(XP, YP, XM, YM, OHMS);

// Assign human-readable names to some common 16-bit color values:

#define BLACK                         0x0000
#define WHITE                         0xFFFF
#define GRAY                          0x8410
#define BLUE                          0x001F
#define RED                           0xF800
#define GREEN                         0x07E0
#define PINK                          0xF8FF
#define ORANGE                        0xFA60
#define CYAN                          0x07FF
#define AQUA                          0x04FF
#define MAGENTA                       0xF81F
#define YELLOW                        0xFFE0

// SD card constants
#define SD_CARD_TYPE_SD1              1     /** Standard capacity V1 SD card */
#define SD_CARD_TYPE_SD2              2     /** Standard capacity V2 SD card */
#define SD_CARD_TYPE_SDHC             3     /** High Capacity SD card */
#define SD_PARTTYPE_UNKNOWN           0
#define SD_PARTTYPE_SUPERFLOPPY       1
#define SD_PARTTYPE_FAT16             2
#define SD_PARTTYPE_FAT32             3
#define SD_INIT_TIMEOUT               2000  /** temps maxi pour l'initialisation */
#define SD_READ_TIMEOUT               300   /** temps maxi pour le debut de la lecture d'un bloc */
#define SD_COMMAND_TIMEOUT            300   /** temps maxi pour repondre a une commande */
#define SD_READY_STATE                0x00  /** status for card in the ready state */
#define SD_IDLE_STATE                 0x01  /** status for card in the idle state */
#define SD_ILLEGAL_COMMAND            0x04  /** status bit for illegal command */
#define SD_DATA_START_BLOCK           0xFE  /** start data token for read or write single block*/

// SD card commands
#define SD_CMD0                       0x00  /** GO_IDLE_STATE - init card in spi mode if CS low */
#define SD_CMD8                       0x08  /** SEND_IF_COND - verify SD Memory Card interface operating condition.*/
#define SD_CMD9                       0x09  /** SEND_CSD - read the Card Specific Data (CSD register), response R1 */
#define SD_CMD10                      0x0A  /** SEND_CID - read the card identification information (CID register), response R1 */
#define SD_CMD12                      0x0C  /** STOP_TRANSMISSION - end multiple block read sequence, response R1b */
#define SD_CMD13                      0x0D  /** SEND_STATUS - read the card status register, response R2 */
#define SD_CMD16                      0x10  /** SET_BLOCKLEN arg0[31:0]: block length, response R1 */
#define SD_CMD17                      0x11  /** READ_SINGLE_BLOCK - read a single data block from the card, response R1 */
#define SD_CMD18                      0x12  /** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card, response R1 */
#define SD_CMD55                      0x37  /** APP_CMD - escape for application specific command */
#define SD_CMD58                      0x3A  /** READ_OCR - read the OCR register of a card */
#define SD_CMD59                      0x3B  /** CRC_ON_OFF - Turns CRC option on or off, response R1 */
#define SD_ACMD41                     0x29  /** SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process */

// SD card error codes
#define SD_ERROR_CMD0                 0x01  /** timeout error for command CMD0 (initialize card in SPI mode), signal problem */
#define SD_ERROR_CMD8                 0x02  /** CMD8 was not accepted - not a valid SD card */
#define SD_ERROR_ACMD41               0x03  /** ACMD41 initialization process timeout */
#define SD_ERROR_CMD58                0x04  /** card returned an error response for CMD58 (read OCR) */
#define SD_ERROR_CMD16                0x05  /** card returned an error response for CMD16 (set block len) */
#define SD_ERROR_VOLTMATCH            0x06  /** card operation voltage range doesn't match (2.7V - 3.6V) */
#define SD_ERROR_READ_TIMEOUT         0x07  /** timeout while waiting for start of read data */
#define SD_ERROR_READ                 0x08  /** card returned error token when tried to read data */
#define SD_ERROR_CMD17                0x09  /** card returned an error response for CMD17 (read single block) */
#define SD_ERROR_CMD9                 0x0e  /** card returned an error response for CMD9  (read CSD) */
#define SD_ERROR_CMD10                0x0f  /** card returned an error response for CMD10 (read CID) */
#define SD_ERROR_CMD18                0x10  /** card returned an error response for CMD18 (read multi block) */
#define SD_ERROR_INVAL_SECT0          0x30  /** No valid MBR/FAT-BS signature found in sector 0 */
#define SD_ERROR_INVAL_BS             0x31  /** Malformed FAT boot sector */
#define SD_ERROR_FAT12                0x32  /** FAT12 is not supported */
#define SD_ERROR_FAT_NOT_INIT         0x33  /** FAT not initialized properly */
#define SD_ERROR_DIR_EOC              0x34  /** End of cluster reached (not a real error, just information) */
#define SD_ERROR_FILE_NOT_FOUND       0x35  /** File not found after reaching end of directory */
#define SD_ERROR_EOF                  0x38  /** End of file reached */

// SD card variables
typedef struct {
  uint8_t     Attributes;
  uint32_t    Size;           // in bytes
  uint32_t    FirstCluster;   // First cluster
  uint32_t    ActSector;      // 0 to (SD_FAT.SecPerClus - 1)
  uint32_t    ActBytePos;     // 0 to Size
} SD_File_t;

SD_File_t LepFileInfo;  

typedef struct  {
  uint8_t     PartType;           // Use this to test whether it is FAT16 or FAT32 or not initialized
  // Stuff from FAT boot sector
  uint8_t     SecPerClus;
  uint16_t    RsvdSecCnt;
  uint8_t     NumFATs;
  uint16_t    RootEntryCount;
  uint32_t    TotalSec;
  uint32_t    SecPerFAT;
  uint32_t    RootClus;
  // For cluster calculations
  uint8_t     ClusterSizeShift;
  uint32_t    ClusterCount;
  // Start addresses (all in blocks / sector addresses)
  uint32_t    BootSectorStart;    // Address of boot sector from FAT
  uint32_t    FatStart;           // First file allocation table starts here
  uint32_t    RootDirStart;       // Root directory starts here
  uint32_t    DataStart;          // Cluster 0 starts here
  uint32_t    ClusterEndMarker;   // if Cluster >= this then end of file reached.
} SD_FAT_t;

SD_FAT_t SD_FAT;

uint8_t  SD_type;        // type de la carte SD
uint8_t  SD_buffer[513]; // SD buffer must hold 512 bytes + 1

char shortname[13]; //nom court du fichier choisi


////////////////////////////////////////////////////////////////////
// Menu de selection du fichier .lep
////////////////////////////////////////////////////////////////////

char* LoadMenu()
{
  int i; 
  byte numentry;                // numero de l'entree de repertoire
  byte numfiles;                // nombres de fichiers affiches
  boolean dirEnd;               // indicateur de fin de repertoire de la carte SD
  boolean prev;                 // indicateur d'existence d'un ecran precedent
  char suf[10];                 // extension du nom de fichier
  char longfilename[25];        // nom long (tronque) d'un fichier
  char shortfilename[8][13];    // noms courts des fichiers affiches
  int  numchoosen;              // numero du fichier choisi
  SdFile entry;                 // entree de repertoire

  // Affichage du titre
  tft.fillScreen(BLACK);      
  tft.fillRect(0,0,320,22,YELLOW);
  tft.setCursor(23,4);
  tft.setTextSize(2);
  tft.setTextColor(BLACK); tft.print(F("SDLEP-READER "));
  tft.setTextColor(RED); tft.print(F("T"));
  tft.setTextColor(GREEN);tft.print(F("F"));
  tft.setTextColor(BLUE);tft.print(F("T"));
  tft.setTextColor(BLACK); tft.print(F("+  "));
  tft.println(F(SDLEP_PLUS_VERSION));

  // Initialisation de la carte SD. Retry en cas d'erreur.
  // CCA : shall not work without optiboot or decently recent stock bootloader

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
   
   Serial.println("SD Init");

   /*
    * Ancienne façon de faire
    * 
  if (!sd.begin(SD_CS_PIN))
  {
    tft.setCursor(16, 40);
    tft.setTextColor(WHITE);
    tft.println(F("SD init..."));
    wdt_enable(WDTO_1S);
    while(1);
  } 

*/

  // Méthode tirée de l'éxemple longfilename.ino de la bibliothèque SDFat
  if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
    tft.setCursor(16, 40);
    tft.setTextColor(WHITE);
    tft.println(F("SD init..."));
 //   wdt_enable(WDTO_1S);    
    sd.initErrorHalt();
  }

 Serial.println("SD Inited");


// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
 
  // Affichage bouton FIRST
  tft.fillRect(0,217,100,22,YELLOW);
  tft.setCursor(20,221);
  tft.print(F("FIRST"));

  // Affichage bouton OK
  tft.fillRect(110,217,100,22,YELLOW);
  tft.setCursor(150,221);
  tft.print(F("OK"));

  // Affichage bouton NEXT
  tft.fillRect(220,217,100,22,YELLOW);
  tft.setCursor(250,221);
  tft.print(F("NEXT"));

  //////////// Choix du fichier a charger ////////////

  // Initialisations
  dirEnd = false;                 // fin de directory pas atteinte
  prev = false;                   // pas d'ecran precedent
  shortname[0] = 0;               // initialisation du nom du fichier choisi
  tft.setTextColor(YELLOW);       // ecriture en jaune

  // Boucle de choix du fichier
  while (1)
  {
    numentry=0;
    numfiles=0;
    numchoosen = -1;

    //affichage de huit lignes au maximum
    tft.fillRect(0,25,320,180,BLUE);
    while (1)
    {
      if(numfiles > 0)
        entry.close();  //fermer la derniere entree lue
      
      if(numentry >= 8)
        break;         //fin quand 8 lignes sont affichees
      
      //lecture de l'entree suivante du repertoire
      //en fin de repertoire sortir de la boucle
      if(!entry.openNext(sd.vwd(), O_READ))
      {
        entry.close();
        dirEnd=true;
        break;      
      }
      
      numfiles++;
      
      if(entry.isDir())
        continue; //ne pas traiter les sous-repertoires

      //recherche du point dans le nom court du fichier  
      entry.getSFN(shortfilename[numentry]);
      for(i=0; i<10; i++)
        if (shortfilename[numentry][i]=='.')
          break;  //point trouve
      
      if(i==10)
        continue; //point pas trouve, entree de repertoire suivante
   
      //test du suffixe
      suf[0]=shortfilename[numentry][i+1];
      suf[1]=shortfilename[numentry][i+2];
      suf[2]=shortfilename[numentry][i+3];
      suf[3]=0;
      
      if( strcmp(suf,"lep")!=0  && strcmp(suf,"LEP")!=0 )
        continue; //ne pas traiter si ce n'est pas un fichier .lep 
   
      //affichage d'un fichier d'extension .lep
      entry.getName(longfilename,24);
      tft.setCursor(35,30+22*numentry);
      tft.print(longfilename);
      tft.drawCircle(15,35+22*numentry,8,WHITE);
      numentry++;
    }
  
    //test de l'action de l'utilisateur
    while (1)
    {
      int x, y;

      //Lire le Touch Screen
      TSPoint p = ts.getPoint();

      //Retablir le sens pour les boches partagees avec l'ecran LCD
      //A3=Chip Select et A2=Command/Data
      FastGPIO::Pin<XM>::setOutput(x);
      FastGPIO::Pin<YP>::setOutput(x);
  
      if ((p.z < MINPRESSURE) || (p.z > MAXPRESSURE))
        continue;

      //calcul des coordonn?es pointees
      //x est defini de la gauche vers la droite de l'affichage   
      //y est defini du haut vers le bas de l'affichage   
      x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
      y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());      
  
      //selection d'un nom de fichier
      if((y > 35) && (y < 205))
      {
        //annuler si necessaire l'ancienne selection
        if(numchoosen >= 0)
          tft.fillCircle(15,35+22*(numchoosen),5,BLUE);        
       
        //nouvelle selection
        numchoosen=((y-35)/22);
        if(numchoosen >= numentry)
          numchoosen = -1;
       
        if(numchoosen >= 0)
          tft.fillCircle(15,35+22*(numchoosen),5,RED);
      }
  
      // Action boutons
      if(y > 216)               //si appui en bas de l'ecran
      {
        //action du bouton FIRST
        if(x < 115 && prev)  // sur bouton first et s'il y a un ecran precedent
        {
          sd.vwd()->rewind();     //revenir au debut de la carte SD
          dirEnd=false;           //remettre a zero l'indicateur de fin
          prev=false;             //remettre a zero l'indicateur d'ecran precedent 
          break;                  //afficher le premier ecran
        }
  
        //action du bouton OK
        if(x > 114 && x < 215 && numchoosen >= 0) // sur bouton OK & fichier choisi
        {
          strcpy(shortname, shortfilename[numchoosen]); 
          break;
        }
     
        //action du bouton NEXT
        if(x > 214 && !dirEnd)              // sur bouton FIRST & il y a d'autres fichiers
        {
          prev=true;              //positionner indicateur d'ecran precedent 
          break;                  //afficher l'ecran suivant 
        }
      }  
    } //fin du test de l'action de l'utilisateur (on en sort par le break des boutons FIRST, OK et NEXT)
  
    if(shortname[0] != 0) break; //le fichier a ?t? choisi par le bouton OK  
  } //fin de la boucle de choix du fichier (on en sort si le fichier a ete choisi)
  
  root.close();
}

/**************************************************************************\
 * Initialize the SD memory card.
 * Power up the card, set SPI mode.
 * Detects the card version (V1, V2, SDHC), sets sector length to 512.
 * \return Zero if successfull, errorcode otherwise
\**************************************************************************/
uint8_t SD_Init()
{
 uint8_t status;
 uint16_t t0 = ((uint16_t)millis());
 uint32_t arg;

 /* Setup ports */
 
 FastGPIO::Pin<SD_CS_PIN>::setOutput(HIGH);
 FastGPIO::Pin<MISO>::setInput();
 FastGPIO::Pin<MOSI>::setOutput(LOW);
 FastGPIO::Pin<SCK>::setOutput(LOW);
 FastGPIO::Pin<SS>::setOutput(HIGH);
  
 /*
 * SPI configuration: 
 * - enable uC for SPI master
 * - typical no interrupts are used for SPI
 * - data order: MSB is transmitted first
 * - clock polarity: CLK is low when idle
 * - clock phase: 1-0 > Sample, 0-1 > Setup
 * - clock frequency: less than 400kHz 
 *   (will be switched to higher value after initialization)
 */
 /* initialize SPI with lowest frequency; max. 400kHz during identification mode of card */
 SPCR = (0 << SPIE) | /* SPI Interrupt Enable */
        (1 << SPE)  | /* SPI Enable */
        (0 << DORD) | /* Data Order: MSB first */
        (1 << MSTR) | /* Master mode */
        (0 << CPOL) | /* Clock Polarity: SCK low when idle */
        (0 << CPHA) | /* Clock Phase: sample on rising SCK edge */
        (1 << SPR1) | /* Clock Frequency: f_OSC / 128 */
        (1 << SPR0);
 SPSR &= ~(1 << SPI2X); /* No doubled clock frequency */
  
 // must supply min of 74 clock cycles with CS high.
 SD_SetCSHigh();
 for (uint8_t i = 0; i < 10; i++) SD_SpiSendByte(0xFF);

 // command to go idle in SPI mode
 while ((SD_CardCommand(SD_CMD0, 0)) != SD_IDLE_STATE)
  if ((((uint16_t)millis()) - t0) > SD_INIT_TIMEOUT) {SD_SetCSHigh(); return(SD_ERROR_CMD0);}
  
 // check SD version ( 2.7V - 3.6V + test pattern )
 SD_type = 0;
 if ((SD_CardCommand(SD_CMD8, 0x1AA) & SD_ILLEGAL_COMMAND)) SD_type = SD_CARD_TYPE_SD1;
    // Not done here: Test if SD or MMC card here using CMD55 + CMD1
 else
 {
  // only need last byte of r7 response
  SD_SpiReadByte();
  SD_SpiReadByte();
  status = SD_SpiReadByte();
  if ((status & 0x01) == 0) // card operation voltage range doesn't match
  {SD_SetCSHigh(); return(SD_ERROR_VOLTMATCH);}
  if (SD_SpiReadByte() != 0xAA) {SD_SetCSHigh(); return(SD_ERROR_CMD8);}
  SD_type = SD_CARD_TYPE_SD2;
 }
  
 // Turn CRC option off
 SD_CardCommand(SD_CMD59, 0);
  
 // initialize card and send host supports SDHC if SD2
 arg = (SD_type == SD_CARD_TYPE_SD2) ? 0X40000000 : 0;
 while ((SD_CardACommand(SD_ACMD41, arg)) != SD_READY_STATE) // check for timeout
  if ((((uint16_t)millis()) - t0) > SD_INIT_TIMEOUT) {SD_SetCSHigh(); return(SD_ERROR_ACMD41);}
  
 // if SD2 read OCR register to check for SDHC card
 if (SD_type == SD_CARD_TYPE_SD2)
 {
  if (SD_CardCommand(SD_CMD58, 0)) {SD_SetCSHigh(); return(SD_ERROR_CMD58);}
  // other implementation test only against 0x40 for SDHC detection...
  if ((SD_SpiReadByte() & 0xC0) == 0xC0) SD_type = SD_CARD_TYPE_SDHC;
  // discard rest of ocr - contains allowed voltage range
  SD_SpiReadByte();
  SD_SpiReadByte();
  SD_SpiReadByte();
 }

 // set block size to 512 bytes
 if(SD_CardCommand(SD_CMD16, 512)) {SD_SetCSHigh(); return(SD_ERROR_CMD16);}
 SD_SetCSHigh();
 SD_SpiSetHighSpeed();
 return 0;
}

/**************************************************************************\
* Set SPI for full operation speed (up to 25 MHz).
* Will be called after first part of card 
* initialization was successful.
\**************************************************************************/
inline void SD_SpiSetHighSpeed(void)
{
 SPCR &= ~((1 << SPR1) | (1 << SPR0)); /* Clock Frequency: f_OSC / 4 */
 SPSR |= (1 << SPI2X);         /* Doubled Clock Frequency: f_OSC / 2 */
}

/**************************************************************************\
* Receives a raw byte from the SPI.
* \returns The byte which should be read.
\**************************************************************************/
inline uint8_t SD_SpiReadByte()
{
 SPDR = 0xff; /* send dummy data for receiving some */
 while(!(SPSR & (1 << SPIF)));
 SPSR &= ~(1 << SPIF);
 return SPDR;
}

/**************************************************************************\
* Read a 512 byte block from an SD card.
* \param[in] blockNumber Logical block to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return 0 is returned for success, error code otherwise
\**************************************************************************/
uint8_t SD_ReadBlock(uint32_t blockNumber) 
{
  uint8_t status;
  uint16_t t0;

 // use address if not SDHC card
 if (SD_type != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
 if (SD_CardCommand(SD_CMD17, blockNumber)) {SD_SetCSHigh(); return(SD_ERROR_CMD17);}

 // wait for start block token
 t0 = ((uint16_t)millis());
 while ((status = SD_SpiReadByte()) == 0xFF)
  if ((((uint16_t)millis()) - t0) > SD_READ_TIMEOUT) {SD_SetCSHigh(); return(SD_ERROR_READ_TIMEOUT);}
 if (status != SD_DATA_START_BLOCK) {SD_SetCSHigh(); return(SD_ERROR_READ);}
  
 // transfer data
 SPDR = 0xFF;
 for (uint16_t i = 0; i < 512; i++)
 {
  while (!(SPSR & (1 << SPIF)));
  SD_buffer[i] = SPDR;
  SPDR = 0xFF;
 }
 while (!(SPSR & (1 << SPIF)));
 SD_buffer[512] = SPDR;

 // discard CRC
 SD_SpiReadByte();
 SD_SpiReadByte();
 SD_SetCSHigh();

return 0;
}



/**************************************************************************\
* Set CS High 
* Sends also one dummy byte to ensure MISO goes high impedance
\**************************************************************************/
inline void SD_SetCSHigh()
{
 FastGPIO::Pin<SD_CS_PIN>::setOutputValueHigh(); 
 SD_SpiSendByte(0xff);
}

/**************************************************************************\
* Sends a raw byte to the SPI - \param[in] b The byte to sent.
\**************************************************************************/
inline void SD_SpiSendByte(uint8_t b)
{
 SPDR = b;
 while(!(SPSR & (1 << SPIF))); /* wait for byte to be shifted out */
 SPSR &= ~(1 << SPIF);
}

/**************************************************************************\
* Send a command to the memory card which responses with a R1 response 
* (and possibly others).
* \param[in] command The command to send.
* \param[in] arg The argument for command.
* \returns The command answer.
\**************************************************************************/
uint8_t SD_CardCommand(uint8_t cmd, uint32_t arg) 
{
 uint8_t response;
 uint8_t crc;
 uint16_t t0;
      
 // select card
 FastGPIO::Pin<SD_CS_PIN>::setOutputValueLow();

 // wait up to timeout if busy
 t0 = ((uint16_t)millis());
 while (SD_SpiReadByte() != 0xFF)
  if ((((uint16_t)millis()) - t0) >= SD_COMMAND_TIMEOUT) break;

 // send command
 SD_SpiSendByte(cmd | 0x40);

 // send argument
 SD_SpiSendByte((arg >> 24) & 0xff);
 SD_SpiSendByte((arg >> 16) & 0xff);
 SD_SpiSendByte((arg >>  8) & 0xff);
 SD_SpiSendByte((arg >>  0) & 0xff);
  
 // send CRC, only required for commands 0 and 8
 crc = 0xFF;
 if (cmd == SD_CMD0) crc = 0x95;  // correct crc for CMD0 with arg 0
 if (cmd == SD_CMD8) crc = 0x87;  // correct crc for CMD8 with arg 0X1AA
 SD_SpiSendByte(crc);

 // skip stuff byte for stop read
 if (cmd == SD_CMD12) SD_SpiReadByte();

 // wait for response
 for(uint8_t i = 0; i < 100; ++i)
 {
  response = SD_SpiReadByte();
  if(response != 0xff) break;
 }
 return response;
}

/**************************************************************************\
* Send an application specific command which responses with a R1 response 
* (and possibly others).
* \param[in] command The command to send.
* \param[in] arg The argument for command.
* \returns The command answer.
\**************************************************************************/
inline uint8_t SD_CardACommand(uint8_t cmd, uint32_t arg)
{
 SD_CardCommand(SD_CMD55, 0);
 return SD_CardCommand(cmd, arg);
}

/**************************************************************************\
* Returns the first sector of a given cluster
\**************************************************************************/
inline uint32_t SD_Cluster2Sector(uint32_t cluster)
{
 return((cluster << SD_FAT.ClusterSizeShift) + SD_FAT.DataStart);
}

/**************************************************************************\
* Initialize the file system .
* Does the lower level initialization and * tries to find the boot sector 
* of the first FAT16 or FAT32 partition and parse it.
* Workbuf must hold at least 512 bytes.
* Workbuf will be used later also for following functions:
* - SD_SearchFile
* - SD_Dir
* \return Zero if successful, error code otherwise
\**************************************************************************/
uint8_t SD_Init_FileSystem()
{
 uint8_t  retval;
 uint8_t  PartType;
 uint16_t temp16;
 uint32_t temp32;

   
 SD_FAT.PartType = SD_PARTTYPE_UNKNOWN;
    
 // Try init SD-Card
 retval = SD_Init();
 if(retval) {
#ifdef DEBUG
    Serial.print("Init ");
    Serial.println(retval);
#endif
    return(retval);
 } 

 // ==== MBR (partition table) access here =====
    
 // Read sector 0 
 retval = SD_ReadBlock(0);
 if(retval) return(retval);

 // Test for signature (valid not only for MBR, but FAT Boot Sector as well!)
 if((SD_buffer[0x1fe] != 0x55) || (SD_buffer[0x1ff] != 0xaa)) return(SD_ERROR_INVAL_SECT0);
    
 // Store most important MBR values for first partition
 PartType = SD_buffer[0x1be + 0x04];
 SD_FAT.BootSectorStart =  (uint32_t)SD_buffer[0x1be + 0x08] 
                        | ((uint32_t)SD_buffer[0x1be + 0x09] << 8UL)
                        | ((uint32_t)SD_buffer[0x1be + 0x0a] << 16UL)
                        | ((uint32_t)SD_buffer[0x1be + 0x0b] << 24UL);
    
    // Check MBR values for plausibility
    if(  ((SD_buffer[0x1be] & 0x7f) == 0)
      && ((PartType == 0x04) || (PartType == 0x06) || (PartType == 0x0B) 
           || (PartType == 0x0C) || (PartType == 0x0E)) )  
    {
        // MBR seems to contain valid FAT16/FAT32 partition entry
        SD_FAT.PartType = ((PartType == 0x0B) || (PartType == 0x0C)) ? SD_PARTTYPE_FAT32 : SD_PARTTYPE_FAT16;
    }
    else
    {
        // MBR seems to contain not an valid entry, so try for super-floppy now
        SD_FAT.BootSectorStart = 0UL;
        SD_FAT.PartType = SD_PARTTYPE_SUPERFLOPPY;
    }
    
    // ====== FAT access here ======
    
    // Read Boot-Sector and test for signature
    retval = SD_ReadBlock(SD_FAT.BootSectorStart);
    if(retval) return(retval);  

    // Test for signature (valid not only for MBR, but FAT Boot Sector as well!)
    if((SD_buffer[0x1fe] != 0x55) || (SD_buffer[0x1ff] != 0xaa)) return(SD_ERROR_INVAL_BS);
    
    // Plausibility checks for FAT
    if((SD_buffer[0x0b] != 0x00) || (SD_buffer[0x0c] != 0x02) || (SD_buffer[0x15] != 0xf8)) return(SD_ERROR_INVAL_BS);

    // Read fields that are same for FAT16 and FAT32
    SD_FAT.SecPerClus = SD_buffer[0x0d];
    SD_FAT.RsvdSecCnt = (uint16_t)SD_buffer[0x0e] | ((uint16_t)SD_buffer[0x0f]<<8U);
    if((SD_FAT.SecPerClus == 0) || (SD_FAT.RsvdSecCnt == 0)) return(SD_ERROR_INVAL_BS);
    SD_FAT.NumFATs = SD_buffer[0x10];
    SD_FAT.RootEntryCount = (uint16_t)SD_buffer[0x11] | ((uint16_t)SD_buffer[0x12]<<8U);
    
    temp16 = (uint16_t)SD_buffer[0x13] | ((uint16_t)SD_buffer[0x14]<<8U);
    temp32 = (uint32_t)SD_buffer[0x20] | ((uint32_t)SD_buffer[0x21]<<8U) | ((uint32_t)SD_buffer[0x22]<<16U) | ((uint32_t)SD_buffer[0x23]<<24U);
    SD_FAT.TotalSec  = temp16 ? temp16 : temp32;
    
    temp16 = (uint16_t)SD_buffer[0x16] | ((uint16_t)SD_buffer[0x17]<<8U);
    temp32 = (uint32_t)SD_buffer[0x24] | ((uint32_t)SD_buffer[0x25]<<8U) | ((uint32_t)SD_buffer[0x26]<<16U) | ((uint32_t)SD_buffer[0x27]<<24U);
    SD_FAT.SecPerFAT  = temp16 ? temp16 : temp32;
    
    // Calculate start sectors
    SD_FAT.FatStart = SD_FAT.BootSectorStart + (uint32_t)SD_FAT.RsvdSecCnt;
    SD_FAT.RootDirStart = SD_FAT.FatStart + SD_FAT.NumFATs * (uint32_t)SD_FAT.SecPerFAT;
    
    // Data area starts at cluster #2
    SD_FAT.DataStart = SD_FAT.RootDirStart+ ((32 * (uint32_t)SD_FAT.RootEntryCount + 511)/512) - (2 * SD_FAT.SecPerClus);
    
    // determine shift that is same as multiply by SD_FAT.SecPerClus
    SD_FAT.ClusterSizeShift = 0;
    while (SD_FAT.SecPerClus != (1 << SD_FAT.ClusterSizeShift)) {
      // error if not power of 2
      if (SD_FAT.ClusterSizeShift++ > 7) return(SD_ERROR_INVAL_BS);
    }  
    
     // Calculate number and shifting of clusters
    // total data blocks
    SD_FAT.ClusterCount = SD_FAT.TotalSec - (SD_FAT.DataStart - SD_FAT.BootSectorStart);
    // divide by cluster size to get cluster count
    SD_FAT.ClusterCount >>= SD_FAT.ClusterSizeShift;  
    
    // determine if FAT16 or FAT32 (only by cluster count as done by M$)
    if (SD_FAT.ClusterCount < 4085) {
        // this would be FAT12, which is not supported
        SD_FAT.PartType = SD_PARTTYPE_UNKNOWN;
        return(SD_ERROR_FAT12);
    } else if (SD_FAT.ClusterCount < 65525) {
        SD_FAT.PartType = SD_PARTTYPE_FAT16;
        SD_FAT.ClusterEndMarker = 0xfff8UL;
    } else {
        temp32 = (uint32_t)SD_buffer[0x2c] | ((uint32_t)SD_buffer[0x2d]<<8U) | ((uint32_t)SD_buffer[0x2e]<<16U) | ((uint32_t)SD_buffer[0x2f]<<24U);
        SD_FAT.RootDirStart = SD_Cluster2Sector(temp32);
        SD_FAT.PartType = SD_PARTTYPE_FAT32;
        SD_FAT.ClusterEndMarker = 0xffffff8UL;
    }
      
    return 0;
}


void loop()
{
 //la lecture est arrivee en fin de fichier
 //clignotement lent de la diode d'activite
 
 FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueHigh();
 delay(100);                   // temporisation
 FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueLow();
 delay(1900);                  // temporisation
}

/**************************************************************************\
* Search a file in the directory.
* Filename must be 8.3 format, terminated by \0 (can not access ".." now...)
* Works only over one cluster of directory information. 
* If SD_ERROR_DIR_EOC is returned call function again with next cluster number. 
* Set cluster to 0 to access root directory.
* Deleted files and long name entries are not shown generally.
* Only files are printed that has their attributes set/unset regarding maskSet/maskUnset.
* Examples for maskSet, maskUnset:
*  Ouput everything:           0x00, 0x00
*  Shortname files only:       0x00, 0x18
*  Shortname files and dirs:   0x00, 0x08
*  Shortname dirs:             0x10, 0x08
*  Volume name:                0x08, 0x10
* Mask bits: B7 = 0, B6 = 0, B5 = archive, B4 = directory, 
*            B3 = volume name, B2 = system, B1 = hidden, B0 = read only
* If file is found, fileinfo gets filled with file attributes, 
* file size in bytes and first cluster.
*  return Zero if successfully, error code otherwise
\**************************************************************************/
uint8_t SD_SearchFile(uint8_t *filename, const uint32_t cluster, const uint8_t maskSet, const uint8_t maskUnset, SD_File_t *fileinfo)
{
 uint16_t maxsect = SD_FAT.SecPerClus;
 uint32_t startsect = SD_Cluster2Sector(cluster);
 char     fnentry[12];

 if((SD_FAT.PartType != SD_PARTTYPE_FAT16) && (SD_FAT.PartType != SD_PARTTYPE_FAT32)) return(SD_ERROR_FAT_NOT_INIT);
 
 if(cluster == 0)
 {
  startsect = SD_FAT.RootDirStart; // Set root dir sector
  if(SD_FAT.PartType == SD_PARTTYPE_FAT16) maxsect = (uint16_t)((32 * (uint32_t)SD_FAT.RootEntryCount + 511)/512);
 }
    
 // convert filename to space-filled uppercase format
 for(uint8_t i = 0; i < 11; i++) fnentry[i] = ' ';
 for(uint8_t i = 0; i < 9; i++)
 {
  uint8_t c = *filename++;
  if((c < 0x20) || (c == '.')) break;
  if((c>='a') && (c<='z')) c -= 0x20;  // to upper case
  fnentry[i] = c;
 }

 for(uint8_t i = 8; i < 11; i++)
 {
  uint8_t c = *filename++;
  if(c < 0x20) break;
  if((c>='a') && (c<='z')) c -= 0x20;  // to upper case
  fnentry[i] = c;
 }
 fnentry[11] = 0;

#ifdef DEBUG
  //Serial.println(fnentry);
#endif
    
 // go through sectors
 for(uint16_t i = 0; i<maxsect; i++)
 {
  uint8_t retval = SD_ReadBlock(startsect + i);
  if(retval) return(retval);
         
  for(uint16_t j = 0; j<512; j+=32)
  {
   uint8_t attrib;
   if(SD_buffer[j] == 0) return(SD_ERROR_FILE_NOT_FOUND);    // Last entry when first character of filename == 0
   if(SD_buffer[j] == 0xe5) continue;  // Skip deleted files
   if(SD_buffer[j] == 0x05) SD_buffer[j] = 0xE5;
            
   attrib = SD_buffer[j+0x0b];
            
   // Test masks (skip long file name entries also)
   if(((attrib & maskSet) == maskSet) && ((attrib & maskUnset) == 0) && (attrib != 0x0f))
   {
    uint16_t k;
                
    // compare filename
    for(k = 0; k < 11; k++) if(SD_buffer[j+k] != fnentry[k]) break;
    if(k >= 11)
    {
     // found it
     fileinfo->Attributes = attrib;
     fileinfo->Size = (uint32_t)SD_buffer[j+0x1c] | (((uint32_t)SD_buffer[j+0x1d])<<8) 
                  | (((uint32_t)SD_buffer[j+0x1e])<<16) | (((uint32_t)SD_buffer[j+0x1f])<<24);
     if(SD_FAT.PartType == SD_PARTTYPE_FAT16)
      {fileinfo->FirstCluster = (uint32_t)SD_buffer[j+0x1a] | (((uint32_t)SD_buffer[j+0x1b])<<8);}
     else
      {fileinfo->FirstCluster = (uint32_t)SD_buffer[j+0x1a] | (((uint32_t)SD_buffer[j+0x1b])<<8) 
                            | (((uint32_t)SD_buffer[j+0x14])<<16) | (((uint32_t)SD_buffer[j+0x15])<<24);} 
                    
     // Initialize some things
     fileinfo->ActSector = SD_Cluster2Sector(fileinfo->FirstCluster);
     fileinfo->ActBytePos = 0;
     return(0);
    }
   }
  }
 }
 if(SD_FAT.PartType == SD_PARTTYPE_FAT16) return(SD_ERROR_FILE_NOT_FOUND);
 return(SD_ERROR_DIR_EOC);
}

void setStatusText(const __FlashStringHelper *text, uint32_t fileSize)
{
  tft.setTextSize(2);
  tft.setTextColor(WHITE);
  tft.fillRect(0,208,320,20,BLACK);
  tft.setCursor(4,208);
  tft.print(text);
  tft.setCursor(fileSize < 100000 ? 164 : 152, 210);
  tft.print(F("("));
  tft.print(fileSize);
  tft.print(F(" bytes)"));
}

void setStatusText(const __FlashStringHelper *text)
{
  tft.fillRect(4,208, 120, 20, BLACK);
  tft.setCursor(4,208);
  tft.print(text);
}

void drawProgressBar(uint16_t progress, uint16_t color)
{
  tft.drawRect(PROGRESSBAR_FRAME_X, PROGRESSBAR_FRAME_Y, PROGRESSBAR_FRAME_WIDTH, PROGRESSBAR_FRAME_HEIGHT, color);

  if( progress > PROGRESSBAR_BAR_X )
  {
    progress -= PROGRESSBAR_BAR_X;
    tft.fillRect(PROGRESSBAR_BAR_X, PROGRESSBAR_BAR_Y, progress, PROGRESSBAR_BAR_HEIGHT, color);
  }
}

inline void fastProgressBarUpdate(uint16_t progress, uint16_t color)
{
  tft.drawFastVLine(progress, PROGRESSBAR_BAR_Y, PROGRESSBAR_BAR_HEIGHT, color);
}

void setup()
{
  int       i;                        // Loop counter
  bool      lepOutput;                // Output signal level
  char      tmpByte;                  // Temporary storage for current byte
  uint16_t  delayUs;                  // Delay in microseconds
  uint32_t  count;                    // Total byte counter
  uint32_t  offset;                   // Start block
  uint16_t  identifier;               // TFT identifier

  uint16_t  progressCountPerStep;
  uint32_t  progressNextStepCount;
  uint16_t  progress;

  Serial.begin(9600);
   
  tft.begin();     // initialisation de l'ecran
  tft.setRotation(1);        // mode paysage
  LoadMenu();                //affichage des fichiers et selection

#ifdef DEBUG
  Serial.println(shortname);
#endif

 //Initialisations
 FastGPIO::Pin<SDLEP_MOTOR_ON_PIN>::setInputPulledUp();          // RX
 FastGPIO::Pin<SDLEP_DIGITAL_DATA_PIN>::setOutput(LOW);          // TX
 FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutput(LOW);
  
 SD_Init_FileSystem();       // initialiser la carte SD
 SD_SpiSetHighSpeed();       // frequence d'horloge maxi 
 
 // Search for file SD_FILE in Rootdir (=cluster 0),
 // search shortname files only (0x00,0x18)
 i = SD_SearchFile((uint8_t *)shortname, 0UL, 0x00, 0x18, &LepFileInfo);

#ifdef DEBUG
 Serial.print("R=");
 Serial.println(i);
#endif
 
 //Si le fichier est trouve alors i est nul et on passe le while sans rien faire.
 //Si le fichier n'est pas trouve alors i n'est pas nul et on boucle indefiniment.
 //Remarque : La fonction delai necessite d'avoir les interruptions actives.
 //           On peut les desactiver apres, mais pas avant.
 while(i)                        //clignotement rapide de la diode d'activite
 {                               //pour signaler un fichier non trouve
  FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueHigh();
  delay(5);                      // temporisation
  FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueLow();
  delay(50);                     // temporisation
 }

 //Affichage compte-rendu
  tft.fillRect(0,217,320,22,BLACK);
  setStatusText(F("Ready."), LepFileInfo.Size);
  drawProgressBar(0, GRAY);
    
 // send CMD18 (multiblock read)
 noInterrupts();                    // desactiver les interruptions
 offset = LepFileInfo.ActSector;    // offset = secteur de debut
 count = 0;                         // nombre d'octets lus
 if (SD_type != SD_CARD_TYPE_SDHC)  // si carte non SDHC
     offset <<= 9;                  // offset = octet
 SD_CardCommand(18, offset);        // lance CMD18

 lepOutput = HIGH;                     // signal a 1 pour detection lep
 FastGPIO::Pin<SDLEP_DIGITAL_DATA_PIN>::setOutputValue(lepOutput);
 
  FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueLow();

 // Initialize progress variables
 progress  = PROGRESSBAR_BAR_X;
 progressCountPerStep = LepFileInfo.Size / PROGRESSBAR_BAR_MAX_WIDTH;
 progressNextStepCount = progressCountPerStep;

  // Attendre moteur ON
  #ifndef DEBUG_IGNORE_MOTOR_ON
    while( FastGPIO::Pin<SDLEP_MOTOR_ON_PIN>::isInputHigh() );
  #else
    for(i = 0; i < 100; i++) delayMicroseconds(10000);       
  #endif
  
  // Change status text
  setStatusText(F("Playing..."));

  // Change progress bar color
  drawProgressBar(0, GREEN);

 while(count < LepFileInfo.Size)    // tant qu'il reste des octets
 {
  // attente octet 0xfe de debut de bloc
  while(SD_SpiReadByte() != 0xfe); 

  // Handle each 512-byte block read from SD 
  for(i = 0; i < 512; i++)
  {
   //lecture d'un octet et sortie du signal
   count++;                        // nombre d'octets lus 
   tmpByte = SD_SpiReadByte();       // lecture d'un octet
   delayUs = abs(tmpByte);             // initialisation delai
   if(tmpByte == 0) delayUs = 127;     // absence de signal (127 unites)
   delayUs *= DELAY_UNIT_US;         // conversion en microsecondes

   #if defined(DEBUG_FIXED_DELAY) && defined(DEBUG_ALWAYS_TOGGLE)  
    lepOutput = (lepOutput == LOW) ? HIGH : LOW;
   #else
    if(tmpByte > 0) lepOutput = HIGH;    // creneau positif
    if(tmpByte < 0) lepOutput = LOW;     // creneau negatif
   #endif
   
   FastGPIO::Pin<SDLEP_DIGITAL_DATA_PIN>::setOutputValue(lepOutput);

   if( count >= progressNextStepCount )
   {
      fastProgressBarUpdate(progress, GREEN);
      progressNextStepCount += progressCountPerStep;
      progress++; 

      #ifdef DELAY_COMPENSATE_PROGRESS_DISPLAY_US
        if( delayUs > DELAY_COMPENSATE_PROGRESS_DISPLAY_US )
          delayUs -= DELAY_COMPENSATE_PROGRESS_DISPLAY_US;
        else
          delayUs = 0;
      #endif
   }

  //Test MOTOR ON
  #ifndef DEBUG_IGNORE_MOTOR_ON
    if( FastGPIO::Pin<SDLEP_MOTOR_ON_PIN>::isInputHigh() )        // si le moteur est arrete   
    {                      
      // Set LEP DIGITAL OUT high (enable LEP detection)           
      FastGPIO::Pin<SDLEP_DIGITAL_DATA_PIN>::setOutputValueHigh(); // sortie haute pour detection LEP

      // Change status text
      setStatusText(F("Paused."));

      // Change progress bar color
      drawProgressBar(progress, GRAY);
      
      // Wait until motor is back on
      while( FastGPIO::Pin<SDLEP_MOTOR_ON_PIN>::isInputHigh() );  // attendre le MOTOR ON

      // Restore last LEP DIGITAL OUT level
      FastGPIO::Pin<SDLEP_DIGITAL_DATA_PIN>::setOutputValue(lepOutput);
      
      // Change status text
      setStatusText(F("Playing..."));

      // Change progress bar color
      drawProgressBar(progress, GREEN);
    }
  #endif

   
    #ifdef DEBUG_FIXED_DELAY
      delayUs = DEBUG_FIXED_DELAY;
    #endif

    #ifdef DELAY_COMPENSATE_BLOCK_CHANGE_US
      if( i == 511 )
      {
        if( delayUs > DELAY_COMPENSATE_BLOCK_CHANGE_US )
          delayUs -= DELAY_COMPENSATE_BLOCK_CHANGE_US;
        else
          delayUs = 0;
      }
    #endif
    
   // Temporisation avant lecture de l'octet suivant
   delayMicroseconds(delayUs);
  }

  //lecture des deux octets de CRC
  SD_SpiReadByte();                // lecture octet CRC1
  SD_SpiReadByte();                // lecture octet CRC2
  
  // Toggle activity LED
  FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueToggle();     
 }
 
 FastGPIO::Pin<SDLEP_ACTIVITY_LED_PIN>::setOutputValueLow(); 
 
 interrupts();                     //activer les interruptions

  // Change status text
  setStatusText(F("Done."));
  
  // Change progress bar color 
  drawProgressBar(progress, GRAY);
}

Pièces jointes
sdlep-tft-plus.zip
(13.77 Kio) Téléchargé 130 fois
Daniel
Messages : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Daniel »

Il y a une discussion sur un problème similaire dans le forum arduino : https://forum.arduino.cc/index.php?topic=587880.0
Ce n'est pas exactement le même cas, mais il y a aussi des liens vers d'autres discussions.

Il y a tellement d'écrans différents et de bibliothèques différentes qu'il y a de quoi se perdre. Le problème peut être une incompatibilité entre la bibliothèque de l'écran et SDFat. Il y a sûrement une solution, mais pas facile à trouver. Il faudrait partir d'un exemple fonctionnel pour le module Adafruit 2.8" v2, par exemple l'affichage à l'écran d'une image de la carte SD, et voir comment il initialise la carte SD et quelles bibliothèques sont utilisées.

Et peut-être aussi poser le problème dans le forum Arduino, il y a de bons spécialistes.
Daniel
L'obstacle augmente mon ardeur.
Ythunder
Messages : 928
Inscription : 21 août 2019 10:12

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Ythunder »

Merci pour la réponse sur la fabrication Daniel.
Vladimir Kr Can
Messages : 33
Inscription : 24 déc. 2020 00:04
Localisation : Quebec

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Vladimir Kr Can »

@Daniel

En fait, j'avais fait un petit code, qui affiche la liste des fichiers LEP sur l'écran TFT et qui fonctionne sans problème.

Mais en me suggérant de (re) tester un petit bout de code, je viens de faire un peu plus attention au message d'avertissement que j'obtiens à la compilation de SDLEP, et qui me dit qu'il ne me reste plus que 343 octets pour les variables locales, et qu'il pourrait y avoir des problèmes de stabilité.

En commentant le #def DEBUG, je gagne une poignée d'octets, et j'arrive à aller plus loin dans le code. Je n'ai plus de reset intempestif, en revanche, la liste des fichiers ne s'affiche pas. On avance, on avance, mais je n'ai pas encore testé la partie tactile non-plus.

J'ai donc l'impression qu'avec les bibliothèques nécessaires à ce blindage, je suis très juste en terme de mémoire, et je vais sans doute devoir chercher où gagner de la mémoire.

Une autre solution, serait de voir si quelqu'un pouvait me recommander une référence (blindage écran + SD) qui fonctionne et qui serait encore disponible sur le marché, pour gagner du temps : j'ai promis à David Murray (8 bit guy) de lui envoyer un des MO5 pour qu'il en fasse une revue vidéo.
Daniel
Messages : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Daniel »

L'insuffisance de la taille mémoire est une bonne piste. J'ai déjà eu le problème avec d'autres applications, ça ne pardonne pas et il est très difficile de comprendre la cause de l'erreur. S'il est impossible de gagner de la mémoire, il faudrait essayer la version précédente de SDLEP-TFT (avant l'amélioration faite par Cyril), elle est plus économe :
http://dcmoto.free.fr/bricolage/sdlep-t ... 190327.zip

Sinon il y a peut-être un gain possible dans la bibliothèque Adafruit, si certaines fonctions ne sont pas utilisées.

J'avais pensé donner des liens vers l'Arduino et les écrans que j'utilise, mais après avoir commandé deux fois le même chez le même fournisseur je me suis aperçu qu'ils se ressemblaient beaucoup mais n'avaient pas le même contrôleur et nécessitaient des bibliothèques différentes. C'est donc une loterie, on ne peut pas savoir à l'avance ce qu'on va recevoir.

Pour ceux-ci il est spécifié ILI9341, ils doivent être compatibles avec la bibliothèque MCUFRIEND que j'utilise.
https://www.ebay.fr/sch/i.html?_from=R4 ... rduino+uno

Image
Daniel
L'obstacle augmente mon ardeur.
Nephilim
Messages : 159
Inscription : 15 déc. 2020 11:51

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Nephilim »

Bonjour Carl

" @Nephilim : Je ne comprend pas....SDLEP est compatible Oric Atmos & Oric 1
Pour faire un essai :
3D MUNCH (F) (1985).7z
(20.51 Kio) Téléchargé 1 fois
Carl "

Carl, C est juste qu'au prix du matos , je fais un sd lep pour chacune de mes machines old school :) :) :) que je range quand pas utilisé ( en mode maniaque ) il me reste celui de l'oric a faire pour remplace mon ancien lecteur de fichier .tap :) :) :)
IMAG3187V2.jpg
IMAG3187V2.jpg (420.36 Kio) Consulté 7312 fois
IMAG3186V2.jpg
IMAG3186V2.jpg (402.29 Kio) Consulté 7312 fois
Dernière modification par Nephilim le 25 déc. 2020 18:39, modifié 1 fois.
Nephilim
Messages : 159
Inscription : 15 déc. 2020 11:51

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Nephilim »

Pour ceux que ça interesse et qui comme moi ont crée le sd lep avec un écran 2,8 pouces ci- dessous les plans pour un boitier en impression 3D
Attention il y a deux versions du fichier pour l'imprimante 3D, un pour l' écran rouge et un pour le bleu :) :) :)
https://www.thingiverse.com/thing:2141775
https://www.thingiverse.com/thing:2141793
Pièces jointes
41049731f049bc685ea7782ceba80194_preview_featured.jpg
41049731f049bc685ea7782ceba80194_preview_featured.jpg (41.92 Kio) Consulté 7313 fois
9074e3c0c67161e31042b0d0dc28b7a4_preview_featured.jpg
9074e3c0c67161e31042b0d0dc28b7a4_preview_featured.jpg (143.79 Kio) Consulté 7313 fois
9f060cd03cb5ba8098ed123bb6e564db_preview_featured.jpg
9f060cd03cb5ba8098ed123bb6e564db_preview_featured.jpg (45.82 Kio) Consulté 7313 fois
c2ce13122eba5cf46580e78cf4559bdd_preview_featured.jpg
c2ce13122eba5cf46580e78cf4559bdd_preview_featured.jpg (43.36 Kio) Consulté 7313 fois
Dernière modification par Nephilim le 08 janv. 2021 22:38, modifié 2 fois.
Vladimir Kr Can
Messages : 33
Inscription : 24 déc. 2020 00:04
Localisation : Quebec

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Vladimir Kr Can »

Daniel a écrit : 24 déc. 2020 18:03 L'insuffisance de la taille mémoire est une bonne piste. J'ai déjà eu le problème avec d'autres applications, ça ne pardonne pas et il est très difficile de comprendre la cause de l'erreur. S'il est impossible de gagner de la mémoire, il faudrait essayer la version précédente de SDLEP-TFT (avant l'amélioration faite par Cyril), elle est plus économe :
Je viens d'essayer, après avoir remplacé la bibliothèque MCUFriend par la bibliothèque AdafruitILI9341, et oui, j'arrive un peu plus loin, j'ai la liste des fichiers ! J'ai tout de même un truc étrange, le point rouge du fichier sélectionné clignote rapidement, mais admettons, j'arrive toujours plus loin qu'avant et ça ressemble à une bataille gagnée.

Maintenant, la partie tactile (résistive), elle ne réagit pas, et honnêtement, le contraire m'aurait (agréablement) surpris. Je regarde la documentation Adafruit, qui dit ceci :
Resistive Touch Controller Pins
  • Digital #13 or ICSP SCLK - This is the hardware SPI clock pin. By default its on digital #13. By cutting a jumper and soldering another on the back, you can move this line from digital to the ICSP 2x3 header. This pin is used for the TFT, microSD and resistive touch screen data clock
  • Digital #12 or ICSP MISO - This is the hardware SPI Microcontroller In Serial Out pin. By default its on digital #12. By cutting a jumper and soldering another on the back, you can move this line from digital to the ICSP 2x3 header. This pin is used for the TFT, microSD and resistive touch screen data
  • Digital #11 or ICSP MOSI - This is the hardware SPI Microcontroller Out Serial In pin. By default its on digital #11.By cutting a jumper and soldering another on the back, you can move this line from digital to the ICSP 2x3 header. This pin is used for the TFT, microSD and resistive touch screen data
  • Digital #8 - This is the STMPE610 Resistive Touch CS (chip select pin). It's used by the Arduino to tell the Resistive controller that it wants to send/receive data from the STMPE610 only
Là encore, je regarde les exemples fournis par Adafruit, et il faut ajouter une bibliothèque pour l'écran tactile. Et là, les gains obtenus en prenant la version simplifiée sont perdus, et je me retrouve à la case départ.

il me reste deux solutions :
- je prends un écran dont je sais qu'il fonctionne (j'en ai commandé un qui devrait arriver début janvier)
- je prends une carte Arduino avec plus de mémoire, et j'adapterai le code pour qu'il soit compatible avec cet écran (je verrai ça plus tard éventuellement)

En tout cas, merci pour votre aide.

P.S.
Je sais que c'est une question con, mais bon, je la pose tout de même comptant sur votre compréhension : sachant qu'une carte raspberry Pi zéro coûte moins de 10 euros, qu'il y a de la mémoire en pagaille, moins de problème de compatibilité d'écran, est-ce que cela vaudrait la peine d'adapter le code pour cette machine ?

Je veux bien essayer de le faire, mais j'aurai besoin d'aide, notamment pour ce qui est de convertir les fichiers LEP en signaux à envoyer aux ordinateurs.
Daniel
Messages : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Daniel »

Je crois que la plupart des bibliothèques ne sont pas optimisées pour minimiser l'utilisation de la mémoire. Dans d'autres projets j'ai réussi à gagner beaucoup de place en ne chargeant que les procédures utiles. En gros je ne charge pas la bibliothèque, le compilateur signale les fonctions manquantes et je les recopie une à une dans le programme.

Pour l'écran tactile c'est encore une fois le manque de documentation qui pose tant de problèmes. Quand on est sûr de la définition des broches il faut ensuite trouver l'axe des x, l'axe des y et leur sens. Reste ensuite à étalonner l'échelle des x et l'échelle des y. On peut écrire un programme de test affichant les coordonnées renvoyées par la dalle tactile pour quelques points remarquables, par exemple les quatre coins, et à partir de ces données on peut recalculer les coordonnées en pixels.

L'utilisation d'un Raspberry Pi pour remplacer l'Arduino est très certainement possible. Le seul point à vérifier est la possibilité de travailler en temps réel avec Linux, ce n'est pas forcément évident. A part ce point particulier, la conversion du programme doit être facile et tous les membres du forum sont certainement prêts à apporter leur aide.
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par hlide »

Je suis passé à l'Arduino MEGA quand il s'est agit de faire un peu plus que le SDLERP. Pour revenir aux histoires de mémoire : si vous utilisez gcc/g++ avec une option d'optimisation (pas le -O0), le code mort de la bibliothèque (i.e, non utilisé) devrait disparaître. Mais il reste à rajouter ceci :

Code : Tout sélectionner

-fdata-sections -ffunction-sections -Wl,--gc-sections
Ces options permettent de retirer les code ou données des symboles inusités.
Vladimir Kr Can
Messages : 33
Inscription : 24 déc. 2020 00:04
Localisation : Quebec

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Vladimir Kr Can »

J'ai l'impression que la version actuelle de l'IDE Arduino (j'utilise la 1.8.13) optimise déjà le code, en retirant les parties inutilisées ; par exemple si je retire des fonctions comme drawTriangle, drawRoundedRectangle, etc. je n'obtiens aucun changement, en revanche si j'efface des caractères du tableau glcdfont, là je réduis la taille du code final.

Ce que j'ai réussi à faire en revanche, c'est de déplacer le tableau SD_Buffer de variable globale, à la fonction setup(), modifiant les fonctions qui utilisent ce tableau (SD_Searchfile, SD_ReadBlock, SD_Init, SD_Init_FileSystem) en conséquence, et tout marche ! Je passe de 82% de mémoire utilisée à 57%.

Je vais pouvoir continuer à creuser le fonctionnement de la dale tactile, jusqu'au prochain point bloquant :)


Pour ce qui est de porter le programme sur Raspberry PI, il serait possible de produire du quasi temps-réel

J'ai commandé le livre, car je préfère le contact papier, plutôt que l'écran, et je regarderai ça en temps donné, mais si ça marche, ça pourrait être une alternative économique et plus standardisée.
Nephilim
Messages : 159
Inscription : 15 déc. 2020 11:51

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Nephilim »

Bonne année @ tous :) :) :)

Hello Carl , j' ai vu que tu avais fait pas mal de test sur plusieurs machines.

Pourrais tu me confirmer le cablage pour :
- Arduino vers oric
- Arduino Vers Zx Spectrum

D'avance Merci :D :D :D :D :D
Nephilim
Messages : 159
Inscription : 15 déc. 2020 11:51

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Nephilim »

IMAG3294-v2.jpg
IMAG3294-v2.jpg (477.71 Kio) Consulté 7083 fois
IMAG3291-v2.jpg
IMAG3291-v2.jpg (488.92 Kio) Consulté 7083 fois
IMAG3298-v2.jpg
IMAG3298-v2.jpg (413.87 Kio) Consulté 7083 fois
IMAG3297-v2.jpg
IMAG3297-v2.jpg (414.39 Kio) Consulté 7083 fois
Daniel
Messages : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.

Message par Daniel »

Magnifique, trois d'un coup ! Bravo !

Je suggère aux utilisateurs de SDLEP-READER et de SDLEP-TFT de publier dans le forum le schéma des câbles de chaque ordinateur.
Les nouveaux amateurs les trouveront ainsi facilement et n'auront plus à se poser la question à chaque fois.
Daniel
L'obstacle augmente mon ardeur.
Répondre