3ms per read is a lot, you shouldn't need so much, also use a thicker wire for gnd.
As you're not using multiple addressing neither writing (for now) you can get rid of some bus signals:
CE -> GND
WE -> VCC
BYTE -> VCC
RST ->10K to VCC, 100nF to GND (self-resets on power-on)
RY/BY can be ignored as you're not doing any write operations and the data set-up time is like 100ns.
10us delay should be enough, even in bad boards, unless there's a faulty/weak connection.
Example loop:
Write address
Wait 10uS
Set OE low
Wait 10us
Read data
Set OE high
Wait 10us
Last year I did the same to read a 29F800 using a STM32, dumping the contents into a usb drive.
To save IO pins I used four 74HC373 latches and enabled Byte mode, taking only 13 pins instead of almost 40.
The 4 latches were configured as:
- Data 0:7
- Addr 0:7
- Addr 8:15
- Addr 16:18, A-1, E, G, W
The signaling was a little more complex but worked well.
Few capacitors and proper grounding made it much easier than using breadboard, was able to greatly reduce the toggling delays to ~100ns, dumping the 512KB into the flash drive in ~25 seconds.
The code was as follows, you can ignore the latch handling, just read the comments.
// GPIO toggling delay
#define delay() asm("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop")
// Configure Data port direction. 1= input, 0=output
void setDataDir(bool input){
GPIOA->MODER = input ? (GPIOA->MODER & 0xFFFF0000) : (GPIOA->MODER & 0xFFFF0000) | 0x5555;
}
// Make a low-active pulse on the specified GPIO pin
void pulse(GPIO_TypeDef *GPIOx, uint32_t pin){
// Set pin low
GPIOx->BSRR=pin;
delay();
// Set pin high
GPIOx->BSRR=pin<<16;
}
// Read Data from latch
uint8_t read(void){
// Set Data port as input
setDataDir(1);
delay();
// Enable Data latch Output
OE_DATA_GPIO_Port->BSRR = OE_DATA_Pin<<16;
// Pulse latch load
pulse(LE_DATA_GPIO_Port, LE_DATA_Pin);
// Read data
uint8_t data = GPIOA->IDR & 0xFF;
// Disable Data latch Output
OE_DATA_GPIO_Port->BSRR = OE_DATA_Pin;
delay();
// Set Data port as output
setDataDir(0);
return data;
}
// Fill buffer, starts reading at the specified address with "size" number of 16-bit reads.
void fillBuffer(uint32_t address, uint8_t* buffer, uint32_t size){ // Size = number of 16-bit reads
while(size--){
// Load address with A-1=0 (lower byte)
GPIOA->ODR = address & 0xFF;
pulse(LE_ADR0_GPIO_Port, LE_ADR0_Pin);
GPIOA->ODR = (address>>8) & 0xFF;
pulse(LE_ADR1_GPIO_Port, LE_ADR1_Pin);
GPIOA->ODR = ((address>>16) & 0xFF) | A_1_EN;
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Enable CE
GPIOA->ODR = ((address>>16) & 0xFF) | (A_1_EN & E_EN);
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Enable G
GPIOA->ODR = ((address>>16) & 0xFF) | (A_1_EN & E_EN & G_EN);
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Read lower byte
*buffer++=read();
// Deselect G
GPIOA->ODR = ((address>>16) & 0xFF) | (A_1_EN & E_EN);
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Deselect E
GPIOA->ODR = ((address>>16) & 0xFF) | (A_1_EN);
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Disable A-1 (read higher byte)
GPIOA->ODR = ((address>>16) & 0xFF) | CTRL_DIS;
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Enable E
GPIOA->ODR = ((address>>16) & 0xFF) | E_EN;
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Enable G
GPIOA->ODR = ((address>>16) & 0xFF) | (E_EN & G_EN);
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Read higher byte
*buffer++=read();
// Deselect G
GPIOA->ODR = ((address>>16) & 0xFF) | E_EN;
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
// Deselect E
GPIOA->ODR = ((address>>16) & 0xFF) | CTRL_DIS;
pulse(LE_ADR2_GPIO_Port, LE_ADR2_Pin);
address++;
count-=2;
}
}