I'm working on restoring an old arcade board that had a broken main MCU. Instead, I soldered the Mega 2560 on a breadboard and wrote code to control all the functions on the board.
All that was left was the sound.
The original MCU (H8S/2390) reads data from the EPROM and transmits it to the audio power amplifier.
I'm trying to do the same.
To read EPROM I use this code (and it works fine):
https://forum.arduino.cc/t/how-to-extract-read-the-code-stored-in-am27c256-120dc-eprom-using-arduino/621926/19
unsigned int
Address;
//PORTF
//can use PINF to read data bus
const byte pinD0 = A0;
const byte pinD1 = A1;
const byte pinD2 = A2;
const byte pinD3 = A3;
const byte pinD4 = A4;
const byte pinD5 = A5;
const byte pinD6 = A6;
const byte pinD7 = A7;
const byte pinA0 = 2;
const byte pinA1 = 3;
const byte pinA2 = 4;
const byte pinA3 = 5;
const byte pinA4 = 6;
const byte pinA5 = 7;
const byte pinA6 = 8;
const byte pinA7 = 9;
const byte pinA8 = 10;
const byte pinA9 = 11;
const byte pinA10 = 12;
const byte pinA11 = 13;
const byte pinA12 = 21;
const byte pinA13 = 20;
const byte pinA14 = 19;
const byte pinG = 18;
const byte grData[] =
{
pinD0, pinD1, pinD2, pinD3,
pinD4, pinD5, pinD6, pinD7
};
const byte grAddr[] =
{
pinA0, pinA1, pinA2, pinA3,
pinA4, pinA5, pinA6, pinA7,
pinA8, pinA9, pinA10, pinA11,
pinA12, pinA13, pinA14
};
char szStr[16];
void setup( void )
{
unsigned char
idx,
databyte;
Serial.begin(115200);
pinMode( pinG, OUTPUT );
digitalWrite( pinG, HIGH );
for( int i=0; i<8; i++ )
pinMode( grData[i], INPUT_PULLUP );
for( int i=0; i<15; i++ )
pinMode( grAddr[i], OUTPUT );
//0bx111 1111 1111 1111
Address = 0x0000;
idx = 0;
do
{
SetAddress( Address );
digitalWrite( pinG, LOW );
databyte = PINF;
digitalWrite( pinG, HIGH );
sprintf( szStr, "%02X ", databyte );
Serial.print( szStr );
idx++;
if( idx == 16 )
{
idx = 0;
Serial.println("");
}//if
Address++;
}while( Address < 0x8000 );
}//setup
void loop( void )
{
}//loop
void SetAddress( unsigned int Adr )
{
for( int i=0; i<15; i++ )
digitalWrite( grAddr[i], (Adr & (1<<i)) ? HIGH:LOW );
}//SetAddress
In order to play back the read bytes I'm trying to add this code:
https://github.com/idanre1/audioStreamArduino/blob/master/arduinoMaster/audioSerial.ino
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define SAMPLE_RATE 8000
#define BUFFER_SIZE 128
#define TRANSFER_SIZE 64
//#define SERIAL_BUFFER_SIZE 512 // ToDo patch in HardwareSerial.cpp inside /usr/share/arduino/hardware/arduino/cores/arduino/
void startPlayback();
void stopPlayback();
long powlong(long x, long y);
void reset();
unsigned long sounddata_length=0;
unsigned char sounddata_data[BUFFER_SIZE];
int BufferHead=0;
int HalfBufferSize=BUFFER_SIZE/2;
int BufferTail=0;
unsigned long sample=0;
unsigned long BytesReceived=0;
unsigned long Temp=0;
unsigned long NewTemp=0;
int ledPin = 13;
int speakerPin = 11;
int Playing = 0;
//Interrupt Service Routine (ISR)
// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect)
{
//If not at the end of audio
if (sample < sounddata_length)
{
//Set the PWM Freq.
OCR2A = sounddata_data[BufferTail];
//If circular buffer is not empty
if (BufferTail != BufferHead)
{
//Increment Buffer's tail index.
if (++BufferTail >= BUFFER_SIZE) BufferTail = 0; //BufferTail = ((BufferTail+1) % BUFFER_SIZE);
//Increment sample number.
sample++;
}//End if
}//End if
else //We are at the end of audio
{
//Stop playing.
stopPlayback();
}//End Else
}//End Interrupt
void startPlayback()
{
//Set pin for OUTPUT mode.
pinMode(speakerPin, OUTPUT);
//---------------TIMER 2-------------------------------------
// Set up Timer 2 to do pulse width modulation on the speaker
// pin.
//This plays the music at the frequency of the audio sample.
// Use internal clock (datasheet p.160)
//ASSR = Asynchronous Status Register
ASSR &= ~(_BV(EXCLK) | _BV(AS2));
// Set fast PWM mode (p.157)
//Timer/Counter Control Register A/B for Timer 2
TCCR2A |= _BV(WGM21) | _BV(WGM20);
TCCR2B &= ~_BV(WGM22);
// Do non-inverting PWM on pin OC2A (p.155)
// On the Arduino this is pin 11.
TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
//16000000 cycles 1 increment 2000000 increments
//-------- * ---- = -------
// 1 second 8 cycles 1 second
//Continued...
//2000000 increments 1 overflow 7812 overflows
//------- * --- = -----
// 1 second 256 increments 1 second
// Set PWM Freq to the sample at the end of the buffer.
OCR2A = sounddata_data[BufferTail];
//--------TIMER 1----------------------------------
// Set up Timer 1 to send a sample every interrupt.
// This will interrupt at the sample rate (8000 hz)
//
cli();
// Set CTC mode (Clear Timer on Compare Match) (p.133)
// Have to set OCR1A *after*, otherwise it gets reset to 0!
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
// No prescaler (p.134)
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set the compare register (OCR1A).
// OCR1A is a 16-bit register, so we have to do this with
// interrupts disabled to be safe.
OCR1A = F_CPU / SAMPLE_RATE; // 16e6 / 8000 = 2000
//Timer/Counter Interrupt Mask Register
// Enable interrupt when TCNT1 == OCR1A (p.136)
TIMSK1 |= _BV(OCIE1A);
//Init Sample. Start from the beginning of audio.
sample = 0;
//Enable Interrupts
sei();
}//End StartPlayback
void stopPlayback()
{
// Disable playback per-sample interrupt.
TIMSK1 &= ~_BV(OCIE1A);
// Disable the per-sample timer completely.
TCCR1B &= ~_BV(CS10);
// Disable the PWM timer.
TCCR2B &= ~_BV(CS10);
digitalWrite(speakerPin, LOW);
}//End StopPlayback
//Use the custom powlong() function because the standard
//pow() function uses floats and has rounding errors.
//This powlong() function does only integer powers.
//Be careful not to use powers that are too large, otherwise
//this function could take a really long time.
long powlong(long x, long y)
{
//Base case for recursion
if (y==0)
{
return(1);
}//End if
else
{
//Do recursive call.
return(powlong(x,y-1)*x);
}//End Else
}
void setup()
{
//Set LED for OUTPUT mode
pinMode(ledPin, OUTPUT);
//Start Serial port. If your application can handle a
//faster baud rate, that would increase your bandwidth
//115200 only allows for 14,400 Bytes/sec. Audio will
//require 8000 bytes / sec to play at the correct speed.
//This only leaves 44% of the time free for processing
//bytes.
Serial.begin(115200);
Serial.println("GO");
//PC sends audio length as 10-digit ASCII
//While audio length hasn't arrived yet
while (Serial.available()<10)
{
//Blink the LED on pin 13.
digitalWrite(ledPin,!digitalRead(ledPin));
delay(100);
}
digitalWrite(ledPin,1);
//Init number of audio samples.
sounddata_length=0;
//Convert 10 ASCII digits to an unsigned long.
for (int i=0;i<10;i++)
{
//Convert from ASCII to int
Temp=Serial.read()-48;
//Shift the digit the correct location.
NewTemp = Temp * powlong(10,9-i);
//Add the current digit to the total.
sounddata_length = sounddata_length + NewTemp;
}//End for
//Tell the remote PC/device that the Arduino is ready
//to begin receiving samples.
Serial.println(sounddata_length);
Serial.println(uint8_t(TRANSFER_SIZE));
//There's data now, so start playing.
//startPlayback();
Playing =0;
}//End Setup
void loop()
{
//If audio not started yet...
if (Playing == 0)
{
//Check to see if the first 1000 bytes are buffered.
if (BufferHead >= HalfBufferSize)
{
Playing=1;
startPlayback();
}//End if
}//End if
//While the serial port buffer has data
while (Serial.available()>0)
{
//If the sample buffer isn't full
if (((BufferHead+1) % BUFFER_SIZE) != BufferTail)
{
//Store the sample freq.
sounddata_data[BufferHead] = Serial.read();
//Increment the buffer's head index.
BufferHead = (BufferHead+1) % BUFFER_SIZE;
//Increment the bytes received
BytesReceived++;
}//End if
//if the Serial port buffer has room
if ((BytesReceived % TRANSFER_SIZE) == 0) {
//Tell the remote PC how much bytes you want.
if ((sounddata_length - BytesReceived) < TRANSFER_SIZE) {
Serial.println(uint8_t(sounddata_length - BytesReceived));
}
else {
Serial.println(uint8_t(TRANSFER_SIZE));
}
// Serial.print("!");
// Serial.println(sounddata_data[BufferHead]);
//Serial.println("@");
}//End if
}//End While
}//End Loop
I increased the BUFFER_SIZE a lot and Iinstead of Serial_data I give it the bytes read:
void loop() {
Address = 0x7000;
idx = 0;
do {
SetAddress( Address );
digitalWrite( pinG, LOW );
databyte = PINK;
digitalWrite( pinG, HIGH );
if (((BufferHead+1) % BUFFER_SIZE) != BufferTail) {
sounddata_data[BufferHead] = databyte;
BufferHead = BufferHead+1 % BUFFER_SIZE;
}
Address++;
if (Playing == 0) {
if (BufferHead >= HalfBufferSize) {
Playing=1;
startPlayback();
}
}
}while( Address < 0xaaff );
}
The buffer is filled and sound playback begins, but only the part limited by this buffer (2-3 seconds) and this part loops and repeats until the end of searching through all the given addresses. It appears that the buffer is not being flushed.
I understand that "startPlayback()" should be outside the loop, but it doesn’t work differently or works with distortion.
It’s also not clear what to do with the "sounddata_length".
Help me figure out how to combine these two sketches.