Dead easy Atmel AVR programmer for BBC Model B

by Dr. Dave Gilbert (avr@treblig.org)


Introduction

Sometime in the depth of 1998 I was asked to figure out how to solve a small problem on a board of TTL logic; basically what was needed was a block of fudge logic.

Flicking through the Maplin catalogue revealed the ATMEL AVR 90S1200A microcontroller; a little 20 pin beasty with Flash ROM, EEPROM and 16 IO lines all for 5 pounds.

A trip down to the local Maplin revealed that they had the datasheet and it described everything - the programming protocol, the instruction set - everything. The datasheet cost me about 10 pounds and I ordered the chip (which they didn't have in stock...).

Note: Get the A version of the chip - this has its internal oscillator enabled so you don't need to bother with a crystal.

I decided that I didn't want to try programming it on a PC because I didn't fancy paying for a replacement motherboard if I popped the parallel port off - so out came one of our trusty Model B's, A breadboard, a few bits of wire, a green LED and a magical 20 way USER port lead.

Setting it up

After getting my Beeb back to a state of health (I removed its ROM expansion cards and Shadow RAM cards which seemed to be too much for this poor machine; after all it is 16 years old now..), I tinned wires 1,5,6,8 and 10 of one end of the cable and plugged the other end into the user port. Wire 1 from the user port gives you +5V, wire 5 gives you 0v, wire 6 gives you PB0, wire 8 gives you PB1, and finally wire 10 gives you PB2. A large dollop of insulation tape kept all the other wires out of the way.

Then I connected it up as follows:
User port pinUser port meaningAVR pinAVR meaning
1+5V20Vcc
50V10Gnd
6PB019PB7/SClk
8PB118PB6/MISO (serial out)
10PB217PB5/MOSI (serial in)
In addition I hooked an on/off switch between the AVR !Reset (pin 1) and 0v, and put a 10K pull up resistor from pin 1 to +5V. Thus when the switch is on you have the AVR in reset. I also put an LED from AVR PB2 (pin 14) through a 220ohm resistor to ground. I put the AVR itself in a turned pin socket and then into the breadboard - that way I can pull the chip+socket in and out a few times of different things only harming the socket.

Switch on...

And nothing should happen (even the LED should be off). Then I wrote the program below (in a few stages). It uses the AVRs serial programming protocol to write instructions into its Flash ROM ; the instructions I've given here are enough to turn the LED on.

Using the program

Switch the power on and put the reset switch ON (i.e. force it into reset). Now run my program; it will ask you to 'briefly un-reset' the AVR - just flick the switch off and then on again. Then hit return on the BBC. What this has done is knock the AVR into realising that you want to program it over the serial connection. The program will then display the AVRs device codes, which for the 90S1200 should end in 1E, 90, and 01 - if those don't come up then check everything out.

After displaying the device codes the program will write into the AVR the instructions held in the DATA statements from lines 5010 upto the line with the -1 on it. After its done this, take the AVR out of reset and by magic the LED should light.

The program memory in the AVR is made of a stuff called FLASH. Its kind of like EEPROM, in that its non-volatile (so when you switch it on again that LED will still light!), but you can only erase entire chunks of it - you can't change individual Bytes like you can with EEPROM. The plus side of Flash is that you can put a lot more of it on a chip than you can with EEPROM. You can erase the current contents of the AVR (which you must do before reprogramming it!) by calling the PROCchipErase

BUT - Flash can only be programmed a limited number of times before it stops working; Atmel say that you should be able to program the AVR about 1000 times before anything nasty starts happening.

Different programs

If you fancy getting your AVR to do something more useful then you need to change the DATA statements after line 5000. They are loaded in order from address 0 of the AVRs program memory. Be careful not to put ANY comma on the DATA lines - EVEN after the REM. The AVR manual gives you all you need to know to figure out the machine code to program it.

Other facilities

The PROCwriteEEPROM and FNreadEEPROM routines let you read and write to the 64 bytes of EEPROM in the AVR; the EEPROM can be written on a byte by byte basis and any byte changed individually - and you can do it about 100 000 times. So its probably good for some data, which the AVRs program can read and write to.

And finally...

I hope this program is of use to some of you; but if you have any problems, spot any errors in the program (or documentation) then please Email me to let me know and fix it. I hold the copyright to the program but intend it to be distributed freely as long as you dont charge for it. I dont warranty the program in any way; if it goes wrong its quite possible it could damage your AVR, your model B or cause some other nastiness - you have been warned. If you want to distribute it on a CD/Magazine or elsewhere ask me first.

Dr. Dave Gilbert


The program

(This program has been transferred from the Beeb by retyping it - their may be errors!)

10REM Atmel AVR Programmer for BBC Model B
20REM Version 0.01
30REM (c) David Alan Gilbert 1998
35REM (avr@treblig.org)
36REM
40REM Set up User port
50REM    User bit 0 - AVR Sck (out)
60REM    User bit 1 - MISO (in)
70REM    User bit 2 - MOSI (out)
80PROCreset
90REM Read device codes
100PRINT"Device manufacturer: ":~FNdoCmd(&30,&A5,0,&5a)
110PRINT"Device size code: ";~FNdoCmd(&30,&A5,1,&5A)
120PRINT"Device subcode: ";~FNdoCmd(&30,&A5,2,&5A)
300RESTORE
310addr%=0
320REPEAT
330READ instr%
335PRINT "Writing: ";~addr%;"  : ";~instr%
340IF instr%<>-1 THEN PROCwriteProgSpace(addr%,instr%)
350addr%=addr%+1
360UNTIL instr%=-1
5000END
5010DATA &C00F: REM 000 Reset vector -  RJMP &010
5011DATA &CFFF: REM 001 Intr vector  -  RJMP .
5012DATA &CFFF: REM 002 Timer0 Vector-  RJMP .
5013DATA &CFFF: REM 003 Analogue     -  RJMP .
5014DATA &CFFF: REM 004 empty        -  RJMP .
5015DATA &CFFF: REM 005 empty        -  RJMP .
5016DATA &CFFF: REM 006 empty        -  RJMP .
5017DATA &CFFF: REM 007 empty        -  RJMP .
5018DATA &CFFF: REM 008 empty        -  RJMP .
5019DATA &CFFF: REM 009 empty        -  RJMP .
5020DATA &CFFF: REM 00a empty        -  RJMP .
5030DATA &CFFF: REM 00b empty        -  RJMP .
5040DATA &CFFF: REM 00c empty        -  RJMP .
5050DATA &CFFF: REM 00d empty        -  RJMP .
5060DATA &CFFF: REM 00e empty        -  RJMP .
5070DATA &CFFF: REM 00f empty        -  RJMP .
5080DATA &E00F: REM 010 Set R16 to &F-  LDI R16 &F
5090DATA &BB07: REM 011 portb 0-3 o/p-  OUT &17 R16
5100DATA &E004: REM 012 Set R16 to 4 -  LDI R16 &4
5110DATA &BB08: REM 013 portb bit 2=1-  OUT &18 R16
5120DATA &CFFF: REM 014 the end      -  RJMP .
9999DATA -1
10000END
10005REM ----------------------------------------------
10010DEFPROCwaitCS
10020LOCAL T%
10030T%=TIME:REPEAT: UNTIL TIME>T%
10040ENDPROC
10050REM ----------------------------------------------
10060DEFPROCreset
10065LOCAL A
10070?&FE62=5
10080?&FE60=0: REM Take all low - including SCLK
10090INPUT "Please briefly Un-RESET AVR and hit return:" A
10100PROCwaitCS
10105PRINT"Enabling serial programming: ";~FNdoCmd(&AC,&53,0,0)
10110ENDPROC
10120REM ----------------------------------------------
10130DEFFNdoCmd(send1%,send2%,send3%,send4%)
10140LOCAL result%
10145REM Send 1st byte (we will discard this first byte of result)
10150result%=FNdoByte(send1%)
10160result%=FNdoByte(send2%)
10170result%=(result%)*256+FNdoByte(send3%)
10180result%=(result%)*256+FNdoByte(send4%)
10190=result%
10200REM ----------------------------------------------
10210DEFFNdoByte(send%)
10220LOCAL bit%,res%,bitv%
10225res%=0
10230FOR bit%=7 TO 0 STEP -1
10232REM Read incoming bit
10234res%=res%*2
10236IF (?&FE60 AND 2)<>0 THEN res%=res% + 1
10240REM set  up data
10250IF (send% AND (2^bit%))<>0 THEN bitv%=4 ELSE bitv%=0
10260?&FE60=bitv%
10270REM Clock high
10280?&FE60=bitv% OR 1
10290REM Clock low (BASIC should be slow enough to avoid an explicit delay)
10300?&FE60=bitv%
10340NEXT bit%
10350=res%
10360REM ----------------------------------------------
10370DEFPROCwriteEEPROM(addr%,val%)
10375LOCAL dummy%
10380dummy%=FNdoCmd(&C0,0,addr%,val%)
10385ENDPROC
10390REM ----------------------------------------------
10400DEFFNreadEEPROM(addr%)
10410=FNdoCmd(&A0,0,addr%,0) AND &FF
10420REM ----------------------------------------------
10430DEFPROCchipErase
10440LOCAL dummy%
10450dummy%=FNdoCmd(&AC,&80,0,0)
10452PROCwaitCS
10455PROCreset
10460ENDPROC
10470REM ----------------------------------------------
10471REM Note: Addresses are word addresses
10480DEFPROCwriteProgSpace(addr%,val%)
10490LOCAL dummy%
10500dummy%=FNdoCmd(&40,addr% DIV 256, addr% MOD 256, val% MOD 256)
10510dummy%=FNdoCmd(&48,addr% DIV 256, addr% MOD 256, val% DIV 256)
10520ENDPROC
10530REM ----------------------------------------------
10540DEFFNreadProgSpace(addr%)
10550LOCAL tmp%,res%
10560tmp%=FNdoCmd(&20,addr% DIV 256, addr% MOD 256, 0)
10570res%=tmp% AND 255
10580tmp%=FNdoCmd(&28,addr% DIV 256, addr% MOD 256,0)
10590res%=res% OR ((tmp% AND 255)*256)
10600=res%