SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By Cannibal
#21705
Hey All, there has been some discussion about how to get a DOSonCHIP module to function with the SPI bus, since the documentation for the product is best described as 'woefully inadequate'. I recently picked one of these up and sat down with it for an evening, and through a combination of intuition and *luck* was able to get it to communicate over the SPI bus.

Below is pseudocode and code. The code serves the function of piping spi<-->uart, and handshaking with the card to figure out when it wants the master to clock data out, so one can work in terminal just to speed debugging.

Pseudocode:

1) you'll need the following pins
SPI_MOSI, SPI_MISO, SPI_CLK, SPI_CS#, BUSY, DIR, RESET

from your microcontroller's point of view, CS, and Reset are outputs, Busy and Dir are inputs.

A) reset the module:

At power up, set Reset to 0,
set CS# to 1
delay for 10uS or longer
set Reset to 1. -- reset is active low, so 1 is 'run', 0 is 'reset'


B) connect to the module:

set up your spi module such that:
clock resting state is HIGH
data clocked out(of microcontroller) on rising edge
data clocked in(to microcontroller) on falling edge
8 bit transmission
unframed

C) you can tell the DOSonCHIP has info to send when:
Direction =0 and Busy = 0
if this is the case, clock an FF into the module to extract a data byte
if Direction and busy don't change state, keep sending FF's until they do
(might work with other characters, haven't tried)

D) Sending requires the following:
Set CS to 0 -- selects the module
delay (I'm using a microsecond or two, might work with tighter timing)
Send data to your SPI output
When data finishes clocking out of the SPI, delay again(microsecond or so)
Set CS to 1 -- informs the module the byte has finished transmitting


Code:

/begin SDaccess.c

//*****************************************************************
//*FILENAME: SDaccess.c
//AUTHOR: Aiden Morrison
//DESCRIPTION: Access and control of an SD media card via a wearable inc
//DOSonCHIP device.
//*****************************************************************
//TARGET DEVICE: DSPIC30F4011
//*****************************************************************

#include "C:\Program Files\Microchip\MPLAB C30\support\h\p30F4011.h"
#include "C:\Program Files\Microchip\MPLAB C30\support\h\spi.h"
#include "C:\Program Files\Microchip\MPLAB C30\support\h\uart.h"
// the above include path may be different for each user. If a compile
// time error appears then check the path for the file above and edit
// the include statement above.




//Define Pins's
#define L0 LATBbits.LATB0 //Indicator LED

//SD Card handling pins
#define SDCS LATFbits.LATF0 //SD card Chip Select signal
#define SDBusy PORTEbits.RE5 //SD card Busy signal
#define SDDir PORTEbits.RE3 //SD card Data Direction Signal
#define SDCD PORTEbits.RE2 //SD card presence detect
#define SDRst LATEbits.LATE0 //DOSonChip Reset signal
//#define FOSC 16000000
//#define BAUDRATE 115200




//---------------------------------------------------------------------
// Definitions for subroutines

void SetupPorts(void);
void Init_UART1(void);
void Init_UART2(void);
void Init_SPI1(void);

//SDcard handling functions
void SendSD(unsigned int databyte);


//Global Variables
unsigned char charbuf;


//---------------------------------------------------------------------

int main(void)
{
SetupPorts(); // Initialize I/O
Init_UART1();
Init_UART2();
Init_SPI1();


while(1)
{
SDRst=0; //Reset SDcard
SDCS=1; //De-select SDcard
L0=1;
WriteUART1('B'); //UART1 'Alive' check character
delay();
L0=0;
SDRst = 1; //Active low reset, release card from reset

while(1)
{
L0=SDBusy;

// This code checks to see if the SDCard has something to say
//if it does, it clocks it out by sending FF's... don't know if
//other characters work just as well or not.
if (SDDir ==0 && SDBusy ==0){
microdelay();
if (SDDir==0 && SDBusy ==0){
SendSD(0xFF);
}
}


}

}

} // end main



//---------------------------------------------------------------------
//---------------------------------------------------------------------

void SetupPorts(void)
{
//B0 LED output - set pin as digital
ADPCFG= 0x0001; //Turn RB0 to digital pin for L0
TRISB = 0xFFFE; //alter tristate to output
TRISE = 0xFFFE; //Everything on Port E is an input but RE0, which is Card Reset
TRISF = 0xFFFE; //" " F " " RF0, " " Chip Select SD card
SDRst = 0;
SDCS = 0;

}

//---------------------------------------------------------------------
//---------------------------------------------------------------------

void Init_UART1(void)
{
unsigned int baudvalue, U1MODEvalue, U1STAvalue;

CloseUART1();
ConfigIntUART1(UART_RX_INT_EN & UART_RX_INT_PR4 & UART_TX_INT_DIS);

U1MODEvalue = UART_EN & UART_IDLE_CON & UART_ALTRX_ALTTX & UART_DIS_WAKE & UART_DIS_LOOPBACK &
UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_1STOPBIT;
U1STAvalue = UART_TX_PIN_NORMAL & UART_TX_ENABLE &
UART_INT_RX_CHAR & UART_ADR_DETECT_DIS & UART_RX_OVERRUN_CLEAR;

baudvalue =8;


//4MHz crystal with 16xpll
//8;//115200
//16; //57600
//25; //38400
//34; //28800
//68; //14400
//((FOSC/(16*BAUDRATE)) - 1); //this expression doesn't work, i'm probably doing something wrong
OpenUART1(U1MODEvalue, U1STAvalue, baudvalue);
}


void Init_UART2(void)
{
unsigned int baudvalue, U1MODEvalue, U1STAvalue;

CloseUART2();
ConfigIntUART2(UART_RX_INT_EN & UART_RX_INT_PR4 & UART_TX_INT_DIS);

U1MODEvalue = UART_EN & UART_IDLE_CON & UART_ALTRX_ALTTX & UART_DIS_WAKE & UART_DIS_LOOPBACK &
UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_1STOPBIT;
U1STAvalue = UART_TX_PIN_NORMAL & UART_TX_ENABLE &
UART_INT_RX_CHAR & UART_ADR_DETECT_DIS & UART_RX_OVERRUN_CLEAR;

baudvalue =8; //((FOSC/(16*BAUDRATE)) - 1); //this expression doesn't work, i'm probably doing something wrong
OpenUART2(U1MODEvalue, U1STAvalue, baudvalue);
}


void Init_SPI1(void)
{
unsigned int SPICONValue, SPISTATValue;

CloseSPI1();

SPICONValue = FRAME_ENABLE_OFF &
FRAME_SYNC_OUTPUT &
ENABLE_SDO_PIN &
SPI_MODE16_OFF &
SPI_SMP_ON &
SPI_CKE_ON &
SLAVE_ENABLE_OFF &
CLK_POL_ACTIVE_HIGH &
MASTER_ENABLE_ON &
SEC_PRESCAL_4_1 &
PRI_PRESCAL_16_1;

SPISTATValue = SPI_ENABLE &
SPI_IDLE_STOP &
SPI_RX_OVFLOW_CLR;

OpenSPI1(SPICONValue,SPISTATValue);
EnableIntSPI1;
SetPriorityIntSPI1(2);

}


void SendSD(unsigned int databyte)
{
SDCS=0;
nanodelay();
nanodelay();
nanodelay();
nanodelay();
WriteSPI1(charbuf);
//SDCS=0;can't do this here, must do it in ISR after byte finishes sending
}


//---------------------------------------------------------------------
//---------------------------------------------------------------------
void _ISR _U1RXInterrupt(void)
{
IFS0bits.U1RXIF = 0;
//while(U1STAbits.URXDA ==1){
charbuf =ReadUART1();

//
SendSD(charbuf);

}


void _ISR _U2RXInterrupt(void)
{
IFS1bits.U2RXIF = 0;
charbuf = ReadUART2();
//WriteUART1(charbuf);

}


void _ISR _SPI1Interrupt(void)
{
IFS0bits.SPI1IF =0;
nanodelay();
nanodelay();
SDCS=1; //Tell SD card that previous byte has finished sending
charbuf = ReadSPI1();
WriteUART1(charbuf);
}

/end sdaccess.c


/begin delays.h

//Delay Routines
void delay(void);
void tinydelay (void);
void microdelay (void);
void nanodelay(void);
void picodelay64(void);

/end delays.h

/begin delays.c


//---------------------------------------------------------------------

void tinydelay (void)
{
unsigned int counter;
unsigned int outer;
unsigned int inner;
counter=0;
for (outer=1; outer <=500 ; outer++)
{
for (inner=1; inner <=100 ; inner++)
{
}
}
if (counter == 0)
{
counter=1;
}
else
{
counter=0;
}
}

//---------------------------------------------------------------------
void microdelay (void)
{
unsigned int counter;
unsigned int outer;
unsigned int inner;
counter=0;
for (outer=1; outer <=100 ; outer++)
{
for (inner=1; inner <=100 ; inner++)
{
}
}
if (counter == 0)
{
counter=1;
}
else
{
counter=0;
}
}


//---------------------------------------------------------------------
void nanodelay (void)
{
unsigned int counter;
unsigned int outer;
unsigned int inner;
counter=0;
for (outer=1; outer <=10 ; outer++)
{
for (inner=1; inner <=10 ; inner++)
{
}
}
if (counter == 0)
{
counter=1;
}
else
{
counter=0;
}
}


//---------------------------------------------------------------------
void picodelay64(void)
{
unsigned int counter;
unsigned int outer;
unsigned int inner;
counter=0;
for (outer=1; outer <=2 ; outer++)
{
for (inner=1; inner <=2 ; inner++)
{
}
}
if (counter == 0)
{
counter=1;
}
else
{
counter=0;
}
}


//---------------------------------------------------------------------

void delay (void)
{
unsigned int counter;
unsigned int outer;
unsigned int inner;
counter=0;
for (outer=1; outer <=5500 ; outer++)
{
for (inner=1; inner <=500 ; inner++)
{
}
}
if (counter == 0)
{
counter=1;
}
else
{
counter=0;
}
}

/end delays.c
Last edited by Cannibal on Sat Nov 11, 2006 10:44 am, edited 1 time in total.
By Cannibal
#21707
Forgot to ask -

If anyone has terminal logs of using the DOSonChip to create a directory and file, and write to it, please let me know... every time I try these operations I blow the file structure and get an error -33 --> Sector out of range write error..

Then any operations yield -128 --> Physical disk read/write error until I reformat the card :)

