Create a gist now

Instantly share code, notes, and snippets.

@marlesan /main.cpp Secret
Last active Oct 31, 2016

What would you like to do?
EN29F002用ROMライタ ファームウェア
#include <Arduino.h>
//#define USE_FULL_ADDR_BUS
const int MAX_BLOCK_SIZE = 96;
unsigned long address = 0;
int blockSize = MAX_BLOCK_SIZE;
char imageBuffer[MAX_BLOCK_SIZE];
void resetRom(unsigned long ms);
void sendAddr(unsigned long addr);
void sendData(unsigned char data);
void actReset();
void inactReset();
void actCE();
void inactCE();
void actOE();
void inactOE();
void actWE();
void inactWE();
int writeBlock();
int readBlock();
int eraseChip();
/*
* [ピンアサイン(Mega2560)]
* PA [22-29] A0-A7
* PC [37-30] A8-A15
* PG [41-40] A16-A17
* PL [49-42] D0-7 Arduino->74HC541->ROM
* PK analog[8-15] D0-7 ROM->Arduino
* PF0 analog[0] RESET#
* PD0 [21] CE#
* PD1 [20] OE#
* PF2 analog[2] OE#@74HC541
* PD3 [18] WE#
*/
void setup()
{
DDRA = 0b11111111;
DDRC = 0b11111111;
DDRG |= 0b00000011;
DDRL = 0b11111111;
DDRK = 0b00000000;
DDRF |= 0b00000101;
DDRD |= 0b00001011;
// 起動時リセット
resetRom(1000);
// 書き込みソフトからの通信要求待機
Serial.begin(115200);
Serial.flush();
Serial.setTimeout(-1);
pinMode(13, OUTPUT);
while (Serial.readStringUntil(':').indexOf('#') < 0)
;
// 通信開始
Serial.print(F("ack:"));
Serial.flush();
}
void loop()
{
/*
* [コマンド一覧]
* a;ADDR; アドレスを設定
* A; 現在のアドレスを取得
* b;SIZE; ブロックサイズを設定
* B; 設定中のブロックサイズを取得
* M; ブロックサイズの最大値を取得
* w;BIN; バイナリをROMに書き込む
* R; ROMからバイナリを読み出す
* c; チップ全消去
* s;N; セクター消去 @TODO 必要になったら実装する
* r; リセット
*/
String cmd = Serial.readStringUntil(';');
switch (cmd.charAt(0)) {
case 'w':
Serial.readBytes(imageBuffer, blockSize);
if (writeBlock()) {
Serial.print(F("done;"));
} else {
Serial.print(F("failed;"));
}
break;
case 'R':
if (readBlock()) {
Serial.write(imageBuffer, blockSize);
}
break;
case 'a':
address = Serial.readStringUntil(';').toInt();
Serial.print(address);
Serial.print(';');
break;
case 'A':
Serial.print(address);
Serial.print(';');
break;
case 'b':
blockSize = Serial.readStringUntil(';').toInt();
blockSize = blockSize > MAX_BLOCK_SIZE ? MAX_BLOCK_SIZE : blockSize;
Serial.print(blockSize);
Serial.print(';');
break;
case 'B':
Serial.print(blockSize);
Serial.print(';');
break;
case 'M':
Serial.print(MAX_BLOCK_SIZE);
Serial.print(';');
break;
case 'c':
if (eraseChip()) {
Serial.print(F("done;"));
} else {
Serial.print(F("failed;"));
}
break;
case 'r':
resetRom(1000);
Serial.print(F("done;"));
break;
default:
// 不明なコマンド
Serial.print(cmd);
Serial.print(F(":unknown;"));
break;
}
}
/* ROMをリセットする */
void resetRom(unsigned long ms)
{
actReset();
delay(ms);
inactCE();
inactOE();
inactWE();
inactReset();
}
/* アドレスバスにアドレスを出力する */
void sendAddr(unsigned long addr)
{
PORTA = addr;
PORTC = addr>>8;
#ifdef USE_FULL_ADDR_BUS
PORTG |= (addr>>16) & 0b11; // 64kBあれば当面は十分
#endif
}
/* データバスにデータを出力する */
void sendData(unsigned char data)
{
PORTL = data;
// PORTL = ~data; // バス制御に540を使うならこっち
}
/* RESET信号のON/OFF(負論理) */
void actReset() { PORTF &= ~_BV(0); }
void inactReset() { PORTF |= _BV(0); }
/* CE信号のON/OFF(負論理) */
void actCE() { PORTD &= ~_BV(0); }
void inactCE() { PORTD |= _BV(0); }
/*
* [OE信号のON/OFF(負論理)]
* ROMのOEをアクティブにする時は541を非アクティブにして
* Arduinoからデータバスへの出力を止める。
*/
void actOE() { PORTF |= _BV(2); PORTD &= ~_BV(1); }
void inactOE() { PORTD |= _BV(1); PORTF &= ~_BV(2); }
/* WE信号のON/OFF(負論理) */
void actWE() { PORTD &= ~_BV(3); }
void inactWE() { PORTD |= _BV(3); }
int writeBlock()
{
inactOE();
inactWE();
actCE();
for (int i = 0; i < blockSize; i++) {
// コマンド書き込み
sendAddr(0x555);
sendData(0xAA);
actWE();
inactWE();
sendAddr(0xAAA);
sendData(0x55);
actWE();
inactWE();
sendAddr(0x555);
sendData(0xA0);
actWE();
inactWE();
// データ書き込み
sendAddr(address++);
sendData(imageBuffer[i]);
actWE();
inactWE();
/*
* [DQ7ポーリング]
* 書き込み進行中はROMのデータピン7bit目(DQ7)に
* 直前に書き込んだデータの7bit目を反転した信号が出力される。
* 書き込み完了すると両者が一致するので、次のバイトの処理に移る。
* DQ5にHが出力されている場合、書き込み時間が異常に長いことを示すので、
* その状態でもう一度DQ7を確認して書き込み完了していないなら、失敗とする。
*/
actOE();
int data7 = imageBuffer[i] & 0b10000000;
while (1) {
if ((PINK & _BV(7)) == data7) {
break;
} else if (PINK & _BV(5)) {
if ((PINK & _BV(7)) == data7) {
break;
} else {
// 書き込み失敗
inactOE();
inactCE();
return 0;
}
}
}
inactOE();
}
inactCE();
return 1;
}
int readBlock()
{
inactOE();
inactWE();
actCE();
for (int i = 0; i < blockSize; i++) {
sendAddr(address++);
actOE();
imageBuffer[i] = PINK;
inactOE();
}
inactCE();
return 1;
}
int eraseChip()
{
inactOE();
inactWE();
actCE();
sendAddr(0x555);
sendData(0xAA);
actWE();
inactWE();
sendAddr(0xAAA);
sendData(0x55);
actWE();
inactWE();
sendAddr(0x555);
sendData(0x80);
actWE();
inactWE();
sendAddr(0x555);
sendData(0xAA);
actWE();
inactWE();
sendAddr(0xAAA);
sendData(0x55);
actWE();
inactWE();
sendAddr(0x555);
sendData(0x10);
actWE();
inactWE();
/*
* [DQ7ポーリング]
* DQ7 == 0 チップ消去進行中
* DQ7 == 1 チップ消去完了
*/
actOE();
while ((PINK & _BV(7)) == 0)
; // @TODO タイムアウト処理
inactOE();
inactCE();
return 1;
}
Owner

marlesan commented Oct 23, 2016

使用上の注意点

  • Mega2560からの出力データ(49-42番ピン)は74HC541を通してROMのデータピンに接続する。
  • 74HC541のOEは2番ピンで制御する。541のOE(もしくはGという名前)は二つあるが、その両方に接続してもいいし、どちらかをGNDに落としてもう片方だけに接続するのでもよい。
  • 1バイト読み書きするごとに内部のaddressがインクリメントされるのに注意。
  • シールド化する場合、Mega2560をリセットするボタンもシールド上に実装しないと不便(のはず)。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment