SDLEP-READER remplace tous les magnétophones d'ordinateurs.
Modérateurs : Papy.G, fneck, Carl
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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)
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.
L'obstacle augmente mon ardeur.
-
- Messages : 33
- Inscription : 24 déc. 2020 00:04
- Localisation : Quebec
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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 :
Les modifs ne sont pas très propres pour le moment, puisque pas encore définitives et que je tâtonne.
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é 132 fois
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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.
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.
L'obstacle augmente mon ardeur.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
Merci pour la réponse sur la fabrication Daniel.
-
- Messages : 33
- Inscription : 24 déc. 2020 00:04
- Localisation : Quebec
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
@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.
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.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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
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
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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
" @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
Dernière modification par Nephilim le 25 déc. 2020 18:39, modifié 1 fois.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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
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 (41.92 Kio) Consulté 7375 fois
-
- 9074e3c0c67161e31042b0d0dc28b7a4_preview_featured.jpg (143.79 Kio) Consulté 7375 fois
-
- 9f060cd03cb5ba8098ed123bb6e564db_preview_featured.jpg (45.82 Kio) Consulté 7375 fois
-
- c2ce13122eba5cf46580e78cf4559bdd_preview_featured.jpg (43.36 Kio) Consulté 7375 fois
Dernière modification par Nephilim le 08 janv. 2021 22:38, modifié 2 fois.
-
- Messages : 33
- Inscription : 24 déc. 2020 00:04
- Localisation : Quebec
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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.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 :
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 :
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.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
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.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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.
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.
L'obstacle augmente mon ardeur.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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 :
Ces options permettent de retirer les code ou données des symboles inusités.
Code : Tout sélectionner
-fdata-sections -ffunction-sections -Wl,--gc-sections
-
- Messages : 33
- Inscription : 24 déc. 2020 00:04
- Localisation : Quebec
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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.
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.
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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
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
Re: SDLEP-READER remplace tous les magnétophones d'ordinateurs.
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.
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.
L'obstacle augmente mon ardeur.