Sorted by Squirrels.

Wednesday 26 March 2008

Itnrtaenrslfeearvse!d

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.

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...

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...

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 :)


// 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 :)

Spaghetti

Here's the basic harness wired into the Arduino board. The three-pin header is the power connector, ready to accept +12, gnd and +5v connections from the transistor switch contraption that I'll put together on a breadboard later.

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]


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?!

Tuesday 11 March 2008

The plan to blow all the fuses

The aims of this mini project are quite humble: Build an out-of-circuit parallel mode programmer lite which can whip a borked avr back into something like its factory condition ready for in-circuit programming. At the moment I'm going to concentrate on one chip only and if it all works, and there's sufficient interest, I'll think about expanding the scope.

I'm not going to go into depth - this is just really a sketch of what's happening in my bonce right now. I'll be abstracting quite heavily and using my own terms because it doesn't make sense to just repeat all the detail in the datasheets. Code and schematics will be posted as and when available.

All the information I needed for V0.1 is in the atmega32 data sheet. It looks like it'll be quite easy to reset the fuses.

I've come across a design for a parallel port programmer which looks like it would be simple to build but I don't relish the thought of soldering up yet another DB25 connector. So I've decided to drive this project with an Arduino. I'm imagining nothing more than a socket on some stripboard connected to some header pins that will mate with my Diecimila, some power regulation and switching. I would have built a shield but for whatever reason the designers of the board decided to use a non-standard spacing between 2 sets of sockets which makes building shields on protoboard difficult. Grrrr.

The procedure will simply be:- Erase chip, reset fuses. Job done. The chip erase step has the effect of resetting any lock bits. As one of the locks controls our ability to write fuses this is essential. The state of the EEPROM will be indeterminate after this step, however, as there is a fuse controlling whether the erase cycle also clears EEPROM. And we don't know how this is set.

There are two ways to enter high voltage programming mode. The first involves gently tickling a port but it relies upon the clock settings for the chip being set to external. I think this might have been how I borked my chip, but as I couldn't ressurect it with an attached clock signal this could well not not be the case. So I'm going to go for the second method. Brute force!

Powering up the target with 12v @ 250uA on its reset line, logic low on four specific pins and a wait of 100uS should be enough for it to become willing to accept programming commands. Which are loaded thus:

  • Set the 'mode select' pins to 'load command'.
  • Present the desired command to the DATA pins.
  • Pulse the clock pin to latch the command. All pulses need to last at least 250nS.

Commands are executed by giving a pulse to ~wr. Progress can be tracked by monitoring the rdy/~bsy pin, which will go low until the action has finished.

Addresses are loaded in the same manner, we just substitute 'load address' as the mode.

All the dirty details of the required pulsing is clearly outlined in the timing waveform diagrams in the data sheet.

In order to check how the hardware and code is functioning I'll read the signature bytes of the chip as my first step.

  • Load the 'read signature' command.
  • Load the address of the desired signature byte, 0-2, to be accessed.
  • Set ~oe low.
  • The target presents the requested byte on the DATA bits.
  • Set ~oe high.

When this is returning what I expect then we'll start on the fuses. First though - some erasing.

  • Load 'erase chip' command.
  • Pulse ~wr.
  • Wait for the command to complete. This will take between 7-9mS.

Once this is done happily then we're ready to blow the fuses! We'll do low, then high.

  • Load the 'write fuse' command.
  • Load DATA with the low fuse byte.
  • Give ~wr a pulse and wait for rdy/~bsy to indicate the process has completed.
For general write operations this will take between 3-5mS.

  • Load the 'write fuse' command.
  • Load DATA with the high fuse value.
  • Give ~wr a pulse and wait for rdy/~bsy.

And that should be that!

I'll let you know how I get on :)

Here's an untested arduino sketch that I've hacked up, which should give a better idea of the specifics. It should be read in concert with the data sheet chapter 'Memory programming'.


/* CUT DUE TO ITS TEMPORARY NATURE - PLEASE SEE LATER POST */

Friday 7 March 2008

Retro Heaven



I've just hacked together an MMC2IEC interface on my breadboard. Thanks Lars!


It's a serial device that pretends to be a commodore 1541 disk drive. It uses SD card storage and disk images to deliver retrogaming goodness to my '128. This is #4 of 4 in my old computers that I've added flash storage to series. Seriously - if you have old computers and a few spare hours it's worth the solder burns.

Having got that device working in its original Lars form I noticed that the open source design has been picked up by a couple of other bods who have taken it to another level. Details are scant, unless you speak German, but I've managed to scrape enough information together to upgrade Lars' original hardware design with some improved firmware called SD2IEC. This supports many more features such as high capacity cards, disk switching, some fastloaders and JiffyDos which should speed things up considerably. Which if you've ever used a C64 disk system you'll know is a must. Some details in English are here.

During the process of upgrading I managed to half-brick an AVR chip. Unlike PICs they are easy to render unprogrammable by changing their configuration fuses in an an unfavourable way. My mistake was not reading the output of this avr fuse calculator carefully enough. Instead of making the AVR desire an external resonator, I made it lust for Clock. Which is different. I've done this a couple of times before with some 168s. In the aftermath of that embarrassing mistake I did some research and found that usually these chips can be resurrected.

If you program a chip to expect a clock, then it should be possible to reprogram it as long as the relevant signal is present. I usually program chips in a little home-made ISP cradle which doesn't have a clock available. For the 168s it proved possible to get them going again by dropping them in my Deicimilia board before attempting to reset the fuses.

The mega32 hasn't responded to this trick though. I used an Arduino to provide what I thought was a good clock to no avail. It's going to need something a little more ... high voltage. AVRs can be programmed using SPI in-circuit, or by more conventional parallel means out-of-circuit. Programming serially you need a well configured support circuit for the chip. Using a parallel programmer you can reset fuses that would prevent SPI from working. So here's my next project - an avr fuse reset device. A cut-down high voltage parallel programmer that only has one job - to reset the fuses to a factory fresh state. Insert AVR, press button, bingo! At least in theory :)

I'm off to grab some of Atmel's brilliant application notes. Then it'll be time for a nice sit down and some strawberry cheesecake.

Saturday 1 March 2008

Hello, world!


I want somewhere to doodle about the mucking around I'm doing with Arduino. This, as you might well have guessed by now, is it.

If you don't dig digital, if you aren't amorous with analog... Oh forget it. It's going to be spoddy!

I'm off to town for a vanilla slice, but when I get back I'll play with this blog thing a little more and start posting about the microcontroller hackery I've been getting up to in the last few months or so.

If you like POV toys, LED tokens of love, zx81s, shoehorning data on and off MMC cards and FSK cassette interfaces then I'm sure you'll be riveted!


If not - well, there will be cake :)

Contact Form

Name

Email *

Message *

This is how we do it

MMC (9) acorn atom (7) zx81 (7) sord m5 (6) arduino (5) multicart (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) 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) 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)
Unless otherwise stated all of the original work presented here is:

Creative Commons License
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.