P2 Programming an ATTiny85
The other day I ran across this MCU and thought it would be a good puzzle to solve.
I like programming and figuring out new devices. It looked like a good win win situation.
There are several you tube videos on the subject but of course none of them deal with the P2 or P1 for that matter. So if I get stuck I can always check them out.
Now the ATTiny85 is at end of life and there are better options out there but for $1.32 it was a small investment to make for my entertainment.
The chip comes in an 8 pin dip so using a bread board was going to work out. I finally found a use for my johnny Mac board.
Armed with the Datasheet I set out to program the ATTiny85 to blink an LED. Once I have that accomplished the puzzle is solved.
To talk to this chip with only 8 pins is very interesting and turns out to be not too bad considering to program the P1 takes a hole lot of crazy code.
The ATTiny85 uses an SPI type of programming where you have MOSI, MISO, and CLK to send and receive data. This makes it simple enough to clock in my data and see the results.
The first step was to put the unit into programming mode so I could program the chip. This turned out to be a little confusing as the docks state the inbound data is clocked on the rising edge and the out bound data is on the falling edge. Well, the outbound data is available before the failing edged so I was losing one bit of data on my return so I was not getting the 0x53 I was expecting.
Next I wrote a function to retrieve the signature bytes so I knew I had an ATTiny85 chip.
After that I thought if I can write to the EEPROM memory I was well on my way. This turned out to be simple enough and was able to program the EEPROM memory.
Now for the hard part. How to write a program that would blink an LED. The program code is difficult to write so I found a program on You Tube that talked about writing a program for the ATTiny85 without Arduino since that is where this came from.
Using avr-gcc I was able to build a program to turn the LED on but not really blink it since figuring out how to delay the processor was not a simple task.
#define DDRB (*(volatile unsigned char*) 0x37) #define PORTB (*(volatile unsigned char*) 0x38) int main() { DDRB |= 1; // set BP0 to output PORTB = 1; }
Now this generates a program in hex that looks like this:
:100000000EC015C014C013C012C011C010C00FC064 :100010000EC00DC00CC00BC00AC009C008C011241E :100020001FBECFE5D2E0DEBFCDBF02D007C0E8CF14 :10003000B89A81E088BB80E090E00895F894FFCF03 :00000001FF
This is the Intel file format that is very easy to read. Coping this into my program as an array and then converting it to hex was simple enough.
Now to program the flash memory which is in words where the EEPROM was in bytes turned out to be a lot confusing. It states that each page is 32 words and there are 128 pages. So each page goes from 0x00 to 0x1f and then starts over like other flash. Wrong, the first page is 0 and the next page is 0x20, 0x30 and so on. Since I went from zero to 1 as my first page that overwrote the first pages data and so on. A real
head scratcher.
So the addresses go from 0 to 128 and you have to commit each 32 word pages as you go.
Now after a couple of days playing with this, I was able to get the LED to light.
Now to use this chip with Arduino where you can program all kinds of things I would need to turn the P2 into an AVRDUDE programmer. This is what Arduino expects to see when it has to write the program to an ATTiny85 chip.
I haven't decided if I want to do that yet but converting the Arduino code to P2 doesn't look too difficult.
I still might try to blink the LED with some kind of loop. The problem is that the compiler sees it as dead code and gets rid of it. Maybe I can enlist one of the timers.
Here is the C program that writes to the ATTiny85. I guess this would be simple enough for someone to write in SPIN.
#include <stdio.h> #include <propeller.h> #define RESET 26 #define MOSI 27 #define MISO 28 #define SCK 29 #define PGMEN 0xAC530000 #define CHPERASE 0xAC800000 #define RDYBSY 0xF0000000 #define LDEADD 0x4D000000 #define LDPGM 0x40000000 #define LDEEPRM 0xC1000000 #define RDPGM 0x20000000 #define RDEEPRM 0xA0000000 #define RDSIGN 0x30000000 #define RDFUSE 0x50000000 #define RDCALB 0x38000000 #define WRPGM 0x4C000000 #define WREEPRM 0xC0000000 #define WRFUSE 0xACA00000 void PgmMode(void); unsigned int SendIt(unsigned int); void Reset(void); unsigned int Signature(void); unsigned char *ReadPgm(int); void WritePgm(int, unsigned char *); unsigned char *ReadEEProm(void); void WriteEEProm(unsigned char *); void Erase(void); int DoHex(unsigned char*); char Hex[][65] = {"0EC015C014C013C012C011C010C00FC00EC00DC00CC00BC00AC009C008C01124", "1FBECFE5D2E0DEBFCDBF02D007C0E8CFB89A81E088BB80E090E00895F894FFCF"}; unsigned char Buffer[256] = {0x21, 0x22, 0x23, 0x24}; unsigned short Instr[256] = { 0x21, 0x22, 0x23, 0x24}; int verbose; int main(int argc, char** argv) { unsigned int data; verbose = 0; data = 0; Reset(); PgmMode(); Erase(); data = SendIt(RDFUSE); printf("Fuse: %X\n", data); //data = SendIt(WRFUSE | 0xE2); //printf("FuseW: %X\n", data); data = SendIt(RDYBSY); printf("Busy: %x\n", data); data = Signature(); printf("Signature: %x\n", data); data = SendIt(RDCALB); printf("Calibration: %x\n", data); ReadPgm(0); ReadEEProm(); DoHex(Hex[0]); DoHex(Hex[1]); // verbose = 1; WritePgm(0, Hex[0]); WritePgm(0x20, Hex[1]); // verbose = 0; ReadPgm(0); ReadPgm(0x20); //WriteEEProm(Buffer); while (1) { _pinl(56); _waitms(1000); _pinh(56); _waitms(1000); } } /** * @brief Send data high byte first * @param cmd * @return unsigned int */ unsigned int SendIt(unsigned int cmd) { int r; if (verbose) printf("S: %x\n", cmd); r = 0; for (int i=0;i<32;i++) { if ((cmd & 0x80000000) == 0) _pinl(MOSI); else _pinh(MOSI); _waitus(100); _pinh(SCK); _waitus(100); r = r << 1; r = r | _pinr(MISO); _pinl(SCK); _waitus(100); cmd = cmd << 1; } return r; } /** * @brief Put chip in program mode */ void Reset() { _pinl(RESET); _waitms(50); _pinl(SCK); _pinh(RESET); _waitms(50); _pinl(RESET); } /** * @brief Enable Program Mode */ void PgmMode() { unsigned int data; data = SendIt(PGMEN); if (data == 0x5300) return; printf("Error\n"); } /** * @brief Read Signature bytes * @return signature bytes */ unsigned int Signature() { unsigned int data; unsigned int value; unsigned int j = RDSIGN; value = 0; for (int i=0;i<3;i++) { data = SendIt(j | (i << 8)); value = (value << 8) | (data & 0xff); } return value; } unsigned char *ReadPgm(int addr) { unsigned short instruction; int p; int v; for (int i=0;i<32;i++) { instruction = SendIt(RDPGM | (i+addr) << 8); instruction |= (SendIt(RDPGM | 0x08000000 | (i+addr) << 8)) << 8; Instr[i] = instruction; printf("Instruction:%4x: %x\n", i+addr, instruction); } return NULL; } void WritePgm(int addr, unsigned char *pgm) { int p; int x; p = addr; for (int i=0;i<64;i++) { if ((i & 0x01) == 0) x = SendIt(LDPGM | (p << 8) | pgm[i]); else x = SendIt((LDPGM | 0x08000000 | (p++ << 8)) | pgm[i]); } p = addr & 0xffffffe0; SendIt(WRPGM | (p << 8)); _waitms(10); while ((SendIt(RDYBSY) & 0x01) == 0x01) _waitms(4); } unsigned char *ReadEEProm() { unsigned char data[16]; printf("Data: "); for (int i=0;i<16;i++) { data[i] = SendIt(RDEEPRM | i << 8); printf("%x ", data[i]); } printf("\n"); return NULL; } void WriteEEProm(unsigned char *data) { for (int i=0;i<4;i++) { SendIt(WREEPRM | (i << 8) | data[i]); while ((SendIt(RDYBSY) & 0x01) == 0x01) _waitus(10); } } void Erase() { SendIt(CHPERASE); while ((SendIt(RDYBSY) & 0x01) == 0x01) _waitms(5); } int DoHex(unsigned char *data) { int i, j, h, l; j = 0; i = 0; while (data[i] != NULL) { h = data[i] - '0'; l = data[i+1] - '0'; if (h > 9) h = h - 7; if (l > 9) l = l - 7; data[j++] = h << 4 | l; i += 2; } return j; }
Mike
Comments
Programming ATTiny84 and ATTiny85 by P1
https://forums.parallax.com/discussion/comment/1044080/#Comment_1044080
Well, I had sum success but ran into issues with programming the ATTiny85. I could not get a program over 64 bytes to work. Just did strange things that I could not figure out.
I finally went and built an Arduino ISP programmer and loaded the code. Then I used AVRDUDE to program the chip and it worked.
Now I ran my program and found I was missing some data?
Then it hit me. The ATTiny85 has 32 words of program and not 32 bytes. I was programming 32 bytes and reading back 32 bytes. All was good except half the program was missing.
Updated my program and tested and it now works as described.
I guess this was more of an excursion then I had planned. Cheap puzzle at that.
Anyway I now can blink the LED.
I really like those LED's I got from Adafruit. They are the ones that fad into different colors while they are turned on. They are also 3 volt so no resistance is needed to hook them up. And if the pin is pulsing they stay red so you know there is activity.
Mike
In my advancement of programming the ATTiny85 I started to write more complex programs as I wanted to do serial in and serial out.
This development get old using just a simple array to write the program code out. So I bit the bullet and converted the AVRISP program written for the Arduino to run on the P2. With this code I could use the AVRDUDE program that comes with the GNU compiler to load programs onto the ATTiny85.
Even though the code is written in C all that is needed is to set the pins that you need to use for your programming environment and compile it with Flexprop. From there it is transparent to just send your Intel Hex file to the P2 using AVRDUDE.
Since there are no special libraries require to run this program it just needs to be compiled.
There are 3 LED's that are needed but not required but show the status of what is happening.
define LED --> Turns on when in programming mode
define LEDE ---> Turns on when there is an Error in serial data
define LEDH ---> Heartbeat that flashes about every 1/2 second.
The steps to build and load a program is as follows:
The output should look like this:
Mike
Interesting idea. This could be useful for repeat programming where you have to program a lot of devices, perhaps with a serial number increment on each one