Program an AVR microcontroller using mbed.

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AVR910.cpp Source File

AVR910.cpp

00001 /**
00002  * @author Aaron Berk
00003  *
00004  * @section LICENSE
00005  *
00006  * Copyright (c) 2010 Aaron Berk
00007  *
00008  * Permission is hereby granted, free of charge, to any person obtaining a copy
00009  * of this software and associated documentation files (the "Software"), to deal
00010  * in the Software without restriction, including without limitation the rights
00011  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00012  * copies of the Software, and to permit persons to whom the Software is
00013  * furnished to do so, subject to the following conditions:
00014  *
00015  * The above copyright notice and this permission notice shall be included in
00016  * all copies or substantial portions of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00021  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00022  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00023  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00024  * THE SOFTWARE.
00025  *
00026  * @section DESCRIPTION
00027  *
00028  * Program AVR chips with the AVR910 ISP (in-system programming) protocol,
00029  * using an mbed.
00030  *
00031  * AVR910 Application Note:
00032  *
00033  * http://www.atmel.com/dyn/resources/prod_documents/doc0943.pdf
00034  */
00035 
00036 /**
00037  * Includes
00038  */
00039 #include "AVR910.h"
00040 
00041 Serial debug(USBTX, USBRX);
00042 
00043 AVR910::AVR910(PinName mosi,
00044                PinName miso,
00045                PinName sclk,
00046                PinName nReset) : spi_(mosi, miso, sclk), nReset_(nReset) {
00047 
00048     //Slow frequency as default to ensure no errors from
00049     //trying to run it too fast. Increase as appropriate.
00050     spi_.frequency(32000);
00051     spi_.format(8, 0);
00052 
00053     int response = 0;
00054 
00055     //Enter serial programming mode by pulling reset line low.
00056     nReset_ = 0;
00057 
00058     //Wait 20ms before issuing first command.
00059     wait_ms(20);
00060 
00061     //Issue a programming enable command.
00062     response = enableProgramming();
00063 
00064     //Simple debugging to see if we get trapped
00065     //in an infinite loop.
00066     DigitalOut working(LED1);
00067     working = 1;
00068 
00069     //TODO: introduce a timeout.
00070     while (response < 0) {
00071 
00072         //Give nReset a positive pulse.
00073         nReset_ = 1;
00074         wait_ms(20);
00075         nReset_ = 0;
00076         wait_ms(20);
00077 
00078         //Issue another programming enable.
00079         response = enableProgramming();
00080 
00081     }
00082 
00083     working = 0;
00084 
00085 }
00086 
00087 int AVR910::program(FILE* binary, int pageSize, int numPages) {
00088 
00089     //Clear memory contents.
00090     chipErase();
00091 
00092     char pageOffset = 0;
00093     int  pageNumber = 0;
00094     int  address    = 0;
00095     int  c          = 0;
00096     int  highLow    = 0;
00097 
00098     //We're dealing with paged memory.
00099     if (numPages > 1) {
00100 
00101         while ((c = getc(binary)) != EOF) {
00102 
00103             //Page is fully loaded, time to write it to flash.
00104             if (pageOffset == (pageSize)) {
00105                 writeFlashMemoryPage(pageNumber);
00106 
00107                 pageNumber++;
00108                 if (pageNumber > numPages) {
00109                     break;
00110                 }
00111                 pageOffset = 0;
00112             }
00113 
00114             //Write low byte.
00115             if (highLow == 0) {
00116                 loadMemoryPage(WRITE_LOW_BYTE, pageOffset, c);
00117                 highLow = 1;
00118             }
00119             //Write high byte.
00120             else {
00121                 loadMemoryPage(WRITE_HIGH_BYTE, pageOffset, c);
00122                 highLow = 0;
00123                 pageOffset++;
00124             }
00125 
00126         }
00127 
00128     }
00129     //We're dealing with non-paged memory.
00130     else {
00131 
00132         while ((c = getc(binary)) != EOF) {
00133 
00134             //Write low byte.
00135             if (highLow == 0) {
00136                 writeFlashMemoryByte(WRITE_LOW_FLASH_BYTE, address, c);
00137                 highLow = 1;
00138             }
00139             //Write high byte.
00140             else {
00141                 writeFlashMemoryByte(WRITE_HIGH_FLASH_BYTE, address, c);
00142                 highLow = 0;
00143                 address++;
00144 
00145                 //Page size is our memory size in the non-paged memory case.
00146                 //Therefore if we've gone beyond our size break because we
00147                 //don't have any more room.
00148                 if (address > pageSize) {
00149                     break;
00150                 }
00151 
00152             }
00153 
00154         }
00155 
00156     }
00157 
00158     //We might have partially filled up a page.
00159     writeFlashMemoryPage(pageNumber);
00160 
00161     int success = -1;
00162     success = checkMemory(pageNumber, binary);
00163 
00164     //Leave serial programming mode by pulling reset line high.
00165     nReset_ = 1;
00166 
00167     return success;
00168 
00169 }
00170 
00171 void AVR910::setFrequency(int frequency) {
00172 
00173     spi_.frequency(frequency);
00174 
00175 }
00176 
00177 int AVR910::enableProgramming(void) {
00178 
00179     int response = 0;
00180     int error    = 0;
00181 
00182     //Programming Enable Command: 0xAC, 0x53, 0x00, 0x00
00183     //Byte two echo'd back in byte three.
00184     spi_.write(0xAC);
00185 
00186     spi_.write(0x53);
00187 
00188     response = spi_.write(0x00);
00189 
00190     if (response == 0x53) {
00191         error =  0;
00192     } else {
00193         error = -1;
00194     }
00195 
00196     spi_.write(0x00);
00197 
00198     return error;
00199 
00200 }
00201 
00202 void AVR910::poll(void) {
00203 
00204     int response = 0;
00205 
00206     do {
00207         spi_.write(0xF0);
00208         spi_.write(0x00);
00209         spi_.write(0x00);
00210         response = spi_.write(0x00);
00211     } while ((response & 0x01) != 0);
00212 
00213 }
00214 
00215 int AVR910::readVendorCode(void) {
00216 
00217     int response = 0;
00218 
00219     //Issue read signature byte command.
00220     //Address 0x00 is vendor code.
00221     spi_.write(0x30);
00222     spi_.write(0x00);
00223     spi_.write(0x00);
00224     response = spi_.write(0x00);
00225 
00226     return response;
00227 
00228 }
00229 
00230 int AVR910::readPartFamilyAndFlashSize(void) {
00231 
00232     int response = 0;
00233 
00234     //Issue read signature byte command.
00235     //Address 0x01 is part family and flash size code.
00236     spi_.write(0x30);
00237     spi_.write(0x00);
00238     spi_.write(0x01);
00239     response = spi_.write(0x00);
00240 
00241     return response;
00242 
00243 }
00244 
00245 int AVR910::readPartNumber(void) {
00246 
00247     int response = 0;
00248 
00249     //Issue read signature byte command.
00250     //Address 0x02 is part number code.
00251     spi_.write(0x30);
00252     spi_.write(0x00);
00253     spi_.write(0x02);
00254     response = spi_.write(0x00);
00255 
00256     return response;
00257 
00258 }
00259 
00260 void AVR910::chipErase(void) {
00261 
00262     //Issue chip erase command.
00263     spi_.write(0xAC);
00264     spi_.write(0x80);
00265     spi_.write(0x00);
00266     spi_.write(0x00);
00267 
00268     poll();
00269 
00270     //Temporarily release reset line.
00271     nReset_ = 1;
00272     nReset_ = 0;
00273 
00274 }
00275 
00276 void AVR910::loadMemoryPage(int highLow, char address, char data) {
00277 
00278     spi_.write(highLow);
00279     spi_.write(0x00);
00280     spi_.write(address & 0x3F);
00281     spi_.write(data);
00282 
00283     poll();
00284 
00285 }
00286 
00287 void AVR910::writeFlashMemoryByte(int highLow, int address, char data) {
00288 
00289     spi_.write(highLow);
00290     spi_.write(address & 0xFF00 >> 8);
00291     spi_.write(address & 0x00FF);
00292     spi_.write(data);
00293 
00294 }
00295 
00296 void AVR910::writeFlashMemoryPage(char pageNumber) {
00297 
00298     spi_.write(0x4C);
00299     spi_.write((pageNumber >> 2) & 0x3F);
00300     spi_.write((pageNumber & 0x03) << 6);
00301     spi_.write(0x00);
00302 
00303     poll();
00304 
00305 }
00306 
00307 char AVR910::readProgramMemory(int highLow, char pageNumber, char pageOffset) {
00308 
00309     int response = 0;
00310 
00311     spi_.write(highLow);
00312     spi_.write((pageNumber >> 2) & 0x3F);
00313     spi_.write(((pageNumber & 0x03) << 6) | (pageOffset & 0x3F));
00314     response = spi_.write(0x00);
00315 
00316     poll();
00317 
00318     return response;
00319 
00320 }
00321 
00322 int AVR910::checkMemory(int numPages, FILE* binary) {
00323 
00324     int success  = 0;
00325     int response = 0;
00326     char c       = 0;
00327 
00328     //Go back to the beginning of the binary file.
00329     fseek(binary, 0, SEEK_SET);
00330 
00331     for (int i = 0; i < numPages; i++) {
00332         for (int j = 0; j < PAGE_SIZE; j++) {
00333             c = getc(binary);
00334             //Read program memory low byte.
00335             response = readProgramMemory(READ_LOW_BYTE, i, j);
00336             //debug.printf("Low byte: 0x%02x\n", response);
00337             if ( c != response ) {
00338                 debug.printf("Page %i low byte %i: 0x%02x\n", i, j, response);
00339                 debug.printf("Correct byte is 0x%02x\n", c);
00340                 success = -1;
00341             }
00342 
00343             c = getc(binary);
00344             //Read program memory high byte.
00345             response = readProgramMemory(READ_HIGH_BYTE, i, j);
00346             //debug.printf("High byte: 0x%02x\n", response);
00347             if ( c != response ) {
00348                 debug.printf("Page %i high byte %i: 0x%02x\n", i, j, response);
00349                 debug.printf("Correct byte is 0x%02x\n", c);
00350                 success = -1;
00351             }
00352         }
00353     }
00354 
00355     return success;
00356 
00357 }