Program At89S51 with Arduino SPI library

Hello to everyone,

I'm wondering if it's possible to program an At89s51 with arduino using the SPI library only. I'm newbie in SPI world, this why I found that to program the at89s51 will allow me to understand the SPI protocol as well as play with this great uController.

I'm asking my self also on how the data is programmed on the chip after sending the "Programming enable instruction" is by using MOSI itself or I have to put something on the address line and data line ? I asked this question because I found in the datasheet "Serial programming instruction set" page 20 the byte 2, 3, 4 have "xxxx A11 A10 A9 A8" or "A7 A6 A5 A4 A3 ..." and I'm struggling to understand what they mean by these.

I've created a test circuit to send the first programming instruction to the chip and try to get the data response back from it, but it's not working. I've put a 6Mhz crystal osillator with two 22pF cap for the uController. (I'm not sure if this is correct or not too ?)

My request is there someone can answer me about what I've asked and tell me if this project is feasible or not ?

I'm newbie in electronic so what you can do in 2 hours I will do it in 2 weeks. :grin:

Thanks in advance.

I'm asking my self also on how the data is programmed on the chip after sending the "Programming enable instruction" is by using MOSI itself or I have to put something on the address line and data line ?

SPI is a serial interface, everything is sent on MOSI and read from MISO.

"...Serial programming instruction set" page 20 the byte 2, 3, 4 have "xxxx A11 A10 A9 A8" or "A7 A6 A5 A4 A3 ..." and I'm struggling to understand what they mean by these.

