mirror of
https://github.com/stargieg/bacnet-stack
synced 2025-10-26 23:35:52 +08:00
715 lines
18 KiB
C++
715 lines
18 KiB
C++
/*****************************************************************************
|
|
*
|
|
* Atmel Corporation
|
|
*
|
|
* File : AVRInSystemProg.cpp
|
|
* Compiler : Dev-C++ 4.9.8.0 - http://bloodshed.net/dev/
|
|
* Revision : $Revision: 1163 $
|
|
* Date : $Date: 2006-08-02 15:38:16 +0200 (on, 02 aug 2006) $
|
|
* Updated by : $Author: ohlia $
|
|
*
|
|
* Support mail : avr@atmel.com
|
|
*
|
|
* Target platform : Win32
|
|
*
|
|
* AppNote : AVR911 - AVR Open-source Programmer
|
|
*
|
|
* Description : A class providing an interface to the AVR ISP described
|
|
* in Application Note AVR910. This class is derived from AVRPRogrammer.
|
|
*
|
|
*
|
|
****************************************************************************/
|
|
#include "AVRInSystemProg.hpp"
|
|
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
#define MEM_PROGRESS_GRANULARITY 256 // For use with progress indicator.
|
|
|
|
|
|
/* Constructor */
|
|
AVRInSystemProg::AVRInSystemProg( CommChannel * _comm ) :
|
|
AVRProgrammer::AVRProgrammer( _comm )
|
|
{
|
|
/* No code here */
|
|
}
|
|
|
|
|
|
/* Destructor */
|
|
AVRInSystemProg::~AVRInSystemProg()
|
|
{
|
|
/* No code here */
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::enterProgrammingMode()
|
|
{
|
|
/* Must select a device from the AVRISP device code table first */
|
|
comm->sendByte( 'T' );
|
|
comm->sendByte( 0x64 ); // Select ATmega163, any device in the table will do.
|
|
comm->flushTX();
|
|
|
|
/* Should return CR */
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Entering programming mode failed! "
|
|
"Programmer did not return CR after 'T'-command." );
|
|
|
|
/* Send command 'P' */
|
|
comm->sendByte( 'P' );
|
|
comm->flushTX();
|
|
|
|
/* Should return CR */
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Entering programming mode failed! "
|
|
"Programmer did not return CR after 'P'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::leaveProgrammingMode()
|
|
{
|
|
/* Send command 'L' */
|
|
comm->sendByte( 'L' );
|
|
comm->flushTX();
|
|
|
|
/* Should return CR */
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Leaving programming mode failed! "
|
|
"Programmer did not return CR after 'L'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::chipErase()
|
|
{
|
|
/* Send command 'e' */
|
|
comm->sendByte( 'e' );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Chip erase failed! "
|
|
"Programmer did not return CR after 'e'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readOSCCAL( long pos, long * value )
|
|
{
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0x38 );
|
|
comm->sendByte( 0x00 );
|
|
comm->sendByte( pos );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->flushTX();
|
|
|
|
*value = comm->getByte();
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "OSCCAL value readout failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readSignature( long * sig0, long * sig1, long * sig2 )
|
|
{
|
|
/* Send command 's' */
|
|
comm->sendByte( 's' );
|
|
comm->flushTX();
|
|
|
|
/* Get actual signature */
|
|
*sig2 = comm->getByte();
|
|
*sig1 = comm->getByte();
|
|
*sig0 = comm->getByte();
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::checkSignature( long sig0, long sig1, long sig2 )
|
|
{
|
|
long sig[3];
|
|
|
|
/* Get signature */
|
|
readSignature( sig, sig+1, sig+2 );
|
|
|
|
/* Compare signature */
|
|
if( sig[0] != sig0 || sig[1] != sig1 || sig[2] != sig2 )
|
|
{
|
|
ostringstream msg;
|
|
msg << "Signature does not match selected device! ";
|
|
msg << "Actual signature: (" << hex
|
|
<< "0x" << setw(2) << sig[0] << " "
|
|
<< "0x" << setw(2) << sig[1] << " "
|
|
<< "0x" << setw(2) << sig[2] << ") "
|
|
<< "Signature from XML-file: (" << hex
|
|
<< "0x" << setw(2) << sig0 << " "
|
|
<< "0x" << setw(2) << sig1 << " "
|
|
<< "0x" << setw(2) << sig2 << ").";
|
|
|
|
throw new ErrorMsg( msg.str() );
|
|
}
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeFlashByte( long address, long value )
|
|
{
|
|
if( address >= 0x20000 )
|
|
throw new ErrorMsg( "Flash addresses above 128k are currently not supported!" );
|
|
|
|
setAddress( address >> 1 ); // Flash operations use word addresses.
|
|
|
|
/* Move data if at odd address */
|
|
if( address & 0x01 ) // Odd address?
|
|
value = (value << 8) | 0x00ff; // Move to high byte of one flash word.
|
|
else
|
|
value |= 0xff00; // Ensure no-write for high byte.
|
|
|
|
/* Send low and high byte */
|
|
writeFlashLowByte( value & 0xff );
|
|
writeFlashHighByte( value >> 8 );
|
|
|
|
/* Issue page write if required */
|
|
if( pagesize != -1 )
|
|
{
|
|
setAddress( address >> 1 ); // The address could be autoincremented.
|
|
writeFlashPage();
|
|
}
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeEEPROMByte( long address, long value )
|
|
{
|
|
if( address >= 0x10000 )
|
|
throw new ErrorMsg( "EEPROM addresses above 64k are currently not supported!" );
|
|
|
|
setAddress( address );
|
|
|
|
/* Send data */
|
|
comm->sendByte( 'D' );
|
|
comm->sendByte( value );
|
|
comm->flushTX();
|
|
|
|
/* Should return CR */
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Writing byte to EEPROM failed! "
|
|
"Programmer did not return CR after 'D'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeFlash( HEXFile * data )
|
|
{
|
|
long start, end; // Data address range.
|
|
bool autoincrement; // Bootloader supports address autoincrement?
|
|
long address;
|
|
|
|
/* Check that pagesize is set */
|
|
if( pagesize == -1 )
|
|
throw new ErrorMsg( "Programmer pagesize is not set!" );
|
|
|
|
/* Get range from HEX file */
|
|
start = data->getRangeStart();
|
|
end = data->getRangeEnd();
|
|
|
|
/* Check autoincrement support */
|
|
comm->sendByte( 'a' );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() == 'Y' )
|
|
autoincrement = true;
|
|
else
|
|
autoincrement = false;
|
|
|
|
/* Set initial address */
|
|
setAddress( start >> 1 ); // Flash operations use word addresses.
|
|
|
|
/* Need to write one odd byte first? */
|
|
address = start;
|
|
if( address & 1 )
|
|
{
|
|
/* Use only high byte */
|
|
writeFlashLowByte( 0xff ); // No-write in low byte.
|
|
writeFlashHighByte( data->getData( address ) );
|
|
|
|
address++;
|
|
|
|
/* Need to write page? */
|
|
if( pagesize != -1 )
|
|
{
|
|
if( !(address % pagesize) ) // Just passed page limit?
|
|
{
|
|
setAddress( (address-1) >> 1 ); // Set to an address inside the page.
|
|
writeFlashPage();
|
|
setAddress( address >> 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write words */
|
|
do
|
|
{
|
|
/* Need to set address again? */
|
|
if( !autoincrement )
|
|
setAddress( address >> 1 );
|
|
|
|
/* Write words */
|
|
writeFlashLowByte( data->getData( address ) );
|
|
writeFlashHighByte( data->getData( address+1 ) );
|
|
|
|
address += 2;
|
|
|
|
if( (address % MEM_PROGRESS_GRANULARITY) == 0 )
|
|
Util.progress( "#" ); // Advance progress indicator.
|
|
|
|
/* Need to write page? */
|
|
if( pagesize != -1 )
|
|
{
|
|
if( (address % pagesize) == 0 ) // Just passed a page boundary?
|
|
{
|
|
setAddress( (address-2) >> 1 ); // Set to an address inside the page.
|
|
writeFlashPage();
|
|
setAddress( address >> 1 );
|
|
}
|
|
}
|
|
} while( address < end );
|
|
|
|
/* Need to write one even byte before finished? */
|
|
if( address == end )
|
|
{
|
|
/* Use only low byte */
|
|
writeFlashLowByte( data->getData( address ) );
|
|
writeFlashHighByte( 0xff ); // No-write in high byte.
|
|
}
|
|
|
|
/* Need to write page? */
|
|
if( pagesize != -1 )
|
|
{
|
|
if( address == end || // One extra byte written...
|
|
(end+1)%pagesize != 0 ) // ...or end is not on page boundary.
|
|
{
|
|
setAddress( (address-2) >> 1 ); // Set to an address inside the page.
|
|
writeFlashPage();
|
|
}
|
|
}
|
|
|
|
Util.progress( "\r\n" ); // Finish progress indicator.
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readFlash( HEXFile * data )
|
|
{
|
|
long start, end; // Data address range.
|
|
bool autoincrement; // Bootloader supports address autoincrement?
|
|
long address;
|
|
|
|
if( pagesize == -1 )
|
|
throw new ErrorMsg( "Programmer pagesize is not set!" );
|
|
|
|
/* Get range from HEX file */
|
|
start = data->getRangeStart();
|
|
end = data->getRangeEnd();
|
|
|
|
/* Check autoincrement support */
|
|
comm->sendByte( 'a' );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() == 'Y' )
|
|
autoincrement = true;
|
|
else
|
|
autoincrement = false;
|
|
|
|
/* Set initial address */
|
|
setAddress( start >> 1 ); // Flash operations use word addresses.
|
|
|
|
/* Need to read one odd byte first? */
|
|
address = start;
|
|
if( address & 1 )
|
|
{
|
|
/* Read both, but use only high byte */
|
|
comm->sendByte( 'R' );
|
|
comm->flushTX();
|
|
|
|
data->setData( address, comm->getByte() ); // High byte.
|
|
comm->getByte(); // Dont use low byte.
|
|
|
|
address++;
|
|
}
|
|
|
|
/* Get words */
|
|
do
|
|
{
|
|
/* Need to set address again? */
|
|
if( !autoincrement )
|
|
setAddress( address >> 1 );
|
|
|
|
/* Get words */
|
|
comm->sendByte( 'R' );
|
|
comm->flushTX();
|
|
|
|
data->setData( address+1, comm->getByte() ); // High byte.
|
|
data->setData( address, comm->getByte() ); // Low byte.
|
|
|
|
address += 2;
|
|
|
|
if( (address % MEM_PROGRESS_GRANULARITY) == 0 )
|
|
Util.progress( "#" ); // Advance progress indicator.
|
|
|
|
} while( address < end );
|
|
|
|
/* Need to read one even byte before finished? */
|
|
if( address == end )
|
|
{
|
|
/* Read both, but use only low byte */
|
|
comm->sendByte( 'R' );
|
|
comm->flushTX();
|
|
|
|
comm->getByte(); // Dont use high byte.
|
|
data->setData( address-1, comm->getByte() ); // Low byte.
|
|
}
|
|
|
|
Util.progress( "\r\n" ); // Finish progress indicator.
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeEEPROM( HEXFile * data )
|
|
{
|
|
long start, end; // Data address range.
|
|
bool autoincrement; // Bootloader supports address autoincrement?
|
|
long address;
|
|
|
|
/* Get range from HEX file */
|
|
start = data->getRangeStart();
|
|
end = data->getRangeEnd();
|
|
|
|
/* Check autoincrement support */
|
|
comm->sendByte( 'a' );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() == 'Y' )
|
|
autoincrement = true;
|
|
else
|
|
autoincrement = false;
|
|
|
|
/* Set initial address */
|
|
setAddress( start );
|
|
|
|
/* Send data */
|
|
address = start;
|
|
do
|
|
{
|
|
/* Need to set address again? */
|
|
if( !autoincrement )
|
|
setAddress( address );
|
|
|
|
/* Send byte */
|
|
comm->sendByte( 'D' );
|
|
comm->sendByte( data->getData( address ) );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Writing byte to EEPROM failed! "
|
|
"Programmer did not return CR after 'D'-command." );
|
|
|
|
if( (address % MEM_PROGRESS_GRANULARITY) == 0 )
|
|
Util.progress( "#" ); // Advance progress indicator.
|
|
|
|
address++;
|
|
} while( address <= end );
|
|
|
|
Util.progress( "\r\n" ); // Finish progress indicator.
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readEEPROM( HEXFile * data )
|
|
{
|
|
long start, end; // Data address range.
|
|
bool autoincrement; // Bootloader supports address autoincrement?
|
|
long address;
|
|
|
|
/* Get range from HEX file */
|
|
start = data->getRangeStart();
|
|
end = data->getRangeEnd();
|
|
|
|
/* Check autoincrement support */
|
|
comm->sendByte( 'a' );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() == 'Y' )
|
|
autoincrement = true;
|
|
else
|
|
autoincrement = false;
|
|
|
|
/* Set initial address */
|
|
setAddress( start );
|
|
|
|
/* Send data */
|
|
address = start;
|
|
do
|
|
{
|
|
/* Need to set address again? */
|
|
if( !autoincrement )
|
|
setAddress( address );
|
|
|
|
/* Get byte */
|
|
comm->sendByte( 'd' );
|
|
comm->flushTX();
|
|
|
|
data->setData( address, comm->getByte() );
|
|
|
|
if( (address % MEM_PROGRESS_GRANULARITY) == 0 )
|
|
Util.progress( "#" ); // Advance progress indicator.
|
|
|
|
address++;
|
|
} while( address <= end );
|
|
|
|
Util.progress( "\r\n" ); // Finish progress indicator.
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeLockBits( long bits )
|
|
{
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0xac );
|
|
comm->sendByte( 0xe0 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( bits );
|
|
|
|
comm->flushTX();
|
|
comm->getByte(); // Ignore return code from SPI communication.
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Writing lock bits failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readLockBits( long * bits )
|
|
{
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0x58 );
|
|
comm->sendByte( 0x00 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->flushTX();
|
|
|
|
*bits = comm->getByte();
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Lock byte readout failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeFuseBits( long bits )
|
|
{
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0xac );
|
|
comm->sendByte( 0xa0 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( bits & 0xff );
|
|
comm->flushTX();
|
|
|
|
comm->getByte(); // Ignore return code from SPI communication.
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Low fuse byte programming failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0xac );
|
|
comm->sendByte( 0xa8 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( bits >> 8 );
|
|
comm->flushTX();
|
|
|
|
comm->getByte(); // Ignore return code from SPI communication.
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "High fuse byte programming failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readFuseBits( long * bits )
|
|
{
|
|
long low, high;
|
|
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0x50 );
|
|
comm->sendByte( 0x00 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->flushTX();
|
|
|
|
low = comm->getByte();
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Low fuse byte readout failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0x58 );
|
|
comm->sendByte( 0x08 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->flushTX();
|
|
|
|
high = comm->getByte();
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Low fuse byte readout failed! "
|
|
"Programmer did not return CR adter '.'-command." );
|
|
|
|
/* Put low and high together */
|
|
*bits = (high << 8) | low;
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::writeExtendedFuseBits( long bits )
|
|
{
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0xac );
|
|
comm->sendByte( 0xa4 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( bits );
|
|
comm->flushTX();
|
|
|
|
comm->getByte(); // Ignore return code from SPI communication.
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Extended fuse byte programming failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::readExtendedFuseBits( long * bits )
|
|
{
|
|
/* Use AVRISP's 4-byte universal command */
|
|
comm->sendByte( '.' );
|
|
comm->sendByte( 0x50 );
|
|
comm->sendByte( 0x08 );
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->sendByte( 0x00 ); // Dummy.
|
|
comm->flushTX();
|
|
|
|
*bits = comm->getByte();
|
|
|
|
if( comm->getByte() != '\r' ) // Check return code from command.
|
|
throw new ErrorMsg( "Extended fuse byte readout failed! "
|
|
"Programmer did not return CR after '.'-command." );
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::programmerSoftwareVersion( long * major, long * minor )
|
|
{
|
|
/* Send command 'V' to get software version */
|
|
comm->sendByte( 'V' );
|
|
comm->flushTX();
|
|
|
|
/* Get data */
|
|
*major = comm->getByte();
|
|
*minor = comm->getByte();
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
bool AVRInSystemProg::programmerHardwareVersion( long * major, long * minor )
|
|
{
|
|
/* Send command 'v' to get hardware version */
|
|
comm->sendByte( 'v' );
|
|
comm->flushTX();
|
|
|
|
/* Get data */
|
|
*major = comm->getByte();
|
|
*minor = comm->getByte();
|
|
|
|
return true; // Indicate supported command.
|
|
}
|
|
|
|
|
|
void AVRInSystemProg::setAddress( long address )
|
|
{
|
|
/* Set current address */
|
|
comm->sendByte( 'A' );
|
|
comm->sendByte( address >> 8 ); // High byte of address.
|
|
comm->sendByte( address & 0xff ); // Low byte.
|
|
comm->flushTX();
|
|
|
|
/* Should return CR */
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Setting address for programming operations failed! "
|
|
"Programmer did not return CR after 'A'-command." );
|
|
}
|
|
|
|
|
|
void AVRInSystemProg::writeFlashLowByte( long value )
|
|
{
|
|
comm->sendByte( 'c' );
|
|
comm->sendByte( value );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Writing Flash low byte failed! "
|
|
"Programmer did not return CR after 'c'-command." );
|
|
}
|
|
|
|
|
|
void AVRInSystemProg::writeFlashHighByte( long value )
|
|
{
|
|
comm->sendByte( 'C' );
|
|
comm->sendByte( value );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Writing Flash high byte failed! "
|
|
"Programmer did not return CR after 'C'-command." );
|
|
}
|
|
|
|
|
|
void AVRInSystemProg::writeFlashPage()
|
|
{
|
|
comm->sendByte( 'm' );
|
|
comm->flushTX();
|
|
|
|
if( comm->getByte() != '\r' )
|
|
throw new ErrorMsg( "Writing Flash page failed! "
|
|
"Programmer did not return CR after 'm'-command." );
|
|
}
|
|
|
|
|
|
/* end of file */
|
|
|