Thanks in advance.

-Aiden
Ok, seem to have answered that question for myself - seems that my kodak camera formats the file system to something unsupported, or something technically not spec.

Formatting the card under windows to either FAT32 or FAT (fat16?) seems to let it work just fine, but there are some deviations from the data sheet you should all know about.

1) getting started

the first thing you have to do to get any action out of the card is send the command cd A:\.... where you can leave it as just A:\ or go straight to a subfolder... by defualt the device seems to start in the ethereal nowhere above A:.

2) operations on file handles
using ow 1 HUEVOS.DAT will kick back an error,
instead, one must use ow #1 HUEVOS.DAT, or preferably ow HUEVOS.DAT, since the dosonchip will kick back the file handle in either case, but omits the # sign. (frustrated me for a while since it seems the user must supply the # sign, but the dosonchip does not.)

3) writing or reading
to write/read from file handle 1 use
w #1 3

this will tell the card you want to write 3 bytes, it will send back a " sign,
then send it XXX, three characters, and it will echo back a 3 then the data bytes and an " sign in the place of the last data byte, no need for a carriage return here.

r #1 2

this will return the numeral 2 followed by the 2 bytes of the file starting at the file pointer location (0 if you just opened the file).


Hope this is helpful to y'all now to find out what the darn sustained data rate of this thing is.
By Rotule
#22269
Thanks, your post helped me a lot.

