The simplest project, ArduiTape, is a simple WAV file player that uses the TMRpcm library and whilst that has been converted to STM32 it stubbornly refuses to verify and comes back with a the following error.
error: ‘TMRpcm’ does not name a type
TMRpcm tmrpcm; //Initialise tmrpcm
You can download the Arduino code here.
https://mega.nz/#!qNty1JCY!jnHxeC-qtmhP … PWMjXD4O0Y
The other two have the same problem which is converting TimerOne library calls to using HardwareTimer instead.
There are several TimerOne functions that are not so easily translated and I was wondering if you can help?
You can download the Arduino codes for TZXDuino one of the projects from here. https://mega.nz/#!yRVkhITZ!geW4k0jrY4BG … HeMSz6N7Gs
I’m a total noob to the STM32 so any and all help would be appreciated.
The simplest project, ArduiTape, is a simple WAV file player that uses the TMRpcm library and whilst that has been converted to STM32 it stubbornly refuses to verify and comes back with a the following error.
error: ‘TMRpcm’ does not name a type
TMRpcm tmrpcm; //Initialise tmrpcm
You can download the Arduino code here.
https://mega.nz/#!qNty1JCY!jnHxeC-qtmhP … PWMjXD4O0Y
The other two have the same problem which is converting TimerOne library calls to using HardwareTimer instead.
There are several TimerOne functions that are not so easily translated and I was wondering if you can help?
You can download the Arduino codes for TZXDuino one of the projects from here. https://mega.nz/#!yRVkhITZ!geW4k0jrY4BG … HeMSz6N7Gs
I’m a total noob to the STM32 so any and all help would be appreciated.
I am using your version. I’ve only just found your wav player example though and figured out where I was going wrong and what needed changing.
I’ve made change so that it now at least verifies and can be uploaded to my Maple Leaf Mini however it looks like I need to do a lot more code to alter including my LCD and SD card code (LCD is on I2C1 and SD is on SPI1 btw).
https://mega.nz/#!GYdHDI7a!YOxbKmn3D7Cm … IG4LnGFYlI
// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player.
// Play WAV files from an SD card. Based on the TMRpcm code.
//#include <SdFat.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
//TMRpcm tmrpcm; //Initialise tmrpcm
SdFat sd; //Initialise Sd card
SdFile entry; //SD card file
#define filenameLength 100
char fileName[filenameLength+1]; //Current filename
char sfileName[13];
const int chipSelect = 7; //Sd card chip select pin
// ---------------------------------------------------------------------------------
// set cardType to 'oldCard' if using an old SD card (more than a few years old) or
// to 'newCard' if using a newly-purchase Class-4 card.
int cardType = SPI_FULL_SPEED;
// ---------------------------------------------------------------------------------
#define btnPlay 20 //Play Button
#define btnStop 19 //Stop Button
#define btnUp 18 //Menu Up button
#define btnDown 17 //Menu Down button
#define btnMotor 10 //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect 11 //Motor Control on/off button
#define scrollSpeed 250 //text scroll delay
#define scrollWait 3000 //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;
int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0; //Was there a file playing?
int motorState = 1; //Current motor control state
int finished = 0; //Has the file finished?
int start = 0; //Currently playing flag
int pauseOn = 0; //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0; //Total number of files in directory
int isDir = 0; //Is the current file a directory
unsigned long timeDiff = 0; //button debounce
void setup() {
lcd.init(); //Initialise LCD (16x2 type)
lcd.backlight();
pinMode(chipSelect, OUTPUT); //Setup SD card chipselect pin
if (!sd.begin(chipSelect,cardType)) { //Start SD card and check it's working
lcd_clearline(0);
lcd.print("No SD Card");
return;
}
sd.chdir(); //set SD to root directory
speakerPin = 9; //Set speaker pin
pinMode(btnPlay,INPUT_PULLUP);
digitalWrite(btnPlay,HIGH);
pinMode(btnStop,INPUT_PULLUP);
digitalWrite(btnStop,HIGH);
pinMode(btnUp,INPUT_PULLUP);
digitalWrite(btnUp,HIGH);
pinMode(btnDown,INPUT_PULLUP);
digitalWrite(btnDown,HIGH);
pinMode(btnMotor, INPUT_PULLUP);
digitalWrite(btnMotor,HIGH);
pinMode(btnMselect, INPUT_PULLUP);
digitalWrite(btnMselect, HIGH);//Setup buttons with internal pullup
lcd.clear();
lcd.print("ArduiTape v1.4");
delay(1000);
lcd.clear();
getMaxFile(); //get the total number of files in the directory
seekFile(currentFile); //move to the first file in the directory
lcd_clearline(0);
lcd.print("Ready");
}
void loop(void) {
if(!TMRpcm_playing && wasPlaying == 1) {
stopFile();
//if the file has finished stop trying to play the file
}
if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
scrollTime = millis()+scrollSpeed;
scrollText(fileName);
scrollPos +=1;
if(scrollPos>strlen(fileName)) {
scrollPos=0;
scrollTime=millis()+scrollWait;
scrollText(fileName);
}
}
motorState=digitalRead(btnMotor);
if (millis() - timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count
if(digitalRead(btnPlay) == LOW) {
if(start==0) {
playFile();
delay(200);
} else {
while(digitalRead(btnPlay)==LOW) {
delay(50);
}
TMRpcm_pause();
if (pauseOn == 0) {
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
if(digitalRead(btnMselect)==LOW){
if(mselectState==0) {
lcd_clearline(0);
lcd.print("Motor CTRL On");
mselectState=1;
} else {
lcd_clearline(0);
lcd.print("Motor CTRL Off");
mselectState=0;
}
while(digitalRead(btnMselect)==LOW) {
delay(50);
}
}
if(digitalRead(btnStop)==LOW && start==1) {
stopFile();
delay(200);
} else {
if (digitalRead(btnStop)==LOW && start==0){
//Return to root of the SD card.
sd.chdir(true);
getMaxFile();
currentFile=1;
seekFile(currentFile);
while(digitalRead(btnStop)==LOW) {
//prevent button repeats by waiting until the button is released.
delay(50);
}
}
}
if(digitalRead(btnUp)==LOW && start==0) {
upFile();
while(digitalRead(btnUp)==LOW) {
delay(50); //wait until button is released
}
}
if(digitalRead(btnDown)==LOW && start==0) {
downFile();
while(digitalRead(btnDown)==LOW) {
delay(50);
}
}
if(mselectState==1 && start==1) { //if file is playing and motor control is on then handle current motor state
if(motorState==1 && pauseOn==0) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
}
if(motorState==0 && pauseOn==1) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
}
void upFile() {
//move up a file in the directory
currentFile--;
if(currentFile<1) {
getMaxFile();
currentFile = maxFile;
}
seekFile(currentFile);
}
void downFile() {
//move down a file in the directory
currentFile++;
if(currentFile>maxFile) { currentFile=1; }
seekFile(currentFile);
}
void seekFile(int pos) {
//move to a set position in the directory, store the filename, and display the name on screen.
entry.cwd()->rewind();
for(int i=1;i<=currentFile;i++) {
entry.openNext(entry.cwd(),O_READ);
entry.getName(fileName,filenameLength);
entry.getSFN(sfileName);
if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
entry.close();
}
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
}
void stopFile() {
TMRpcm_stopPlayback();
if(start==1){
lcd_clearline(0);
lcd.print("Stopped");
start=0;
}
}
void playFile() {
if(isDir==1) {
changeDir();
} else {
if(entry.cwd()->exists(sfileName)) {
lcd_clearline(0);
lcd.print("Playing");
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
TMRpcm_play(sfileName);
wasPlaying = 1;
start=1;
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(1);
lcd.print("No File Selected");
}
}
}
void getMaxFile() {
//gets the total files in the current directory and stores the number in maxFile
entry.cwd()->rewind();
maxFile=0;
while(entry.openNext(entry.cwd(),O_READ)) {
entry.getName(fileName,filenameLength);
entry.close();
maxFile++;
}
entry.cwd()->rewind();
}
void lcd_clearline(int l) {
//clear a single line on the LCD
lcd.setCursor(0,l);
lcd.print(" ");
lcd.setCursor(0,l);
}
void changeDir() {
//change directory, if fileName="ROOT" then return to the root directory
//SDFat has no easy way to move up a directory, so returning to root is the easiest way.
//each directory (except the root) must have a file called ROOT (no extension)
if(!strcmp(fileName, "ROOT")) {
sd.chdir(true);
} else {
sd.chdir(fileName, true);
}
getMaxFile();
currentFile=1;
seekFile(currentFile);
}
void scrollText(char* text)
{
if(scrollPos<0) scrollPos=0;
char outtext[16];
for(int i=0;i<16;i++)
{
int p=i+scrollPos;
if(p<strlen(text))
{
outtext[i]=text[p];
} else {
outtext[i]='\0';
}
}
lcd_clearline(1);
lcd.print(outtext);
}
I am using your version. I’ve only just found your wav player example though and figured out where I was going wrong and what needed changing.
I’ve made change so that it now at least verifies and can be uploaded to my Maple Leaf Mini however it looks like I need to do a lot more code to alter including my LCD and SD card code (LCD is on I2C1 and SD is on SPI1 btw).
https://mega.nz/#!GYdHDI7a!YOxbKmn3D7Cm … IG4LnGFYlI
// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player.
// Play WAV files from an SD card. Based on the TMRpcm code.
//#include <SdFat.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
//TMRpcm tmrpcm; //Initialise tmrpcm
SdFat sd; //Initialise Sd card
SdFile entry; //SD card file
#define filenameLength 100
char fileName[filenameLength+1]; //Current filename
char sfileName[13];
const int chipSelect = 7; //Sd card chip select pin
// ---------------------------------------------------------------------------------
// set cardType to 'oldCard' if using an old SD card (more than a few years old) or
// to 'newCard' if using a newly-purchase Class-4 card.
int cardType = SPI_FULL_SPEED;
// ---------------------------------------------------------------------------------
#define btnPlay 20 //Play Button
#define btnStop 19 //Stop Button
#define btnUp 18 //Menu Up button
#define btnDown 17 //Menu Down button
#define btnMotor 10 //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect 11 //Motor Control on/off button
#define scrollSpeed 250 //text scroll delay
#define scrollWait 3000 //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;
int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0; //Was there a file playing?
int motorState = 1; //Current motor control state
int finished = 0; //Has the file finished?
int start = 0; //Currently playing flag
int pauseOn = 0; //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0; //Total number of files in directory
int isDir = 0; //Is the current file a directory
unsigned long timeDiff = 0; //button debounce
void setup() {
lcd.init(); //Initialise LCD (16x2 type)
lcd.backlight();
pinMode(chipSelect, OUTPUT); //Setup SD card chipselect pin
if (!sd.begin(chipSelect,cardType)) { //Start SD card and check it's working
lcd_clearline(0);
lcd.print("No SD Card");
return;
}
sd.chdir(); //set SD to root directory
speakerPin = 9; //Set speaker pin
pinMode(btnPlay,INPUT_PULLUP);
digitalWrite(btnPlay,HIGH);
pinMode(btnStop,INPUT_PULLUP);
digitalWrite(btnStop,HIGH);
pinMode(btnUp,INPUT_PULLUP);
digitalWrite(btnUp,HIGH);
pinMode(btnDown,INPUT_PULLUP);
digitalWrite(btnDown,HIGH);
pinMode(btnMotor, INPUT_PULLUP);
digitalWrite(btnMotor,HIGH);
pinMode(btnMselect, INPUT_PULLUP);
digitalWrite(btnMselect, HIGH);//Setup buttons with internal pullup
lcd.clear();
lcd.print("ArduiTape v1.4");
delay(1000);
lcd.clear();
getMaxFile(); //get the total number of files in the directory
seekFile(currentFile); //move to the first file in the directory
lcd_clearline(0);
lcd.print("Ready");
}
void loop(void) {
if(!TMRpcm_playing && wasPlaying == 1) {
stopFile();
//if the file has finished stop trying to play the file
}
if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
scrollTime = millis()+scrollSpeed;
scrollText(fileName);
scrollPos +=1;
if(scrollPos>strlen(fileName)) {
scrollPos=0;
scrollTime=millis()+scrollWait;
scrollText(fileName);
}
}
motorState=digitalRead(btnMotor);
if (millis() - timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count
if(digitalRead(btnPlay) == LOW) {
if(start==0) {
playFile();
delay(200);
} else {
while(digitalRead(btnPlay)==LOW) {
delay(50);
}
TMRpcm_pause();
if (pauseOn == 0) {
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
if(digitalRead(btnMselect)==LOW){
if(mselectState==0) {
lcd_clearline(0);
lcd.print("Motor CTRL On");
mselectState=1;
} else {
lcd_clearline(0);
lcd.print("Motor CTRL Off");
mselectState=0;
}
while(digitalRead(btnMselect)==LOW) {
delay(50);
}
}
if(digitalRead(btnStop)==LOW && start==1) {
stopFile();
delay(200);
} else {
if (digitalRead(btnStop)==LOW && start==0){
//Return to root of the SD card.
sd.chdir(true);
getMaxFile();
currentFile=1;
seekFile(currentFile);
while(digitalRead(btnStop)==LOW) {
//prevent button repeats by waiting until the button is released.
delay(50);
}
}
}
if(digitalRead(btnUp)==LOW && start==0) {
upFile();
while(digitalRead(btnUp)==LOW) {
delay(50); //wait until button is released
}
}
if(digitalRead(btnDown)==LOW && start==0) {
downFile();
while(digitalRead(btnDown)==LOW) {
delay(50);
}
}
if(mselectState==1 && start==1) { //if file is playing and motor control is on then handle current motor state
if(motorState==1 && pauseOn==0) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
}
if(motorState==0 && pauseOn==1) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
}
void upFile() {
//move up a file in the directory
currentFile--;
if(currentFile<1) {
getMaxFile();
currentFile = maxFile;
}
seekFile(currentFile);
}
void downFile() {
//move down a file in the directory
currentFile++;
if(currentFile>maxFile) { currentFile=1; }
seekFile(currentFile);
}
void seekFile(int pos) {
//move to a set position in the directory, store the filename, and display the name on screen.
entry.cwd()->rewind();
for(int i=1;i<=currentFile;i++) {
entry.openNext(entry.cwd(),O_READ);
entry.getName(fileName,filenameLength);
entry.getSFN(sfileName);
if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
entry.close();
}
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
}
void stopFile() {
TMRpcm_stopPlayback();
if(start==1){
lcd_clearline(0);
lcd.print("Stopped");
start=0;
}
}
void playFile() {
if(isDir==1) {
changeDir();
} else {
if(entry.cwd()->exists(sfileName)) {
lcd_clearline(0);
lcd.print("Playing");
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
TMRpcm_play(sfileName);
wasPlaying = 1;
start=1;
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(1);
lcd.print("No File Selected");
}
}
}
void getMaxFile() {
//gets the total files in the current directory and stores the number in maxFile
entry.cwd()->rewind();
maxFile=0;
while(entry.openNext(entry.cwd(),O_READ)) {
entry.getName(fileName,filenameLength);
entry.close();
maxFile++;
}
entry.cwd()->rewind();
}
void lcd_clearline(int l) {
//clear a single line on the LCD
lcd.setCursor(0,l);
lcd.print(" ");
lcd.setCursor(0,l);
}
void changeDir() {
//change directory, if fileName="ROOT" then return to the root directory
//SDFat has no easy way to move up a directory, so returning to root is the easiest way.
//each directory (except the root) must have a file called ROOT (no extension)
if(!strcmp(fileName, "ROOT")) {
sd.chdir(true);
} else {
sd.chdir(fileName, true);
}
getMaxFile();
currentFile=1;
seekFile(currentFile);
}
void scrollText(char* text)
{
if(scrollPos<0) scrollPos=0;
char outtext[16];
for(int i=0;i<16;i++)
{
int p=i+scrollPos;
if(p<strlen(text))
{
outtext[i]=text[p];
} else {
outtext[i]='\0';
}
}
lcd_clearline(1);
lcd.print(outtext);
}
EDIT: Managed to get everything verifying but can’t get it to recognise the SD Card at the moment. I know it’s connected correctly as a SD.h example can find and read it. I probably need to use a different library.
EDIT
Altered some of the code and the pins and some of the buttons work properly now. I can at least scroll up and down the files.
Can’t get anything to play though yet.
// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player.
// Play WAV files from an SD card. Based on the TMRpcm code.
#include <SPI.h>
#include <SdFat.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
//TMRpcm tmrpcm; //Initialise tmrpcm
SdFat SD; //Initialise Sd card
SdFile entry; //SD card file
#define filenameLength 100
char fileName[filenameLength+1]; //Current filename
char sfileName[13];
const int SD_ChipSelectPin = 8; //Sd card chip select pin
// ---------------------------------------------------------------------------------
//int cardType = SPI_CLOCK_DIV2;
// ---------------------------------------------------------------------------------
#define btnPlay 3 //Play Button
#define btnStop 2 //Stop Button
#define btnUp 1 //Menu Up button
#define btnDown 0 //Menu Down button
#define btnMotor 10 //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect 11 //Motor Control on/off button
#define scrollSpeed 250 //text scroll delay
#define scrollWait 3000 //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;
int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0; //Was there a file playing?
int motorState = 1; //Current motor control state
int finished = 0; //Has the file finished?
int start = 0; //Currently playing flag
int pauseOn = 0; //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0; //Total number of files in directory
int isDir = 0; //Is the current file a directory
unsigned long timeDiff = 0; //button debounce
void setup() {
lcd.init(); //Initialise LCD (16x2 type)
lcd.backlight();
pinMode(SD_ChipSelectPin, OUTPUT); //Setup SD card chipselect pin
if (!SD.begin(SD_ChipSelectPin,SPI_CLOCK_DIV4)) { //Start SD card and check it's working
lcd_clearline(0);
lcd.print("No SD Card");
return;
}
SD.chdir(); //set SD to root directory
speakerPin = 9; //Set speaker pin
pinMode(btnPlay,INPUT_PULLUP);
digitalWrite(btnPlay,HIGH);
pinMode(btnStop,INPUT_PULLUP);
digitalWrite(btnStop,HIGH);
pinMode(btnUp,INPUT_PULLUP);
digitalWrite(btnUp,HIGH);
pinMode(btnDown,INPUT_PULLUP);
digitalWrite(btnDown,HIGH);
//pinMode(btnMotor, INPUT_PULLUP);
//digitalWrite(btnMotor,LOW);
//pinMode(btnMselect, INPUT_PULLUP);
//digitalWrite(btnMselect, LOW);//Setup buttons with internal pullup
lcd.clear();
lcd.print("ArduiTape v1.4");
delay(1000);
lcd.clear();
getMaxFile(); //get the total number of files in the directory
seekFile(currentFile); //move to the first file in the directory
lcd_clearline(0);
lcd.print("Ready");
}
void loop(void) {
if(!TMRpcm_playing && wasPlaying == 1) {
stopFile();
//if the file has finished stop trying to play the file
}
if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
scrollTime = millis()+scrollSpeed;
scrollText(fileName);
scrollPos +=1;
if(scrollPos>strlen(fileName)) {
scrollPos=0;
scrollTime=millis()+scrollWait;
scrollText(fileName);
}
}
motorState=digitalRead(btnMotor);
if (millis() - timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count
if(digitalRead(btnPlay) == LOW) {
if(start==0) {
playFile();
delay(200);
} else {
while(digitalRead(btnPlay)==LOW) {
delay(50);
}
TMRpcm_pause();
if (pauseOn == 0) {
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
/*if(digitalRead(btnMselect)==LOW){
if(mselectState==0) {
lcd_clearline(0);
lcd.print("Motor CTRL On");
mselectState=1;
} else {
lcd_clearline(0);
lcd.print("Motor CTRL Off");
mselectState=0;
}
while(digitalRead(btnMselect)==LOW) {
delay(50);
}
}*/
if(digitalRead(btnStop)==LOW && start==1) {
stopFile();
delay(200);
} else {
if (digitalRead(btnStop)==LOW && start==0){
//Return to root of the SD card.
SD.chdir(true);
getMaxFile();
currentFile=1;
seekFile(currentFile);
while(digitalRead(btnStop)==LOW) {
//prevent button repeats by waiting until the button is released.
delay(50);
}
}
}
if(digitalRead(btnUp)==LOW && start==0) {
upFile();
while(digitalRead(btnUp)==LOW) {
delay(50); //wait until button is released
}
}
if(digitalRead(btnDown)==LOW && start==0) {
downFile();
while(digitalRead(btnDown)==LOW) {
delay(50);
}
}
/*if(mselectState==1 && start==1) { //if file is playing and motor control is on then handle current motor state
if(motorState==1 && pauseOn==0) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
}
if(motorState==0 && pauseOn==1) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}*/
}
}
void upFile() {
//move up a file in the directory
currentFile--;
if(currentFile<1) {
getMaxFile();
currentFile = maxFile;
}
seekFile(currentFile);
}
void downFile() {
//move down a file in the directory
currentFile++;
if(currentFile>maxFile) { currentFile=1; }
seekFile(currentFile);
}
void seekFile(int pos) {
//move to a set position in the directory, store the filename, and display the name on screen.
entry.cwd()->rewind();
for(int i=1;i<=currentFile;i++) {
entry.openNext(entry.cwd(),O_READ);
entry.getName(fileName,filenameLength);
entry.getSFN(sfileName);
if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
entry.close();
}
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
}
void stopFile() {
TMRpcm_stopPlayback();
if(start==1){
lcd_clearline(0);
lcd.print("Stopped");
start=0;
}
}
void playFile() {
if(isDir==1) {
changeDir();
} else {
if(entry.cwd()->exists(sfileName)) {
lcd_clearline(0);
lcd.print("Playing");
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
TMRpcm_play(sfileName);
wasPlaying = 1;
start=1;
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(1);
lcd.print("No File Selected");
}
}
}
void getMaxFile() {
//gets the total files in the current directory and stores the number in maxFile
entry.cwd()->rewind();
maxFile=0;
while(entry.openNext(entry.cwd(),O_READ)) {
entry.getName(fileName,filenameLength);
entry.close();
maxFile++;
}
entry.cwd()->rewind();
}
void lcd_clearline(int l) {
//clear a single line on the LCD
lcd.setCursor(0,l);
lcd.print(" ");
lcd.setCursor(0,l);
}
void changeDir() {
//change directory, if fileName="ROOT" then return to the root directory
//SDFat has no easy way to move up a directory, so returning to root is the easiest way.
//each directory (except the root) must have a file called ROOT (no extension)
if(!strcmp(fileName, "ROOT")) {
SD.chdir(true);
} else {
SD.chdir(fileName, true);
}
getMaxFile();
currentFile=1;
seekFile(currentFile);
}
void scrollText(char* text)
{
if(scrollPos<0) scrollPos=0;
char outtext[16];
for(int i=0;i<16;i++)
{
int p=i+scrollPos;
if(p<strlen(text))
{
outtext[i]=text[p];
} else {
outtext[i]='\0';
}
}
lcd_clearline(1);
lcd.print(outtext);
}
EDIT
Altered some of the code and the pins and some of the buttons work properly now. I can at least scroll up and down the files.
Can’t get anything to play though yet.
// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player.
// Play WAV files from an SD card. Based on the TMRpcm code.
#include <SPI.h>
#include <SdFat.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
//TMRpcm tmrpcm; //Initialise tmrpcm
SdFat SD; //Initialise Sd card
SdFile entry; //SD card file
#define filenameLength 100
char fileName[filenameLength+1]; //Current filename
char sfileName[13];
const int SD_ChipSelectPin = 8; //Sd card chip select pin
// ---------------------------------------------------------------------------------
//int cardType = SPI_CLOCK_DIV2;
// ---------------------------------------------------------------------------------
#define btnPlay 3 //Play Button
#define btnStop 2 //Stop Button
#define btnUp 1 //Menu Up button
#define btnDown 0 //Menu Down button
#define btnMotor 10 //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect 11 //Motor Control on/off button
#define scrollSpeed 250 //text scroll delay
#define scrollWait 3000 //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;
int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0; //Was there a file playing?
int motorState = 1; //Current motor control state
int finished = 0; //Has the file finished?
int start = 0; //Currently playing flag
int pauseOn = 0; //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0; //Total number of files in directory
int isDir = 0; //Is the current file a directory
unsigned long timeDiff = 0; //button debounce
void setup() {
lcd.init(); //Initialise LCD (16x2 type)
lcd.backlight();
pinMode(SD_ChipSelectPin, OUTPUT); //Setup SD card chipselect pin
if (!SD.begin(SD_ChipSelectPin,SPI_CLOCK_DIV4)) { //Start SD card and check it's working
lcd_clearline(0);
lcd.print("No SD Card");
return;
}
SD.chdir(); //set SD to root directory
speakerPin = 9; //Set speaker pin
pinMode(btnPlay,INPUT_PULLUP);
digitalWrite(btnPlay,HIGH);
pinMode(btnStop,INPUT_PULLUP);
digitalWrite(btnStop,HIGH);
pinMode(btnUp,INPUT_PULLUP);
digitalWrite(btnUp,HIGH);
pinMode(btnDown,INPUT_PULLUP);
digitalWrite(btnDown,HIGH);
//pinMode(btnMotor, INPUT_PULLUP);
//digitalWrite(btnMotor,LOW);
//pinMode(btnMselect, INPUT_PULLUP);
//digitalWrite(btnMselect, LOW);//Setup buttons with internal pullup
lcd.clear();
lcd.print("ArduiTape v1.4");
delay(1000);
lcd.clear();
getMaxFile(); //get the total number of files in the directory
seekFile(currentFile); //move to the first file in the directory
lcd_clearline(0);
lcd.print("Ready");
}
void loop(void) {
if(!TMRpcm_playing && wasPlaying == 1) {
stopFile();
//if the file has finished stop trying to play the file
}
if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
scrollTime = millis()+scrollSpeed;
scrollText(fileName);
scrollPos +=1;
if(scrollPos>strlen(fileName)) {
scrollPos=0;
scrollTime=millis()+scrollWait;
scrollText(fileName);
}
}
motorState=digitalRead(btnMotor);
if (millis() - timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count
if(digitalRead(btnPlay) == LOW) {
if(start==0) {
playFile();
delay(200);
} else {
while(digitalRead(btnPlay)==LOW) {
delay(50);
}
TMRpcm_pause();
if (pauseOn == 0) {
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
/*if(digitalRead(btnMselect)==LOW){
if(mselectState==0) {
lcd_clearline(0);
lcd.print("Motor CTRL On");
mselectState=1;
} else {
lcd_clearline(0);
lcd.print("Motor CTRL Off");
mselectState=0;
}
while(digitalRead(btnMselect)==LOW) {
delay(50);
}
}*/
if(digitalRead(btnStop)==LOW && start==1) {
stopFile();
delay(200);
} else {
if (digitalRead(btnStop)==LOW && start==0){
//Return to root of the SD card.
SD.chdir(true);
getMaxFile();
currentFile=1;
seekFile(currentFile);
while(digitalRead(btnStop)==LOW) {
//prevent button repeats by waiting until the button is released.
delay(50);
}
}
}
if(digitalRead(btnUp)==LOW && start==0) {
upFile();
while(digitalRead(btnUp)==LOW) {
delay(50); //wait until button is released
}
}
if(digitalRead(btnDown)==LOW && start==0) {
downFile();
while(digitalRead(btnDown)==LOW) {
delay(50);
}
}
/*if(mselectState==1 && start==1) { //if file is playing and motor control is on then handle current motor state
if(motorState==1 && pauseOn==0) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
}
if(motorState==0 && pauseOn==1) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}*/
}
}
void upFile() {
//move up a file in the directory
currentFile--;
if(currentFile<1) {
getMaxFile();
currentFile = maxFile;
}
seekFile(currentFile);
}
void downFile() {
//move down a file in the directory
currentFile++;
if(currentFile>maxFile) { currentFile=1; }
seekFile(currentFile);
}
void seekFile(int pos) {
//move to a set position in the directory, store the filename, and display the name on screen.
entry.cwd()->rewind();
for(int i=1;i<=currentFile;i++) {
entry.openNext(entry.cwd(),O_READ);
entry.getName(fileName,filenameLength);
entry.getSFN(sfileName);
if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
entry.close();
}
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
}
void stopFile() {
TMRpcm_stopPlayback();
if(start==1){
lcd_clearline(0);
lcd.print("Stopped");
start=0;
}
}
void playFile() {
if(isDir==1) {
changeDir();
} else {
if(entry.cwd()->exists(sfileName)) {
lcd_clearline(0);
lcd.print("Playing");
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
TMRpcm_play(sfileName);
wasPlaying = 1;
start=1;
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(1);
lcd.print("No File Selected");
}
}
}
void getMaxFile() {
//gets the total files in the current directory and stores the number in maxFile
entry.cwd()->rewind();
maxFile=0;
while(entry.openNext(entry.cwd(),O_READ)) {
entry.getName(fileName,filenameLength);
entry.close();
maxFile++;
}
entry.cwd()->rewind();
}
void lcd_clearline(int l) {
//clear a single line on the LCD
lcd.setCursor(0,l);
lcd.print(" ");
lcd.setCursor(0,l);
}
void changeDir() {
//change directory, if fileName="ROOT" then return to the root directory
//SDFat has no easy way to move up a directory, so returning to root is the easiest way.
//each directory (except the root) must have a file called ROOT (no extension)
if(!strcmp(fileName, "ROOT")) {
SD.chdir(true);
} else {
SD.chdir(fileName, true);
}
getMaxFile();
currentFile=1;
seekFile(currentFile);
}
void scrollText(char* text)
{
if(scrollPos<0) scrollPos=0;
char outtext[16];
for(int i=0;i<16;i++)
{
int p=i+scrollPos;
if(p<strlen(text))
{
outtext[i]=text[p];
} else {
outtext[i]='\0';
}
}
lcd_clearline(1);
lcd.print(outtext);
}
exit status 1
‘swap’ was not declared in this scope
I do have a ILI9341 though so I could try that. Just need to figure out what to wire to where.
exit status 1
‘swap’ was not declared in this scope
I do have a ILI9341 though so I could try that. Just need to figure out what to wire to where.
The sound is terrible for all the various wavs I’ve tried but it’s a start. Making progress and that’s what counts.
The sound is terrible for all the various wavs I’ve tried but it’s a start. Making progress and that’s what counts.
I’m just seeing if I can get your player working on the ILI9341. Got the graphics working but no sound as of yet although it may be my SD card at the moment.
I’m just seeing if I can get your player working on the ILI9341. Got the graphics working but no sound as of yet although it may be my SD card at the moment.
https://hackaday.io/project/2876-arduitape
If so, I was reading you rather take a square wave out.
For that it would be better if you skip all the PWM out stuff, and just set a pin high or low depending on whether the sample value is bigger or smaller than a number. That would be much easier to implement at all levels, and would give you a perfectly square wave.
The latest version of all the projects I’m trying to convert can be found here. http://arduitapemarkii.blogspot.co.uk/
The latest version of all the projects I’m trying to convert can be found here. http://arduitapemarkii.blogspot.co.uk/
At the moment I’m having issues anyway.
I uploaded my Maple Mini to Bootloader 2 and SDFat has stopped working and isn’t detecting the SD card.
I’ve just made sure I have your latest SDFat library and that’s throwing an error.
At the moment I’m having issues anyway.
I uploaded my Maple Mini to Bootloader 2 and SDFat has stopped working and isn’t detecting the SD card.
I’ve just made sure I have your latest SDFat library and that’s throwing an error.
The bootloader should not affect in anyway the SD card. The bootloader handles full control to the code uploaded. Did you update anything else?
The bootloader should not affect in anyway the SD card. The bootloader handles full control to the code uploaded. Did you update anything else?
I used the example SDInfo in SDFat and got the following.
SdFat version: 20150321
Assuming the SD is the only SPI device.
Edit DISABLE_CHIP_SELECT to disable another device.
Assuming the SD chip select pin is: 8
Edit SD_CHIP_SELECT to change the SD chip select pin.
type any character to start
cardBegin failed
SD errorCode: 0X1
SD errorData: 0X0
type any character to start
I used the example SDInfo in SDFat and got the following.
SdFat version: 20150321
Assuming the SD is the only SPI device.
Edit DISABLE_CHIP_SELECT to disable another device.
Assuming the SD chip select pin is: 8
Edit SD_CHIP_SELECT to change the SD chip select pin.
type any character to start
cardBegin failed
SD errorCode: 0X1
SD errorData: 0X0
type any character to start
Double-check the initialization part, make a diff between those two cases.
Okay. After a complete uninstall I get the following when using SDInfo example so SDFat is working with my SD card and reader. Now I just have to figure out why it hates me in the code.
SdFat version: 20150321
Assuming the SD is the only SPI device.
Edit DISABLE_CHIP_SELECT to disable another device.
Assuming the SD chip select pin is: 8
Edit SD_CHIP_SELECT to change the SD chip select pin.
type any character to start
init time: 0 ms
Card type: SDHC
Manufacturer ID: 0X1B
OEM ID: SM
Product: 00000
Version: 1.0
Serial number: 0X3F359301
Manufacturing date: 3/2011
cardSize: 8064.60 MB (MB = 1,000,000 bytes)
flashEraseSize: 128 blocks
eraseSingleBlock: true
OCR: 0XC0FF8000
SD Partition Table
part,boot,type,start,length
1,0X0,0XC,8192,15742976
2,0X0,0X0,0,0
3,0X0,0X0,0,0
4,0X0,0X0,0,0
Volume is FAT32
blocksPerCluster: 64
clusterCount: 245856
freeClusters: 186105
freeSpace: 6098.29 MB (MB = 1,000,000 bytes)
fatStartBlock: 12542
fatCount: 2
blocksPerFat: 1921
rootDirStart: 2
dataStartBlock: 16384
type any character to start
Some pins on the STM32 are 5V tollerent but I dont know if MISO is one of them.
the level converter on the ‘interface’ pcb is expecting arduino levels of 0 -> 5v is converting a 3v3 signal down
by approx 1/2-2/3’s equals marginal or a maybe state;
particularly if its using resistors, not sure if that’s also true for a ‘real ic/tr/fet’ level converter
the 3v3 data returning from the card via the converter is quite happily becoming 0->5v ; those the stm32 inputs can handle.
as roger says needs checking (time crossed post)
hth
stephen
Good point about the converter, possibly trying to convert the 3.3V logic signals from the STM32 as if they were 5V.
I guess it will depend on whether the level converter is active or passive.
Either way, using a SD adaptor with level converters is not the correct way to connect 3.3V logic to 3.3V logic and is likely to lead to problems.
http://www.ebay.co.uk/itm/191736124794? … EBIDX%3AIT
it is 3v3 ONLY, no regulator and has smt resistors, easy enough to lose them or just short circuit if required
at £0.60 for 2, i got 2 lots
have it wired in, not tried as yet.
stephen
- coos_wav_player_setModule2_ILI9341.zip
- (4.44 KiB) Downloaded 29 times
let me conclude: your SD card and adapter are working fine using the SD info demo sketch but not working with your software, right?
Is your software the one you posted?
If yes, then it is a potential problem that there is no setModule() before initializing the SD card with SD.begin(…).
Also, you are using both SPI ports, but only one SPI port – for the display – is initialized in the setup() function. The one used by the SD card (SPI 1) seems to be initialized in another place, undefined time. I would suggest to init the card also in setup(). For testing, you could use the Sd card info example part which is working in the standalone version in setup(), too.
Alternatively, you could try to map the display to SPI 1 and the Sd card to SPI 2 without having to use the setModule() function. For this, use the latest SdFat beta from greiman, because it can be set to use SPI_2. I am using his version directly from github and it works:
SdFat sd(2); // use SPI 2 to access SD card
let me conclude: your SD card and adapter are working fine using the SD info demo sketch but not working with your software, right?
Is your software the one you posted?
If yes, then it is a potential problem that there is no setModule() before initializing the SD card with SD.begin(…).
Also, you are using both SPI ports, but only one SPI port – for the display – is initialized in the setup() function. The one used by the SD card (SPI 1) seems to be initialized in another place, undefined time. I would suggest to init the card also in setup(). For testing, you could use the Sd card info example part which is working in the standalone version in setup(), too.
Alternatively, you could try to map the display to SPI 1 and the Sd card to SPI 2 without having to use the setModule() function. For this, use the latest SdFat beta from greiman, because it can be set to use SPI_2. I am using his version directly from github and it works:
SdFat sd(2); // use SPI 2 to access SD card
The way the example is set, should be using SPI1 for the card and SPI2 for the screen:
...
SPI.setModule(2);
tft.begin();
...
let me conclude: your SD card and adapter are working fine using the SD info demo sketch but not working with your software, right?
Is your software the one you posted?
If yes, then it is a potential problem that there is no setModule() before initializing the SD card with SD.begin(…).
Also, you are using both SPI ports, but only one SPI port – for the display – is initialized in the setup() function. The one used by the SD card (SPI 1) seems to be initialized in another place, undefined time. I would suggest to init the card also in setup(). For testing, you could use the Sd card info example part which is working in the standalone version in setup(), too.
Alternatively, you could try to map the display to SPI 1 and the Sd card to SPI 2 without having to use the setModule() function. For this, use the latest SdFat beta from greiman, because it can be set to use SPI_2. I am using his version directly from github and it works:
SdFat sd(2); // use SPI 2 to access SD card
However even though I don’t have a board to test on I will be carrying on with trying to convert the TimerOne library calls from TZXDuino/CASDuino to HardwareTimer to see if I can at least get them to verify.
EDIT:
For those interested the codes I’m trying to translate from Arduino programmes to SMT32 are the following
MapleTape which I have already started to alter so that is works on Maple Mini and is compiling but due to the SD and now the Mini issues isn’t working as of yet.
https://mega.nz/#!vVNlDTwT!ro5dbqHEGdfv … ew9tCo7SHo
CASMaple I haven’t altered yet and is still in it’s Arduino working form
https://mega.nz/#!bUkhBCzB!lvNOBlQaIp1a … Yk47Nxus2w
TZXMaple is the same story as CASMaple but because the code is so similar if I get one working then the other one should work too.
https://mega.nz/#!6Md1hTDI!5rk7c6fWwChG … dioJGyDB0E
However even though I don’t have a board to test on I will be carrying on with trying to convert the TimerOne library calls from TZXDuino/CASDuino to HardwareTimer to see if I can at least get them to verify.
A note of advice, when working on my port of the TMRPCM library I found HardwareTimer provides only the most basic features of the timers, but there are some core functions that can do more, and then all registers and bits are defined in the core, so for anything advanced other than generate PWM output and trigger an interrupt, you should go and have a good read on the stm32f1 reference manual and learn how the timer peripherals work, then you can go setting registers as you need.
BTW, there is a thread called something like “My maple mini just let go of the magic smoke” that shows some issues with the hardware, and some could be corrected. I started it when I thought I had burnt the MCU, and found out it was just the voltage regulator and a diode and was able to replace them.
Timer one is similar to HardwareTimer for the Maple Mini.
Best example of what it does can be found here http://playground.arduino.cc/Code/Timer1 Although we use Paul Stoffregen’s modified TimerOne library which is faster and works on more boards.
We use it to help generate the sound pulses required to convert TZX/TAP/CAS files into noise that the computers can detect.
SPIClass tftSPI(2);
We use it to help generate the sound pulses required to convert TZX/TAP/CAS files into noise that the computers can detect.
Timer one is similar to HardwareTimer for the Maple Mini.
Best example of what it does can be found here http://playground.arduino.cc/Code/Timer1 Although we use Paul Stoffregen’s modified TimerOne library which is faster and works on more boards.
We use it to help generate the sound pulses required to convert TZX/TAP/CAS files into noise that the computers can detect.
There’s no reason as to why I can’t.
Good idea.
That’s why I need other people to do the thinking for me. I get so stuck in my own ways I fail to see the other possibilities.
The COM port appears after the first program has been flashed, so that serial USB is active.
The COM port appears after the first program has been flashed, so that serial USB is active.
Sadly it looks like my alterations to my projects code haven’t worked and I haven’t been able to get the wav player to play wavs either yet but time to crack on.
TZXMaple and CASMaple don’t play any sound and I think that’s due to the timer. The Arduino code used Timer1 Library and I’ve tried to change the functions to HardwareTimer library and they just aren’t the same.
MapleTape zip https://mega.nz/#!vVNlDTwT!ro5dbqHEGdfv … ew9tCo7SHo
CASMaple zip (Plays MSX .CAS files) https://mega.nz/#!bUkhBCzB!lvNOBlQaIp1a … Yk47Nxus2w
TZXMaple zip (plays ZX Spectrum TRZ/TAP files) https://mega.nz/#!6Md1hTDI!5rk7c6fWwChG … dioJGyDB0E
I also converted CoOS Wav Player setModule2 to work with an ILI9341 changing only the TFT code but it doesn’t seem to be able to search for .WAV files on the SD card as it did in the original even though I didn’t actually mess with that part of the coding.
TZXMaple and CASMaple don’t play any sound and I think that’s due to the timer. The Arduino code used Timer1 Library and I’ve tried to change the functions to HardwareTimer library and they just aren’t the same.
MapleTape zip https://mega.nz/#!vVNlDTwT!ro5dbqHEGdfv … ew9tCo7SHo
CASMaple zip (Plays MSX .CAS files) https://mega.nz/#!bUkhBCzB!lvNOBlQaIp1a … Yk47Nxus2w
TZXMaple zip (plays ZX Spectrum TRZ/TAP files) https://mega.nz/#!6Md1hTDI!5rk7c6fWwChG … dioJGyDB0E
I also converted CoOS Wav Player setModule2 to work with an ILI9341 changing only the TFT code but it doesn’t seem to be able to search for .WAV files on the SD card as it did in the original even though I didn’t actually mess with that part of the coding.
Okay changed to the following.
if(TMRpcm_sample_RATE > 48000 ){ TMRpcm_sample_RATE = 48000;
#if defined (debug)
Serial.print("TMRpcm_sample RATE TOO HIGH: ");
Serial.println(TMRpcm_sample_RATE);
#endif
}
if(TMRpcm_sample_RATE == 48000 ){
resolution = 260; //250
repetition = 5;
}
else if(TMRpcm_sample_RATE == 44100 ){
resolution = 285; //272
repetition = 5;
}
else if(TMRpcm_sample_RATE == 22050 ){
resolution = 285; //272
repetition = 11;
}
else if(TMRpcm_sample_RATE == 8000 ){
resolution = 260; //250
repetition = 35;
}
else {
resolution = 72000000/TMRpcm_sample_RATE;
repetition = resolution/256;
resolution = resolution/repetition;
repetition = repetition-1;
}
Okay changed to the following.
if(TMRpcm_sample_RATE > 48000 ){ TMRpcm_sample_RATE = 48000;
#if defined (debug)
Serial.print("TMRpcm_sample RATE TOO HIGH: ");
Serial.println(TMRpcm_sample_RATE);
#endif
}
if(TMRpcm_sample_RATE == 48000 ){
resolution = 260; //250
repetition = 5;
}
else if(TMRpcm_sample_RATE == 44100 ){
resolution = 285; //272
repetition = 5;
}
else if(TMRpcm_sample_RATE == 22050 ){
resolution = 285; //272
repetition = 11;
}
else if(TMRpcm_sample_RATE == 8000 ){
resolution = 260; //250
repetition = 35;
}
else {
resolution = 72000000/TMRpcm_sample_RATE;
repetition = resolution/256;
resolution = resolution/repetition;
repetition = repetition-1;
}
That seems to have done the trick.
Still some bugs (doesn’t stop at the end of the file) but it plays .WAV files. Now to see if the computers will recognise them.
That seems to have done the trick.
Still some bugs (doesn’t stop at the end of the file) but it plays .WAV files. Now to see if the computers will recognise them.


I used to use the EEPROM on the arduino to store a setting but obviously not entirely practical on the Mini so instead of writing the setting to an EEPROM I want to write it to a file on the SDCard.
I can get the setting writing to a file but each time the setting is changed it adds another line to the file whereas I would prefer it to overwrite. Then I’m struggling to read the file and set that as an integer.
EG
There is a choice of BAUDRATE 1200, 2400, 3600. Once you choose that it will save 1200,2400,3600 to a file on the SD Card. When the board is switched on I want to read that file and for BAUDRATE to equal what it’s just read.
Great work on the library Victor.
Another bug I think I’ve found is that when it reaches the end of the WAV file TMRpcm doesn’t stop
Another bug I think I’ve found is that when it reaches the end of the WAV file TMRpcm doesn’t stop
Another bug I think I’ve found is that when it reaches the end of the WAV file TMRpcm doesn’t stop
// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player.
// Play WAV files from an SD card. Based on the TMRpcm code.
//#include <SdFat.h>
#include <SPI.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
//TMRpcm tmrpcm; //Initialise tmrpcm
SdFat SD; //Initialise Sd card
SdFile entry;
#define filenameLength 100
char fileName[filenameLength+1]; //Current filename
char sfileName[13];
const int SD_ChipSelectPin = 8; //Sd card chip select pin
// ---------------------------------------------------------------------------------
//int cardType = SPI_CLOCK_DIV2;
// ---------------------------------------------------------------------------------
#define btnPlay 20 //Play Button
#define btnStop 19 //Stop Button
#define btnUp 18 //Menu Up button
#define btnDown 17 //Menu Down button
#define btnMotor 21 //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect 22 //Motor Control on/off button
#define scrollSpeed 250 //text scroll delay
#define scrollWait 3000 //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;
int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0; //Was there a file playing?
int motorState = 1; //Current motor control state
int finished = 0; //Has the file finished?
int start = 0; //Currently playing flag
int pauseOn = 0; //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0; //Total number of files in directory
int isDir = 0; //Is the current file a directory
unsigned long timeDiff = 0; //button debounce
void setup() {
lcd.init(); //Initialise LCD (16x2 type)
lcd.backlight();
pinMode(SD_ChipSelectPin, OUTPUT); //Setup SD card chipselect pin
SPI.setModule(1);
if (!SD.begin(SD_ChipSelectPin,SPI_CLOCK_DIV2)) { //Start SD card and check it's working
lcd_clearline(0);
lcd.print("No SD Card");
return;
}
SD.chdir(); //set SD to root directory
speakerPin = 27; //TMR1 CH1
speakerPin2 = 26; //TMR1 CH2
pinMode(btnPlay,INPUT_PULLUP);
digitalWrite(btnPlay,HIGH);
pinMode(btnStop,INPUT_PULLUP);
digitalWrite(btnStop,HIGH);
pinMode(btnUp,INPUT_PULLUP);
digitalWrite(btnUp,HIGH);
pinMode(btnDown,INPUT_PULLUP);
digitalWrite(btnDown,HIGH);
pinMode(btnMotor, INPUT_PULLUP);
digitalWrite(btnMotor,HIGH);
pinMode(btnMselect, INPUT_PULLUP);
digitalWrite(btnMselect, HIGH);//Setup buttons with internal pullup
lcd.clear();
lcd.print("MapleTape 0.2a");
delay(1000);
lcd.clear();
getMaxFile(); //get the total number of files in the directory
seekFile(currentFile); //move to the first file in the directory
lcd_clearline(0);
lcd.print("Ready..");
}
void loop(void) {
if (TMRpcm_playing) {
TMRpcm_buffer_load();
}
if(!TMRpcm_playing && wasPlaying == 1) {
stopFile();
//Serial.println("Stopped");
//if the file has finished stop trying to play the file
}
if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
scrollTime = millis()+scrollSpeed;
scrollText(fileName);
scrollPos +=1;
if(scrollPos>strlen(fileName)) {
scrollPos=0;
scrollTime=millis()+scrollWait;
scrollText(fileName);
}
}
motorState=digitalRead(btnMotor);
if (millis() - timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count
if(digitalRead(btnPlay) == LOW) {
if(start==0) {
playFile();
delay(200);
} else {
while(digitalRead(btnPlay)==LOW) {
delay(50);
}
TMRpcm_pause();
if (pauseOn == 0) {
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
if(digitalRead(btnMselect)==LOW){
if(mselectState==0) {
lcd_clearline(0);
lcd.print("Motor CTRL On");
mselectState=1;
} else {
lcd_clearline(0);
lcd.print("Motor CTRL Off");
mselectState=0;
}
while(digitalRead(btnMselect)==LOW) {
delay(50);
}
}
if(digitalRead(btnStop)==LOW && start==1) {
stopFile();
delay(200);
} else {
if (digitalRead(btnStop)==LOW && start==0){
//Return to root of the SD card.
SD.chdir(true);
getMaxFile();
currentFile=1;
seekFile(currentFile);
while(digitalRead(btnStop)==LOW) {
//prevent button repeats by waiting until the button is released.
delay(50);
}
}
}
if(digitalRead(btnUp)==LOW && start==0) {
upFile();
while(digitalRead(btnUp)==LOW) {
delay(50); //wait until button is released
}
}
if(digitalRead(btnDown)==LOW && start==0) {
downFile();
while(digitalRead(btnDown)==LOW) {
delay(50);
}
}
if(mselectState==1 && start==1) { //if file is playing and motor control is on then handle current motor state
if(motorState==1 && pauseOn==0) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
}
if(motorState==0 && pauseOn==1) {
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Playing");
pauseOn = 0;
}
}
}
}
void upFile() {
//move up a file in the directory
currentFile--;
if(currentFile<1) {
getMaxFile();
currentFile = maxFile;
}
seekFile(currentFile);
}
void downFile() {
//move down a file in the directory
currentFile++;
if(currentFile>maxFile) { currentFile=1; }
seekFile(currentFile);
}
void seekFile(int pos) {
//move to a set position in the directory, store the filename, and display the name on screen.
entry.cwd()->rewind();
for(int i=1;i<=currentFile;i++) {
entry.openNext(entry.cwd(),O_READ);
entry.getName(fileName,filenameLength);
entry.getSFN(sfileName);
if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
entry.close();
}
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
}
void stopFile() {
TMRpcm_stopPlayback();
if(start==1){
lcd_clearline(0);
lcd.print("Stopped");
start=0;
}
}
void playFile() {
if(isDir==1) {
changeDir();
} else {
if(entry.cwd()->exists(sfileName)) {
lcd_clearline(0);
lcd.print("Playing");
lcd_clearline(1);
scrollPos=0;
scrollText(fileName);
TMRpcm_play(sfileName);
wasPlaying = 1;
start=1;
TMRpcm_pause();
lcd_clearline(0);
lcd.print("Paused");
pauseOn = 1;
} else {
lcd_clearline(1);
lcd.print("No File Selected");
}
}
}
void getMaxFile() {
//gets the total files in the current directory and stores the number in maxFile
entry.cwd()->rewind();
maxFile=0;
while(entry.openNext(entry.cwd(),O_READ)) {
entry.getName(fileName,filenameLength);
entry.close();
maxFile++;
}
entry.cwd()->rewind();
}
void lcd_clearline(int l) {
//clear a single line on the LCD
lcd.setCursor(0,l);
lcd.print(" ");
lcd.setCursor(0,l);
}
void changeDir() {
//change directory, if fileName="ROOT" then return to the root directory
//SDFat has no easy way to move up a directory, so returning to root is the easiest way.
//each directory (except the root) must have a file called ROOT (no extension)
if(!strcmp(fileName, "ROOT")) {
SD.chdir(true);
} else {
SD.chdir(fileName, true);
}
getMaxFile();
currentFile=1;
seekFile(currentFile);
}
void scrollText(char* text)
{
if(scrollPos<0) scrollPos=0;
char outtext[16];
for(int i=0;i<16;i++)
{
int p=i+scrollPos;
if(p<strlen(text))
{
outtext[i]=text[p];
} else {
outtext[i]='\0';
}
}
lcd_clearline(1);
lcd.print(outtext);
}
It’s a longshot that I’ll be able to get help without showing the whole code for context or a lengthy explanation of what the code does but I thought I’d try.
int ReadWord(unsigned long pos) {
//Read 2 bytes from the file, and move file position on two if successful
byte out[2];
int i=0;
if(entry.seekSet(pos)) {
i = entry.read(out,2);
if(i==2) bytesRead += 2;
}
outWord = word(out[1],out[0]);
return i;
}
int ReadLong(unsigned long pos) {
//Read 3 bytes from the file, and move file position on three if successful
byte out[3];
int i=0;
if(entry.seekSet(pos)) {
i = entry.read(out,3);
if(i==3) bytesRead += 3;
}
outLong = (word(out[2],out[1]) << 8) | out[0];
return i;
}
int ReadDword(unsigned long pos) {
//Read 4 bytes from the file, and move file position on four if successful
byte out[4];
int i=0;
if(entry.seekSet(pos)) {
i = entry.read(out,4);
if(i==4) bytesRead += 4;
}
outLong = (word(out[3],out[2]) << 16) | word(out[1],out[0]);
return i;
}
The Maple Mini is processing microseconds too quickly.
It’s supposed to produce an initial pulse of 619 microseconds and its producing one of just over 500.
Unfortunately with the TZXMaple code timing really is everything and it just seems to be running way too fast. Oddly though the length of file it plays is correct but the wavelength of the pulses is too short. The same processing works perfectly well now on the Nano.
- TZXMaple_v0.1a.zip
- (9.42 KiB) Downloaded 16 times
The Maple Mini is processing microseconds too quickly.
It’s supposed to produce an initial pulse of 619 microseconds and its producing one of just over 500.
Unfortunately with the TZXMaple code timing really is everything and it just seems to be running way too fast. Oddly though the length of file it plays is correct but the wavelength of the pulses is too short. The same processing works perfectly well now on the Nano.
TZXMaple_v0.1a.zip