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

Avatar de l’utilisateur
Carl
Modérateur
Messages : 13253
Inscription : 08 avr. 2007 13:21
Localisation : http://www.doledujura.fr
Contact :

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

Message par Carl »

Je viens de faire un autre wav de donkey Kong avec 2 niveaux d'amplifications et le programme se charge mais finalement le jeu plante... donc ce n'est pas un soucis de LEP uniquement... ça va pas être simple... je ferai d'autres tests de lecture wav depuis mon pc portable avec le volume au 3/4....

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

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

Message par Daniel »

C'est à cause de ces problèmes sans fin que j'ai définitivement abandonné les cassettes pour mes ordinateurs de collection.
C'est une source inépuisable d'erreurs et de perte de temps. L'avantage avec SDLEP, c'est que si ça marche une fois ça marchera ensuite toujours, ce qui n'est pas le cas avec un lecteur analogique.
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
riquet
Messages : 48
Inscription : 20 mai 2013 22:56

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

Message par riquet »

Concernant le MSX, les fichiers .wav générés par CAS Tools ou MSX Taper sont très aléatoires dans leur fonctionnement.
Ces dernières années, j'ai converti plusieurs dizaines de fichiers pour n'en garder qu'une petite vingtaine.
Il faudrait essayer avec le nouvel outil de Louthrax qui semble donner de bons résultats (je n'ai pas testé) d'après les retours. Cet utilitaire s'appelle sofacas, à télécharger sur son site http://lhalter.free.fr/mgr/sofacas.html
MSX - MSX2 - MSX2+ - MSXtR - VG5000
Trouvez l'intrus
Avatar de l’utilisateur
Carl
Modérateur
Messages : 13253
Inscription : 08 avr. 2007 13:21
Localisation : http://www.doledujura.fr
Contact :

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

Message par Carl »

Merci riquet, je vais faire des tests...

Carl
Avatar de l’utilisateur
gleike
Messages : 1341
Inscription : 16 oct. 2014 11:12
Localisation : Ludres (54710) Meurthe & Moselle

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

Message par gleike »

Dans le but d'optimiser l'affichage LCD et l'utilisation du module SDLEP- READER
que je suis en train de mettre au point,
y-a-t-il un moyen dans le croquis Arduino de calculer le temps (minutes, secondes) de chargement
d'un programme en fonction de sa taille
avec la commande "AudioFileInfo.Size" ou autre ?
j'aimerais pouvoir l'afficher, ça aide a patienter.

Merci.
Dernière modification par gleike le 01 avr. 2017 18:19, modifié 1 fois.
Daniel
Messages : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

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

Message par Daniel »

Il est possible de calculer la durée du chargement, mais ce n'est pas simple. La durée n'est pas fonction de la taille du fichier.
Le fichier .lep contient une succession de périodes. Voir ici le format : http://dcmoto.free.fr/bricolage/sdlep-reader/index.html
Pour calculer la durée, il faut lire chaque octet du fichier, en déduire la période correspondante et additionner toutes les périodes.

La période se calcule ainsi :
- Lire un octet (8 bits signés). S'il est nul prendre 127, sinon prendre la valeur absolue.
- Multiplier par l'unité de période (50 microsecondes).
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
gleike
Messages : 1341
Inscription : 16 oct. 2014 11:12
Localisation : Ludres (54710) Meurthe & Moselle

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

Message par gleike »

Effectivement, ce n'est pas simple et ça nécessite de lire déjà une fois le fichier
avant de l'envoyer au to7, rien que pour calculer la durée.

Et je ne peux même pas afficher un compteur qui décrémenterais les octets envoyés de la taille total du fichier
car apparemment l’accès a l'affichage LCD n'est pas possible pendant la lecture avec les interruptions désactivés.

Tant pis, je vais me contenter de regarder clignoter la LED d'activité.
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 croquis Arduino utilisé par SDLEP-READER accède à la carte SD grâce à la bibliothèque SimpleSDAudio, mais n'utilise pas les fonctions de lecture et d'interprétation des fichiers .wav. Pour gagner de la place, j'ai fait une nouvelle version "expurgée", qui n'inclut pas la bibliothèque complète. Elle recopie seulement les fonctions utiles, et devient indépendante.

On passe ainsi de 5968 à 4178 octets de programme, et de 651 à 598 octets de variables globales. Dans la pratique ça ne change strictement rien au programme lui-même, par contre ça laisse un peu plus de place aux bidouilleurs qui voudront ajouter d'autres fonctions.

Code : Tout sélectionner

/**************************************************\
*                  S D - L E P                     * 
*           (c) 2017 - Daniel Coulom               *  
*           http://dcmoto.free.fr/                 *
*           http://forum.system-cfg.com/           *
*--------------------------------------------------*
* 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.*
\**************************************************/

/***************************************************
*                Version 2017.03.13                *
****************************************************
Historique
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

Emulation d'un lecteur de cassette Thomson TO ou MO
Lecture des donnees sur carte SD dans un fichier au
format .lep (image de cassette Thomson).

Connexions vers module Catalex pour carte micro SD
 GND --> GND
 D13 --> SCK
 D12 --> MISO
 D11 --> MOSI
 VCC --> VCC (5V)
 D10 --> CS 

Connexions vers DIN magnetophone
GND --> DIN 2
 D0 --> DIN 1  (MOTOR)
 D1 --> DIN 4  (READ DATA) 
   
Connexion vers DIN crayon optique
VCC --> DIN 5  (+5V)
   
Connexion LED d'activite
 D2 --> LED d'activite + resistance 1.5K
   
Connexion des interrupteurs de selection du fichier
 D3 --> interrupteur 1
 D4 --> interrupteur 2
 D5 --> interrupteur 3
 D6 --> interrupteur 4
 D7 --> interrupteur 5
 D8 --> interrupteur 6
 D9 --> interrupteur 7
 
*****************************************************************
SD card management is adapted from SimpleSDAudio library.
Visit SimpleSDAudio website for more information:
http://www.hackerspace-ffm.de/wiki/index.php?title=SimpleSDAudio
*****************************************************************
*/

// 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
#define SD_READ_TIMEOUT       300
#define SD_COMMAND_TIMEOUT    300
#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_FRAGMET_FOUND  0x36 /** Fragmentation found */
#define SD_ERROR_FAT_ENTRY      0x37 /** Error in FAT entry */
#define SD_ERROR_EOF            0x38 /** End of file reached */
#define SD_ERROR_WORKBUF        0x39 /** WorkBuf not set */

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

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_CSPin;      // numero de la broche CS de la carte SD
uint8_t  string[512];   // SD workbuf must hold at least 512 bytes
uint8_t  *SD_workBuf;   // pointeur vers buffer de travail

#define UNITE 50  //unite du delai en microsecondes


/**************************************************************************\
* Lecture du fichier .lep
\**************************************************************************/
void setup()
{
 int i;                      // compteur de boucle
 int delai;                  // delai en microsecondes 
 bool niveau;                // niveau du signal en sortie
 bool activite;              // niveau diode d'activite
 char octet;                 // zone de stockage d'un octet
 uint32_t count = 0;         // compteur d'octets
 char filename[13];          // nom du fichier .lep
 
 //Initialisations
 pinMode(0, INPUT_PULLUP);   // configurer pin0(RX) en entree (commande moteur)
 pinMode(1, OUTPUT);         // configurer pin1(TX) en sortie (donnees lues)
 pinMode(2, OUTPUT);         // configurer pin2 en sortie (led activite)
 pinMode(3, INPUT_PULLUP);   // interrupteur 1
 pinMode(4, INPUT_PULLUP);   // interrupteur 2
 pinMode(5, INPUT_PULLUP);   // interrupteur 3
 pinMode(6, INPUT_PULLUP);   // interrupteur 4
 pinMode(7, INPUT_PULLUP);   // interrupteur 5
 pinMode(8, INPUT_PULLUP);   // interrupteur 6
 pinMode(9, INPUT_PULLUP);   // interrupteur 7
 SD_CSPin = 10;              // definir CS de la carte SD en D10
 SD_Init_FileSystem(string); // initialiser la carte SD
 SD_SpiSetHighSpeed();       // frequence d'horloge maxi 

 //Determination du nom du fichier (extension .lep)
 //en fonction de la position des 7 interrupteurs (128 possibilites)
 //Le nom du fichier est compose de sept chiffres 0 ou 1
 //decrivant la position des interrupteurs 1 à 7
 //Exemple : interrupteurs 2, 4, 6 sur ON --> fichier 0101010.lep 
 //Exemple : interrupteurs 5 sur ON --> fichier 0000100.lep
 //Exemple : interrupteurs 1,2,3,5,7 sur ON --> fichier 1110101.lep 
 strcpy(filename, "0000000.lep");
 if(digitalRead(3) == LOW) filename[0] = '1';
 if(digitalRead(4) == LOW) filename[1] = '1';
 if(digitalRead(5) == LOW) filename[2] = '1';
 if(digitalRead(6) == LOW) filename[3] = '1';
 if(digitalRead(7) == LOW) filename[4] = '1';
 if(digitalRead(8) == LOW) filename[5] = '1';
 if(digitalRead(9) == LOW) filename[6] = '1';
 
 // Search for file SD_FILE in Rootdir (=cluster 0),
 // search shortname files only (0x00,0x18)
 i = SD_SearchFile((uint8_t *)filename, 0UL, 0x00, 0x18, &AudioFileInfo);
 //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
  digitalWrite(2, HIGH);   // allumer led activite 
  delay(5);                // temporisation
  digitalWrite(2, LOW);    // eteindre led activite 
  delay(50);               // temporisation
 }
 
 // send CMD18 (multiblock read)
 noInterrupts();                               // desactiver les interruptions
 uint32_t offset = AudioFileInfo.ActSector;    // offset = secteur
 if (SD_type != SD_CARD_TYPE_SDHC) // si carte non SDHC
     offset <<= 9;                             // offset = octet
 SD_CardCommand(18, offset);                // lance CMD18
 niveau = HIGH;                                // signal a 1 pour detection lep
 digitalWrite(1, niveau);                      // initialisation sortie
 activite = LOW;                               // signal a 0 pour led d'activite
 digitalWrite(2, activite);                    // eteindre led activite

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

  //traitement d'un bloc de 512 octets lus sur la carte SD 
  for(i = 0; i < 512; i++)
  {
   if(digitalRead(0) == HIGH)       // si le moteur est arrete,
   {                                // pour que le LEP soit detecte,
    digitalWrite(1, HIGH);          // mettre le signal en sortie a 1
    digitalWrite(2, LOW);           // eteindre la led d'activite
    while(digitalRead(0) == HIGH);  // attendre le MOTOR ON
   }
   count++;                         // nombre d'octets lus 
   octet = SD_SpiRecvByte();     // lecture d'un octet
   delai = abs(octet);              // initialisation delai
   if(octet == 0) delai = 127;      // absence de signal (127 unites)
   delai *= UNITE;                  // conversion en microsecondes
   if(octet > 0) niveau = HIGH;     // creneau positif
   if(octet < 0) niveau = LOW;      // creneau negatif
   digitalWrite(1, niveau);         // signal en sortie
   //if(i == 511) delai -= 49;        // compensation temps chgt bloc 
   delayMicroseconds(delai);        // temporisation
  }

  //lecture des deux octets de CRC
  SD_SpiRecvByte();              // lecture octet CRC1
  SD_SpiRecvByte();              // lecture octet CRC2
  
  //clignotement diode d'activite
  activite = (activite == LOW) ? HIGH : LOW;
  digitalWrite(2, activite);        
 }
 digitalWrite(2, 0);                // eteindre led activite 
 interrupts();                      //activer les interruptions
}

