You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
763 lines
31 KiB
763 lines
31 KiB
/*
|
|
LUFA Library
|
|
Copyright (C) Dean Camera, 2009.
|
|
|
|
dean [at] fourwalledcubicle [dot] com
|
|
www.fourwalledcubicle.com
|
|
*/
|
|
|
|
/*
|
|
Copyright 2009 Dean Camera (dean [at] fourwalledcubicle [dot] com)
|
|
AVR ISP Programmer code Copyright 2009 Opendous Inc. (www.opendous.org)
|
|
For more info and usage instructions for this firmware, visit:
|
|
http://code.google.com/p/avropendous/wiki/AVR_ISP_Programmer
|
|
|
|
Note that this firmware is designed to work with AVRdude:
|
|
http://savannah.nongnu.org/projects/avrdude
|
|
But should work with other software that supports the AVR910 ISP
|
|
programmer or STK200 hardware.
|
|
|
|
Permission to use, copy, modify, and distribute this software
|
|
and its documentation for any purpose and without fee is hereby
|
|
granted, provided that the above copyright notice appear in all
|
|
copies and that both that the copyright notice and this
|
|
permission notice and warranty disclaimer appear in supporting
|
|
documentation, and that the name of the author not be used in
|
|
advertising or publicity pertaining to distribution of the
|
|
software without specific, written prior permission.
|
|
|
|
The author disclaim all warranties with regard to this
|
|
software, including all implied warranties of merchantability
|
|
and fitness. In no event shall the author be liable for any
|
|
special, indirect or consequential damages or any damages
|
|
whatsoever resulting from loss of use, data or profits, whether
|
|
in an action of contract, negligence or other tortious action,
|
|
arising out of or in connection with the use or performance of
|
|
this software.
|
|
*/
|
|
|
|
/*
|
|
Usage:
|
|
avrdude -vv -F -P COM7 -c avr910 -p t261
|
|
Note -F flag which overrides signature check and enables programming
|
|
of any "In-System Programmable via SPI Port" AVR MCU. Part number,
|
|
t261, should be set to your target device.
|
|
avrdude -vv -F -P COM7 -c avr910 -p t261 -U flash:w:PROG.hex
|
|
PROG.hex is the hex file to program your t261 AVR with
|
|
avrdude -vv -F -P COM7 -b 115200 -c avr910 -p t261 -U flash:w:test.hex
|
|
The -b 115200 sets the SPI clock to 62.5kHz from the default 125kHz and may
|
|
work when the default programming speed fails.
|
|
AVROSP.exe -dATtiny261 -cCOM7 -rf
|
|
AVRosp is the Open Source AVR ISP Programming Software available from Atmel.com
|
|
|
|
Note: on Linux systems, COM7 should be replaced with someting like /dev/ttyACM0
|
|
You can determine this value by running dmesg after plugging in the device
|
|
Note: you must RESET the programmer after each use (AVRdude session).
|
|
|
|
Note: If you experience errors with older devices, try changing the DELAY defines
|
|
|
|
MISO, MOSI, and SCK are connected directly from the AVRopendous board
|
|
to the pin of the same functionality on the target. RESET pin on the target
|
|
can be connected either to SS (PB0), or PC2. Do not have any other pins
|
|
connected - especially HWB pin, to avoid unintentional behaviour.
|
|
|
|
AVR910 functionality was overlayed on USBtoSerial functionality.
|
|
Keep this in mind when looking over the code.
|
|
Default target speed is 125kHz and corresponds to 19200 baud, which
|
|
is the default setting for AVRdude.
|
|
|
|
Changing "Baud-Rate" will change the SPI speed. Defualt SPI clock speed
|
|
is 8Mhz / 4 = 2MHz. 8Mhz is the device clock speed. This is the setting at
|
|
9600 baud. The following is a table of baud-rate vs. SPI Speed that will result
|
|
9600 = 2Mhz
|
|
14400 = 1MHz
|
|
19200 = 125kHz (AVRdude Default)
|
|
38400 = 250kHz
|
|
57600 = 500kHz
|
|
115200 = 62.5kHz
|
|
|
|
Before running, you will need to install the INF file that
|
|
is located in the project directory. This will enable
|
|
Windows to use its inbuilt CDC drivers, negating the need
|
|
for special Windows drivers for the device. To install,
|
|
right-click the .INF file and choose the Install option.
|
|
*/
|
|
|
|
/* TODO: - fix the requirement that a RESET must be performed after each session, which
|
|
is only an issue under Windows. Everything works fine under Linux
|
|
*/
|
|
|
|
#include "AVRISP_Programmer.h"
|
|
|
|
/* Project Tags, for reading out using the ButtLoad project */
|
|
BUTTLOADTAG(ProjName, "LUFA AVR910 ISP Programmer");
|
|
BUTTLOADTAG(BuildTime, __TIME__);
|
|
BUTTLOADTAG(BuildDate, __DATE__);
|
|
BUTTLOADTAG(LUFAVersion, "LUFA V" LUFA_VERSION_STRING);
|
|
|
|
|
|
#define RESETPORT PORTB
|
|
#define RESETPIN PB0
|
|
#define RESETPORT2 PORTC
|
|
#define RESETPIN2 PC2
|
|
#define CR_HEX '\r'
|
|
|
|
#define DELAY_VERYSHORT 0x01
|
|
#define DELAY_SHORT 0x02
|
|
#define DELAY_MEDIUM 0x03
|
|
#define DELAY_LONG 0x05
|
|
#define DELAY_MULTIPLE 0x02
|
|
|
|
|
|
/* AVR Device Codes - Can have a maximum of 14 but can be any you want.
|
|
Note that these are completely irrelevent. If AVRdude supports a device,
|
|
then that device is programmable. Use -F switch to ignore device codes. */
|
|
#define AVRDEVCODE01 0x55 /* ATtiny12 */
|
|
#define AVRDEVCODE02 0x56 /* ATtiny15 */
|
|
#define AVRDEVCODE03 0x5E /* ATtiny261 */
|
|
#define AVRDEVCODE04 0x76 /* ATmega8 */
|
|
#define AVRDEVCODE05 0x74 /* ATmega16 */
|
|
#define AVRDEVCODE06 0x72 /* ATmega32 */
|
|
#define AVRDEVCODE07 0x45 /* ATmega64 */
|
|
#define AVRDEVCODE08 0x74 /* ATmega644 */
|
|
#define AVRDEVCODE09 0x43 /* ATmega128 */
|
|
#define AVRDEVCODE10 0x63 /* ATmega162 */
|
|
#define AVRDEVCODE11 0x78 /* ATmega169 */
|
|
#define AVRDEVCODE12 0x6C /* AT90S4434 */
|
|
#define AVRDEVCODE13 0x38 /* AT90S8515A */
|
|
#define AVRDEVCODE14 0x65 /* AT90S8555 */
|
|
|
|
|
|
/* Scheduler Task List */
|
|
TASK_LIST
|
|
{
|
|
{ Task: USB_USBTask , TaskStatus: TASK_STOP },
|
|
{ Task: CDC_Task , TaskStatus: TASK_STOP },
|
|
};
|
|
|
|
/* Globals: */
|
|
/** Contains the current baud rate and other settings of the virtual serial port.
|
|
*
|
|
These values are set by the host via a class-specific request, and the physical USART should be reconfigured to match the
|
|
new settings each time they are changed by the host.
|
|
*/
|
|
CDC_Line_Coding_t LineCoding = { BaudRateBPS: 9600,
|
|
CharFormat: OneStopBit,
|
|
ParityType: Parity_None,
|
|
DataBits: 8 };
|
|
|
|
/** Ring (circular) buffer to hold the RX data - data from the host to the attached device on the serial port. */
|
|
RingBuff_t Rx_Buffer;
|
|
|
|
/** Ring (circular) buffer to hold the TX data - data from the attached device on the serial port to the host. */
|
|
RingBuff_t Tx_Buffer;
|
|
|
|
/** Flag to indicate if the USART is currently transmitting data from the Rx_Buffer circular buffer. */
|
|
volatile bool Transmitting = false;
|
|
|
|
/* some global variables used throughout */
|
|
uint16_t currAddress = 0;
|
|
|
|
/** Main program entry point. This routine configures the hardware required by the application, then
|
|
starts the scheduler to run the application tasks.
|
|
*/
|
|
int main(void)
|
|
{
|
|
/* Disable watchdog if enabled by bootloader/fuses */
|
|
MCUSR &= ~(1 << WDRF);
|
|
wdt_disable();
|
|
|
|
/* Disable Clock Division */
|
|
SetSystemClockPrescaler(0);
|
|
|
|
/* Hardware Initialization */
|
|
LEDs_Init();
|
|
ReconfigureSPI();
|
|
|
|
DDRC |= ((1 << PC2) | (1 << PC4) | (1 << PC5) | (1 << PC6) | (1 << PC7)); //AT90USBxx2
|
|
// PC2 is also used for RESET, so set it HIGH initially - note 'P' command sets it to LOW (Active)
|
|
PORTC |= ((1 << PC2) | (1 << PC4) | (1 << PC5) | (1 << PC6) | (1 << PC7)); //AT90USBxx2
|
|
DDRD = 0;
|
|
PORTD = (1 << PB7); // only PB7(HWB) should be High as this is the bootloader pin
|
|
// Prepare PortB for SPI - set PB0(^SS), PB1(SCK), PB2(MOSI) as output as well as all other pins except PB3(MISO)
|
|
DDRB = (1 << PB0) | (1 << PB1) | (1 << PB2) | (0 << PB3) | (1 << PB4) | (1 << PB5) | (1 << PB6) | (1 << PB7);
|
|
PORTB |= (1 << PB0);
|
|
// make sure DataFlash devices to not interfere - deselect them by setting PE0 and PE1 HIGH:
|
|
PORTE = 0xFF;
|
|
DDRE = 0xFF;
|
|
|
|
/* Ringbuffer Initialization */
|
|
Buffer_Initialize(&Rx_Buffer);
|
|
Buffer_Initialize(&Tx_Buffer);
|
|
|
|
/* Indicate USB not ready */
|
|
UpdateStatus(Status_USBNotReady);
|
|
|
|
/* Initialize Scheduler so that it can be used */
|
|
Scheduler_Init();
|
|
|
|
/* Initialize USB Subsystem */
|
|
USB_Init();
|
|
|
|
/* Scheduling - routine never returns, so put this last in the main function */
|
|
Scheduler_Start();
|
|
}
|
|
|
|
/** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and
|
|
starts the library USB task to begin the enumeration and USB management process.
|
|
*/
|
|
EVENT_HANDLER(USB_Connect)
|
|
{
|
|
/* Start USB management task */
|
|
Scheduler_SetTaskMode(USB_USBTask, TASK_RUN);
|
|
|
|
/* Indicate USB enumerating */
|
|
UpdateStatus(Status_USBEnumerating);
|
|
}
|
|
|
|
/** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via
|
|
the status LEDs and stops the USB management and CDC management tasks.
|
|
*/
|
|
EVENT_HANDLER(USB_Disconnect)
|
|
{
|
|
/* Stop running CDC and USB management tasks */
|
|
Scheduler_SetTaskMode(CDC_Task, TASK_STOP);
|
|
Scheduler_SetTaskMode(USB_USBTask, TASK_STOP);
|
|
|
|
/* Indicate USB not ready */
|
|
UpdateStatus(Status_USBNotReady);
|
|
}
|
|
|
|
/** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration
|
|
of the USB device after enumeration - the device endpoints are configured and the CDC management task started.
|
|
*/
|
|
EVENT_HANDLER(USB_ConfigurationChanged)
|
|
{
|
|
/* Setup CDC Notification, Rx and Tx Endpoints */
|
|
Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPNUM, EP_TYPE_INTERRUPT,
|
|
ENDPOINT_DIR_IN, CDC_NOTIFICATION_EPSIZE,
|
|
ENDPOINT_BANK_SINGLE);
|
|
|
|
Endpoint_ConfigureEndpoint(CDC_TX_EPNUM, EP_TYPE_BULK,
|
|
ENDPOINT_DIR_IN, CDC_TXRX_EPSIZE,
|
|
ENDPOINT_BANK_SINGLE);
|
|
|
|
Endpoint_ConfigureEndpoint(CDC_RX_EPNUM, EP_TYPE_BULK,
|
|
ENDPOINT_DIR_OUT, CDC_TXRX_EPSIZE,
|
|
ENDPOINT_BANK_SINGLE);
|
|
|
|
/* Indicate USB connected and ready */
|
|
UpdateStatus(Status_USBReady);
|
|
|
|
/* Start CDC task */
|
|
Scheduler_SetTaskMode(CDC_Task, TASK_RUN);
|
|
}
|
|
|
|
/** Event handler for the USB_UnhandledControlPacket event. This is used to catch standard and class specific
|
|
control requests that are not handled internally by the USB library (including the CDC control commands,
|
|
which are all issued via the control endpoint), so that they can be handled appropriately for the application.
|
|
*/
|
|
EVENT_HANDLER(USB_UnhandledControlPacket)
|
|
{
|
|
uint8_t* LineCodingData = (uint8_t*)&LineCoding;
|
|
|
|
/* Process CDC specific control requests */
|
|
switch (bRequest)
|
|
{
|
|
case REQ_GetLineEncoding:
|
|
if (bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
|
|
{
|
|
/* Acknowedge the SETUP packet, ready for data transfer */
|
|
Endpoint_ClearSetupReceived();
|
|
|
|
/* Write the line coding data to the control endpoint */
|
|
Endpoint_Write_Control_Stream_LE(LineCodingData, sizeof(LineCoding));
|
|
|
|
/* Finalize the stream transfer to send the last packet or clear the host abort */
|
|
Endpoint_ClearSetupOUT();
|
|
}
|
|
|
|
break;
|
|
case REQ_SetLineEncoding:
|
|
if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
|
|
{
|
|
/* Acknowedge the SETUP packet, ready for data transfer */
|
|
Endpoint_ClearSetupReceived();
|
|
|
|
/* Read the line coding data in from the host into the global struct */
|
|
Endpoint_Read_Control_Stream_LE(LineCodingData, sizeof(LineCoding));
|
|
|
|
/* Finalize the stream transfer to clear the last packet from the host */
|
|
Endpoint_ClearSetupIN();
|
|
|
|
/* Reconfigure the USART with the new settings */
|
|
ReconfigureSPI();
|
|
}
|
|
|
|
break;
|
|
case REQ_SetControlLineState:
|
|
if (bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
|
|
{
|
|
/* Acknowedge the SETUP packet, ready for data transfer */
|
|
Endpoint_ClearSetupReceived();
|
|
|
|
/* Send an empty packet to acknowedge the command */
|
|
Endpoint_ClearSetupIN();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** Task to manage CDC data transmission and reception to and from the host, from and to the physical USART. */
|
|
TASK(CDC_Task)
|
|
{
|
|
if (USB_IsConnected)
|
|
{
|
|
/* Select the Serial Rx Endpoint */
|
|
Endpoint_SelectEndpoint(CDC_RX_EPNUM);
|
|
|
|
if (Endpoint_ReadWriteAllowed())
|
|
{
|
|
/* Read the received data endpoint into the transmission buffer */
|
|
while (Endpoint_BytesInEndpoint())
|
|
{
|
|
/* Wait until the buffer has space for a new character */
|
|
while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
|
|
|
|
/* Store each character from the endpoint */
|
|
Buffer_StoreElement(&Rx_Buffer, Endpoint_Read_Byte());
|
|
|
|
/* Run the given command once enough data is available. */
|
|
if (Rx_Buffer.Elements)
|
|
{
|
|
const uint8_t ZeroDataByteCommands[] = {'P', 'a', 'm', 'R', 'd', 'e', 'L', 's', 't', 'S', 'V', 'v', 'p', 'F'};
|
|
const uint8_t OneDataByteCommands[] = {'T', 'c', 'C', 'D', 'l', 'f', 'x', 'y'};
|
|
const uint8_t TwoDataByteCommands[] = {'A', 'Z'};
|
|
const uint8_t ThreeDataByteCommands[] = {':'};
|
|
const uint8_t FourDataByteCommands[] = {'.'};
|
|
|
|
const struct
|
|
{
|
|
const uint8_t TotalCommands;
|
|
const uint8_t* CommandBytes;
|
|
} AVR910Commands[] = {{sizeof(ZeroDataByteCommands), ZeroDataByteCommands},
|
|
{sizeof(OneDataByteCommands), OneDataByteCommands},
|
|
{sizeof(TwoDataByteCommands), TwoDataByteCommands},
|
|
{sizeof(ThreeDataByteCommands), ThreeDataByteCommands},
|
|
{sizeof(FourDataByteCommands), FourDataByteCommands}};
|
|
|
|
/* Determine the data length of the issued command */
|
|
uint8_t CommandDataLength = (Rx_Buffer.Elements - 1);
|
|
|
|
/* Loop through each of the possible command bytes allowable from the given command data length */
|
|
for (uint8_t CurrentCommand = 0; CurrentCommand < AVR910Commands[CommandDataLength].TotalCommands; CurrentCommand++)
|
|
{
|
|
/* If issues command matches an allowable command, process it */
|
|
if (Buffer_PeekElement(&Rx_Buffer) == AVR910Commands[CommandDataLength].CommandBytes[CurrentCommand])
|
|
processHostSPIRequest();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear the endpoint buffer */
|
|
Endpoint_ClearCurrentBank();
|
|
}
|
|
|
|
/* Select the Serial Tx Endpoint */
|
|
Endpoint_SelectEndpoint(CDC_TX_EPNUM);
|
|
|
|
/* Check if the Tx buffer contains anything to be sent to the host */
|
|
if (Tx_Buffer.Elements)
|
|
{
|
|
/* Wait until Serial Tx Endpoint Ready for Read/Write */
|
|
while (!(Endpoint_ReadWriteAllowed()));
|
|
|
|
/* Check before sending the data if the endpoint is completely full */
|
|
bool IsFull = (Endpoint_BytesInEndpoint() == CDC_TXRX_EPSIZE);
|
|
|
|
/* Write the transmission buffer contents to the received data endpoint */
|
|
while (Tx_Buffer.Elements && (Endpoint_BytesInEndpoint() < CDC_TXRX_EPSIZE))
|
|
Endpoint_Write_Byte(Buffer_GetElement(&Tx_Buffer));
|
|
|
|
/* Send the data */
|
|
Endpoint_ClearCurrentBank();
|
|
|
|
/* If a full endpoint was sent, we need to send an empty packet afterwards to terminate the transfer */
|
|
if (IsFull)
|
|
{
|
|
/* Wait until Serial Tx Endpoint Ready for Read/Write */
|
|
while (!(Endpoint_ReadWriteAllowed()));
|
|
|
|
/* Send an empty packet to terminate the transfer */
|
|
Endpoint_ClearCurrentBank();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Function to manage status updates to the user. This is done via LEDs on the given board, if available, but may be changed to
|
|
* log to a serial port, or anything else that is suitable for status updates.
|
|
*
|
|
* \param CurrentStatus Current status of the system, from the USBtoSerial_StatusCodes_t enum
|
|
*/
|
|
void UpdateStatus(uint8_t CurrentStatus)
|
|
{
|
|
uint8_t LEDMask = LEDS_NO_LEDS;
|
|
|
|
/* Set the LED mask to the appropriate LED mask based on the given status code */
|
|
switch (CurrentStatus)
|
|
{
|
|
case Status_USBNotReady:
|
|
LEDMask = (LEDS_LED1);
|
|
break;
|
|
case Status_USBEnumerating:
|
|
LEDMask = (LEDS_LED1 | LEDS_LED2);
|
|
break;
|
|
case Status_USBReady:
|
|
LEDMask = (LEDS_LED2 | LEDS_LED4);
|
|
break;
|
|
}
|
|
|
|
/* Set the board LEDs to the new LED mask */
|
|
LEDs_SetAllLEDs(LEDMask);
|
|
}
|
|
|
|
/** Reconfigures SPI to match the current serial port settings issued by the host. */
|
|
void ReconfigureSPI(void)
|
|
{
|
|
uint8_t SPCRmask = (1 << SPE) | (1 << MSTR); // always enable SPI as Master
|
|
uint8_t SPSRmask = 0;
|
|
|
|
/* Determine stop bits - 1.5 stop bits is set as 1 stop bit due to hardware limitations */
|
|
/* For SPI, determine whether format is LSB or MSB */
|
|
if (LineCoding.CharFormat == TwoStopBits) {
|
|
SPCRmask |= (1 << DORD);
|
|
} else if (LineCoding.CharFormat == OneStopBit) {
|
|
SPCRmask |= (0 << DORD);
|
|
}
|
|
|
|
/* Determine data size - 5, 6, 7, or 8 bits are supported */
|
|
/* Changing line coding changes SPI Mode
|
|
CPOL=0, CPHA=0 Sample (Rising) Setup (Falling) SPI-Mode0 == 8 bits line coding
|
|
CPOL=0, CPHA=1 Setup (Rising) Sample (Falling) SPI-Mode1 == 7 bits line coding
|
|
CPOL=1, CPHA=0 Sample (Falling) Setup (Rising) SPI-Mode2 == 6 bits line coding
|
|
CPOL=1, CPHA=1 Setup (Falling) Sample (Rising) SPI-Mode3 == 5 bits line coding
|
|
*/
|
|
if (LineCoding.DataBits == 5) {
|
|
SPCRmask |= ((1 << CPOL) | (1 << CPHA));
|
|
} else if (LineCoding.DataBits == 6) {
|
|
SPCRmask |= ((1 << CPOL) | (0 << CPHA));
|
|
} else if (LineCoding.DataBits == 7) {
|
|
SPCRmask |= ((0 << CPOL) | (1 << CPHA));
|
|
} else if (LineCoding.DataBits == 8) {
|
|
SPCRmask |= ((0 << CPOL) | (0 << CPHA));
|
|
}
|
|
|
|
|
|
/* Set the USART baud rate register to the desired baud rate value */
|
|
/* also alter the SPI speed via value of baud rate */
|
|
if (LineCoding.BaudRateBPS == 9600) { // 2Mhz SPI (Fosc / 4)
|
|
SPCRmask |= ((0 << SPR1) | (0 << SPR0));
|
|
SPSRmask |= (0 << SPI2X);
|
|
} else if (LineCoding.BaudRateBPS == 14400) { // 1Mhz SPI (Fosc / 8)
|
|
SPCRmask |= ((0 << SPR1) | (1 << SPR0));
|
|
SPSRmask |= (1 << SPI2X);
|
|
} else if (LineCoding.BaudRateBPS == 57600) { // 500kHz SPI (Fosc / 16)
|
|
SPCRmask |= ((0 << SPR1) | (1 << SPR0));
|
|
SPSRmask |= (0 << SPI2X);
|
|
} else if (LineCoding.BaudRateBPS == 38400) { // 250kHz SPI (Fosc / 32)
|
|
SPCRmask |= ((1 << SPR1) | (0 << SPR0));
|
|
SPSRmask |= (1 << SPI2X);
|
|
} else if (LineCoding.BaudRateBPS == 19200) { // 125kHz SPI (Fosc / 64)
|
|
SPCRmask |= ((1 << SPR1) | (0 << SPR0));
|
|
SPSRmask |= (0 << SPI2X);
|
|
} else if (LineCoding.BaudRateBPS == 115200) { // 62.5kHz SPI (Fosc / 128)
|
|
SPCRmask |= ((1 << SPR1) | (1 << SPR0));
|
|
SPSRmask |= (0 << SPI2X);
|
|
}
|
|
|
|
SPCR = SPCRmask;
|
|
SPSR = SPSRmask;
|
|
}
|
|
|
|
|
|
/* process data according to AVR910 protocol */
|
|
void processHostSPIRequest(void) {
|
|
|
|
uint8_t readByte1 = 0;
|
|
uint8_t readByte2 = 0;
|
|
uint8_t readByte3 = 0;
|
|
uint8_t readByte4 = 0;
|
|
uint8_t firstByte = 0;
|
|
|
|
/* Taken from a90isp_ver23.asm:
|
|
+-------------+------------+------+
|
|
;* Commands | Host writes | Host reads | |
|
|
;* -------- +-----+-------+------+-----+ |
|
|
;* | ID | data | data | | Note |
|
|
;* +-----------------------------------+-----+-------+------+-----+------+
|
|
;* | Enter programming mode | 'P' | | | 13d | 1 |
|
|
;* | Report autoincrement address | 'a' | | | 'Y' | |
|
|
;* | Set address | 'A' | ah al | | 13d | 2 |
|
|
;* | Write program memory, low byte | 'c' | dd | | 13d | 3 |
|
|
;* | Write program memory, high byte | 'C' | dd | | 13d | 3 |
|
|
;* | Issue Page Write | 'm' | | | 13d | |
|
|
;* | Read program memory | 'R' | |dd(dd)| | 4 |
|
|
;* | Write data memory | 'D' | dd | | 13d | |
|
|
;* | Read data memory | 'd' | | dd | | |
|
|
;* | Chip erase | 'e' | | | 13d | |
|
|
;* | Write lock bits | 'l' | dd | | 13d | |
|
|
;* | Write fuse bits | 'f' | dd | | 13d | 11 |
|
|
;* | Read fuse and lock bits | 'F' | | dd | | 11 |
|
|
;* | Leave programming mode | 'L' | | | 13d | 5 |
|
|
;* | Select device type | 'T' | dd | | 13d | 6 |
|
|
;* | Read signature bytes | 's' | | 3*dd | | |
|
|
;* | Return supported device codes | 't' | | n*dd | 00d | 7 |
|
|
;* | Return software identifier | 'S' | | s[7] | | 8 |
|
|
;* | Return sofware version | 'V' | |dd dd | | 9 |
|
|
;* | Return hardware version | 'v' | |dd dd | | 9 |
|
|
;* | Return programmer type | 'p' | | dd | | 10 |
|
|
;* | Set LED | 'x' | dd | | 13d | 12 |
|
|
;* | Clear LED | 'y' | dd | | 13d | 12 |
|
|
;* | Universial command | ':' | 3*dd | dd | 13d | |
|
|
;* | New universal command | '.' | 4*dd | dd | 13d | |
|
|
;* | Special test command | 'Z' | 2*dd | dd | | |
|
|
*/
|
|
|
|
firstByte = Buffer_GetElement(&Rx_Buffer);
|
|
Buffer_Initialize(&Tx_Buffer); // make sure the buffer is clear before proceeding
|
|
|
|
if (firstByte == 'P') { // enter Programming mode
|
|
// enable SPI -- already done
|
|
// enter programming mode on target:
|
|
//PORTB = 0; // set clock to zero
|
|
RESETPORT = (1 << RESETPIN); // set RESET pin on target to 1
|
|
RESETPORT2 = (1 << RESETPIN2);
|
|
_delay_ms(DELAY_SHORT);
|
|
//RESETPORT = (RESETPORT & ~(1 << RESETPIN)); // set RESET pin on target to 0 - Active
|
|
RESETPORT = 0x00;
|
|
RESETPORT2 = 0;
|
|
_delay_ms(DELAY_SHORT);
|
|
SPI_SendByte(0xAC);
|
|
SPI_SendByte(0x53);
|
|
SPI_SendByte(0x00);
|
|
SPI_SendByte(0x00);
|
|
_delay_ms(DELAY_VERYSHORT);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'T') { // Select device type
|
|
Buffer_GetElement(&Rx_Buffer); // set device type
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'a') { // Report autoincrement address
|
|
Buffer_StoreElement(&Tx_Buffer, 'Y'); // return 'Y' - Auto-increment enabled
|
|
|
|
} else if (firstByte == 'A') { //Load Address
|
|
// get two bytes over serial and set currAddress to them
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer); // high byte
|
|
readByte2 = Buffer_GetElement(&Rx_Buffer); // low byte
|
|
currAddress = (readByte1 << 8) | (readByte2);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'c') { // Write program memory, low byte
|
|
// send 4 bytes over SPI; 0x40, then Address High Byte, then Low, then data
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
SPI_SendByte(0x40);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
SPI_SendByte(readByte1); // data
|
|
_delay_ms(DELAY_MEDIUM); // certain MCUs require a delay of about 24585 cycles
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'C') { // Write program memory, high byte
|
|
// send 4 bytes over SPI; 0x48, then Address High Byte, then Low, then data
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
SPI_SendByte(0x48);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
SPI_SendByte(readByte1); // data
|
|
currAddress++; // increment currAddress
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'm') { // Write Program Memory Page
|
|
// send 4 bytes over SPI; 0x4c, then Address High Byte, then Low, then 0x00
|
|
SPI_SendByte(0x4C);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
SPI_SendByte(0x00);
|
|
_delay_ms(DELAY_LONG);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'R') { // Read Program Memory
|
|
// send 4 bytes over SPI; 0x28, then Address High Byte, then Low, then send back read data from 4th byte over serial
|
|
SPI_SendByte(0x28);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
readByte1 = SPI_TransferByte(0x00); // read in data
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
// send 4 bytes over SPI; 0x20, then Address High Byte, then Low, then send back read data from 4th byte over serial
|
|
SPI_SendByte(0x20);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
readByte2 = SPI_TransferByte(0x00); // read in data
|
|
Buffer_StoreElement(&Tx_Buffer, readByte2);
|
|
currAddress++; // increment currAddress
|
|
|
|
} else if (firstByte == 'D') { // Write Data Memory
|
|
// send 4 bytes over SPI; 0xc0, then Address High Byte, then Low, then data
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
SPI_SendByte(0xC0);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
SPI_SendByte(readByte1); // data
|
|
_delay_ms(DELAY_MEDIUM);
|
|
currAddress++; // increment currAddress
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'd') { // Read Data Memory
|
|
// send 4 bytes over SPI; 0xa0, then Address High Byte, then Low, then send back read data from 4th byte over serial
|
|
SPI_SendByte(0xA0);
|
|
SPI_SendByte((currAddress >> 8)); // high byte
|
|
SPI_SendByte((currAddress)); // low byte
|
|
readByte1 = SPI_TransferByte(0x00); // read in data
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
currAddress++; // increment currAddress
|
|
|
|
} else if (firstByte == 'e') { // erase the target device
|
|
// send 4 bytes over SPI; 0xac, 0x80, 0x04, 0x00
|
|
SPI_SendByte(0xAC);
|
|
SPI_SendByte(0x80);
|
|
SPI_SendByte(0x04);
|
|
SPI_SendByte(0x00);
|
|
_delay_ms(DELAY_LONG);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'l') { // write lock bits
|
|
// send 4 bytes over SPI; 0xac, [andi s_data 0x06], 0xe0, 0x00
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer); // read in lock bits data
|
|
SPI_SendByte(0xAC);
|
|
SPI_SendByte(((0x06 & readByte1) | 0xE0)); // TODO - is this correct???
|
|
SPI_SendByte(0x00);
|
|
SPI_SendByte(0x00);
|
|
_delay_ms(DELAY_MEDIUM);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'f') { // write fuse bits
|
|
// ignore this command, but need to remove data from the receive buffer
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'L') { // leave programming mode
|
|
RESETPORT |= (1 << RESETPIN); // set RESET pin on target to 1
|
|
RESETPORT2 |= (1 << RESETPIN2); // set RESET pin on target to 1
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 's') { // Read signature bytes
|
|
// send 4 bytes over SPI; 0x30, 0x00, 0x02, read and send last byte over serial
|
|
SPI_SendByte(0x30);
|
|
SPI_SendByte(0x00);
|
|
SPI_SendByte(0x02);
|
|
readByte1 = SPI_TransferByte(0x00); // read in data
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
SPI_SendByte(0x30);
|
|
SPI_SendByte(0x00);
|
|
SPI_SendByte(0x01);
|
|
readByte1 = SPI_TransferByte(0x00); // read in data
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
SPI_SendByte(0x30);
|
|
SPI_SendByte(0x00);
|
|
SPI_SendByte(0x00);
|
|
readByte1 = SPI_TransferByte(0x00); // read in data
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
|
|
} else if (firstByte == 't') { // Return supported device codes
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE01);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE02);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE03);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE04);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE05);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE06);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE07);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE08);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE09);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE10);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE11);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE12);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE13);
|
|
Buffer_StoreElement(&Tx_Buffer, AVRDEVCODE14);
|
|
Buffer_StoreElement(&Tx_Buffer, 0x00);
|
|
|
|
} else if (firstByte == 'S') { // Return software identifier
|
|
// return string[7] with "AVR ISP"
|
|
Buffer_StoreElement(&Tx_Buffer, 'A');
|
|
Buffer_StoreElement(&Tx_Buffer, 'V');
|
|
Buffer_StoreElement(&Tx_Buffer, 'R');
|
|
Buffer_StoreElement(&Tx_Buffer, 0x20);
|
|
Buffer_StoreElement(&Tx_Buffer, 'I');
|
|
Buffer_StoreElement(&Tx_Buffer, 'S');
|
|
Buffer_StoreElement(&Tx_Buffer, 'P');
|
|
|
|
} else if (firstByte == 'V') { // Return sofware version
|
|
//return two bytes, software Major then Minor
|
|
Buffer_StoreElement(&Tx_Buffer, '2');
|
|
Buffer_StoreElement(&Tx_Buffer, '3');
|
|
|
|
} else if (firstByte == 'v') { // Return hardware version
|
|
//return two bytes, hardware Major then Minor
|
|
Buffer_StoreElement(&Tx_Buffer, ('1'));
|
|
Buffer_StoreElement(&Tx_Buffer, ('0'));
|
|
|
|
} else if (firstByte == 'p') { // Return programmer type
|
|
// return 'S' for Serial Programmer
|
|
Buffer_StoreElement(&Tx_Buffer, 'S');
|
|
|
|
} else if (firstByte == 'x') { // set LED
|
|
// ignore this command, but need to remove data from the receive buffer
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'y') { // clear LED
|
|
// ignore this command, but need to remove data from the receive buffer
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == ':') { // Universal Command
|
|
// get 3 bytes over serial
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
readByte2 = Buffer_GetElement(&Rx_Buffer);
|
|
readByte3 = Buffer_GetElement(&Rx_Buffer);
|
|
SPI_SendByte(readByte1);
|
|
SPI_SendByte(readByte2);
|
|
SPI_SendByte(readByte3);
|
|
readByte1 = SPI_TransferByte(0x00);
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
_delay_ms(DELAY_MEDIUM);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == '.') { // New Universal Command
|
|
// get 4 bytes over serial
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
readByte2 = Buffer_GetElement(&Rx_Buffer);
|
|
readByte3 = Buffer_GetElement(&Rx_Buffer);
|
|
readByte4 = Buffer_GetElement(&Rx_Buffer);
|
|
SPI_SendByte(readByte1);
|
|
SPI_SendByte(readByte2);
|
|
SPI_SendByte(readByte3);
|
|
readByte1 = SPI_TransferByte(readByte4);
|
|
Buffer_StoreElement(&Tx_Buffer, readByte1);
|
|
_delay_ms(DELAY_MEDIUM);
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
|
|
} else if (firstByte == 'Z') { // Special test command
|
|
// do nothing, but need to remove data from the receive buffer
|
|
readByte1 = Buffer_GetElement(&Rx_Buffer);
|
|
readByte2 = Buffer_GetElement(&Rx_Buffer);
|
|
|
|
} else {
|
|
// do nothing, but need to return with a carriage return
|
|
Buffer_StoreElement(&Tx_Buffer, CR_HEX); // return carriage return (CR_HEX) if successful
|
|
}
|
|
}
|
|
|