A fork of mAVRISP by Aaron Berk. This version does not use a local file system to hold the AVR program. Instead it uses a serial connection to a PC and a python script to send the AVR program to the mbed.

Dependencies:   mbed

Fork of mAVRISP by Aaron Berk

AVR910_Serial.cpp

Committer:
jeroenmbed
Date:
2015-01-31
Revision:
4:ceee1eb7062e

File content as of revision 4:ceee1eb7062e:

/**
 * @author Aaron Berk, Jeroen Voogd
 *
 * @section LICENSE
 *
 * Copyright (c) 2010 Aaron Berk
 * Copyright (c) 2015 Jeroen Voogd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 *
 * Program AVR chips with the AVR910 ISP (in-system programming) protocol,
 * using an mbed. The version of Aaron Berk used a local file system, this version
 * useses a python script to feed the data via a serial connection.
 *
 * AVR910 Application Note:
 *
 * http://www.atmel.com/dyn/resources/prod_documents/doc0943.pdf
 */

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

Serial debug(USBTX, USBRX);

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

    //Slow frequency as default to ensure no errors from
    //trying to run it too fast. Increase as appropriate.
    spi_.frequency(32000);
    spi_.format(8, 0);

    int response = 0;

    //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();

    //TODO: introduce a timeout.
    while (response < 0) {

        //Give nReset a positive pulse.
        nReset_ = 1;
        wait_ms(20);
        nReset_ = 0;
        wait_ms(20);

        //Issue another programming enable.
        response = enableProgramming();
    }
}


int AVR910::programData(char* myData, int dataSize, int pageSize, int numPages)
{

    //Clear memory contents.
    chipErase();

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

    //In this version we're always dealing with paged memory.

    for (int i=0; i<dataSize; i++) {
        c = myData[i];
        //Page is fully loaded, time to write it to flash.
        if (pageOffset == (pageSize)) {
            writeFlashMemoryPage(pageNumber);

            pageNumber++;
            if (pageNumber > numPages) {
                break;
            }
            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 = checkMemoryData(pageNumber, myData, dataSize);

    //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;
    }

    response = 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::writeFlashMemoryByte(int highLow, int address, char data)
{

    spi_.write(highLow);
    spi_.write(address & 0xFF00 >> 8);
    spi_.write(address & 0x00FF);
    spi_.write(data);

}

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::checkMemoryData(int numPages, char* myData, int dataSize)
{
    
    int success  = 0;
    int response = 0;
    char c       = 0;

    // start at the beginning of the original data
    int dpoint=0;
    
    for (int i = 0; i <= numPages; i++) {
        for (int j = 0; j < PAGE_SIZE; j++) {

            c = myData[dpoint];
            //Read program memory low byte.
            response = readProgramMemory(READ_LOW_BYTE, i, j);
            if ( c != response ) {
                success = -1;
            }
            
            // next data item to check, break if all data has been checked
            dpoint++;
            if (dpoint==dataSize) break;
            c = myData[dpoint];
            
            //Read program memory high byte.
            response = readProgramMemory(READ_HIGH_BYTE, i, j);
            if ( c != response ) {
                success = -1;
            }
            
            // next data item to check, break if all data has been checked
            dpoint++;
            if (dpoint==dataSize) break;
        }
    }

    return success;

}