/**************************************************************************\
* Flash toutes les deux secondes apres la fin de fichier
\**************************************************************************/
void loop(void) {
//la lecture est arrivee en fin de fichier
//clignotement lent de la diode d'activite
digitalWrite(2, HIGH);   // allumer led activite 
delay(100);              // temporisation
digitalWrite(2, LOW);    // eteindre led activite 
delay(1900);             // temporisation
}

/**************************************************************************\
* Set CS High 
* Sends also one dummy byte to ensure MISO goes high impedance
\**************************************************************************/
void SD_SetCSHigh()
{
 digitalWrite(SD_CSPin, HIGH);
 SD_SpiSendByte(0xff);
}

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

/**************************************************************************\
* wait for card to go not busy
\**************************************************************************/
uint8_t SD_WaitNotBusy(uint16_t timeout) 
{
  uint16_t t0 = ((uint16_t)millis());
  while (SD_SpiRecvByte() != 0xFF) {if ((((uint16_t)millis()) - t0) >= timeout) return 0;}
  return 1;
}

/**************************************************************************\
* 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 = 0xFF;
      
  // select card
  digitalWrite(SD_CSPin, LOW);

  // wait up to timeout if busy
  SD_WaitNotBusy(SD_COMMAND_TIMEOUT);

  // 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
  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_SpiRecvByte();

  // wait for response
  for(uint8_t i = 0; i < 100; ++i)
  {
      response = SD_SpiRecvByte();
      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.
\**************************************************************************/
uint8_t SD_CardACommand(uint8_t cmd, uint32_t arg)
{
    SD_CardCommand(SD_CMD55, 0);
    return SD_CardCommand(cmd, arg);
}

/**************************************************************************\
 * 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;
  // 16-bit init start time allows over a minute
  uint16_t t0 = ((uint16_t)millis());
  uint32_t arg;
  SD_type = 0;

  /* Setup ports */
  pinMode(SD_CSPin, OUTPUT);
  digitalWrite(SD_CSPin, HIGH);
  pinMode(MISO, INPUT);
  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, OUTPUT);
  
  digitalWrite(SCK, LOW);
  digitalWrite(MOSI, LOW);
  digitalWrite(SS, 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 )
  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_SpiRecvByte();
    SD_SpiRecvByte();
    status = SD_SpiRecvByte();
    if ((status & 0x01) == 0) {
      // card operation voltage range doesn't match
      SD_SetCSHigh();
      return(SD_ERROR_VOLTMATCH);       
    }
    if (SD_SpiRecvByte() != 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_SpiRecvByte() & 0xC0) == 0xC0) SD_type = SD_CARD_TYPE_SDHC;
    // discard rest of ocr - contains allowed voltage range
    SD_SpiRecvByte();
    SD_SpiRecvByte();
    SD_SpiRecvByte();
  }

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

/**************************************************************************\
* 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 *pWorkBuf)
{
    uint8_t  retval;
    uint8_t  PartType;
    uint16_t temp16;
    uint32_t temp32;
    
    SD_FAT.PartType = SD_PARTTYPE_UNKNOWN;
    
    if(pWorkBuf == NULL) return(SD_ERROR_WORKBUF);
    SD_workBuf = pWorkBuf;
    
    // Try init SD-Card
    retval = SD_Init();
    if(retval) return(retval);
    
    // ==== MBR (partition table) access here =====
    
    // Read sector 0 
    retval = SD_ReadBlock(0, SD_workBuf);
    if(retval) return(retval);

    // Test for signature (valid not only for MBR, but FAT Boot Sector as well!)
    if((SD_workBuf[0x1fe] != 0x55) || (SD_workBuf[0x1ff] != 0xaa)) return(SD_ERROR_INVAL_SECT0);
    
    // Store most important MBR values for first partition
    PartType = SD_workBuf[0x1be + 0x04];
    SD_FAT.BootSectorStart =  (uint32_t)SD_workBuf[0x1be + 0x08] 
                             | ((uint32_t)SD_workBuf[0x1be + 0x09] << 8UL)
                             | ((uint32_t)SD_workBuf[0x1be + 0x0a] << 16UL)
                             | ((uint32_t)SD_workBuf[0x1be + 0x0b] << 24UL);
    
    // Check MBR values for plausibility
    if(  ((SD_workBuf[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, SD_workBuf);
    if(retval) return(retval);  

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

    // Read fields that are same for FAT16 and FAT32
    SD_FAT.SecPerClus = SD_workBuf[0x0d];
    SD_FAT.RsvdSecCnt = (uint16_t)SD_workBuf[0x0e] | ((uint16_t)SD_workBuf[0x0f]<<8U);
    if((SD_FAT.SecPerClus == 0) || (SD_FAT.RsvdSecCnt == 0)) return(SD_ERROR_INVAL_BS);
    SD_FAT.NumFATs = SD_workBuf[0x10];
    SD_FAT.RootEntryCount = (uint16_t)SD_workBuf[0x11] | ((uint16_t)SD_workBuf[0x12]<<8U);
    
    temp16 = (uint16_t)SD_workBuf[0x13] | ((uint16_t)SD_workBuf[0x14]<<8U);
    temp32 = (uint32_t)SD_workBuf[0x20] | ((uint32_t)SD_workBuf[0x21]<<8U) | ((uint32_t)SD_workBuf[0x22]<<16U) | ((uint32_t)SD_workBuf[0x23]<<24U);
    SD_FAT.TotalSec  = temp16 ? temp16 : temp32;
    
    temp16 = (uint16_t)SD_workBuf[0x16] | ((uint16_t)SD_workBuf[0x17]<<8U);
    temp32 = (uint32_t)SD_workBuf[0x24] | ((uint32_t)SD_workBuf[0x25]<<8U) | ((uint32_t)SD_workBuf[0x26]<<16U) | ((uint32_t)SD_workBuf[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_workBuf[0x2c] | ((uint32_t)SD_workBuf[0x2d]<<8U) | ((uint32_t)SD_workBuf[0x2e]<<16U) | ((uint32_t)SD_workBuf[0x2f]<<24U);
        SD_FAT.RootDirStart = SD_Cluster2Sector(temp32);
        SD_FAT.PartType = SD_PARTTYPE_FAT32;
        SD_FAT.ClusterEndMarker = 0xffffff8UL;
    }
      
    return 0;
}

/**************************************************************************\
* Set SPI for full operation speed (up to 25 MHz).
* Will be called after first part of card 
* initialization was successful.
\**************************************************************************/
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.
\**************************************************************************/
uint8_t SD_SpiRecvByte()
{
 SPDR = 0xff; /* send dummy data for receiving some */
 while(!(SPSR & (1 << SPIF)));
 SPSR &= ~(1 << SPIF);
 return SPDR;
}

