Some back-of-the-envelope calculations later I'm looking at about an 8-10khz playback rate. At 10khz I'll be needing to feed the DAC a new sample about every 100uS. Each byte transfer over the SPI bus takes up to 40uS. By breaking up the transfer process into interleavable chunks we should be able to achieve the required rate - but waaiiiit a minute. The DAC requires a 16bit transfer. So I guess I'm looking at 8khz. I know I could get better by using serial EEPROM, FRAM, SRAM, BLUEJAM or whatever. But that isn't the point! I always like to just have a go and make what I've got work.
If you're familiar with transferring data from MMC you'll know that there are command blocks involved in the process. These are 48bit monsters. It might be that with the latency involved in sending the commands and the MMCs reply delay we are looking at even less than the 8khz as a replay rate. We'd need to be pushing a sample out as fast as we can read them which means the time taken for sending command blocks would cause glitches.
I'll have to do some more sums and see how long we can sustain continuous playback with some buffering. With the Arduino's 2k of SRAM we could possibly hold 1024 pre-loaded samples whilst reading a new sector from the card... My head is saying about 3.5 seconds - roughly 50 sectors of 512 bytes.
Sorted by Squirrels.
Wednesday, 26 March 2008
Tuesday, 25 March 2008
SPI vs SPI
I've taken the software spi master code as published by Atmel and converted it into c++ to allow it to be modified. I spent a frustrating couple of days, a few moments at a time, trying to work out why MMC card access was broken when using the software master. It turned out to be that I was sampling the input on the falling edge of the clock. This was most likely due to some subtle detail lost when reading the original AVR ASM code, or dense stupidity :) I found it using, my now favourite tool in the box, the parallel port logic analyzer.
I've upgraded the software so that the grabber runs under 2K, XP and (probably) up. This is way more convenient than having to have a machine booted into DOS as I'm sure you can imagine. I've also worked on the viewer app so that you can zoom in to waveforms and see information like sample position and time - both absolute and relative to a marker.
Features, creepy in their nature, keep suggesting themselves and I'm having a hard time keeping off the code, which is presented in source and precompiled form, here.
Usual caveats apply, use at own risk, no responsibility taken for damage to potted plants ahem ahem.
Here's a couple of shots showing both the soft and hardware waveforms as snaffled up by the analyser. The ordering of channels is different as the pin assignments are different in the two modes. The cursor in both cases is poised at the moment that we expect our first byte of data back from the card. As you can see if sampling is done on the falling clock edge then a mis-read will occur. The incoming bits will be read as 0xfe,0x03 instead of 0xff,0x01. Bugger!
Soft:

'Ard:

As always, these posts are just a little flavour of the cake that is the tortured analogy for my development hobby. If you want the full recipe, then an email or comment will do the job.
And while you're considering that, there's this...
I've upgraded the software so that the grabber runs under 2K, XP and (probably) up. This is way more convenient than having to have a machine booted into DOS as I'm sure you can imagine. I've also worked on the viewer app so that you can zoom in to waveforms and see information like sample position and time - both absolute and relative to a marker.
Features, creepy in their nature, keep suggesting themselves and I'm having a hard time keeping off the code, which is presented in source and precompiled form, here.
Usual caveats apply, use at own risk, no responsibility taken for damage to potted plants ahem ahem.
Here's a couple of shots showing both the soft and hardware waveforms as snaffled up by the analyser. The ordering of channels is different as the pin assignments are different in the two modes. The cursor in both cases is poised at the moment that we expect our first byte of data back from the card. As you can see if sampling is done on the falling clock edge then a mis-read will occur. The incoming bits will be read as 0xfe,0x03 instead of 0xff,0x01. Bugger!
Soft:

'Ard:

