Last active
December 14, 2018 17:33
-
-
Save dev-zzo/9620fe33b8938ec2d6f157b39eed9fc5 to your computer and use it in GitHub Desktop.
Arduino EPROM programmer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import argparse | |
| import serial | |
| import struct | |
| import sys | |
| class CommunicationError(Exception): | |
| pass | |
| def ping(port): | |
| port.write('?') | |
| rsp = port.read(1) | |
| if rsp != '!': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| def read(port, address, count): | |
| # Write command, check status | |
| port.write('R' + struct.pack('>IB', address, count)) | |
| rsp = port.read(1) | |
| if rsp == '!': | |
| raise ValueError("wrong arguments/state") | |
| elif rsp != '.': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| # Read data | |
| data = port.read(count) | |
| if len(data) != count: | |
| raise CommunicationError("unexpected data length: %d" % len(data)) | |
| # Check status | |
| rsp = port.read(1) | |
| if rsp != '.': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| return data | |
| def set_type(port, ic_type): | |
| if ic_type == "2764": | |
| real_type = '0' | |
| elif ic_type == "27256": | |
| real_type = '1' | |
| else: | |
| raise ValueError("unknown IC type provided") | |
| # Write command, check status | |
| port.write('T' + real_type) | |
| rsp = port.read(1) | |
| if rsp != '.': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| def program(port, address, data): | |
| # Write command, check status | |
| port.write('P' + struct.pack('>IB', address, len(data))) | |
| rsp = port.read(1) | |
| if rsp == '!': | |
| raise ValueError("wrong arguments/state") | |
| elif rsp != '.': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| # Write data, check status | |
| port.write(data) | |
| rsp = port.read(1) | |
| if rsp != '.': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| # Wait for programming status | |
| rsp = port.read(1) | |
| if rsp == '!': | |
| offset, expected, verified = struct.unpack("BBB", port.read(3)) | |
| raise ValueError("programming error at address %08X: expected %02X, verified %02X" % (address+offset, expected, verified)) | |
| elif rsp != '.': | |
| raise CommunicationError("unexpected response: '%s'" % rsp) | |
| def program_file(port, address, fp): | |
| while True: | |
| data = fp.read(0x40) | |
| if not data: | |
| break | |
| print("Programming %06X" % address) | |
| program(port, address, data) | |
| address += len(data) | |
| def read_file(port, address, count, fp): | |
| while count > 0: | |
| data = read(port, address, min(0x40, count)) | |
| fp.write(data) | |
| count -= len(data) | |
| address += len(data) | |
| def dump(port, count): | |
| with open("dump.bin", "wb") as fp: | |
| read_file(port, 0, count, fp) | |
| if __name__ == '__main__': | |
| port = serial.Serial("com12", baudrate=250000, timeout=5) | |
| try: | |
| ping(port) | |
| print("Ping OK") | |
| except: | |
| pass | |
| try: | |
| ping(port) | |
| print("Ping OK") | |
| except: | |
| pass | |
| try: | |
| ping(port) | |
| print("Ping OK") | |
| except: | |
| pass | |
| port.timeout = 30 | |
| set_type(port, "27256") | |
| if False: | |
| with open("Battle City (J) [p1].prg", "rb") as fp: | |
| program_file(port, 0, fp) | |
| else: | |
| dump(port, 0x8000) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Arduino-based EPROM programmer | |
| Pin assignments: | |
| * EPROM D0~D7 => PB0,PB1,PD2~PD7 | |
| * EPROM PGM# => PC0 | |
| * EPROM CE# => PC1 | |
| * EPROM OE# => PC2 | |
| * EPROM Ax => '595 Qx | |
| * '595 SIn => PB3/MOSI | |
| * '595 CLK => PB5/SCK | |
| * '595 RCLK => PB2/SS# | |
| */ | |
| /* General notes | |
| * 2764 / 27128 / 27010 / 27020 | |
| - CE# stays asserted | |
| - PGM# programs | |
| - OE# verifies | |
| * 27256 | |
| - CE# programs | |
| - PGM# is absent | |
| - OE# verifies | |
| * 27512 | |
| - CE# programs and verifies | |
| - OE# connected to VPP | |
| */ | |
| #define __AVR_ATmega328P__ 1 | |
| #define F_CPU 16000000UL | |
| #include <avr/io.h> | |
| #include <util/delay.h> | |
| #define ADDRESS_CHAIN_SIZE 3 | |
| static void set_address(uint32_t address) | |
| { | |
| /* Assert SS */ | |
| PORTB &= ~_BV(PB2); | |
| SPDR = address & 0xFF; | |
| while (!(SPSR & _BV(SPIF))); | |
| #if ADDRESS_CHAIN_SIZE > 1 | |
| SPDR = (address >> 8) & 0xFF; | |
| while (!(SPSR & _BV(SPIF))); | |
| #endif | |
| #if ADDRESS_CHAIN_SIZE > 2 | |
| SPDR = (address >> 16) & 0xFF; | |
| while (!(SPSR & _BV(SPIF))); | |
| #endif | |
| /* Deassert SS */ | |
| PORTB |= _BV(PB2); | |
| } | |
| static void assert_PGM(void) | |
| { | |
| PORTC &= ~_BV(PC0); | |
| } | |
| static void deassert_PGM(void) | |
| { | |
| PORTC |= _BV(PC0); | |
| } | |
| static void assert_CE(void) | |
| { | |
| PORTC &= ~_BV(PC1); | |
| } | |
| static void deassert_CE(void) | |
| { | |
| PORTC |= _BV(PC1); | |
| } | |
| static void assert_OE(void) | |
| { | |
| PORTC &= ~_BV(PC2); | |
| } | |
| static void deassert_OE(void) | |
| { | |
| PORTC |= _BV(PC2); | |
| } | |
| static void set_Dx_inputs(void) | |
| { | |
| /* Enable pull-up */ | |
| PORTD |= 0xFC; | |
| PORTB |= 0x03; | |
| /* Set to input */ | |
| DDRD &= 0x03; | |
| DDRB &= 0xFC; | |
| } | |
| static void set_Dx_outputs(void) | |
| { | |
| /* Set to output */ | |
| DDRD |= 0xFC; | |
| DDRB |= 0x03; | |
| } | |
| static void write_Dx(uint8_t value) | |
| { | |
| PORTD = (PORTD & 0x03) | (value & 0xFC); | |
| PORTB = (PORTB & 0xFC) | (value & 0x03); | |
| } | |
| static uint8_t read_Dx(void) | |
| { | |
| return (PIND & 0xFC) | (PINB & 0x03); | |
| } | |
| /* 16M = 62.5 ns */ | |
| static void read(uint32_t address, uint8_t *bytes, uint8_t count) | |
| { | |
| set_Dx_inputs(); | |
| assert_CE(); | |
| assert_OE(); | |
| while (count--) { | |
| set_address(address++); | |
| /* tACC = 250 ns max */ | |
| __asm__ __volatile__("nop; nop; nop; nop;"::); | |
| __asm__ __volatile__("nop; nop; nop; nop;"::); | |
| *bytes++ = read_Dx(); | |
| } | |
| deassert_OE(); | |
| deassert_CE(); | |
| } | |
| typedef uint8_t (*program_func_t)(uint32_t address, const uint8_t *bytes, uint8_t count); | |
| static uint8_t fail_index, expected, verified; | |
| static uint8_t program_2764(uint32_t address, const uint8_t *bytes, uint8_t count) | |
| { | |
| uint8_t index; | |
| uint8_t try_count; | |
| deassert_OE(); | |
| assert_CE(); | |
| index = 0; | |
| while (count--) { | |
| uint8_t byte_written = bytes[index]; | |
| uint8_t byte_read; | |
| set_address(address); | |
| try_count = 15; | |
| do { | |
| /* OE float delay */ | |
| __asm__ __volatile__("nop;"::); | |
| set_Dx_outputs(); | |
| write_Dx(byte_written); | |
| /* tDS = 2 us min */ | |
| _delay_us(2.0); | |
| /* tPW = 100 us */ | |
| assert_PGM(); | |
| switch (try_count) { | |
| case 25: _delay_ms(1.0); break; | |
| case 24: _delay_ms(3.0 * 1); break; | |
| case 23: _delay_ms(3.0 * 2); break; | |
| case 22: _delay_ms(3.0 * 3); break; | |
| case 21: _delay_ms(3.0 * 4); break; | |
| case 20: _delay_ms(3.0 * 5); break; | |
| case 19: _delay_ms(3.0 * 6); break; | |
| case 18: _delay_ms(3.0 * 7); break; | |
| case 17: _delay_ms(3.0 * 8); break; | |
| case 16: _delay_ms(3.0 * 9); break; | |
| default: | |
| _delay_ms(3.0 * 10); | |
| break; | |
| } | |
| deassert_PGM(); | |
| /* tDH = 2 us min */ | |
| _delay_us(2.0); | |
| set_Dx_inputs(); | |
| /* tOES = 2 us min */ | |
| _delay_us(2.0); | |
| assert_OE(); | |
| /* tOE = 100 ns max */ | |
| __asm__ __volatile__("nop; nop; nop; nop;"::); | |
| byte_read = read_Dx(); | |
| deassert_OE(); | |
| if (byte_written == byte_read) | |
| break; | |
| } while (--try_count); | |
| if (!try_count) { | |
| fail_index = index; | |
| expected = byte_written; | |
| verified = byte_read; | |
| break; | |
| } | |
| /* Move to next address */ | |
| address++; | |
| index++; | |
| } | |
| deassert_CE(); | |
| deassert_OE(); | |
| return try_count != 0; | |
| } | |
| static uint8_t program_27256(uint32_t address, const uint8_t *bytes, uint8_t count) | |
| { | |
| uint8_t index; | |
| index = 0; | |
| while (count--) { | |
| uint8_t byte_written = bytes[index]; | |
| uint8_t byte_read; | |
| uint8_t try_count = 25; | |
| set_address(address); | |
| do { | |
| /* tDF = 130 ns max */ | |
| __asm__ __volatile__("nop;"::); | |
| set_Dx_outputs(); | |
| write_Dx(byte_written); | |
| /* tDS = 2 us min */ | |
| _delay_us(2.0); | |
| /* tPW = 100 us */ | |
| assert_CE(); | |
| _delay_us(100.0); | |
| deassert_CE(); | |
| /* tDH = 2 us min */ | |
| _delay_us(2.0); | |
| set_Dx_inputs(); | |
| /* tOES = 2 us min */ | |
| _delay_us(2.0); | |
| assert_OE(); | |
| /* tOE = 100 ns max */ | |
| __asm__ __volatile__("nop; nop;"::); | |
| byte_read = read_Dx(); | |
| deassert_OE(); | |
| if (byte_written == byte_read) | |
| break; | |
| } while (--try_count); | |
| if (!try_count) { | |
| fail_index = index; | |
| expected = byte_written; | |
| verified = byte_read; | |
| break; | |
| } | |
| /* Move to next address */ | |
| address++; | |
| index++; | |
| } | |
| deassert_CE(); | |
| deassert_OE(); | |
| return try_count != 0; | |
| } | |
| static void hif_transmit(uint8_t value) | |
| { | |
| while (!(UCSR0A & _BV(UDRE0))); | |
| UDR0 = value; | |
| } | |
| static uint8_t hif_receive(void) | |
| { | |
| while (!(UCSR0A & _BV(RXC0))); | |
| return UDR0; | |
| } | |
| static void setup() | |
| { | |
| /* Setup pins */ | |
| set_Dx_inputs(); | |
| PORTC = 0xFF; | |
| DDRC = _BV(PC0) | _BV(PC1) | _BV(PC2); | |
| DDRB = _BV(PB2) | _BV(PB3) | _BV(PB5); | |
| /* Setup SPI */ | |
| /* Master; MSB first; clock speed = fOSC/4 */ | |
| SPCR = _BV(SPE) | _BV(MSTR); | |
| /* Setup UART */ | |
| /* Baud rate = 250k */ | |
| UBRR0L = 3; | |
| UBRR0H = 0; | |
| UCSR0A = 0; | |
| UCSR0B = _BV(RXEN0) | _BV(TXEN0); | |
| /* Asynchronous USART; 8N1 */ | |
| UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); | |
| } | |
| static program_func_t program = 0; | |
| static uint8_t buffer[128]; | |
| static void do_read() | |
| { | |
| uint32_t address; | |
| uint8_t count; | |
| uint8_t index; | |
| address = (uint32_t)hif_receive() << 24; | |
| address |= (uint32_t)hif_receive() << 16; | |
| address |= hif_receive() << 8; | |
| address |= hif_receive(); | |
| count = hif_receive(); | |
| if (count > sizeof(buffer)) { | |
| hif_transmit('!'); | |
| return; | |
| } | |
| hif_transmit('.'); | |
| read(address, buffer, count); | |
| for (index = 0; index < count; ++index) { | |
| hif_transmit(buffer[index]); | |
| } | |
| hif_transmit('.'); | |
| } | |
| static void do_program() | |
| { | |
| uint32_t address; | |
| uint8_t count; | |
| uint8_t index; | |
| address = (uint32_t)hif_receive() << 24; | |
| address |= (uint32_t)hif_receive() << 16; | |
| address |= hif_receive() << 8; | |
| address |= hif_receive(); | |
| count = hif_receive(); | |
| if (count > sizeof(buffer) || !program) { | |
| hif_transmit('!'); | |
| return; | |
| } | |
| hif_transmit('.'); | |
| for (index = 0; index < count; ++index) { | |
| buffer[index] = hif_receive(); | |
| } | |
| hif_transmit('.'); | |
| if (!program(address, buffer, count)) { | |
| hif_transmit('!'); | |
| hif_transmit(fail_index); | |
| hif_transmit(expected); | |
| hif_transmit(verified); | |
| } | |
| else { | |
| hif_transmit('.'); | |
| } | |
| } | |
| static void do_set_type() | |
| { | |
| switch (hif_receive()) { | |
| case '0': | |
| program = &program_2764; | |
| break; | |
| case '1': | |
| program = &program_27256; | |
| break; | |
| default: | |
| program = 0; | |
| hif_transmit('!'); | |
| return; | |
| } | |
| hif_transmit('.'); | |
| } | |
| int main() | |
| { | |
| setup(); | |
| for (;;) { | |
| set_Dx_inputs(); | |
| deassert_PGM(); | |
| deassert_CE(); | |
| deassert_OE(); | |
| switch (hif_receive()) { | |
| case '?': | |
| hif_transmit('!'); | |
| break; | |
| case 'R': | |
| do_read(); | |
| break; | |
| case 'P': | |
| do_program(); | |
| break; | |
| case 'T': | |
| do_set_type(); | |
| break; | |
| } | |
| } | |
| } | |
| FUSES = | |
| { | |
| .low = (FUSE_CKSEL3 & FUSE_SUT0 & FUSE_SUT1), | |
| .high = HFUSE_DEFAULT, | |
| .extended = EFUSE_DEFAULT, | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| CC=avr-gcc | |
| OBJCOPY=avr-objcopy | |
| OBJS=main.o | |
| PROJECT=eprog | |
| CFLAGS=-Os -mmcu=atmega328p | |
| LDFLAGS=-Os -mmcu=atmega328p | |
| .PHONY: all burn clean | |
| all: $(PROJECT).flash.hex | |
| $(PROJECT).flash.hex: $(PROJECT).elf | |
| $(OBJCOPY) -R .eeprom -R .fuse -R .lock -O ihex $< $@ | |
| $(PROJECT).elf: $(OBJS) | |
| $(CC) $(LDFLAGS) -o $@ $< | |
| burn: $(PROJECT).flash.hex | |
| avrdude -v -V -patmega328p -carduino -PCOM12 -b115200 -D -Uflash:w:$(PROJECT).flash.hex:i -Ulfuse:w:0xFF:m -Uhfuse:w:0xDE:m -Uefuse:w:0x05:m | |
| clean: | |
| del *.o |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment