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

ccapublic
Messages : 31
Inscription : 23 nov. 2019 11:02

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

Message par ccapublic »

Quel âne... Je n'avais vu que la balise et pas les onglets en bas... Je fais ça ce soir ou demain, selon le speed des enfants ;)
ccapublic
Messages : 31
Inscription : 23 nov. 2019 11:02

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

Message par ccapublic »

Voici.
Au cours de la selection (rien de neuf)
Au cours de la selection (rien de neuf)
SDLEP TFT+ (Selection).jpg (126.08 Kio) Consulté 3328 fois
En cours de lecture (Motor ON)
En cours de lecture (Motor ON)
SDLEP TFT+ (Motor ON).jpg (111.35 Kio) Consulté 3328 fois
En pause (Motor OFF)
En pause (Motor OFF)
SDLEP TFT+ (Motor OFF).jpg (88.63 Kio) Consulté 3328 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 »

C'est clairement mieux que l'écran noir et blanc de la version initiale. La barre de progression est aussi très utile.

A la vue de ces images, j'ai une nouvelle idée d'amélioration : en mode Pause on pourrait accepter la sélection d'un autre fichier .lep (ou le même pour revenir au début de la cassette). On éviterait ainsi la nécessité de réinitialiser l'Arduino.
Daniel
L'obstacle augmente mon ardeur.
ccapublic
Messages : 31
Inscription : 23 nov. 2019 11:02

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

Message par ccapublic »

Comme c'est déjà sportif de patienter qu'une k7 se charge (je voulais justement une barre de progrès pour savoir combien d'oeufs à la coque je pourrais encore me cuire), j'avoue que je n'avais pas pensé que l'on pourrait vouloir changer de k7 :)

Ceci dit, ce serait certainement (et au moins) plus propre. Je le pousse dans la todo list.

PS : HS mais même si je vais probablement acheter un PCB + eeprom (lien que vous m'avez passé), j'ai pu ce soir lancer le LOGO à partir d'une sram programmée à l'arduino (j'ai balancé une dizaine d'images en dur dans sa flash code que je peux pousser à loisir dans la SRAM) puis montée sur un pcb de memo7 monobanque. Même si je l'avais refait tourner il y a qques temps avec votre emulo, ça fait tout drôle de le revoir tourner sur la vraie bécane... une vraie machine a remonter le temps : j'étais en CM1 et j'ai 43 ans......
Avatar de l’utilisateur
6502man
Messages : 12286
Inscription : 12 avr. 2007 22:46
Localisation : VAR
Contact :

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

Message par 6502man »

En voyant cette modification du fonctionnement de SDLEP-READER et même si je veux plus utiliser les K7 ou système émulant les K7 (trop long pour charger un soft) il me viens une idée à explorer, je vous en dirais plus dès que j'ai réunis tous ce qui me faut ...
Phil.

www.6502man.com

To bit or not to bit.
1 or 0.
ccapublic
Messages : 31
Inscription : 23 nov. 2019 11:02

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

Message par ccapublic »

Tu nous la fais donc suspense... :)
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 »

Sûrement pas une histoire de compression parce que c'est spécifique à chaque machine.
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 »

Vous savez peut-être qu'il existe un système de carte SD pour Oric ressemblant à SDDRIVE, sauf qu'il n'émule pas une disquette : il charge des fichiers .tap (images de cassette). L'intérêt est la vitesse, bien supérieure à celle de la vraie cassette.

Malheureusement ce n'est pas possible pour le MO5, à cause des protections diaboliques inventées par les éditeurs. Il faudrait refaire toute la logithèque avec un format standard, c'est un travail de fou. Autant convertir les cassettes en disquettes, ce n'est pas plus difficile.
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
6502man
Messages : 12286
Inscription : 12 avr. 2007 22:46
Localisation : VAR
Contact :

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

Message par 6502man »

Sur MO5 on pourrait faire dans le même esprit de charger directement des ".K7" mais il faudrait modifier la routine de chargement de K7 malheureusement la ROM n'est pas sur support comme dans les Oric, et de plus comme le signal Daniel certains jeux utilise des routines spécifiques de chargement K7 (protection).

Quoique si ces routines ne font que lire un bit mais ça serait trop simple :roll:
Phil.

www.6502man.com

To bit or not to bit.
1 or 0.
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 »

Dans les jeux protégés du MO5, les routines de lecture de la cassette concernent à la fois la lecture d'un bit, la lecture d'un octet, la lecture d'un bloc, le décodage de la structure des fichiers. Comme je l'ai écrit plus haut il est moins difficile de transformer la cassette en disquette que de reconstituer une cassette sans protection.
Daniel
L'obstacle augmente mon ardeur.
JihemB
Messages : 68
Inscription : 31 mai 2014 16:01

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

Message par JihemB »

Bonjour à tous.

Désolé, je n'ai pas eu le courage de regarder les 36 pages (!) de ce sujet, pardonnez-moi.

J'ai quelques émulateurs de lecteurs de cassettes pour mes différents ordinateurs 8 bits (de mémoire Tapuino pour les Commodore, TZXDuino pour les Z80 style Dragon, Sinclair, MSX, Amstrad, CASDuino pour jouer des fichiers WAV 22Khz Mono pour Oric, VG5000, Alice et SD-Drive Max pour les Atari).

Je n'ai en revanche rien pour simuler la lecture des softs cassettes sur les MO-TO d'où mon intéret pour SDLEP-READER.

N'ayant aucune habilité ni compétences pour faire des montages électronique y-a-t-il quelqu'un qui fabriquerait ce dispositif à la demande ?
Dans cette éventualité, merci de me contacter par MP.

JihemB
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 »

Le module écran s'enfiche sur l'Arduino UNO sans soudure. Il y a juste quatre fils à relier au connecteur cassette de l'ordinateur.
Daniel
L'obstacle augmente mon ardeur.
JihemB
Messages : 68
Inscription : 31 mai 2014 16:01

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

Message par JihemB »

Au temps pour moi Daniel, j'étais encore sur la version précédente :-(

Pourrais-tu cependant donner des liens (ou via MP) vers les écrans ("shields") qui ont été testés avec succès car j'ai retenu de ma lecture qu'une des difficultés était de bien paramétrer le logiciel de l'Arduino pour que le menu s'affiche bien sur l'écran ?

JihemB
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 est très difficile de donner des liens, car en achetant le même écran chez le même vendeur il n'y a pas toujours le même chipset ni la même dalle tactile. Le vendeur lui-même est incapable de donner l'information. C'est donc une loterie, mais on gagne à tous les coups. Pour les quatre écrans que j'ai achetés (tous différents) il n'y a pas eu de difficulté pour trouver les bonnes bibliothèques.

Donc on achète l'écran, on exécute le programme d'identification du chipset (fourni avec les bibliothèques) et on adapte le programme au chipset. Dans le forum nous avons recensé plus d'une demi-douzaine de modèles différents, avec un peu de chance ce sera l'un d'eux et nous pourrons apporter de l'aide. Je ne dis pas que c'est facile, mais avec de la ténacité et les conseils de la communauté on arrive toujours.
Daniel
L'obstacle augmente mon ardeur.
ccapublic
Messages : 31
Inscription : 23 nov. 2019 11:02

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

Message par ccapublic »

Salut à tous,

Pas passé bcp de temps dessus, mais pour ceux que cela intéresse ci dessous le code modifié (voir history). Le reste - particulièrement la config de la dalle tactile - ne change pas. Utilisé seulement sur TO7 & TO7/70.

Bonnes fêtes de fin d'année à tous !

Cyril

Code : Tout sélectionner

/***************************************************
*             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 
*/

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                             10          // 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;

// 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
  if (!sd.begin(SD_CS_PIN))
  {
    tft.setCursor(16, 40);
    tft.setTextColor(WHITE);
    tft.println(F("SD init..."));
    wdt_enable(WDTO_1S);
    while(1);
  } 

  // 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;
  
  tft.reset();
  identifier = tft.readID(); // lecture de l'identifiant

#ifdef DEBUG
  Serial.begin(9600);
  Serial.println(F("TFT"));
  if(identifier == 0x9325) {
    Serial.println(F("Found ILI9325 LCD driver"));
  } else if(identifier == 0x9327) {
    Serial.println(F("Found ILI9327 LCD driver"));
  } else if(identifier == 0x9328) {
    Serial.println(F("Found ILI9328 LCD driver"));
  } else if(identifier == 0x7575) {
    Serial.println(F("Found HX8347G LCD driver"));
  } else if(identifier == 0x9341) {
    Serial.println(F("Found ILI9341 LCD driver"));
  } else if(identifier == 0x8357) {
    Serial.println(F("Found HX8357D LCD driver"));
  } else if(identifier == 0x0154) {
    Serial.println(F("Found S6D0154 LCD driver"));
  } else {
    Serial.print(F("Unknown LCD driver chip: "));
    Serial.println(identifier, HEX);
    Serial.println(F("If using the Adafruit 2.8\" TFT Arduino shield, the line:"));
    Serial.println(F("  #define USE_ADAFRUIT_SHIELD_PINOUT"));
    Serial.println(F("should appear in the library header (Adafruit_TFT.h)."));
    Serial.println(F("If using the breakout board, it should NOT be #defined!"));
    Serial.println(F("Also if using the breakout, double-check that all wiring"));
    Serial.println(F("matches the tutorial."));
    return;
  }
#endif
  
  tft.begin(identifier);     // 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);
}
_
Répondre