Dependencies:   mbed

AVR910.cpp

Committer:
chris
Date:
2010-11-01
Revision:
1:ce6a12e62219
Parent:
0:20fc0ecfb510

File content as of revision 1:ce6a12e62219:

//****************************************************************************/
// Description:
//
//  Program AVR chips with the AVR910 ISP (in-system programming) protocol,
//  using an mbed.
//
// AVR Application Note:
//
//  http://www.atmel.com/dyn/resources/prod_documents/doc0943.pdf
//****************************************************************************/

//****************************************************************************/
// Includes
//****************************************************************************/
#include "AVR910.h"

Serial debug(USBTX, USBRX);

AVR910::AVR910(PinName mosi, PinName miso, PinName sclk, PinName nReset) {

    spi_    = new SPI(mosi, miso, sclk);
    spi_->frequency(32000);
    spi_->format(8, 1);
    nReset_ = new DigitalOut(nReset);

}

int AVR910::program(FILE* binary) {

    int response = 0;

    debug.printf("mbed AVR910 Programmer starting\n");

    //Enter serial programming mode by pulling reset line low.
    *nReset_ = 0;

    //Wait 20ms before issuing first command.
    wait_ms(20);

    //Issue a programming enable command.
    response = enableProgramming();

    if (response < 0) {
        debug.printf("Enable programming command not successful\n");
    } else {
        debug.printf("Enable programming command successful\n");
    }

    //Read vendor code, part family and part number.
    response = readVendorCode();

    if (response == ATMEL_VENDOR_CODE) {
        debug.printf("Microcontroller is an Atmel [0x%02x]\n", response);
    } else if (response == DEVICE_LOCKED) {
        debug.printf("Device is locked\n");
    } else {
        debug.printf("Microcontroller is not an Atmel\n");
    }

    response = readPartFamilyAndFlashSize();

    if (response == 0xFF) {
        debug.printf("Device code erased or target missing\n");
    } else if (response == 0x01) {
        debug.printf("Device locked\n");
    } else {
        debug.printf("Part family and flash size code is: 0x%02x\n", response);
    }

    response = readPartNumber();

    if (response == 0xFF) {
        debug.printf("Device code erased or target missing\n");
    } else if (response == 0x02) {
        debug.printf("Device locked\n");
    } else {
        debug.printf("Part number code is: 0x%02x\n", response);
    }

    //Clear memory contents.
    chipErase();

    char pageOffset = 0;
    int  pageNumber = 0;
    int  c          = 0;
    int  highLow    = 0;

    while ((c = getc(binary)) != EOF) {

        //Page is fully loaded, time to write it to flash.
        if (pageOffset == (PAGE_SIZE)) {
            writeFlashMemoryPage(pageNumber);
            debug.printf("Page %i written\n", pageNumber);
            pageNumber++;
            pageOffset = 0;
        }

        //Write low byte.
        if (highLow == 0) {
            loadMemoryPage(WRITE_LOW_BYTE, pageOffset, c);
            highLow = 1;
        }
        //Write high byte.
        else {
            loadMemoryPage(WRITE_HIGH_BYTE, pageOffset, c);
            highLow = 0;
            pageOffset++;
        }

    }

    //We might have partially filled up a page.
    writeFlashMemoryPage(pageNumber);

    int success = -1;
    success = checkMemory(pageNumber, binary);

    //Leave serial programming mode by pulling reset line high.
    *nReset_ = 1;
    
    return success;

}

void AVR910::setFrequency(int frequency){

    spi_->frequency(frequency);

}

int AVR910::enableProgramming(void) {

    int response = 0;
    int error    = 0;

    //Programming Enable Command: 0xAC, 0x53, 0x00, 0x00
    //Byte two echo'd back in byte three.
    spi_->write(0xAC);

    spi_->write(0x53);

    response = spi_->write(0x00);

    if (response == 0x53) {
        error =  0;
    } else {
        error = -1;
    }

    spi_->write(0x00);

    return error;

}

void AVR910::poll(void) {

    int response = 0;

    do {
        spi_->write(0xF0);
        spi_->write(0x00);
        spi_->write(0x00);
        response = spi_->write(0x00);
    } while ((response & 0x01) != 0);

}

int AVR910::readVendorCode(void) {

    int response = 0;

    //Issue read signature byte command.
    //Address 0x00 is vendor code.
    spi_->write(0x30);
    spi_->write(0x00);
    spi_->write(0x00);
    response = spi_->write(0x00);

    return response;

}

int AVR910::readPartFamilyAndFlashSize(void) {

    int response = 0;

    //Issue read signature byte command.
    //Address 0x01 is part family and flash size code.
    spi_->write(0x30);
    spi_->write(0x00);
    spi_->write(0x01);
    response = spi_->write(0x00);

    return response;

}

int AVR910::readPartNumber(void) {

    int response = 0;

    //Issue read signature byte command.
    //Address 0x02 is part number code.
    spi_->write(0x30);
    spi_->write(0x00);
    spi_->write(0x02);
    response = spi_->write(0x00);

    return response;

}

void AVR910::chipErase(void) {

    //Issue chip erase command.
    spi_->write(0xAC);
    spi_->write(0x80);
    spi_->write(0x00);
    spi_->write(0x00);

    poll();

    //Temporarily release reset line.
    *nReset_ = 1;
    *nReset_ = 0;

}

void AVR910::loadMemoryPage(int highLow, char address, char data) {

    spi_->write(highLow);
    spi_->write(0x00);
    spi_->write(address & 0x3F);
    spi_->write(data);

    poll();

}

void AVR910::writeFlashMemoryPage(char pageNumber) {

    spi_->write(0x4C);
    spi_->write((pageNumber >> 2) & 0x3F);
    spi_->write((pageNumber & 0x03) << 6);
    spi_->write(0x00);

    poll();

}

char AVR910::readProgramMemory(int highLow, char pageNumber, char pageOffset) {

    int response = 0;

    spi_->write(highLow);
    spi_->write((pageNumber >> 2) & 0x3F);
    spi_->write(((pageNumber & 0x03) << 6) | (pageOffset & 0x3F));
    response = spi_->write(0x00);

    poll();

    return response;

}

int AVR910::checkMemory(int numPages, FILE* binary){

    int success  = 0;
    int response = 0;
    char c       = 0;

    //Go back to the beginning of the binary file.
    fseek(binary, 0, SEEK_SET);

    for(int i = 0; i < numPages; i++){
        for (int j = 0; j < PAGE_SIZE; j++) {
            c = getc(binary);
            //Read program memory low byte.
            response = readProgramMemory(READ_LOW_BYTE, i, j);
            //debug.printf("Low byte: 0x%02x\n", response);
            if( c != response ){
                debug.printf("page %i low byte %i: 0x%02x\n", i, j, response);
                debug.printf("correct byte is 0x%02x\n", c);
                success = -1;
            }

            c = getc(binary);
            //Read program memory high byte.
            response = readProgramMemory(READ_HIGH_BYTE, i, j);
            //debug.printf("High byte: 0x%02x\n", response);
            if( c != response ){
                debug.printf("page %i high byte %i: 0x%02x\n", i, j, response);
                debug.printf("correct byte is 0x%02x\n", c);
                success = -1;
            }
        }
    }
    
    return success;

}