As always, these posts are just a little flavour of the cake that is the tortured analogy for my development hobby. If you want the full recipe, then an email or comment will do the job.
And while you're considering that, there's this...
Monday, 17 March 2008
Sampling
I've had a weekend away from thinking about maladjusted fuses, and now I'm feeling much, much, better. I had occasion to add some flashing pulsing coloured lighting to the cardboard rocket that I built with my boys on Saturday morning so naturally the Arduino came out. It's amazing how something as inconsequential as some modicum of lighting can transform the lifeless cardboard tube into a dynamic play area.
While I was at it I thought that it would be easy to add some silver-foil capacitive switches which would trigger sound effects. With my collection of freebie chips in front of me now I can see that I have a brace of MCP4922 DACs that I ordered from MicroChip a while ago that remained unused. These are SPI compatible 12bit DACs which should be trivial to connect up and drive.
I'm imagining a circuit with both a DAC and some form of persistent storage on an SPI bus. I'll probably go for SD/MMC as I'm familiar with driving these and I have some solid code to work with. Most likely I'll be using a software SPI master as the DACs are unbuffered. This way I can read from the MMC and write to the DACs in an interleaved fashion which will give me greater flexibility in coding and control over playback rate.
MicroChip have a relaxed attitude to people requesting samples that I wish other companies would follow. They must go by the assumption that the more people experience using their parts the better. Word of mouth is a powerful advertising medium after all. If you've never thought of doing this I recommend giving it a try. What's to lose? :)
Another company that's sample-friendly is Dallas/Maxim - makers of the ever-so useful Max232/3 serial level converters amongst other devices.
The trick with sampling is to have the time to sit and browse the entire product range. After all you might be inspired when you see that digital potentiometer...
While I was at it I thought that it would be easy to add some silver-foil capacitive switches which would trigger sound effects. With my collection of freebie chips in front of me now I can see that I have a brace of MCP4922 DACs that I ordered from MicroChip a while ago that remained unused. These are SPI compatible 12bit DACs which should be trivial to connect up and drive.
I'm imagining a circuit with both a DAC and some form of persistent storage on an SPI bus. I'll probably go for SD/MMC as I'm familiar with driving these and I have some solid code to work with. Most likely I'll be using a software SPI master as the DACs are unbuffered. This way I can read from the MMC and write to the DACs in an interleaved fashion which will give me greater flexibility in coding and control over playback rate.
MicroChip have a relaxed attitude to people requesting samples that I wish other companies would follow. They must go by the assumption that the more people experience using their parts the better. Word of mouth is a powerful advertising medium after all. If you've never thought of doing this I recommend giving it a try. What's to lose? :)
Another company that's sample-friendly is Dallas/Maxim - makers of the ever-so useful Max232/3 serial level converters amongst other devices.
The trick with sampling is to have the time to sit and browse the entire product range. After all you might be inspired when you see that digital potentiometer...
Friday, 14 March 2008
More poking...no more joy.
Hmm. The waveforms look about right.
There wasn't any software that would drive the parallel port breakout cable that I built. (Yes, I know it's ironic given my previous statement about how I wasn't prepared to solder up another DB25!!)
So I ended up crashing together an app in OpenWatcom for DOS to do the grabbing, and a C# program to display the data. Talk about a technology clash! I think at this point unless anyone can point out the problem with the attached code I'm going to concede defeat! For now ;)
ChaN's programmer is looking a lot more like the next project. I have a couple of old TTL-stuffed boards that I've been meaning to get the hot-air gun aimed towards. The need for a '299 for the programmer means I might just do that this weekend.
All this to recover a £3.25 microcontroller? No! Just for the sheer amusement!!
Here's the final draft of the code. I've been over it so many times now I've gone codeblind so if you see any howlers please keep your derision to good-humoured banter :)
There wasn't any software that would drive the parallel port breakout cable that I built. (Yes, I know it's ironic given my previous statement about how I wasn't prepared to solder up another DB25!!)So I ended up crashing together an app in OpenWatcom for DOS to do the grabbing, and a C# program to display the data. Talk about a technology clash! I think at this point unless anyone can point out the problem with the attached code I'm going to concede defeat! For now ;)
ChaN's programmer is looking a lot more like the next project. I have a couple of old TTL-stuffed boards that I've been meaning to get the hot-air gun aimed towards. The need for a '299 for the programmer means I might just do that this weekend.
All this to recover a £3.25 microcontroller? No! Just for the sheer amusement!!
Here's the final draft of the code. I've been over it so many times now I've gone codeblind so if you see any howlers please keep your derision to good-humoured banter :)
// XA1 XA0 Action when XTAL1 is Pulsed
// --- --- ---------------------------
// 0 0 Load Flash or EEPROM Address (High or low address byte determined by BS1)
// 0 1 Load Data (High or Low data byte for Flash determined by BS1)
// 1 0 Load Command
// 1 1 No Action, Idle
//
const byte XA_LOAD_ADDR = B00;
const byte XA_LOAD_DATA = B01;
const byte XA_LOAD_CMND = B10;
const byte XA_NOP = B11;
const byte CMD_ERASE = B10000000;
const byte CMD_WRITE_FUSE = B01000000;
const byte CMD_READ_SIG = B00001000;
const byte CMD_READ_FUSE = B00000100;
#define DLY delayMicroseconds(100)
inline void SETB(byte x)
{
PORTB |= _BV(x);
DLY;
}
inline void SETD(byte x)
{
PORTD |= _BV(x);
DLY;
}
inline void CLRB(byte x)
{
PORTB &= ~_BV(x);
DLY;
}
inline void CLRD(byte x)
{
PORTD &= ~_BV(x);
DLY;
}
// PORTB
#define PIN_BS1 0
#define PIN_XA0 1
#define PIN_XA1 2
#define PIN_BS2 3
#define PIN_PAGEL 4
#define PIN_POWERTOCHIP 5
#define SET_BS1 SETB(PIN_BS1)
#define CLR_BS1 CLRB(PIN_BS1)
void SET_XA(byte xtal_action)
{
PORTB &= ~(_BV(PIN_XA1)|_BV(PIN_XA0));
PORTB |= xtal_action << PIN_XA0;
DLY;
}
void CLR_XA()
{
PORTB &= ~(_BV(PIN_XA1)|_BV(PIN_XA0));
DLY;
}
#define SET_BS2 SETB(PIN_BS2)
#define CLR_BS2 CLRB(PIN_BS2)
#define SET_PAGEL SETB(PIN_PAGEL)
#define CLR_PAGEL CLRB(PIN_PAGEL)
#define SET_POWERTOCHIP SETB(PIN_POWERTOCHIP)
#define CLR_POWERTOCHIP CLRB(PIN_POWERTOCHIP)
// PORTD
#define PIN_XTAL1 2
#define PIN_RDYNOTBSY 3
#define PIN_OE 4
#define PIN_WR 5
// <- PORTD3 rdy/bsy
//
void AWAIT_READY()
{
while ((PIND & _BV(PIN_RDYNOTBSY)) == 1);
long time = millis();
while ((PIND & _BV(PIN_RDYNOTBSY)) == 0);
Serial.println(millis()-time,DEC);
}
#define SET_OE SETD(PIN_OE)
#define CLR_OE CLRD(PIN_OE)
#define SET_WR SETD(PIN_WR)
#define CLR_WR CLRD(PIN_WR)
void PULSE_WR(bool wait = true);
void PULSE_WR(bool wait)
{
PORTD &= ~_BV(PIN_WR);
delayMicroseconds(100);
PORTD |= _BV(PIN_WR);
if (wait)
{
AWAIT_READY();
}
}
#define SET_XTAL1 SETD(PIN_XTAL1)
#define CLR_XTAL1 CLRD(PIN_XTAL1)
void PULSE_XTAL(bool wait = true);
void PULSE_XTAL(bool wait)
{
PORTD |= _BV(PIN_XTAL1);
delayMicroseconds(100);
PORTD &= ~_BV(PIN_XTAL1);
if (wait)
{
AWAIT_READY();
}
}
// <-> PORTD6...7 will be used as DATA6...7
// <-> PORTC0...5 will be used as DATA0...5
//
void SET_DATA_IN(void)
{
DDRC &= B11000000;
DDRD &= B00111111;
}
byte GET_DATA()
{
return (PORTC & B00111111) | (PORTD & B11000000);
}
void SET_DATA(byte data)
{
DDRC |= B00111111;
PORTC = (PORTC & B11000000) | (data & B00111111);
DDRD |= B11000000;
PORTD = (PORTD & B00111111) | (data & B11000000);
DLY;
}
//
//
//
void LOAD_COMMAND(byte command)
{
SET_XA(XA_LOAD_CMND);
SET_DATA(command);
PULSE_XTAL();
CLR_XA();
}
void LOAD_ADDRESS(byte address)
{
SET_XA(XA_LOAD_ADDR);
SET_DATA(address);
PULSE_XTAL();
CLR_XA();
}
void LOAD_DATA(byte data, bool low)
{
SET_XA(XA_LOAD_DATA);
if (!low)
{
SET_BS1;
}
SET_DATA(data);
PULSE_XTAL();
CLR_XA();
CLR_BS1;
}
byte GET_SIG(byte offset)
{
LOAD_COMMAND(CMD_READ_SIG);
LOAD_ADDRESS(offset);
SET_DATA_IN();
CLR_OE;
byte x = GET_DATA();
SET_OE;
}
inline void PROG_ENABLE(void)
{
SET_POWERTOCHIP;
delayMicroseconds(100);
}
inline void PROG_DISABLE(void)
{
CLR_POWERTOCHIP;
}
inline void PROG_ENABLE_PROPER(void)
{
SET_XTAL1;
CLR_XTAL1;
SET_XTAL1;
CLR_XTAL1;
SET_XTAL1;
CLR_XTAL1;
SET_POWERTOCHIP;
}
//
//
//
void setup(void)
{
Serial.begin(115200);
// PORTB 5...0 all outputs. Zero the ports before setting them as outputs
// to prevent spikes.
//
PORTB = 0;
DDRB |= B00111111;
// Not interested in DATA pins, they'll get set later as required
//
// PORTD0/1 are rx/tx: leave alone
// PORTD3 is input
// D6/7 are set on demand
// the rest are output
//
PORTD = 0;
DDRD = (DDRD & B11110111) | B00110100;
// bring the active low pins to their inactive state.
//
SET_OE;
SET_WR;
}
void loop(void)
{
Serial.println("Fuser 1.0 Ready.");
while(!Serial.available());
if (Serial.read() == 'p')
{
Serial.println("Sig: ");
PROG_ENABLE_PROPER();
Serial.println(GET_SIG(0),HEX);
Serial.println(GET_SIG(1),HEX);
Serial.println(GET_SIG(2),HEX);
}
else
{
Serial.println("Burn: ");
PROG_ENABLE();
delay(2);
Serial.println("Erase: ");
LOAD_COMMAND(CMD_ERASE);
PULSE_WR();
Serial.println("Lo fuse: ");
LOAD_COMMAND(CMD_WRITE_FUSE);
LOAD_DATA(0xE1, true);
PULSE_WR();
Serial.println("Hi fuse: ");
LOAD_COMMAND(CMD_WRITE_FUSE);
LOAD_DATA(0x99, false);
PULSE_WR();
}
PROG_DISABLE();
}
Thursday, 13 March 2008
Heroic failure!
I've slapped it all into shape and electrically things look solid - but no joy.
Result: Heroic failure!
I'll probably poke around a bit more before consigning this to the 'nearly works' pile as I get the same nonsense results with a known good chip. So you never know.
This might be the time to get my cheap-as-chips parallel-port logic analyzer built... It's easy to see how my project stack grows :)
Result: Heroic failure!
I'll probably poke around a bit more before consigning this to the 'nearly works' pile as I get the same nonsense results with a known good chip. So you never know.
This might be the time to get my cheap-as-chips parallel-port logic analyzer built... It's easy to see how my project stack grows :)
Spaghetti
I used very fine enamelled wire cohabiting the protoboard holes with the socket pins. A good application of heat and solder later - bingo!
To connect the wire to the header pins I put a tiny blob of solder on the iron tip and insert the cut end of the wire. Within a second or so the enamel melts and you can see the solder flow around the wire. It's then ready to fix it to its destination.
And what better to listen to whilst soldering than some very fine oldskool belgian skizzo??
Wednesday, 12 March 2008
Arduino to planet fuser
Here's how I plan to wire the fuser board to my Diecimila:
[Apologies to some viewers: I've just looked here using IE7 and the diagram is flummoxed. Adjust the font size downwards and things will look a little better]
The power switching is going to be implemented identically to how ChaN implements his. I'm lazy that way! Which is to say, The Good Way. Why reinvent?!
[Apologies to some viewers: I've just looked here using IE7 and the diagram is flummoxed. Adjust the font size downwards and things will look a little better]
Target - atmega32
____ ____
| pb0 =| U |= pa0 | bs2
| 1 =| |=
/ | 2 =| |=
DATA | 3 =| |=
\ | 4 =| |=
| 5 =| |=
| 6 =| |=
| 7 =| |=
reset =| |= aref
vcc =| |= gnd
gnd =| |= avcc
=| |=
xtal1 | xtal1 =| |=
pd0 =| |=
rdy/~bsy | 1 =| |=
~oe | 2 =| |=
~wr | 3 =| |=
bs1 | 4 =| |=
xa0 | 5 =| |=
xa1 | 6 =|_________|= pd7 | pagel
Diecimila - pin assignment
ANA-IN5 |o| DATA5
4 |o| 4
3 |o| 3
2 |o| 2
1 |o| 1
0 |o| 0
D0 [rx] |o| -
1 [tx] |o| -
2 |o| xtal1
3 |o| rdy/~bsy
4 |o| ~oe
5 |o| ~wr
6 |o| DATA6
7 |o| DATA7
D8 |o| bs1
9 |o| xa0
10 |o| xa1
11 |o| bs2
12 |o| pagel
13 |o| juice
gnd |o| -
aref |o| -
The power switching is going to be implemented identically to how ChaN implements his. I'm lazy that way! Which is to say, The Good Way. Why reinvent?!
Subscribe to:
Posts (Atom)
Contact Form
This is how we do it
MMC
(9)
acorn atom
(7)
sord m5
(7)
zx81
(7)
multicart
(6)
arduino
(5)
Sord
(4)
tatung einstein
(4)
Atari 800
(3)
Chuckie egg
(3)
M5
(3)
M5Multi
(3)
c128
(3)
sd card
(3)
sd2iec
(3)
sio2sd
(3)
6502
(2)
Max6956
(2)
QL
(2)
RCM
(2)
assembler
(2)
avr
(2)
c64
(2)
cadsoft eagle
(2)
eeprom
(2)
einSDein
(2)
mmbeeb
(2)
multi-cart
(2)
spi
(2)
system 80
(2)
ufat2
(2)
vic20
(2)
video genie
(2)
6502 second processor
(1)
6522
(1)
8255
(1)
Acorn BBC Micro
(1)
Apple 2e
(1)
Apple ][ 2 two
(1)
BBC 6502 second processor
(1)
BBC micro
(1)
DevicePrint
(1)
Double Choc Chip Muffins
(1)
FAT
(1)
IO
(1)
Jupiter Ace
(1)
LED
(1)
Master 128
(1)
PCB
(1)
PIC
(1)
POV
(1)
PROGMEM
(1)
Pineapple
(1)
ST
(1)
Spectrum 128
(1)
antex
(1)
arcade spinner
(1)
arduino shield
(1)
atari
(1)
atmel
(1)
bakewell tart
(1)
beer
(1)
bird's nest
(1)
bitbucket
(1)
brokenated XC special
(1)
cake
(1)
cassette
(1)
cassette interface
(1)
colecovision
(1)
compact flash
(1)
convert
(1)
dac
(1)
de-yellowing
(1)
dev cart
(1)
development tool
(1)
eaca
(1)
efficient
(1)
einsdein. z80
(1)
esp32
(1)
esp8266
(1)
eye strain
(1)
failosophy
(1)
filesystem
(1)
finally
(1)
fram
(1)
french polishing
(1)
fuse
(1)
fuses
(1)
game development
(1)
games
(1)
gaming
(1)
github
(1)
glue
(1)
google chrome
(1)
heroic failure
(1)
high voltage programming
(1)
hot irons
(1)
if
(1)
jiffydos
(1)
joey beltram
(1)
lego robot
(1)
library
(1)
lying
(1)
machine code
(1)
matron
(1)
microcode
(1)
mmc interface
(1)
mmc2iec
(1)
mmm
(1)
mouse guts
(1)
oscilloscopes
(1)
pcm
(1)
pic32mx
(1)
porn
(1)
proto shield
(1)
purple
(1)
repo
(1)
retro computer museum
(1)
retro hard-on
(1)
rom box
(1)
sd
(1)
sd-x
(1)
sd2mmc
(1)
seadragon
(1)
silliness
(1)
small
(1)
software master
(1)
soldering
(1)
spi software master
(1)
stray capacitance
(1)
string
(1)
techadventure
(1)
test equipment porn
(1)
ts1000
(1)
turtle cheesecake
(1)
tweaking
(1)
vc20
(1)
video head
(1)
video ram replacement
(1)
weewee
(1)
wingasm
(1)
wire library
(1)
wodges of IO
(1)
xilinx cpld
(1)
yellowing
(1)
z80
(1)
zx spectrum
(1)
zxpander
(1)
About Sir Morris
Unless otherwise stated all of the original work presented here is:

Licensed under a Creative Commons Attribution-Noncommercial 2.5 Generic License.
The work of others where referenced will be attributed appropriately. If I've failed to do this please let me know.
Licensed under a Creative Commons Attribution-Noncommercial 2.5 Generic License.
The work of others where referenced will be attributed appropriately. If I've failed to do this please let me know.