/**************************************************************************\
* SPI read data - only one call so force inline 
* Receives nbytes in buf. Send 0xff all the time.
\**************************************************************************/
//inline __attribute__((always_inline)) 
void SD_SpiRecvBlock(uint8_t *buf, uint16_t nbyte) 
{
 if (nbyte-- == 0) return;
 SPDR = 0xFF;
 for (uint16_t i = 0; i < nbyte; i++) {
   while (!(SPSR & (1 << SPIF)));
   buf[i] = SPDR;
   SPDR = 0xFF;
 }
 while (!(SPSR & (1 << SPIF)));
 buf[nbyte] = SPDR;
}

/**************************************************************************\
* Read one block of data from card.
* \return Error code, 0 if ok.
\**************************************************************************/
uint8_t SD_ReadData(uint8_t *dst, uint16_t count) 
{
  // wait for start block token
  uint8_t status;
  uint16_t t0 = ((uint16_t)millis());
  
  while ((status = SD_SpiRecvByte()) == 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
  SD_SpiRecvBlock(dst, count);

  // discard CRC
  SD_SpiRecvByte();
  SD_SpiRecvByte();
  SD_SetCSHigh();

  return 0;
}

/**************************************************************************\
* 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 *dst) 
{
 // 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);}
 return SD_ReadData(dst, 512);
}

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

/**************************************************************************\
* 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;
 //Serial.println(fnentry);
    
 // go through sectors
 for(uint16_t i = 0; i<maxsect; i++)
 {
  uint8_t retval = SD_ReadBlock(startsect + i, SD_workBuf);
  if(retval) return(retval);
        
  for(uint16_t j = 0; j<512; j+=32)
  {
   uint8_t attrib;
   if(SD_workBuf[j] == 0) return(SD_ERROR_FILE_NOT_FOUND);    // Last entry when first character of filename == 0
   if(SD_workBuf[j] == 0xe5) continue;  // Skip deleted files
   if(SD_workBuf[j] == 0x05) SD_workBuf[j] = 0xE5;
            
   attrib = SD_workBuf[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_workBuf[j+k] != fnentry[k]) break;
    if(k >= 11)
    {
     // found it
     fileinfo->Attributes = attrib;
     fileinfo->Size = (uint32_t)SD_workBuf[j+0x1c] | (((uint32_t)SD_workBuf[j+0x1d])<<8) 
                      | (((uint32_t)SD_workBuf[j+0x1e])<<16) | (((uint32_t)SD_workBuf[j+0x1f])<<24);
     if(SD_FAT.PartType == SD_PARTTYPE_FAT16)
       {fileinfo->FirstCluster = (uint32_t)SD_workBuf[j+0x1a] | (((uint32_t)SD_workBuf[j+0x1b])<<8);}
     else
      {fileinfo->FirstCluster = (uint32_t)SD_workBuf[j+0x1a] | (((uint32_t)SD_workBuf[j+0x1b])<<8) 
       | (((uint32_t)SD_workBuf[j+0x14])<<16) | (((uint32_t)SD_workBuf[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);
}
Daniel
L'obstacle augmente mon ardeur.
Samy83
Messages : 7
Inscription : 15 juil. 2016 21:50

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

Message par Samy83 »

Bonjour à tous...
...et plus particulièrement à Daniel à qui s'adresseront mes questions.

J'ai fait il y a quelques jours la démonstration à un ami du contrôleur CS91-282, installé il y a quelques mois dans mon MO5.
Il a été enchanté de revoir certains jeux qui l'avaient marqué dans sa jeunesse.
Cependant, il a mis le doigt sur une "lacune" qui n'est pas propre au contrôleur mais qui l'a un peu déçu lors de la démonstration : il n'a pas pu revoir certains jeux qui n'existent qu'au format k7 comme par exemple "Pop Pop" qui l'avait marqué je ne sais pourquoi ?

Je me suis donc dis qu'un génial informaticien bricoleur :wink: avait certainement trouvé une solution à ce problème !
Après la recherche et la lecture (un peu rapide il est vrai) des 13 pages du forum dédiées au SDLEP-Reader, une question principale se pose à moi : les conversions successives des fichiers .k7 en .wav (avec DCMOK7) puis de .wav en .lep (avec DCLEP) fonctionnent t-elles à coup sûr ou existe t-il une autre méthode ?

Quoi qu'il en soit, je serait intéressé par l'achat des lots 1 et 2 s'ils sont disponibles ?

Merci d'avance.

Sam
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

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

Message par __sam__ »

Si c'est pour mo5 il y a une solution sauvant l 'état de l’émulateur dans un fichier sd utilisable sur mo5 je crois.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
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 »

Les fichiers .lep pour n'importe quel ordinateur Thomson fonctionnent à coup sûr quand je les crée moi-même. Jusqu'à maintenant la réussite est de 100%, aussi bien pour les jeux commerciaux protégés que pour les programmes Basic. La seule différence avec un magnétophone est la suppression totale et définitive des erreurs de lecture.

Si le but est simplement d'utiliser le jeu sur MO5, la solution donnée par __sam__ est certainement plus simple, et surtout le chargement est beaucoup plus rapide : une dizaine de secondes avec le fichier .sd, contre une dizaine de minutes pour une cassette ou un fichier .lep. Voir ici : viewtopic.php?f=24&t=5759&start=78

Pour que la sauvegarde de l'état de l'émulateur fonctionne bien avec le "vrai" MO5, il faut la faire à un moment judicieux. Normalement juste après la fin du chargement et juste avant le début du jeu. En cas de difficulté n'hésitez pas à demander de l'aide dans le fil de discussion DCMOTO nouveau.
Daniel
L'obstacle augmente mon ardeur.
Samy83
Messages : 7
Inscription : 15 juil. 2016 21:50

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

Message par Samy83 »

Merci Sam et Daniel.

Effectivement, c'est beaucoup plus simple (et économique) :D
Je vais tester ça dès que possible et je vous tiens informé en cas de difficulté.
Avatar de l’utilisateur
Carl
Modérateur
Messages : 13253
Inscription : 08 avr. 2007 13:21
Localisation : http://www.doledujura.fr
Contact :

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

Message par Carl »

Daniel, tu trouveras le wav généré avec le logiciel sofacas et le wav sortie SDLEP, le 1er se charge en lecteur wav, le second se plante SDLEP...

Si tu as une idée...
spectre.jpg
spectre.jpg (286.44 Kio) Consulté 4615 fois
test sofacas.7z
(160.63 Kio) Téléchargé 117 fois

Enregistrement (9).7z
https://download.system-cfg.com/f.php?h=3j3tJsEH&d=1

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

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

Message par Daniel »

J'ai fait l'essai suivant :
1) Génération du fichier .lep à partir de test sofacas.wav
2) Enregistrement de la sortie de SDLEP-READER avec une carte son de PC
Résultat (disponible 1 mois) : http://dl.free.fr/fhSroFssB

Le fichier .wav obtenu est pratiquement identique au fichier original. Il n'y a jamais plus de 50 µs d'écart dans les périodes des signaux.

Le problème n'est donc pas du à la logique de codage et de décodage du signal. Il peut avoir différentes causes matérielles : mauvais contacts dans le câble, mauvaise adaptation d'impédance entre la sortie de l'Arduino et l'entrée de l'ordinateur, problème d'alimentation, parasites, etc. Je n'ai pas malheureusement pas de MSX pour faire l'essai moi-même.

Autre point : attention aux formats de compression audio avec pertes. Ils ne sont pas adaptés à la compression de données, comme celles qui se trouvent sur les cassettes de programmes. A la limite ces compressions peuvent être acceptables pour des fichiers audio (bien que les véritables audiophiles ne les supportent pas). Pour des données numériques c'est un source d'erreurs de décodage.
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
Carl
Modérateur
Messages : 13253
Inscription : 08 avr. 2007 13:21
Localisation : http://www.doledujura.fr
Contact :

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

Message par Carl »

En effet le Wav issu du SDLEP passe très bien sur MSX...

Je viens de mettre une légère amplification du signal de sortie jack audio du SDLEP et bingo, les fichiers LEP se sont chargés à 100% 8)

conclusion :

VG5000___________pas besoin d'ampli
Alice 4/32/90______pas besoin d'ampli
EXL 100___________pas besoin d'ampli
MSX______________besoin ampli


Carl
Répondre