It means you first send the opcode (byte1), then in byte 2 you send the highest bits of the address (highest 4 bits are don't care: xxxx), byte 3 holds the 8 least significant bits of the address and finally byte 4 is the data you want to write at that address.

My request is there someone can answer me about what I've asked and tell me if this project is feasible or not ?

It is feasable and a good exercise as you indicate.
But assure yourself you want to do the exercise: incidently I just bought a programmer (an sp200s clone) for only 12$, that can do the at89s52. It seems to work but it is black box, you don't see what is happening so this does not learn you anything on ISP or SPI of coarse.

If you are still convinced you want to do it from your arduino, this link may be interesting:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=99161
It indicates you can program the at89s52 with avrdude + usbasp. I would expect that avrdude + arduinoISP should work too.

Thanks for your reply.

So, it can be done. However, can I use the SPI library from arduino or should I make my own avr code ? Because I think the SPI protocol change from a device to another.

What you think ?

Regards.

This might help.
Programming At89s52 with ArduinoISP:

http://softgeniedoc.dk/contents/projects/atmel89sxx/89s5xx.php

Interesting link. Though it is not using ArduinoISP, it uses some sketch dedicated to the programming tool also available on that side.

In my reply above, my expectation that avrdude+ArduinoISP could do the job is wrong: ArduinoISP does not interprete info from avrdude about e.g. what command to issue for a "program enable". All this is hard coded... When I come to think about it, this is a pity: ArduinoISP is used by a lot of people and it could be a lot more usefull if it used more of the info coming from avrdude...

Indeed is a great link. I'll study it minutely, especially the arduino side.

Last question, the AT89S51 SPI mode must be switched to slave mode or is already in it

Regards.

Hi,

I have coded a sketch that can send (read byte command , programming enable command, chip erase). However, I still can't understand how data is sent to me ? Should I read the MISO pin each time I send a bit or should I wait till the command is completely sent ?

I try to read the output from the chip when the programming enable command is sent.

Just for info, I've used the first method, I read MISO each time I send a bit but I got the same command that I send just before. Is that behavior correct ?

This is my code:

#define ENABLE_PROGRAMMING 0xAC530069L
#define CHIP_ERASE         0xAC800000L
#define READ_BYTE          0x20000000L
#define WRITE_BYTE         0x40000041L

int RST_P = 9;
int MOSI_P = 10;
int MISO_P = 11;
int CLK_P  = 12;

void setup()
{
  Serial.begin(9600);
  
  pinMode(MOSI_P, OUTPUT);
  pinMode(MISO_P, INPUT);
  pinMode(RST_P, OUTPUT);
  pinMode(CLK_P, OUTPUT);
  
  digitalWrite(MOSI_P, HIGH);
  digitalWrite(RST_P, HIGH);
  digitalWrite(CLK_P, HIGH);

}

void enableRST()
{
  digitalWrite(RST_P, HIGH);
}

void disableRST()
{
  digitalWrite(RST_P, LOW);
}

void sendCmd(long cmd, boolean output)
{
  digitalWrite(CLK_P, LOW);
  delay(1);
  
  for (int i=31; i>=0; i--)
  {
    if (cmd&bit(i))
    {
      digitalWrite(MOSI_P, HIGH);
      //Serial.println(1);
    }
    else
    {
      digitalWrite(MOSI_P, LOW);
      //Serial.println(0);
    }
    
    digitalWrite(CLK_P, HIGH);
    delay(1);
  
    if (output)
    Serial.print(digitalRead(11));
    
    digitalWrite(CLK_P, LOW);
    delay(1);
  }
}

void programmingEnable(boolean x)
{ 
  if (x)
  {
    digitalWrite(MOSI_P, LOW);
    digitalWrite(RST_P, LOW);
    digitalWrite(CLK_P, LOW);
    delay(50);
    digitalWrite(RST_P, HIGH);
    delay(50);
    //digitalWrite(CLK_P, HIGH);
  }
  else
  {
    digitalWrite(RST_P, LOW);
    digitalWrite(MOSI_P, HIGH);
    digitalWrite(CLK_P, HIGH);
  }
  
  delay(1);
  
}

void loop()
{  
  programmingEnable(true);
  sendCmd(ENABLE_PROGRAMMING, true);
  delay(1);
  //sendCmd(CHIP_ERASE, false);
  //delay(1000);
  //sendCmd(WRITE_BYTE, false);
  //delay(1000);
  //sendCmd(READ_BYTE, true);
  while(1);
}

Thanks for your explanations.

It is not defined (in the datasheet) what is shifted in while you shift out the first 3 bytes.
But while you shift out the 4th byte, you should see 0x69 being shifted in.
It does not matter what you shift out as 4th byte but is makes sense to shift out 0x00 and check whether you shift in 0x69:

#define ENABLE_PROGRAMMING 0xAC530000L

I tried your sketch on a leonardo connected to an at89s52. I see this:

11111111111111111111111101101001

The 4th byte reads 0x69 indeed.

I extended the skech so it also reads in the signature bytes:

#define READ_SIGNATURE_0  0x28000000L
#define READ_SIGNATURE_1   0x28010000L
#define READ_SIGNATURE_2  0x28020000L
...

sendCmd(READ_SIGNATURE_0, true);
sendCmd(READ_SIGNATURE_1, true);
sendCmd(READ_SIGNATURE_2, true);

This gives me this extra output:

00000000001010000000000000011110
00000000001010000000000101010010
00000000001010000000001000000110

The last bytes from each line are: 0x1E 0x52 0x06, which is indeed the signature of an at89s52.

A couple of years ago I wanted to explore the 8051. For this purpose I sampled two types of the more recent at89lp series. I added at89lp support into ArduinoISP so I could use avrdude to program the chips.
I digged up that project and am adding support for the at89s52 which looks not a lot of work. I can keep you posted if you want.

Thanks PeterVH.

I don't know why my sketch doesn't work ?

The circuit is very easy; I have 4 pins connected to arduino for serial commands, 6Mhz crystal oscillator (The only one that I have for the time being) with 2 22pF caps and Vcc + Gnd.

What do you think, the chip is faulty ?

Thks.

Wiring reliable? caps and crystal close enough to ic?
Maybe post a pic of your setup.

I don't know if it helps but I get almost the same output with the sketch:
I use a 16MHz and 2 20pf caps

11111111111111111111111101101001
01101001001010000000000000011110
00000000001010000000000101010010
00000000001010000000001000000110

A USBasp programmer connected to the circuit also works and for some reason that I don't understand I don't have to invert the reset line.

6MHz should be ok but it is worth tring with another crystal. Mine is 12 MHz. caps are 2x 20 pF.

@erni: What I don't understand either is that usbasp manages to read the signature bits: for the at89s52 thse are not in contiguous places in memory, they are at 0, 0x100 and 0x200. I wonder how usbasp manages to read these 3 bytes, without special ad hoc code, just with the info coming from avrdude?

BlackSharp
If you are using a breadboard, you could try moving the processor to another spot, that has helped me sometimes.
Also cleaning the legs on the processor is worth a try.
I apologize if you feel I hiijacked your thread, I can start a new if you wish

PeterVH
I have managed to program the chip with a Pololu programmer. The programming process works without errors.
But my blink program dosn't work (the LED is always on). Is there something magical you should do after programming are done. I have tried holding the reset high and low through a 10k resistor, but that makes no difference.

The program is below, I compiled it in Keil, and in the Keil logic analyzer it works.

#include <reg52.h> 
#define ON 0 
#define OFF 1 
sbit LED = P2^4; 
void DelayMs(unsigned int msec); 
void DelayMs(unsigned int msec)  
{ 
 unsigned int x,y; 
 for(x=0; x<=msec;x++) 
 { 
 for(y=0;y<=110;y++); 
 } 
} 
void main(void) 
{ 
 for(;;) 
 { 
 LED = ON;  
 DelayMs(50); 
 LED = OFF;  
 DelayMs(50); 
 } 
}

Maybe it blinks but only weakly? I just observed that he ports p1 through p3 cannot source a lot of current.
So if you wire like this: "mcu port - led anode - R330 - ground", the led only lights up weakly.
If wire like this "mcu port - led cathode - R330 - 5V" it is more bright. Current is then about 15mA which is on the edge of the spec.

Does the hex file in the avr freaks link I sent above work?

I noticed that after programming the flash, I have to power cycle the chip, but you probably already tried that.
(In mean time I manage to program the chip using (a modified) ArduinoISP)

Got it !

What I missed was connecting the EA/VPP pin to VCC, and the 10uF capacitor as shown in your link from avrfreaks.
I use 1k resistor to be safe, and you are right, if connected like mcu port - led anode - R330 - ground, the blink is very weak and could be missed.

No problem Erni, since it will help you out.

Regrading my problem I tried several things:

  • I changed the crystal oscillator from 6 to 11Mhz.
  • I added pulldown resistors to prevent the circuit from noise.

Unfortunately, no result.

In the attachment is my circuit, feel free to ask for more different images if needed.

Question:
This type of uController is little bit difficult to find it in the market nowdays and especially in my country. My question is there any devices or game consoles that have the 8051 architecture to remove it from there and use it for this purpose ? Because the only thing now that I suspect is the chip.

Regards.

Did you try to connect the EA pin (pin 31) and the 10 uF capacitor as shown in the avrfreaks link.

Hi,

I don't know if I should continue on this topic or create a new one. Please advise.

I tried what you have said but is still not working. However, I changed this chip with Atmega328p just to test the "Programming enable instruction" and some other stuffs and Boooom!! it works !!.

So, since I don't have for the time being the "At89s51" I will continue working on Atmega328p and how to program it from scratch using Arduino. I know that there are great ISP programmers for AVR in the market, but I try to build it and code it myself to learn as much as possible.

I'm now struggling to write to the "Program memory page" in the Atmega, and what I've understood till now is that I should load the program memory page at first with low and high data byte, and after send write instruction to the chip to save data in the memory page. (Correct me if i'm wrong).

This is the code that I'm using:

#define ENABLE_PROGRAMMING 0xAC530000L
#define CHIP_ERASE         0xAC800000L

#define READ_BYTE_LSB      0x20000000L
#define READ_BYTE_MSB      0x28000000L

#define WRITE_BYTE         0x4C000000L

#define LOAD_BYTE_MSB      0x48000041L
#define LOAD_BYTE_LSB      0x40000041L

#define SIGNATURE0         0x30000000L
#define SIGNATURE1         0x30000100L
#define SIGNATURE2         0x30000200L

#define READ_LOCK_BITS     0x58000000L
#define WRITE_LOCK_BITS    0xACE00000L

#define LOCKB_NOPROTECT    0x000000FFL
#define LOCKB_NOPROGRAM    0x000000FEL
#define LOCKB_ALLPROTECT   0x000000FCL

int RST_P = 9;
int MOSI_P = 10;
int MISO_P = 11;
int CLK_P  = 12;

void setup()
{
  Serial.begin(9600);
  
  pinMode(8, OUTPUT);
  
  pinMode(MOSI_P, OUTPUT);
  pinMode(MISO_P, INPUT);
  pinMode(RST_P, OUTPUT);
  pinMode(CLK_P, OUTPUT);
  
  digitalWrite(RST_P, HIGH);
  digitalWrite(MOSI_P, HIGH);
  digitalWrite(CLK_P, LOW);
}

void sendCmd(long cmd, boolean output)
{
  boolean counter_bool = false;
  int counter = 0;
  
  for (int i=31; i>=0; i--)
  {
    if (cmd&bit(i))
    {
      digitalWrite(MOSI_P, HIGH);
    }
    else
    {
      digitalWrite(MOSI_P, LOW);
    }
    
    digitalWrite(CLK_P, HIGH);
    delay(1);
    
    if (output/*&&counter_bool*/)
    Serial.print(digitalRead(MISO_P));
    
    digitalWrite(CLK_P, LOW);
  }
  Serial.println("");
}

void programmingEnable(boolean x)
{ 
  int  c = 0;
  
  if (x)
  {
    digitalWrite(MOSI_P, LOW);
    digitalWrite(CLK_P, LOW);
    digitalWrite(RST_P, LOW);
    
    //A LED indicator
    for (int i=0; i<4; i++) {
    delay(40);
    digitalWrite(8, c=(~c));
    } 
    //digitalWrite(CLK_P, HIGH);
  }
  else
  {
    digitalWrite(RST_P, HIGH);
    digitalWrite(MOSI_P, HIGH);
    digitalWrite(CLK_P, HIGH);
  }
  
  delay(1);
  
}

void programChip(char *program)
{
  long address = 0x00000000;
  int counter = 0;
  int len = strlen(program);
  while (counter != len)
  {
    for (;;)
    counter ++;
  }
}

void loop()
{  
  programmingEnable(true);
  sendCmd(ENABLE_PROGRAMMING, false);
  sendCmd(LOAD_BYTE_LSB, false);
  sendCmd(LOAD_BYTE_MSB, false);
  sendCmd(WRITE_BYTE, false);
  sendCmd(WRITE_BYTE, false);
  sendCmd(READ_BYTE_LSB, true);
  
  while(1);
}

What am I supposed to do to write correctly to the program memory ?

Thanks

That code snippet should program 0x4141 in page 0, commit the page and read one byte back in.
Doesn't it do that?

B.t.w. I think you'll learn things much faster if you study existing code that does this kind of things.

Personally, I found it helphfull to add println's to ArduinoISP and observe it in action. But to do that, you need an arduino (sanguino, leonardo, mega...) with an extra uart for the printl's because avrdude uses the default one.

Alternatively you could study Nick Gammon's programmer. It has the serial port free for println's and it does exactly what you are building.