i still have a little problem...

have you successfully wrote a carriage return in your text file ??

i want this to make a compatible .CSV file so other guys from my formula SAE team don't have to modify the file for oppening it in excel.

anyway if you did tell me your trick please,

else : thanks for your help
By Cannibal
#22568
have you successfully wrote a carriage return in your text file ??
Yes, that was one of the first things I tested, and the DOSonCHIP treated it just like another character.

However, there could be something else going on here.

Windows, notepad, and possibly other applications use a combined Carriage return, Line Feed to represent 'enter', or what's commonly referred to as carriage return.

so anywhere you think you need a carriage return, try writing 0x0D 0x0A as a pair, and see if excel accepts that.


Sorry it took me so long to respond to you, if you need anything else, please PM me.

-Aiden
By bretth
#22687
Cannibal wrote: Hope this is helpful to y'all now to find out what the darn sustained data rate of this thing is.
If you do figure out the RW data rates, I'd love to see them, if you don't mind posting them to the forum.

Thanks,
Brett
By Cannibal
#22728
Cannibal wrote:

Hope this is helpful to y'all now to find out what the darn sustained data rate of this thing is.


If you do figure out the RW data rates, I'd love to see them, if you don't mind posting them to the forum.

Thanks,
Brett
Best write speed I was able to sustain was in the vicinity of 480bytes per second... granted I was sending data in chunks of 512bytes which may be suboptimal for thiis unit but still... 480 bytes per second... yuck.

