Code to program an AVR microcontroller over SPI using the mBed's first SPI port. The AVR must support ICSP. Alter max page size from 64 to the matching page size for your microcontroller. Alter the clock to the minimum clock speed for your microcontroller. Has only been tested with ATMega88.

Dependencies:   mbed

main.cpp

Committer:
Chamilton09
Date:
2010-06-21
Revision:
0:52ce2a69824f

File content as of revision 0:52ce2a69824f:

// Code to implement ICSP of an AVR using the ISP on the mBed pins 5,6,7 
// and 8 as RESET

//NOTES:    -load hex file onto the mBed as a file named "avr"
//          -Change TARGET_CLK to a suiteable value for your particular avr (it gets divided by 2 for ATMega88)
//          -Change the max page size to suite the microcontroller (change the 64 only)

#include "mbed.h"
#define LSB(I) ((I) & 0xFF)
#define MSB(I) (((I) & 0xF00) >> 8)

#define TARGET_CLK 1000000L
#define MAX_PAGE_SIZE (64 >> 1) //divide max page size by 2 to get number of words per page                                    
                                                   
SPI spi(p5, p6, p7); // mosi, miso, sclk/
DigitalOut reset(p8);   //RESET

Serial pc(USBTX, USBRX); // tx, rx

LocalFileSystem local("local");


int enable_prog_mode() //function to enable prog mode
{
    int failed = 0;
    
    while(failed < 5) //loop to try to enable programming mode 5 times
    {
        spi.write(0xAC);
        spi.write(0x53);
        int prog_en_check = spi.write(0x00);
        if(prog_en_check == 0x53) { //if data read on 3rd byte load is 0x53
            spi.write(0x00);        //programming mode was enabled...good job!
            pc.printf("\nProgramming mode enabled...\n");
            return 0;
        }      
        else{
            pc.printf("Programming mode failed!\n");
            pc.printf("%d\n",prog_en_check); 
            failed++;
            reset = 1; //pulse reset and try again
            reset = 0;
            pc.printf("Trying again...\n");
            wait(0.5);
        }
     }
    return -1; //Bummer...
}

//A function to poll BSY to prevent loading a new instruction prematurely
void still_programming() 
{
    int check = 1;
    while(check == 1)
    {
        spi.write(0xF0);
        spi.write(0x00);
        spi.write(0x00);
        check = spi.write(0x00); //read data byte out
    }
}

//A function to send the chip erase command
void chip_erase() 
{
        pc.printf("Chip erase initiated...\n");
        spi.write(0xAC);
        spi.write(0x80);
        spi.write(0x00);
        spi.write(0x00);
        still_programming();
        pc.printf("Chip erase completed!\n");
        reset = 1;
        wait(0.020);
        reset = 0;
        wait(0.020);
        enable_prog_mode();
}


void write_prog_page(uint16_t addr)
{
    //write program memory page
    spi.write(0x4C);
    spi.write(MSB(addr));
    spi.write(LSB(addr));
    spi.write(0x00);
    still_programming(); //make sure the operation worked
   // pc.printf("Wrote a flash page.\n"); 
}


//function to load and write program memory
//args: addr is the 12 bit address of the memory location
//      low_data and high_data are the values to write to that location
void load_prog_page(uint16_t addr, uint8_t low_data, uint8_t high_data)//int addr_MSB, 
{
    //load program memory low byte (little endian)
    spi.write(0x40);
    spi.write(0x00);
    spi.write(LSB(addr));
    spi.write(low_data);
    
    //load program memory high byte
    spi.write(0x48);
    spi.write(0x00);
    spi.write(LSB(addr));
    spi.write(high_data);
    //pc.printf(" Wrote: %x %x to LSB address: %x\n", high_data, low_data, LSB(addr));
   
    // write page if 32 words have now been written
    if(addr % MAX_PAGE_SIZE == (MAX_PAGE_SIZE-1))
        write_prog_page(addr);
}

void write_lock_bits(uint8_t bits)
{
    bits = bits & 0xFF;
    spi.write(0xAC);
    spi.write(0xE0);
    spi.write(0x00);
    spi.write(bits);
    still_programming();
    pc.printf("Wrote lock bits...\n");
}

