#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
#define INPUT2(port,pin) DDR ## port &= ~_BV(pin)
#define OUTPUT2(port,pin) DDR ## port |= _BV(pin)
#define CLEAR2(port,pin) PORT ## port &= ~_BV(pin)
#define SET2(port,pin) PORT ## port |= _BV(pin)
#define TOGGLE2(port,pin) PORT ## port ^= _BV(pin)
#define READ2(port,pin) ((PIN ## port & _BV(pin))?1:0)
#define INPUT(x) INPUT2(x)
#define OUTPUT(x) OUTPUT2(x)
#define CLEAR(x) CLEAR2(x)
#define SET(x) SET2(x)
#define TOGGLE(x) TOGGLE2(x)
#define READ(x) READ2(x)
#define PULLUP_ON(x) INPUT2(x); SET2(x)
#define PULLUP_OFF(x) INPUT2(x); CLEAR2(x)
#define RESET_0 B,2
#define MOSI B,3
#define MSIO B,4
#define SCK B,5
void
spi_init(
void
);
uint8_t
spi_char(
uint8_t
send);
void
spi_cmd(
uint8_t
*send,
uint8_t
*reply,
int
length);
void
show_device_name(
void
);
char
get_signature(
void
);
void
uart_write_PSTR(
char
*x);
uint8_t
uart_read_q(
void
);
void
wait_AVR_Busy();
uint8_t
enter_programming_mode();
void
leave_programming_mode(
void
);
void
uart_writeX(
char
x);
#define HIGH(x) ( (uint8_t) (x >> 8) )
#define LOW(x) ( (uint8_t) x )
#define SPI_OK 0
#define SPI_TIMEOUT 1
#define SPI_ERROR 2
#define CMD_LEN 4
#define SIG_LEN 3
#define MEMORY_TYPE_FLASH 'F'
#define MEMORY_TYPE_EEPROM 'E'
uint8_t
SPI_Cmd[CMD_LEN];
uint8_t
SPI_Reply[CMD_LEN];
uint8_t
AVR_Signature[SIG_LEN];
uint8_t
SPCR_enable_mask;
uint16_t
address;
uint16_t
page_address;
uint16_t
byte_count;
uint8_t
memory_type;
bool
in_programming_mode;
uint8_t
target_port;
uint16_t
flash_byte_count;
uint16_t
eeprom_byte_count;
#define Q_SIZE 140
volatile
uint8_t
q_buff[Q_SIZE];
volatile
uint8_t
qin;
volatile
uint8_t
qout;
#define PROGRAMMER_ID "AVR ISP"
#define HARDWARE_VERSION "01"
#define SOFTWARE_VERSION "30"
typedef
struct
{
char
name[10];
uint8_t
signature[SIG_LEN];
uint8_t
device_code;
uint16_t
page_size;
uint16_t
page_count;
} AVR_DEVICE;
AVR_DEVICE target_avr;
#define DEVICE_COUNT 3
const
AVR_DEVICE avr_info[DEVICE_COUNT] PROGMEM = {
{
"ATtiny85"
,
"\x1E\x93\x0B"
,
0x01,
64,
128
},
{
"ATmega168"
,
"\x1E\x94\x06"
,
0x02,
128,
128
},
{
"ATmega328"
,
"\x1E\x95\x0F"
,
0x03,
128,
256
}
};
FILE
lcd_stream;
#define BLANK_LINE " "
int
main() {
lcd_init();
fdev_setup_stream(&lcd_stream, lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_clear_and_home();
fprintf_P
(&lcd_stream,
PSTR
(
"AVR ISP v3.0"
));
uart_init();
UCSR0A = 0;
UBRR0L = (F_CPU/(16*BAUD))-1;
UCSR0B |= _BV(RXCIE0);
sei
();
spi_init();
int
i;
char
command;
while
(
true
){
command=uart_read_q();
switch
(command){
case
'P'
:
if
(!enter_programming_mode())
uart_writeX(
'\r'
);
break
;
case
'L'
:
leave_programming_mode();
uart_writeX(
'\r'
);
break
;
case
'a'
:
uart_writeX(
'Y'
);
break
;
case
'A'
:
address=(uart_read_q()<<8)|uart_read_q();
uart_writeX(
'\r'
);
break
;
case
'c'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x40\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[2]=LOW(address);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
flash_byte_count++;
uart_writeX(
'\r'
);
break
;
case
'C'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x48\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[2]=LOW(address);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
address++;
flash_byte_count++;
uart_writeX(
'\r'
);
break
;
case
'm'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x4C\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
break
;
case
'R'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x28\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
memcpy_P
(SPI_Cmd,
PSTR
(
"\x20\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
address++;
break
;
case
'D'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xC0\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
eeprom_byte_count++;
uart_writeX(
'\r'
);
break
;
case
'd'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xA0\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
break
;
case
'e'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xAC\x80\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
break
;
case
'b'
:
uart_writeX(
'Y'
);
if
(!enter_programming_mode())
if
(!get_signature()){
uart_writeX(target_avr.page_size>>8);
uart_writeX(target_avr.page_size&0xFF);
}
break
;
case
'B'
:
byte_count=(uart_read_q()<<8)|uart_read_q();
memory_type=uart_read_q();
wait_AVR_Busy();
if
(memory_type==MEMORY_TYPE_FLASH){
page_address=address;
for
(i=0;i<byte_count;i+=2){
memcpy_P
(SPI_Cmd,
PSTR
(
"\x40\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[2]=LOW(address);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
memcpy_P
(SPI_Cmd,
PSTR
(
"\x48\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[2]=LOW(address);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
address++;
flash_byte_count+=2;
}
memcpy_P
(SPI_Cmd,
PSTR
(
"\x4C\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(page_address);
SPI_Cmd[2]=LOW(page_address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
}
else
if
(memory_type==MEMORY_TYPE_EEPROM){
for
(i=0;i<byte_count;i++){
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xC0\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
address++;
eeprom_byte_count++;
}
uart_writeX(
'\r'
);
}
break
;
case
'g'
:
wait_AVR_Busy();
byte_count=(uart_read_q()<<8)|uart_read_q();
memory_type=uart_read_q();
if
(memory_type==MEMORY_TYPE_FLASH){
for
(i=0;i<byte_count;i+=2){
memcpy_P
(SPI_Cmd,
PSTR
(
"\x20\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
memcpy_P
(SPI_Cmd,
PSTR
(
"\x28\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
address ++;
}
}
else
if
(memory_type==MEMORY_TYPE_EEPROM)
for
(i=0;i<byte_count;i+=1){
memcpy_P
(SPI_Cmd,
PSTR
(
"\xA0\x00\x00\x00"
),CMD_LEN);
SPI_Cmd[1]=HIGH(address);
SPI_Cmd[2]=LOW(address);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
address ++;
}
break
;
case
'f'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xAC\xA0\x00\x00"
),CMD_LEN);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
break
;
case
'F'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x50\x00\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
break
;
case
'n'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xAC\xA8\x00\x00"
),CMD_LEN);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
break
;
case
'N'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x58\x08\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
break
;
case
'q'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xAC\xA4\x00\x00"
),CMD_LEN);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
break
;
case
'Q'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x50\x08\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
break
;
case
'l'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\xAC\xE0\x00\x00"
),CMD_LEN);
SPI_Cmd[3]=uart_read_q();
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(
'\r'
);
break
;
case
'r'
:
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x58\x00\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
break
;
case
'T'
:
uart_read_q();
uart_writeX(
'\r'
);
break
;
case
's'
:
if
(!enter_programming_mode())
if
(!get_signature())
for
(i=SIG_LEN-1;i>=0;i--)
uart_writeX(AVR_Signature[i]);
show_device_name();
break
;
case
't'
:
if
(!enter_programming_mode())
if
(!get_signature())
uart_writeX(target_avr.device_code);
uart_writeX(0x00);
break
;
case
'S'
:
uart_write_PSTR(
PSTR
(PROGRAMMER_ID));
break
;
case
'V'
:
uart_write_PSTR(
PSTR
(SOFTWARE_VERSION));
break
;
case
'v'
:
uart_write_PSTR(
PSTR
(HARDWARE_VERSION));
break
;
case
'p'
:
uart_writeX(
'S'
);
break
;
case
':'
:
wait_AVR_Busy();
SPI_Cmd[0]=uart_read_q();
SPI_Cmd[1]=uart_read_q();
SPI_Cmd[2]=uart_read_q();
SPI_Cmd[3]=0x00;
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
uart_writeX(
'\r'
);
break
;
case
'.'
:
wait_AVR_Busy();
SPI_Cmd[0]=uart_read_q();
SPI_Cmd[1]=uart_read_q();
SPI_Cmd[2]=uart_read_q();
SPI_Cmd[3]=uart_read_q();
SPI_Reply[3]=0;
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
uart_writeX(SPI_Reply[3]);
uart_writeX(
'\r'
);
break
;
case
'E'
:
uart_writeX(
'\r'
);
break
;
case
'Z'
:
i=uart_read_q();
if
(i>=48&&i<=57)
target_port=i-48;
lcd_goto_position(1,0);
fprintf_P
(&lcd_stream,
PSTR
(BLANK_LINE));
lcd_goto_position(1,0);
fprintf_P
(&lcd_stream,
PSTR
(
">>port %d"
),target_port);
uart_writeX(
'\r'
);
break
;
case
0x1B:
break
;
default
:
uart_writeX(
'?'
);
break
;
}
}
}
void
spi_init(
void
){
# define SPR_bitrate_div_4 0
# define SPR_bitrate_div_16 1
# define SPR_bitrate_div_64 2
# define SPR_bitrate_div_128 3
SPCR = 0;
INPUT(SCK);
INPUT(MOSI);
INPUT(MSIO);
PULLUP_ON(RESET_0);
SPCR_enable_mask = _BV(SPE)|_BV(MSTR)|SPR_bitrate_div_128;
sei
();
}
char
get_signature(
void
){
wait_AVR_Busy();
memcpy_P
(SPI_Cmd,
PSTR
(
"\x30\x00\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
AVR_Signature[0]=SPI_Reply[3];
memcpy_P
(SPI_Cmd,
PSTR
(
"\x30\x00\x01\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
AVR_Signature[1]=SPI_Reply[3];
memcpy_P
(SPI_Cmd,
PSTR
(
"\x30\x00\x02\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
AVR_Signature[2]=SPI_Reply[3];
int
i;
for
(i=0;i<DEVICE_COUNT;i++){
if
(!
memcmp_P
(AVR_Signature,avr_info[i].signature,SIG_LEN)){
int
j;
for
(j=0;j<
sizeof
(AVR_DEVICE);j++)
((
char
*)&target_avr)[j] = pgm_read_byte_near(&((
char
*)&avr_info[i])[j]);
return
(SPI_OK);
}
}
return
(SPI_ERROR);
}
void
show_device_name(
void
){
lcd_goto_position(2,0);
fprintf_P
(&lcd_stream,
PSTR
(BLANK_LINE));
lcd_goto_position(2,0);
fprintf_P
(&lcd_stream,
PSTR
(
">>"
));
if
(!get_signature())
fprintf
(&lcd_stream, target_avr.name);
else
fprintf_P
(&lcd_stream,
PSTR
(
"error"
));
}
void
wait_AVR_Busy(
void
){
SPI_Reply[3]=0x01;
while
(SPI_Reply[3]) {
memcpy_P
(SPI_Cmd,
PSTR
(
"\xF0\x00\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);{
SPI_Reply[3]&=0x01;
}
}
}
uint8_t
spi_char(
uint8_t
send){
SPDR = send;
while
(!(SPSR & _BV(SPIF)));
return
(SPDR);
}
void
spi_cmd(
uint8_t
*send,
uint8_t
*reply,
int
length){
int
i;
for
(i=0;i<length;i++)
reply[i]=spi_char(send[i]);
}
uint8_t
enter_programming_mode(
void
){
if
(!in_programming_mode){
in_programming_mode=
true
;
flash_byte_count=0;
eeprom_byte_count=0;
lcd_goto_position(3,0);
fprintf_P
(&lcd_stream,
PSTR
(BLANK_LINE));
OUTPUT(SCK);
OUTPUT(MOSI);
INPUT(MSIO);
OUTPUT(RESET_0);
CLEAR(RESET_0);
SPCR = SPCR_enable_mask;
_delay_ms(21);
memcpy_P
(SPI_Cmd,
PSTR
(
"\xAC\x53\x00\x00"
),CMD_LEN);
spi_cmd(SPI_Cmd, SPI_Reply, CMD_LEN);
if
(SPI_Reply[2]==0x53)
return
(SPI_OK);
leave_programming_mode();
return
(SPI_ERROR);
}
return
(SPI_OK);
}
void
leave_programming_mode(
void
){
if
(in_programming_mode){
in_programming_mode=
false
;
SPCR = 0;
PULLUP_OFF(SCK);
PULLUP_OFF(MOSI);
PULLUP_OFF(MSIO);
PULLUP_ON(RESET_0);
lcd_goto_position(3,0);
fprintf_P
(&lcd_stream,
PSTR
(
">>F:%4d E:%4d"
), flash_byte_count, eeprom_byte_count);
}
}
void
uart_write_PSTR(
char
*data){
int
i;
for
(i=0;pgm_read_byte_near((
char
*)&data[i]);i++)
uart_writeX(pgm_read_byte_near((
char
*)&data[i]));
}
uint8_t
uart_read_q(
void
){
uint8_t
data;
while
(qout==qin);
data=q_buff[qout++];
if
(qout==Q_SIZE) qout=0;
return
(data);
}
ISR(USART_RX_vect, ISR_BLOCK){
q_buff[qin++]=UDR0;
if
(qin==Q_SIZE) qin=0;
}
void
uart_writeX(
char
x) {
while
((UCSR0A & (1<<UDRE0))==0);
UDR0 = x;
}