It's all working!
Yep!
Ok, so this took a lot longer than it should have. It's all staring me in the face and I just didn't see it. I had the Logic 8 hooked up and all of the commands look good, but there is NOTHING coming back out of the FRAM chip on the SO pin (which connects to the MISO input on the Core). I swear I checked the wiring 7 times... however, I did it with your schematic -__- So on the 8th time I used the datasheet and low and behold I found the problem. You have pins 5 and 6 flipped on your schematic. I swapped those connections and MOSI was talking to SI now and SCK was going to the CLOCK input on the FRAM... finally a write sequence!
http://www.imgur.com/LI1yWVl
Here I send the WREN (0x06) command, then WRITE (0x02), the address (0x00) (0x01), and the data (0xAA)
Here I send the READ (0x03) command, then the address (0x00) (0x01), and the dummy byte (0x7E), the FRAM spits out (0xAA) on the SO pin (MISO input on Core) Woot!
Here it is reading out the last of 16 0xAA's:
Test Code:
#define CMD_WREN 0x06 // 0000 0110 Set Write Enable Latch
#define CMD_WRDI 0x04 // 0000 0100 Write Disable
#define CMD_RDSR 0x05 // 0000 0101 Read Status Register
#define CMD_WRSR 0x01 // 0000 0001 Write Status Register
#define CMD_READ 0x03 // 0000 0011 Read Memory Data
#define CMD_WRITE 0x02 // 0000 0010 Write Memory Data
#define FRAM_ADDR_MAX 0x1fff // Max address value
#define U3_CS_PIN D2 // chip select 1
#define U4_CS_PIN D3 // chip select 2
/**
* Write to FRAM (assuming 2 FM25C160 are used)
* addr: starting address
* buf: pointer to data
* count: data length.
* If this parameter is omitted, it is defaulted to one byte.
* returns: 0 operation is successful
* -1 address out of range
*/
int8_t FRAMWrite(uint16_t addr, uint8_t* buf, uint16_t count=1)
{
uint16_t cs = 0;
if (addr > FRAM_ADDR_MAX) {
addr -= FRAM_ADDR_MAX + 1;
cs = U4_CS_PIN;
} else {
cs = U3_CS_PIN;
}
if (addr > (FRAM_ADDR_MAX * 2) + 1) return -1;
uint8_t addrMSB = (addr >> 8) & 0xff;
uint8_t addrLSB = addr & 0xff;
digitalWrite(cs, LOW);
SPI.transfer(CMD_WREN); //write enable
digitalWrite(cs, HIGH);
digitalWrite(cs, LOW);
SPI.transfer(CMD_WRITE); //write command
SPI.transfer(addrMSB);
SPI.transfer(addrLSB);
for (uint16_t i = 0;i < count;i++) SPI.transfer(buf[i]);
digitalWrite(cs, HIGH);
return 0;
}
/**
* Read from FRAM (assuming 2 FM25C160 are used)
* addr: starting address
* buf: pointer to data
* count: data length.
* If this parameter is omitted, it is defaulted to one byte.
* returns: 0 operation is successful
* -1 address out of range
*/
int8_t FRAMRead(uint16_t addr, uint8_t* buf, uint16_t count=1)
{
uint16_t cs = 0;
if (addr > FRAM_ADDR_MAX) {
addr -= FRAM_ADDR_MAX + 1;
cs = U4_CS_PIN;
} else {
cs = U3_CS_PIN;
}
if (addr > (FRAM_ADDR_MAX * 2) + 1) return -1;
uint8_t addrMSB = (addr >> 8) & 0xff;
uint8_t addrLSB = addr & 0xff;
digitalWrite(cs, LOW);
SPI.transfer(CMD_READ);
SPI.transfer(addrMSB);
SPI.transfer(addrLSB);
for (uint16_t i=0; i < count; i++) buf[i] = SPI.transfer(0x7e);
digitalWrite(cs, HIGH);
return 0;
}
void setup()
{
Serial.begin(115200); // OPEN YOUR SERIAL TERMINAL NOW...
while(!Serial.available()) SPARK_WLAN_Loop(); // PRESS ENTER TO CONTINUE
Serial.flush();
pinMode(U3_CS_PIN, OUTPUT);
digitalWrite(U3_CS_PIN, HIGH);
pinMode(U4_CS_PIN, OUTPUT);
digitalWrite(U4_CS_PIN, HIGH);
//Setting up the SPI bus
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
//SPI.setClockDivider(SPI_CLOCK_DIV2); // this is 16MHz / 2 on arduino = 4MHz
SPI.setClockDivider(SPI_CLOCK_DIV4); // 72MHz / 4MHz = 18, but the closest divider is 16 (4.5MHz)
SPI.begin(); // Chip Select line will default to A2, however we're going to manual use D2, and D3
}
void loop()
{
//Test
char buf1[] = {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,'\0'};
char buf2[] = {0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,'\0'};
int8_t status;
status = FRAMWrite(1, (uint8_t*)buf1, strlen(buf1)); // Write buf1 to FRAM
if(status == -1) {
Serial.println("WRITE ERR: Address out of range!");
while(1) SPARK_WLAN_Loop(); // Die, with the possibility of reincarnation.
}
delay(10);
status = FRAMRead(1, (uint8_t*)buf2, strlen(buf1)); // Read saved value back into buf2, if it works it won't display "Failed to read."
if(status == -1) {
Serial.println("READ ERR: Address out of range!");
while(1) SPARK_WLAN_Loop(); // Die, with the possibility of reincarnation.
}
// printed this way because buf2 may not be null terminated properly.
// It is, but this is just because both strings happen to be exactly 16 chars already.
for(uint8_t i=0; i<16; i++) Serial.print(buf2[i],HEX);
Serial.println(" Press any key to try again!");
delay(50);
while(Serial.available()) Serial.read(); // MAKESHIFT FLUSH
while(!Serial.available()) SPARK_WLAN_Loop(); // PRESS ENTER TO CONTINUE
}
I also tested it at 4.5MHz and 18MHz. Reading 16 bytes took 67us at 4.5MHz, but with the SPI set 4 times faster the total time was only reduced to 42us. The reason is because even though the SPI is clocking at 18MHz, the delay between transfers is slow, and pads it out to basically the same time. I'm sure this can be improved in the Spark core firmware, otherwise the speed of the Spark is under-utilized. cc: @zachary @satishgn
This FRAM is very nice and I'm sure we'll start to see many people use it for ring buffers and all kinds of cool extra external RAM uses.