void write_fuse_bits(uint8_t bits)
{
    bits = bits & 0xFF;
    spi.write(0xAC);
    spi.write(0xA0);
    spi.write(0x00);
    spi.write(bits);
    still_programming();
    pc.printf("Wrote fuse bits...\n");
}

void write_fuse_bits_high(uint8_t bits)
{
    bits = bits & 0xFF;
    spi.write(0xAC);
    spi.write(0xA8);
    spi.write(0x00);
    spi.write(bits);
    still_programming();
    pc.printf("Wrote fuse bits high...\n");
}

void write_extended_fuse_bits(uint8_t bits)
{
    bits = bits & 0xFF;
    spi.write(0xAC);
    spi.write(0xA4);
    spi.write(0x00);
    spi.write(bits);
    still_programming();
    pc.printf("Wrote extended fuse bits...\n");
}

uint8_t read_byte(FILE *file)
{
    char ascii_char[2];
    fread(&ascii_char, 1, 2, file);
    return   (((ascii_char[0] < 65) ? (ascii_char[0]-48) : (ascii_char[0]-55)) << 4)
            | ((ascii_char[1] < 65) ? (ascii_char[1]-48) : (ascii_char[1]-55));
}

void read_prog_flash(uint8_t addr_MSB, uint8_t addr_LSB)
{
    spi.write(0x28);
    spi.write(addr_MSB);
    spi.write(addr_LSB);
    pc.printf("At address LSB %x: MSB: %x - %x ",addr_LSB,addr_MSB,spi.write(0x00));
    spi.write(0x20);
    spi.write(addr_MSB);
    spi.write(addr_LSB);
    pc.printf("%x\n",spi.write(0x00));      
}

int main() {
    reset = 1;
    // Setup the spi for 8 bit data,writing data on the rising SCK edge,
    // and reading data on the falling SCK edge as according to AVR specs
    // with a 1MHz clock rate
    pc.printf("\nPress any button to program the AVR.\n");
    char c = pc.getc();
    
    spi.format(8,1);        //SPI format: write data on rising edge, read on falling
                            //w/ a base clock value of zero
    spi.frequency(TARGET_CLK >> 2);
    //wait(0.5);
    pc.printf("SPI interface started...\n");
    
    // Select the device by seting chip select low
    reset = 0;
    wait(0.02); 

    if(enable_prog_mode() != 0)  //Enable programming mode and check for errors
        return -1;
    spi.write(0x30);
    spi.write(0x00);
    spi.write(0x00);
    pc.printf("SIGNATURE BYTE: %x\n",spi.write(0x00));
    chip_erase();
    
    write_lock_bits(0xFF);                  //1111 1111
    write_fuse_bits(0x62);                  //1110 0010
    write_fuse_bits_high(0xDF);             //1101 1111
    write_extended_fuse_bits(0xF9);         //1111 1001
    
    //Opening a file on local FS as in example code
    pc.printf("Opening File...\n"); 
    int flag = 0;
    FILE *hex_file;
    hex_file = fopen("/local/avr","r");
    if(hex_file !=NULL)
    {    
        uint16_t address = 0;
        char temp;
        temp = fgetc(hex_file);
        while(flag == 0)
        {
            if(temp == ':')
            {
                uint8_t length = read_byte(hex_file);
                if(length == 0){
                    flag = 1;
                    printf("EOF line reached.\n");
                }
                else{
                    fseek(hex_file, 6, SEEK_CUR); //2 if reading address
                    
                    for(uint8_t i=0;i<length;i+=2){
                        uint8_t low_data = read_byte(hex_file);
                        uint8_t high_data = read_byte(hex_file);
                       
                        //load data bytes here
                        load_prog_page(address, low_data, high_data);
                        
                        address++;
                    }
                    while((temp = fgetc(hex_file)) != ':');
                    //pc.printf("\n");             
                }
            }
            else flag =1;
        }
        pc.printf("WRITE THE PAGE!\n");//write page here
        write_prog_page(address);
        fclose(hex_file);
    }
    else {
        pc.printf("Failed to open the file...check the name.\n");          
    }
    pc.printf("Successfully programmed!(hopefully...)\n");    
    
    // Deselect the device
    reset = 1;
    wait(0.02);
    return 0;    
}