Кому надо прошить на скорую руку SPI флеш - сюда.
Починил видеорегистратор. Заодно поправил "прошивочный скетч ардуины, добавил задержку при стирании блока. И ралочку чипа (команда "u" в порт).
Компилится под IDE 1.5.x, под 1.6 - ругается на PROGMEM, лень разобраться.
Код:
#include <avr\pgmspace.h>
//#include "C:\Work\arduino-1.6.1\hardware\arduino\sam\cores\arduino\avr\pgmspace.h"
#define DATAOUT 11//MOSI
#define DATAIN 12//MISO
#define SPICLOCK 13//sck
#define SLAVESELECT 10//ss
//opcodes
#define WREN 6
#define WRDI 4
#define RDSR 5
#define WRSR 1
#define READ 3
#define WRITE 2
#define SECTOR_ERASE 0x20
#define CHIP_ERASE 0xC7
byte buffer [256];
/* // Via http://excamera.com/sphinx/article-crc.html
static PROGMEM prog_uint32_t crc_table[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
*/
prog_uint32_t crc_table[16] PROGMEM = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
unsigned long crc_update(unsigned long crc, byte data)
{
byte tbl_idx;
tbl_idx = crc ^ (data >> (0 * 4));
crc = pgm_read_dword_near(crc_table + (tbl_idx & 0x0f)) ^ (crc >> 4);
tbl_idx = crc ^ (data >> (1 * 4));
crc = pgm_read_dword_near(crc_table + (tbl_idx & 0x0f)) ^ (crc >> 4);
return crc;
}
unsigned long crc_buffer(void)
{
unsigned long crc = ~0L;
for(int i=0; i < 256; i++)
crc = crc_update(crc, buffer[i]);
crc = ~crc;
return crc;
}
void setup()
{
Serial.begin(115200);
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK,OUTPUT);
pinMode(SLAVESELECT,OUTPUT);
digitalWrite(SLAVESELECT,HIGH); //disable device
// SPCR = 01010000
//interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
//sample on leading edge of clk,system clock/4 rate (fastest)
//SPCR = (1<<SPE)|(1<<MSTR);
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPI2X);
byte clr;
clr=SPSR;
clr=SPDR;
delay(10);
buffer[0] = 0xca;
buffer[1] = 0xfe;
/* digitalWrite(SLAVESELECT,LOW); //Выбираем чип
delay(20);
spi_transfer(1); //Регистр статуса на запись
delay(20);
spi_transfer(0); //Запись в регистр
//spi_transfer(0); //Запрос статуса (регистра)
delay(20);
Serial.println(spi_transfer(0xff));
delay(20);
digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
*/
}
void loop()
{
byte addr1, addr2;
while(Serial.available() == 0)
;
int cmd = Serial.read();
switch(cmd) {
case '>':
Serial.print('>');
break;
case 'e':
erase_all();
break;
case 'r':
addr1 = read_hex();
addr2 = read_hex();
read_page(addr1, addr2);
break;
case 'w':
addr1 = read_hex();
addr2 = read_hex();
write_page(addr1, addr2);
break;
case 'd':
dump_buffer();
break;
case 'c':
dump_buffer_crc();
break;
case 'l':
load_buffer();
break;
case 's':
addr1 = read_hex();
addr2 = read_nybble() << 4;
erase_sector(addr1, addr2);
break;
case '?':
case 'h':
Serial.println("\nSPI Flash programmer");
Serial.println(" e : erase chip");
Serial.println(" sXXX : erase 4k sector XXX (hex)");
Serial.println(" c : print buffer CRC-32");
Serial.println(" rXXXX: read 256-byte page XXXX (hex) to buffer");
Serial.println(" wXXXX: write buffer to 256-byte page XXXX (hex)");
Serial.println(" d : display the buffer (in hex)");
Serial.println(" l<b> : load the buffer with <b>, where b is 256 bytes of data");
Serial.println();
Serial.println("Ex: r3700 - read 256 bytes from page 0x3700");
Serial.println("Ex: lcafe[...]3737 - load the buffer with 256 bytes, first byte 0xca...");
break;
case 't':
int counter;
Serial.println("Testing");
read_page(0, 0);
Serial.println("Read 1");
dump_buffer();
// erase_sector(0, 0);
for(counter = 0; counter < 256; counter++)
{
buffer[counter] = 0x55;
}
dump_buffer();
write_page(0, 0);
Serial.println("Writing");
for(counter = 0; counter < 256; counter++)
{
buffer[counter] = 0x11;
}
read_page(0, 0);
Serial.println("Read 2");
dump_buffer();
//READ EEPROM
break;
case 'u':
digitalWrite(SLAVESELECT,LOW); //Выбираем чип
spi_transfer(5); //Запрос статуса (регистра)
Serial.print("Status=");
Serial.println(spi_transfer(0xff));
delay(20);
digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
delay(20);
digitalWrite(SLAVESELECT,LOW); //Выбираем чип
delay(20);
spi_transfer(1); //Регистр статуса на запись
delay(20);
spi_transfer(0); //Запись в регистр
delay(20);
Serial.println(spi_transfer(0xff));
delay(20);
digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
break;
}
Serial.flush();
}
void dump_buffer()
{
int counter;
for(counter = 0; counter < 256; counter++) {
Serial.print(buffer[counter] >> 4, HEX);
Serial.print(buffer[counter] & 0xF, HEX);
}
Serial.println();
}
void dump_buffer_crc()
{
unsigned long crc = crc_buffer();
Serial.print(crc_buffer(), HEX);
Serial.println();
}
void load_buffer()
{
int counter;
for(counter = 0; counter < 256; counter++) {
buffer[counter] = read_hex();
}
}
byte read_nybble()
{
int nybble;
while((nybble = Serial.read()) == -1)
;
if(nybble >= 'A') {
// works for lowercase as well (but no range checking of course)
return 9 + (nybble & 0x0f);
} else {
return nybble & 0x0f;
}
}
byte read_hex()
{
byte val;
val = (read_nybble() & 0xf) << 4;
val |= read_nybble();
return val;
}
byte spi_transfer(volatile char data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1<<SPIF))) // Wait for the end of the transmission
{
};
return SPDR; // return the received byte
}
void
read_page(byte adr1, byte adr2)
{
//READ EEPROM
int counter;
digitalWrite(SLAVESELECT,LOW);
spi_transfer(READ);
spi_transfer(adr1); // bits 23 to 16
spi_transfer(adr2); // bits 15 to 8
spi_transfer(0); // bits 7 to 0
for(counter = 0; counter < 256; counter++) {
buffer[counter] = spi_transfer(0xff);
}
digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
}
void wait_for_write()
{
byte statreg = 0x1;
while((statreg & 0x1) == 0x1) {
// Wait for the chip.
digitalWrite(SLAVESELECT, LOW);
spi_transfer(RDSR);
statreg = spi_transfer(RDSR);
digitalWrite(SLAVESELECT, HIGH);
}
}
void
write_page(byte adr1, byte adr2)
{
int counter;
digitalWrite(SLAVESELECT,LOW);
delay(2);
spi_transfer(WREN); //write enable
digitalWrite(SLAVESELECT,HIGH);
delay(2);
digitalWrite(SLAVESELECT,LOW);
delay(1);
spi_transfer(WRITE); //write instruction
spi_transfer(adr1); // bits 23 to 16
spi_transfer(adr2); // bits 15 to 8
spi_transfer(0); // bits 7 to 0
for (counter = 0; counter < 256; counter++)
{
spi_transfer(buffer[counter]);
// delay(1);
}
delay(2);
digitalWrite(SLAVESELECT,HIGH);
delay(5);
wait_for_write();
}
void
erase_all()
{
int counter;
digitalWrite(SLAVESELECT,LOW);
spi_transfer(WREN); //write enable
digitalWrite(SLAVESELECT,HIGH);
delay(10);
digitalWrite(SLAVESELECT,LOW);
Serial.println(" e : erase chip start");
spi_transfer(CHIP_ERASE);
delay(1000);
digitalWrite(SLAVESELECT,HIGH);
delay(1);
wait_for_write();
Serial.println(" e : erase chip end");
}
void
erase_sector(byte addr1, byte addr2)
{
digitalWrite(SLAVESELECT,LOW);
delay(12);
spi_transfer(WREN);
delay(12);
digitalWrite(SLAVESELECT,HIGH);
delay(12);
digitalWrite(SLAVESELECT,LOW);
spi_transfer(SECTOR_ERASE);
spi_transfer(addr1);
spi_transfer(addr2);
spi_transfer(0);
delay(82);
digitalWrite(SLAVESELECT,HIGH);
delay(62);
}
А то китаец прислал чипы, но в них было прошито что-то, похожее на фирмварь электронной книги. И часть флешки - запрещена на запись. Пришлось курить мануал.
А флешки новые, непаяные. С завода упер, что-ли?
Заодно чуть подправил питоновский скрипт прошивочный.
spi_flash_programmer_client.py
Код:
import time
import serial
import argparse
import binascii
import sys
MAX_SIZE = 16 * 1024 * 1024
SECTOR_SIZE = 4096
class NoResponse(Exception):
pass
def decodeHex(hexBytes):
return binascii.unhexlify(hexBytes)
def encodeHex(data):
return binascii.hexlify(data)
class SerialChatChat:
def __init__(self, filename):
self.sock = serial.Serial(filename, 115200, timeout=1)
# Wait for the Arduino bootloader
time.sleep(2)
def _readCRC(self):
crc = b''
self.sock.flushInput()
self.sock.write(b'c')
while not crc.endswith(b'\r\n'):
crc += self.sock.read(1)
return int(crc, 16)
def _readPageOnce(self, page):
# Reads page, returns CRC
cmd = 'r%02x%02x' % ((page & 0xff00) >> 8, (page & 0xff))
self.sock.write(cmd.encode('iso-8859-1'))
self.sock.flush()
return self._readCRC()
def _readPageMultiple(self, page):
# Keep reading until we get two pages reads the same.
crc = [self._readPageOnce(page), self._readPageOnce(page)]
count = 0
while crc[0] != crc[1] and count < 3:
print("Retrying read of page %d" % (page))
crc.pop(0)
crc.append(self._readPageOnce(page))
count += 1
if crc[0] != crc[1]:
#raise Exception()
print("CRC if error")
return crc[1]
def readPage(self, page):
self._readPageMultiple(page)
# Dump the buffer.
self.sock.write(b'd')
result = []
amt_read = 0
while amt_read < 512:
fromChip = self.sock.read(512 - amt_read)
amt_read += len(fromChip)
result.append(fromChip)
result = b''.join(result)
assert len(result) == 512, len(result)
return decodeHex(result)
def writePage(self, page, data):
# Write the page and verify that it was written correctly.
expectedCRC = binascii.crc32(data)
data = encodeHex(data)
assert len(data) == 512, (len(data), data)
self.sock.write(b'l' + data)
self.waitReady()
# This shouldn't fail if we're using a reliable connection.
if self._readCRC() != expectedCRC:
print("CRC error")
raise Exception()
# Write it, read it back, fail if we can't read what we wrote.
cmd = 'w%02x%02x' % ((page & 0xff00) >> 8, (page & 0xff))
self.sock.write(cmd.encode('iso-8859-1'))
self.sock.flush()
self.waitReady()
writtenCRC = self._readPageMultiple(page)
if writtenCRC != expectedCRC:
print("CRC error verify")
###print("written CRC", writtenCRC, "expected CRC", expectedCRC, page, data)
print("written CRC", writtenCRC, "expected CRC", expectedCRC, page)
return writtenCRC == expectedCRC
#return 1
def waitReady(self, seconds_to_wait=5):
self.sock.flushInput()
self.sock.write(b'>')
self.sock.flush()
for retry in range(seconds_to_wait):
data = self.sock.read(1)
if data == b'>':
break
elif data:
print('unexpected', data)
else:
raise NoResponse()
def verify(self, filename, flashStartByte=0, fileStartByte=0):
assert flashStartByte % 256 == 0
assert fileStartByte % 256 == 0
idx = flashStartByte
print("Verifying %s with flash start %d and file start %d" % (filename, flashStartByte, fileStartByte))
with open(filename, 'rb') as h:
h.seek(fileStartByte)
while True:
sys.stdout.write('\r%d' % (idx))
sys.stdout.flush()
fromFile = h.read(256)
if fromFile == b'':
break
assert len(fromFile) == 256, len(fromFile)
fileCRC = binascii.crc32(fromFile)
for retry in range(3):
deviceCRC = self._readPageOnce(idx // 256)
if deviceCRC != fileCRC:
print("Mismatch %x != %x, retrying" % (deviceCRC, fileCRC))
else:
break
else:
raise Exception("Mismatch")
idx += 256
def eraseSector(self, sectorNumber):
cmd = 's%03x' % (sectorNumber)
self.sock.write(cmd.encode('iso-8859-1'))
self.waitReady()
def _writeSectorOnce(self, byteOffset, sectorData):
assert byteOffset % SECTOR_SIZE == 0
assert len(sectorData) == SECTOR_SIZE
pageData = []
for idx in range(0, SECTOR_SIZE, 256):
pageData.append(sectorData[idx:idx+256])
pageOffset = byteOffset // 256
expectedCRCs = [binascii.crc32(page) for page in pageData]
actualCRCs = [self._readPageMultiple(pageOffset + idx) for idx in range(SECTOR_SIZE // 256)]
if expectedCRCs != actualCRCs:
self.eraseSector(byteOffset // SECTOR_SIZE)
for idx, page in enumerate(pageData):
if not self.writePage(pageOffset + idx, page):
return False
return True
def writeFile(self, filename, offset=0):
with open(filename, 'rb') as h:
data = h.read(MAX_SIZE)
print('Writing', filename, 'at', offset)
for idx in range(0, len(data), SECTOR_SIZE):
sys.stdout.write('\r%d of %d' % (idx, len(data)))
sys.stdout.flush()
pageData = data[idx:idx + SECTOR_SIZE]
assert len(pageData) == SECTOR_SIZE
for retry in range(144):
if self._writeSectorOnce(offset + idx, pageData):
break
else:
print("Retrying")
else:
print("Error control of write")
#raise Exception()
def readToFile(self, filename, size, flashStartByte):
assert size % 256 == 0
assert flashStartByte % 256 == 0
flashStartPage = flashStartByte // 256
with open(filename, 'wb') as handle:
for idx in range(0, size, 256):
sys.stdout.write('\r%d of %d' % (idx, size))
sys.stdout.flush()
page = idx // 256
pageData = self.readPage(page + flashStartPage)
handle.write(pageData)
def main():
parser = argparse.ArgumentParser(description="Interface with an Arduino-based SPI flash programmer")
parser.add_argument('-f', dest='filename')
parser.add_argument('-d', dest='device', default='/dev/tty.usbserial-A700ekGi')
parser.add_argument('-s', dest='size', default='4096', help="size in KB")
parser.add_argument('--flash-offset', dest='flash_offset', default='0', help='offset for flash read/write in bytes')
parser.add_argument('--file-offset', dest='file_offset', default='0', help='offset for file read/write in bytes')
parser.add_argument('command', choices=('write', 'read', 'verify'))
args = parser.parse_args()
chat = SerialChatChat(args.device)
if args.command=='write':
chat.writeFile(args.filename, int(args.flash_offset))
elif args.command=='read':
chat.readToFile(args.filename, int(args.size) * 1024, int(args.flash_offset))
elif args.command=='verify':
chat.verify(args.filename, int(args.flash_offset), int(args.file_offset))
else:
raise NotImplementedError()
if __name__ == '__main__':
main()
Фирмварь к регистратору из скачанных с инета подошла первая ж. Дольше искал - куда ж я батарейку от него сунул, когда разбирал.