Didn't test the read speed sorry to say. After figuring out the write speed was so abysmal, I switched to a different chip (uALFAT) that I'm now able to do 25+ Kbyte/sec write sustained.

Good luck.
By Rotule
#22746
it really sux, if i would have known before, i would'nt had spent so much time on that piece of crap.

a guy i know know a guy who work on a spi interface directly on a pic (spi) i'll try to have some info.
By bretth
#22842
Yeah, I figured the uALFAT might be a better approach... thanks for confirming the performance stats.
By Cannibal
#22883
Yeah, I figured the uALFAT might be a better approach... thanks for confirming the performance stats.
No problem, but take them with a grain of salt.. I was sending data in 512 byte chunks and tested at 480 and 960 bytes per second... real performance may be near 1kbyte per second :P


If you do get a uALFAT just post back here or PM me and I can give you interface pseudocode to get you over the initial bumps with the uALFAT.

Just a note: plan to use a chip with a bit of ram on it, since there are 55ms delays during sector erases. This means that if you have a data rate of 11520byte/sec you need a buffer of 634 bytes just for the sector erase to not cause a data overflow.

if you have two streams at 11520 you need to have 2*2*634 bytes of buffer space since you can get back to back sector erases. (That's why i've only been able to test my uALFAT up to 25kbyte/sec... it should go higher but my dspic30f4011 only has 2048 bytes of SRAM so I can't buffer enough for higher data rate. :)
By bretth
#22899
I have a uALFAT on order, should be here later this week. Any pseudocode you can provide would be much appreciated!

-Brett
By Cannibal
#22913
Ok, for basic communication with the uALFAT you will need to set your SPI speed initially to be 1.25MHz or lower. This is because after reset, the uALFAT defaults to low power mode, but once in high power mode you can run the SPI up to 8MHz.

Also, unlike the DOSonCHIP, the uALFAT clocks data in AND out on the rising edge... you'll have to watch your SPI config for this.



Initialization:
Set CS high - deselect the uALFAT
Set /RS low - reset uALFAT
wait a few ms
Set /RS high - start uALFAT

wait for SDDR to go high - waits for reset to complete

after sddr is high the reset is complete - gather the 90 character splashscreen including bootloader version number and firmware revision number. personally i piped these to the serial port so i could read what version of ualfat i had.

while (less than 90 chars gathered)
sdcs=0;
send(0xFF)
read the returned character after transmission complete
sdcs=1;
delay of ~ 100+uSec
end while


-now increase the speed to full
//char fullpower[9]="Z F>DCEF\r";

for(index=0; index<9; index++)
{
SDCS=0;
WriteSPI1(fullpower[index]);
while(DataRdySPI1()==0){}
SDCS=1;
scratch=ReadSPI1();
nanodelay(); // a 20us delay
}

--gather response to 'change power' command (if it was successfull, the substring !00 will be returned... i don't check for that)

while (less than 9 chars gathered)
sdcs=0;
send(0xFF)
read the returned character after transmission complete
sdcs=1;
delay of ~ 4-5uSec (or more, up to you)
end while


--Now you can increase your SPI speed to a value less than 8MHz.. personally I'm using 2.8-3.3MHz just because those are convenient frequencies with the crystal i'm using.

--mount the file system and verify that it was properly mounted
//char const mountcard[2]="I\r";
filesystemcheck =0x00;
while(filesystemcheck != 0x30)
{
//outer while loop ensures that file system is properly mounted
for(index=0; index<2; index++)
{
SDCS=0;
WriteSPI1(mountcard[index]);
while(DataRdySPI1()==0){}
SDCS=1;
scratch=ReadSPI1();
nanodelay();
}

nanodelay();
while(SDDR==0){} //wait for uALFAT to finish file system mount
charcount=0;
while(charcount < 5)
{
if ((SDDR==1))
{
SDCS=0;
WriteSPI1(0xFF);
while(DataRdySPI1()==0){}
SDCS=1;
splashscreen[charcount]=ReadSPI1();
charcount++;
//WriteUART1(splashscreen[(charcount-1)]);
filesystemcheck= ((splashscreen[2]) | (splashscreen[3]));
nanodelay();

}
}
if (filesystemcheck != 0x30)
{
L0=!L0;
tinydelay(); //delay of 16ms + to give visible blink
tinydelay();
tinydelay();

}
}


You will need to then open file handles for write using
"O XW>FILENAME.???\r" where X is 0-3 file handle number
or "O XR>FILENAME.???\r" for read

When writing to a file it's best to use block sizes of 100 bytes, which allows you to ignore the 'Busy' line (as long as you wait for the SDDR line for feedback before another write). Writing 100 bytes to file zero is accomplished by sending
"W 0>64\r"; --64 is hex 100 decimal

wait for data ready, gather the 6 character feedback

for(index=0; index<(BufLen); index++)
{
SenduALF(F1Seg1[index]);
scratchWC=ReadSPI1();
chardelay64(); ----- approx 4us
//while (SDBusy==1){L0=1;}
}

Since the ualfat uses special escape sequences for 0xFF and 0xFE, data that may contain these characters must be sent using the following:

void SenduALF(unsigned char out)
{
unsigned char scratchSALF;
STATE=0x01;
//while(SDBusy==1){L0=1;}
if (out == 0xFF)
{//FF denoted by FE, 0
SDCS=0;
WriteSPI1(0xFE);
while(DataRdySPI1()==0){}
SDCS=1;
scratchSALF=ReadSPI1();
chardelay64();
out=0x00;
}
else if (out == 0xFE)
{//FE denoted by FE, FE
SDCS=0;
WriteSPI1(0xFE);
while(DataRdySPI1()==0){}
SDCS=1;
scratchSALF=ReadSPI1();
chardelay64();
out=0xFE;
}

//Just send the character itself
SDCS=0;
WriteSPI1(out);
while(DataRdySPI1()==0){}
SDCS=1;
}


This will trigger an additional 5 chars + 15 chars of feedback that must be gathered before writing another segment (actually you can allow up to ~100 characters of feedback to gather before collecting it, but it's not recommended to wait too long).


When you're done writing to a file you must close the file with

void CloseFileHandles(void)
{
//const char CloseFile1[4]="C 0\r"; //command to close file 1 / handle 0
//const char CloseFile2[4]="C 1\r"; //command to close file 2 / handle 1
unsigned char index;
unsigned char scratchCFH;

for(index=0; index<4; index++)
{
SDCS=0;
WriteSPI1(CloseFile1[index]);
while(DataRdySPI1()==0){}
SDCS=1;
scratchCFH=ReadSPI1();
}

chardelay64();

for(index=0; index<4; index++)
{//Gather expected response
SDCS=0;
WriteSPI1(CloseFile1[index]);
while(DataRdySPI1()==0){}
SDCS=1;
scratchCFH=ReadSPI1();
}

for(index=0; index<4; index++)
{
SDCS=0;
WriteSPI1(CloseFile2[index]);
while(DataRdySPI1()==0){}
SDCS=1;
scratchCFH=ReadSPI1();
}

}


Good luck!
By rokicki
#23871
Interesting! Can you let me know where or how you found out about the
55ms delays? Is that in some spec, or is that by empirical measurement?
Thanks!

> Just a note: plan to use a chip with a bit of ram on it, since there are 55ms > delays during sector erases.
By Cannibal
#23876
Interesting! Can you let me know where or how you found out about the
55ms delays? Is that in some spec, or is that by empirical measurement?
Thanks!

The empirical measurements agree with a >50ms delay being necessary, and I can't for the life of me remember where, but I recall '50ms' being thrown around in a past flash card discussion.

I believe that it can be lower on faster cards, but that amount of buffering worked well for me, so I recommend starting there and seeing how it goes for you.
By cengel_ou
#25720
I just purchased a DOSonCHIP module and am dissapointed to hear that the write speed is so poor. Do you beleive this may be a problem with the firmware that could possibly be fixed in the future?

I was planning to use this module to log data from a navigation sensor. 38 bytes per message at 100 Hz, for 3800 bytes/sec. So I guess this is out of the question to be able to do this with this chip? Cannibal you mentioned you sustained a write speed of 480 bytes/sec, but you were sending data in 512 byte chunks. Do you think better write speeds (hopefully meeting my requirements) could be obtained if data is sent in smaller chunks?

Thanks for the help!
By monzasteeve
#25738
I was going to purchase the chip as well but stopped after reading about the low performances. I then went to uALFAT but i also stopped because of the relatively high price..
I had already good experience with the EFS (Embedded File System) library on an ATmega and I think I'll stick with it.
I was writing definitely faster than the DosOnChip! Roughly i would say at least 5KB/s with unoptimized code, in 512 bytes blocks on a microsd card with system clock 8Mhz.
Just have a look at it, might suite some applications.

Ste