@bko created an excellent NTP-based time library that is pretty awesome.
Ok, time to teach troubleshooting 101
So you have a function thatās designed to spit out failure messages spitting them out all of the time? Must be a failure⦠but what failure?
So the following code is always spitting out INIT FAILEDā¦
if (!SD.begin(chipSelect)) {
Serial.println("initialization failed!");
}
SD.being() must be returning 0.
Since we are calling the class begin() method directly, itās likely in the SD.cpp file. Searching that file for ābeginā, second hit is this function.
// HARDWARE SPI
boolean SDClass::begin(uint8_t csPin) {
/*
Performs the initialisation required by the sdfatlib library.
Return true if initialization succeeds, false otherwise.
*/
return card.init(SPI_HALF_SPEED, csPin) &&
volume.init(card) &&
root.openRoot(volume);
}
Notice the 3 separate actions that are ran and the output of all of the are ANDed together. If any one of them fails, the result of the AND operation will be 0. So, back to the question⦠which one is failing?
We need to break them up to find out. So looking at the other examples proves useful. The SpeedTest example already has the code we need.
// initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur.
if (!card.init(SPI_FULL_SPEED)) error("card.init failed");
// initialize a FAT volume
if (!volume.init(&card)) error("volume.init failed!");
Serial.print("Type is FAT");
Serial.println(volume.fatType(), DEC);
if (!root.openRoot(&volume)) error("openRoot failed");
Notice in this code the card.init() only includes one argument. The second one is normally the SS pin. Since we are not defining one, the default will be set based on our class prototype declaration and be set to āSSā which is actually equal to A2.
So give this code a try and see whatās failing. Replace the ābeginā code with this code. Perhaps your card is not formatted properly. Sometimes those micro sockets can be flaky too, eject/insert a few times can help.
I havenāt debug this but will give it a shot later
Did some improvements for the board but will need to populate the FRAMs and do some tests first!
@BDub and the rest, any test code i can use or the functions for the External flash is reusable for this case?
Thanks!!
So I populated a FRAM.
Whatās next?
@kennethlimcp, Well Done. I think the timing is perfect to test this:
Only change you would need to do is edit this part in https://github.com/spark/core-common-lib/blob/feature/sdcard-fatfs-support/SPARK_Firmware_Driver/inc/sdcard_spi.h#L50:
#define SD_CS_PIN GPIO_Pin_4 /* PA.04 */
#define SD_CS_GPIO_PORT GPIOA /* GPIOA */
#define SD_CS_GPIO_CLK RCC_APB2Periph_GPIOA
It should match the chip select line on your shield. I can provide a low level method to overwrite this CS line in application code if necessary.
This should get yourself going with FatFs Application interface functions for mounting SD card, read/write files etc. the sample code you can find over here: http://elm-chan.org/fsw/ff/00index_e.html
Thanks for the input!
The SD card is on the default SS, A2
I havenāt gotten local compile up and running but i guess itās time i do so.
Would definitely love to help test this!
The Status LED is working for the SD!
Have some test code for the FRAM but iām getting garbage from the Serial.output. Hoping the FRAM isnāt burnt or something.
Time for bed! will work on the FRAM tmr and do a demo
There is something mesmerizing about the breathing cyan and the flashing red LED. Iām pretty sure you could reprogram human brains with that sequence and subliminal messaging.
Share your code and we'll see if we can find any reason it shouldn't work. Have you checked continuity on all of the pins between the spark core and the FRAM IC legs? Also no shorts where there shouldn't be?
I checked it after soldering but Iāll do it again just to be sure.
Using this for the code:
http://www.kerrywong.com/2012/01/15/using-fram-as-nonvolatile-memory-with-arduino/
Ok I THINK this should work. Now based on my previous recommendations of moving the CS lines to D2 and D3, I completely failed and routed them MYSELF to D0 and D1 (slaps forehead)⦠you should really fix that.
I re-wrote a bunch of little stuff. This compiles, but I obviously canāt test it. Iām just going to assume that the FRAM you populated (U3) is the low bank of memory, and U4 will be the upper bank. I didnāt check the range (0x7ff), please change that to match the FRAM you populated⦠this should be a #define as well.
const uint8_t CMD_WREN = 0x06; //0000 0110 Set Write Enable Latch
const uint8_t CMD_WRDI = 0x04; //0000 0100 Write Disable
const uint8_t CMD_RDSR = 0x05; //0000 0101 Read Status Register
const uint8_t CMD_WRSR = 0x01; //0000 0001 Write Status Register
const uint8_t CMD_READ = 0x03; //0000 0011 Read Memory Data
const uint8_t CMD_WRITE = 0x02; //0000 0010 Write Memory Data
const uint16_t U3_CS_PIN = D1; // chip select 1
const uint16_t U4_CS_PIN = D0; // 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 > 0x7ff) {
addr -=0x800;
cs = U4_CS_PIN;
} else {
cs = U3_CS_PIN;
}
if (addr > 0x7ff) 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 > 0x7ff) {
addr -=0x800;
cs = U4_CS_PIN;
} else {
cs = U3_CS_PIN;
}
if (addr > 0x7ff) 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(0x00);
digitalWrite(cs, HIGH);
return 0;
}
void setup()
{
Serial.begin(9600); // OPEN YOUR SERIAL TERMINAL NOW...
while(!Serial.available()) SPARK_WLAN_Loop(); // PRESS ENTER TO CONTINUE
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.begin(); // Chip Select line will default to A2, however we're going to manual use D0, and D1
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
//SPI.setClockDivider(SPI_CLOCK_DIV2); // this is 16MHz / 2 on arduino = 4MHz
SPI.setClockDivider(SPI_CLOCK_DIV16); // 72MHz / 4MHz = 18, but the closest divider is 16 (4.5MHz)
//Test
char buf1[]="Successful read";
char buf2[]="Failed to read!";
int8_t 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.
}
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 15 chars already.
for (uint8_t i = 0; i < 15; i++) Serial.print(buf2[i]);
}
void loop()
{
}
BTW these routines donāt seemlessly handle a write/read that crosses BANKS of FRAM⦠Not sure if thatās necessary. Iād say no, but someone with large chunks of data may think otherwise.
What's the issue for this?
Here's where I mentioned it before...
Initial tests didn't worked. I added some Serial.print to debug. Maybe there's some hardware/soldering issues so i'll do a check later smiley
The code all ran fine but the fine Serial.print of buf2 gave an empty print out
@BDub, Ah so i'm on the I2C pins. Ok good point! Will change it later in the day
EDIT:
The FRAM i bought is 64k which gives 4,192 x 8 bits. So my address max is 0x1FFF
Do i need to change the MSB and LSB masks?
> int8_t FRAMWrite(uint16_t addr, uint8_t *buf, uint16_t count=1)
> {
> uint16_t cs = 0;
> if (addr > 0x1fff) {
> addr -=0x2000;
> cs = U4_CS_PIN;
> } else {
> cs = U3_CS_PIN;
> }
> if (addr > 0x1fff) return -1;
> uint8_t addrMSB = (addr >> 8) & 0xff;
> uint8_t addrLSB = addr & 0xff;
Also,
This line in the FRAMRead()
doesn't seem correct:
for (int i=0; i < count; i++) buf[i] = SPI.transfer(0x00);
0x00 is not one of the 6 Op-code
Hmm, letās hope itās an open MISO connection.
BDub, in FRAMWrite(), you donāt need to set CS HIGH between writes in the code since it is a multi-byte write sequence.
Correct, and it's not doing that as far as I know. FWIW I just copied that code @kennethlimcp posted and re-wrote some of the variable types to be more explicit for the STM32.. and cleaned up the comments and example in general.
@kennethlimcp 0x1FFF is correct, but it's 8,192 words x 8 bits = 65,536 (64Ki) ... just a typo I'm sure. Masks are fine as is.
This line:
if (addr > 0x1fff) return -1;
Should be:
if (addr > 0x3fff) return -1;
I haven't looked, but I'm guessing the 0x00 is just a dummy value. With SPI you always SEND a byte at the same time you RECEIVE a byte. So we have to send some value to clock through the system. I think this is typically 0xFF, but if the FRAM ignores it then it doesn't matter.
Hmmm⦠I used the original code as reference so his was 16Kbit (0x7ff for 2047 x 8bits)
So Iām thinking the addr is for BYTE and I used 0x1fff x 8 bits
Yes no maybe so?
@kennethlimcp ... if things are not working at all still, have you considered lowering the SPI clock speed?
(Sorry if this has already been covered earlier in the thread. A quick scan didn't show anything to my eyes)
I could not get any sense out of my AT86RF231 2.4GHz radio chip just recently, until I dropped from the default fOSC/4 (18MHz) to 1.125MHz clock -- fOSC/64. Like this ...
SPI.setClockDivider(SPI_CLOCK_DIV64);
I also needed to change from the default Mode 3 to Mode 0 ...
SPI.setDataMode(0);
Then after those, your ...
SPI.begin(<YOUR_CS_PIN_HERE>);
Actually, let me go grab the data sheet from the link you gave me and check the data mode for you ...
OK. Seems our chip should work in either Mode 0 or Mode 3. Page 4 says, ...
MB85RS64A corresponds to the SPI mode 0 (CPOL = 0, CPHA = 0) , and SPI mode 3 (CPOL = 1, CPHA = 1) .
Personally, I prefer Mode 0. Seems strange to me to have a system where the high going edge of the clock is used and yet the idle state is also high. shrug
Nice data sheet by the way. Very clear and easy to follow, compared to some anyway.