Difference between revisions of "Dosonchip"

From OpenCircuits
Jump to navigation Jump to search
(→‎related: moved MMC details to MMC.)
 
(15 intermediate revisions by 6 users not shown)
Line 1: Line 1:
The DOSOnChip module is perhaps the most poorly documented piece of technology I've yet come across. After many days of trying to get this thing to communicate via SPI, I finally have it working. However, some caveats:
+
== NEW FIRMWARE & ROYALTY-FREE API C SOURCE CODE AVAILABLE (MANY IMPROVEMENTS) ==
 +
There is a new host interface with royalty-free host-side C source code available direct from the DOSonCHIP website:
  
DosOnChip "SPI" for Dummies (IE, me two days ago):
+
http://dosonchip.com
  
1. SPI
+
that works with the new firmware version 2.xx which is available on all new chips.
 +
 
 +
-SDHC up to 32GB is supported.
 +
-Speeds are MUCH MUCH faster.
 +
-The SPI interface is now a true SPI interface without the need for the DIR and BUSY handshake signals.
 +
-The UART interface only requires 2-pins: RX and TX (hardware handshake signals are optional (UART_CTS should be set to GND if not used).
 +
-New functionality.
 +
 
 +
 
 +
 
 +
= PLEASE NOTE THAT THE SUBSEQENT INFORMATION APPLIES TO FIRMWARE VERSIONS 1.xx =
 +
 
 +
 
 +
 
 +
Host communication via SPI:
 +
 
 +
"SPI" for Dummies (IE, me two days ago):
 +
 
 +
1. SPI (see any SPI tutorial website for more info)
  
 
SPI is a four-wire interface consisting of the following signals:
 
SPI is a four-wire interface consisting of the following signals:
Line 33: Line 52:
 
Aside from VCC and GND, these are assigned to bits in the header file in the first code section below.
 
Aside from VCC and GND, these are assigned to bits in the header file in the first code section below.
  
== WHY THIS ISN'T SPI ==
+
Notes:
 +
 
 +
1. When using only one device on SPI, you should be able to leave the CS  signal in the active state at all times, also called "3 wire mode." The DOSOnChip module (DOC for short) recommends that you wiggle this pin as part of its communications protocol in noisy environments (more later).
 +
 
 +
2. In addition to the above signals, DOC requires that you monitor an additional pins: BUSY.
  
 +
3. DOC requires that you toggle the RESET pin to reset the device. It is possible to put the module to sleep via SPI, but reawakening the device requires that you disconnect power and reapply it. Even toggling the RESET pin will not cause the device to wake up.
  
1. When using only one device on SPI, you should be able to leave the CS  signal in the active state at all times, also called "3 wire mode." The DOSOnChip module (DOC for short) requires that you wiggle this pin as part of its communications protocol (more later).
+
So, the "SPI"-like interface consists of the following signals:
  
2. In addition to the above signals, DOC requires that you monitor two additional pins: BUSY and DIR. Now we're up to six pins (not including the power supply) which is not in accordance with SPI.
 
  
3. DOC requires that you toggle the RESET pin to reset the device. While I understand this from a design point of view, it should be possible to reset the device via an SPI command. It IS possible to put the module to sleep via SPI, but reawakening the device requires that you disconnect power and reapply it. Even toggling the RESET pin will not cause the device to wake up.  
+
'''SCLK''' - clock. Must be toggled up and down to write or read a bit. (two transitions per bit).
  
So, the "SPI"-like interface consists of the following signals:
+
'''MISO''' - master in/slave (DOC) out
 +
 
 +
'''MOSI''' - master out/ slave (DOC) in
 +
 
 +
'''CS''' - ChipSelect
  
SCLK - clock. This pin is normally low, and must be toggled up and down to write or read a bit. (two transitions per bit).
+
'''DIR''' - read only from master point-of-view. DOC sets this to 1 when it is ready to receive data, and sets it to 0 when it has data to send.
MISO - master in/slave (DOC) out
 
MOSI - master out/ slave (DOC) in
 
CS - ChipSelect
 
DIR - read only from master point-of-view. DOC sets this to 1 when it is ready to receive data, and sets it to 0 when it has data to send.
 
BUSY - Read-only from master point of view. DOC sets this to 1 when it can't be bothered to communicate.
 
RESET - used to reset or intialize the device.
 
  
That gives us 7 signals. Personally, when I order a device that advertises itself as SPI compatible, I expect to be able to drive it with the host controller's SPI system. Especially when I am using a Silicon Labs C8051F124, a close cousin of the C8051F310 that DOC burns their firmware into and sells as their own device. Since both SPI systems were designed by the same people, you'd think they could talk to each other. However, getting this to work within the context of the SPI interrupt handler is difficult. I wound up using a spare timer interrupt instead so I could have bit- and clock-level control.
+
'''BUSY''' - Read-only from master point of view. DOC sets this to 1 when it can't be bothered to communicate.  
  
== OTHER COMPLAINTS ==
+
'''RESET''' - used to reset or intialize the device.
  
 +
That gives us 7 signals. Personally, when I order a device that advertises itself as SPI compatible, I expect to be able to drive it with the host controller's SPI system. Especially when I am using a Silicon Labs C8051F124, a close cousin of the C8051F310 that DOC burns their firmware into and sells as their own device. Since both SPI systems were designed by the same people, you'd think they could talk to each other. However, getting this to work within the context of the SPI interrupt handler is difficult. I wound up using a spare timer interrupt instead so I could have bit- and clock-level control.
  
The documentation for this device is inadequate. SparkFun should point this out on their product page, or, if they plan on continuing to hawk this device, obtain or write some example code or at least a short pdf with, at very least, an accurate timing diagram. The PDF datasheet for the DOC has no timing specifications, no example code (one section includes the directive "Insert Pseudocode Here") no mention of the case-sensitivity of the command interpreter.
 
  
The device tends to spit out an extra bit or two after reset which throws the byte-level SPI buffer on my mcu out of sync. In my timer interrupt, I shift each bit into an unsigned int (2 bytes) until the int is equal to 0x0D3E, the prompt string from the DOC. At that point, I set a "synchronized" flag to true and assume that the next bit starts a new byte. This usually necessitates tossing out a few bits at the beginning.  
+
The documentation for this device is inadequate. The PDF datasheet for the DOC has no timing specifications, no example code (one section includes the directive "Insert Pseudocode Here") no mention of the case-sensitivity of the command interpreter. However, new documentation is planned for later in 2007.
  
  
Line 65: Line 86:
  
  
+ Your device should NEVER write to the BUSY or DIR pins. Doing so will cause very strange things to happen.
+
+ Your device should NEVER write to the BUSY or DIR pins as these are output pins on the DOC silicon. Doing so will cause very strange things to happen.
 
 
+ After sending a complete command (which may be multiple bytes), you must toggle the CS line to inform the DOC that you have completed sending a command. If its firmware were well-written, it would be able to tell this by the fact that you must ALSO send a 0x0D carriage return at the end of a command.
 
  
 
+ BUSY is set to a 1 by the DOC to tell you that it's, well, BUSY. This makes sense, but is not documented adequately.  
 
+ BUSY is set to a 1 by the DOC to tell you that it's, well, BUSY. This makes sense, but is not documented adequately.  
Line 77: Line 96:
 
When you get the DOC working, itms to remember:
 
When you get the DOC working, itms to remember:
  
+ commands must be LOWER CASE and filenames must be UPPER CASE.  
+
+ commands must be LOWER CASE and filenames must be UPPER CASE (this follows the Microsoft FAT file naming convention).  
  
 
+ To write data to a file:
 
+ To write data to a file:
  
+ The SD card must be formatted as FAT 16.
+
+ The SD card must be formatted as FAT16 or FAT32 (not FAT12).
  
+ when you attempt ot use a file or directory name with LOWERCASE, even if is a FAT 16 file structure, DOC will respond with an error -14, "Long Filenames Not Supported." This is confusing when using a filename like "aadsf.txt," wich is 8.3-filename (FAT16) compliant.
+
+ when you attempt to use a file or directory name with LOWERCASE, even if is a FAT16 file structure, DOC will respond with an error -14, "Long Filenames Not Supported." Note that a filename like "aadsf.txt," is not 8.3-filename (FAT16) compliant - this should be "AADSF.TXT".
  
 
<pre>
 
<pre>
Line 89: Line 108:
 
</pre>
 
</pre>
  
--> chip responds with an integer from 1 to 4, which is teh file handle number.
+
--> chip responds with an integer from 1 to 4, which is the file handle number.
 
<pre>
 
<pre>
 
>w #1 10<cr>
 
>w #1 10<cr>
Line 102: Line 121:
  
 
For example:
 
For example:
</pre>
+
<code>
>ow LOG.TXT ; <-- openfile for writing
+
<pre>
 +
&gt;ow LOG.TXT ; <-- openfile for writing
 
1 ; --> DOC returns integer file handle
 
1 ; --> DOC returns integer file handle
>w #1 10 ; <-- write 10 bytes to file #1
+
&gt;w #1 10 ; <-- write 10 bytes to file #1
" ; --> ", so go ahead and send bytes.
+
&quot; ; --> ", so go ahead and send bytes.
 
0123456789 ; <-- byte stream...
 
0123456789 ; <-- byte stream...
" ; --> " from DOC, so you have sent all the bytes you requested.
+
&quot; ; --> " from DOC, so you have sent all the bytes you requested.
 
+
&gt;q ; <-- close all open files (that were opened for writing).
>q ; <-- close all open files (that were opened for writing).
 
 
</pre>
 
</pre>
 +
</code>
  
== SUMMARY AND IMPRESSIONS ==
+
== SUMMARY ==
  
  
 
That's about it. I had some issues with receiving bit streams that were off by a bit because the DOC clocks data out and in on different edges, so if your clock transition is 1-0-1 instead of 0-1-0, you'll drop the last bit of the transmission and the first bit you receive will be incorrect.  
 
That's about it. I had some issues with receiving bit streams that were off by a bit because the DOC clocks data out and in on different edges, so if your clock transition is 1-0-1 instead of 0-1-0, you'll drop the last bit of the transmission and the first bit you receive will be incorrect.  
  
Now that I have this thing working, it's great. It's interesting that the DOC is a processor almost as powerful than I am using to drive two OLEDs, a GPS receiver and the DOC itself in addition to JTAG and a serial interface to my PC, and it doesn't provide sufficiently correct errors (Long Filenames Not SUpported?! "ASDF.TXT" is not a LONG FILENAME!) or diagnostic information.  
+
Now that I have this thing working, it's great.  
  
My ISR is attached to a post above for anyone who wants to study it. Please do not reuse this code in any commercial apps, but you're welcome to use it as a reference for your own designs.
+
== CODE for the C8051F124 ==
 +
My ISR is attached to a post above for anyone who wants to study it. Please do not  
 +
reuse this code in any commercial apps, but you're welcome to use it as a reference  
 +
for your own designs.
  
 
First, the header file SPI.H:
 
First, the header file SPI.H:
Line 161: Line 184:
 
SPI.C
 
SPI.C
  
This is a mock-SPI interface for the DOSOnChip module, written in Keil C for the Silicon Labs C8051F124.
+
This is a mock-SPI interface for the DOSOnChip module,
All code is copyright 2007 by Steven David Lively (david.lively@arm.com) and may not be reused in a commercial product without my permission.
+
written in Keil C for the Silicon Labs C8051F124.All code  
 +
is copyright 2007 by Steven David Lively (david dot lively at arm)  
 +
and may not be reused in a commercial product without  
 +
my permission.
  
 
*/
 
*/
Line 406: Line 432:
 
}
 
}
 
</pre>
 
</pre>
 +
 +
== related ==
 +
 +
* [http://www.sparkfun.com/commerce/product_info.php?products_id=7955 Breakout Board with DOSonCHIP + SD socket]
 +
* [http://www.sparkfun.com/commerce/product_info.php?products_id=8215 Breakout Board with DOSonCHIP + micro-SD socket]
 +
* [http://www.sparkfun.com/commerce/product_info.php?products_id=7956 DOSonCHIP]
 +
* DOSonCHIP Website http://dosonchip.com/
 +
* [http://chipdos.com/library/CD17B10_User_Guide_0v1.pdf CD17B10 User Guide]
 +
* [http://chipdos.com/library/CD17Bxx-0v8.pdf CD17Bxx Datasheet]
 +
 +
Alternatives:
 +
 +
See the many alternatives at [[MMC]].

Latest revision as of 22:35, 5 December 2008

NEW FIRMWARE & ROYALTY-FREE API C SOURCE CODE AVAILABLE (MANY IMPROVEMENTS)[edit]

There is a new host interface with royalty-free host-side C source code available direct from the DOSonCHIP website:

http://dosonchip.com

that works with the new firmware version 2.xx which is available on all new chips.

-SDHC up to 32GB is supported. -Speeds are MUCH MUCH faster. -The SPI interface is now a true SPI interface without the need for the DIR and BUSY handshake signals. -The UART interface only requires 2-pins: RX and TX (hardware handshake signals are optional (UART_CTS should be set to GND if not used). -New functionality.


PLEASE NOTE THAT THE SUBSEQENT INFORMATION APPLIES TO FIRMWARE VERSIONS 1.xx[edit]

Host communication via SPI:

"SPI" for Dummies (IE, me two days ago):

1. SPI (see any SPI tutorial website for more info)

SPI is a four-wire interface consisting of the following signals:

SCLK - serial clock, driven by the master (your processor in this case).

MISO - "Master In Slave Out", the pin the DosOnChip controller wiggles to send data to your micro.

MOSI - "Master Out, Slave In" the pin you wiggle to send data to the DosOnChip module.

NSS/CS - Chipselect. When attaching multiple devices to the SPI bus, each device needs its own chipselect so that you can tell it when to listen to changes in SCLK and MOSI and when it should respond by changing MISO to send dat to the host. When CS is inactive, DosOnChip and other peripherals are supposed to ignore any pin state changes on the SPI bus.

(Bus, in this case, refers to SCLK, MISO and MOSI as a group.)

HARDWARE CONNECTION[edit]

The DOSOnChip breakout board sold by SparkFun.com comes configured to work in SPI mode - no jumper changes, etc are necessary. The pins that must be connected are:

  • SCLK
  • MISO
  • MOSI
  • CS
  • RESET
  • DIR
  • BUSY
  • VCC
  • GND

Aside from VCC and GND, these are assigned to bits in the header file in the first code section below.

Notes:

1. When using only one device on SPI, you should be able to leave the CS signal in the active state at all times, also called "3 wire mode." The DOSOnChip module (DOC for short) recommends that you wiggle this pin as part of its communications protocol in noisy environments (more later).

2. In addition to the above signals, DOC requires that you monitor an additional pins: BUSY.

3. DOC requires that you toggle the RESET pin to reset the device. It is possible to put the module to sleep via SPI, but reawakening the device requires that you disconnect power and reapply it. Even toggling the RESET pin will not cause the device to wake up.

So, the "SPI"-like interface consists of the following signals:


SCLK - clock. Must be toggled up and down to write or read a bit. (two transitions per bit).

MISO - master in/slave (DOC) out

MOSI - master out/ slave (DOC) in

CS - ChipSelect

DIR - read only from master point-of-view. DOC sets this to 1 when it is ready to receive data, and sets it to 0 when it has data to send.

BUSY - Read-only from master point of view. DOC sets this to 1 when it can't be bothered to communicate.

RESET - used to reset or intialize the device.

That gives us 7 signals. Personally, when I order a device that advertises itself as SPI compatible, I expect to be able to drive it with the host controller's SPI system. Especially when I am using a Silicon Labs C8051F124, a close cousin of the C8051F310 that DOC burns their firmware into and sells as their own device. Since both SPI systems were designed by the same people, you'd think they could talk to each other. However, getting this to work within the context of the SPI interrupt handler is difficult. I wound up using a spare timer interrupt instead so I could have bit- and clock-level control.


The documentation for this device is inadequate. The PDF datasheet for the DOC has no timing specifications, no example code (one section includes the directive "Insert Pseudocode Here") no mention of the case-sensitivity of the command interpreter. However, new documentation is planned for later in 2007.


NOTES[edit]

+ Your device should NEVER write to the BUSY or DIR pins as these are output pins on the DOC silicon. Doing so will cause very strange things to happen.

+ BUSY is set to a 1 by the DOC to tell you that it's, well, BUSY. This makes sense, but is not documented adequately.


MORE NOTES[edit]

When you get the DOC working, itms to remember:

+ commands must be LOWER CASE and filenames must be UPPER CASE (this follows the Microsoft FAT file naming convention).

+ To write data to a file:

+ The SD card must be formatted as FAT16 or FAT32 (not FAT12).

+ when you attempt to use a file or directory name with LOWERCASE, even if is a FAT16 file structure, DOC will respond with an error -14, "Long Filenames Not Supported." Note that a filename like "aadsf.txt," is not 8.3-filename (FAT16) compliant - this should be "AADSF.TXT".

>ow LOG.TXT<cr>

--> chip responds with an integer from 1 to 4, which is the file handle number.

>w #1 10<cr>

--> write to file handle 1 (you MUST include the # in this command) 10 bytes, to follow.

"

(chip responds with a quote, indicating that it is ready to receive bytes into the file. Send no more than the number of characters you requested with the "w #1..." command. When the chip has read that many bytes, it will respond with another "


For example:

>ow LOG.TXT		; <-- openfile for writing
1			; --> DOC returns integer file handle
>w #1 10		; <-- write 10 bytes to file #1
"			; --> ", so go ahead and send bytes.
0123456789		; <-- byte stream...
"			; --> " from DOC, so you have sent all the bytes you requested.
>q			; <-- close all open files (that were opened for writing).

SUMMARY[edit]

That's about it. I had some issues with receiving bit streams that were off by a bit because the DOC clocks data out and in on different edges, so if your clock transition is 1-0-1 instead of 0-1-0, you'll drop the last bit of the transmission and the first bit you receive will be incorrect.

Now that I have this thing working, it's great.

CODE for the C8051F124[edit]

My ISR is attached to a post above for anyone who wants to study it. Please do not reuse this code in any commercial apps, but you're welcome to use it as a reference for your own designs.

First, the header file SPI.H:


#ifndef _spi_h_
#define _spi_h_


#include <C8051F120.H>

sbit SPI_SCLK=P0^2;
sbit SPI_MISO=P0^3;
sbit SPI_MOSI=P0^4;
sbit SPI_NSS=P0^5;


sbit SPI_RESET=P3^0;
sbit SPI_DIR=P3^2;
sbit SPI_BUSY=P3^4;

extern xdata unsigned int spi_interrupts;
extern bit SPI_SD_SYNCHRONIZED;

void spi_reset_device();
char spi_read();
void spi_write(char _data);
void spi_writestr(char *t);

unsigned int spi_t_size();
unsigned int spi_r_size();

#endif

SPI.C

/*
SPI.C

This is a mock-SPI interface for the DOSOnChip module,
written in Keil C for the Silicon Labs C8051F124.All code 
is copyright 2007 by Steven David Lively (david dot lively at arm) 
and may not be reused in a commercial product without 
my permission.

*/

#include "spi.h"
#include <C8051F120.H>

xdata unsigned int spi_interrupts=0;
xdata unsigned char spi_int_restart=0;

xdata unsigned char t_buf[256];
xdata unsigned char t_in=0, t_out=0;

xdata unsigned char r_buf[256];
xdata unsigned char r_in=0,r_out=0;

bit SPI_SD_SYNCHRONIZED=0;

unsigned int spi_t_size() {
	return (t_in-t_out);
}
unsigned int spi_r_size() {
	return (r_in-r_out);
}

char iStateStack[10];
char iStateStackPtr=0;

void statePush(unsigned char c) {
	iStateStack[iStateStackPtr++]=c;
}

char statePop() {
	if(iStateStackPtr) 
		return iStateStack[--iStateStackPtr];
	else
		return 0;
}

// enumerate state names here so we don't have to fuss with the
// mess involved in renumbering states later.
enum {
	st_RESET,
	st_SYNC,
	st_CHECK_RECEIVE,
	st_CHECK_SEND
};


int iState=st_RESET;

void spi_reset_device() {
	int i;
	
	ET2=0;
/*	SPI_RESET=0;

	for(i=0;i<1000;i++);

	SPI_RESET=1;*/
	
	t_out=t_in=0;
	r_out=r_in=0;
	iState=st_RESET;

	ET2=1;
}

#define SPI_CLOCK_DELAY 100
#define SYNC_PATTERN  0x0D3E
// pseudo-SPI interrupt.
void timer2_interrupt(void) interrupt 5 {

	unsigned char sfrpage=SFRPAGE;


	static unsigned int sync_buffer=0x0000;

	if (SPI_BUSY) return;

	SFRPAGE=SPI0_PAGE;

	switch(iState) {
		int i;

		case st_RESET: //
			SPI_SD_SYNCHRONIZED=0;

			SPI_RESET=0;
			SPI_NSS=1;
										 
			SPI_SCLK=0;
			SPI_MOSI=1;

			for(i=0;i<1000;i++);
			SPI_RESET=1;

			SPI_NSS=0;

			sync_buffer=0x0000;

			iState=st_SYNC;
			break;

		case st_SYNC: 
			/* synchronize with the SD card by waiting or 0D 3E and adding to the receiver buffer.
			   sometimes, for some wierd reason (probably havin to do with reset/chipselect timing),
			   the SD card reader spits out a string of 1s for awhile before getting to the 
			   carriage-return, prompt sequence 0x0D 0x3E ">". This state waits for that pattern
			   before releasing the input buffer to the outside world.
			   */
			// synchronized with SD reader?
			if (sync_buffer==SYNC_PATTERN) {
				SPI_SD_SYNCHRONIZED=1;
				r_buf[r_in++]=(unsigned char)((sync_buffer&0xFF00)>>8);
				r_buf[r_in++]=(unsigned char)(sync_buffer&0x00FF);
			
				iState=st_CHECK_RECEIVE;
			}
			else if(!SPI_BUSY) // wait for chip to come out of "busy" mode
			{
				//grab another bit and check for sync.
				SPI_MOSI=1;
				sync_buffer<<=1;
				sync_buffer|=SPI_MISO;
				
				// add the bit to the receive buffer for debug purposes
				//r_buf[r_in++]=((unsigned char)SPI_BUSY<<4) | SPI_MISO; 

				SPI_SCLK=1;
				for(i=0;i<SPI_CLOCK_DELAY;i++);
				SPI_SCLK=0;
				for(i=0;i<SPI_CLOCK_DELAY;i++);

			}

			break;

		case st_CHECK_RECEIVE:	
			// is the device waiting to send data?
			SPI_MOSI=1;	   // make sure we only clock in 0xFF. Set this pin once and we're done

			while ( ! (SPI_BUSY|SPI_DIR) ) {
				// yes, so read it in.
				unsigned char bits_left;
				unsigned char recv_byte=0;

				for(bits_left=8;bits_left>0;bits_left--) {
					recv_byte<<=1;
					recv_byte|=SPI_MISO;

					SPI_SCLK=1;
					for(i=0;i<SPI_CLOCK_DELAY;i++);
					SPI_SCLK=0;
					for(i=0;i<SPI_CLOCK_DELAY;i++);
				}

				r_buf[r_in++]=recv_byte;
			} 

			iState=st_CHECK_SEND;

			break;

		case st_CHECK_SEND:
			// can we send?
			if(t_out!=t_in && (!SPI_BUSY & SPI_DIR))  {

				while((t_out!=t_in) && (!SPI_BUSY & SPI_DIR)) {
					// yes, so send it while we can. Stop when the chip objects.
					unsigned char bits_left;
					unsigned char xmit_byte;
					unsigned char echo_byte=0;

					xmit_byte=t_buf[t_out++];
	
					for(bits_left=8;bits_left>0;bits_left--) {
						SPI_MOSI=(xmit_byte&0x80)!=0;
						xmit_byte<<=1;
	
						echo_byte<<=1;
						echo_byte|=(unsigned char)SPI_MISO;
	
						SPI_SCLK=1;
						for(i=0;i<SPI_CLOCK_DELAY;i++);
						SPI_SCLK=0;
						for(i=0;i<SPI_CLOCK_DELAY;i++);
					}

/*					if (echo_byte!=t_buf[t_out-1]) {
						r_buf[r_in++]='!';
						r_buf[r_in++]=echo_byte;
						r_buf[r_in++]='\n';
						r_buf[r_in++]='\r';
					} */
	
					SPI_NSS=1;				  
					for(i=0;i<SPI_CLOCK_DELAY;i++);
					SPI_NSS=0;
					for(i=0;i<SPI_CLOCK_DELAY;i++);
	
				}

			} // if (tout!=t_in && ...

			iState=st_CHECK_RECEIVE;

			break;

	

		default :
			iState=st_CHECK_RECEIVE;

	}

	SFRPAGE=sfrpage;

} 



// called by outside code to send data on SPI
#pragma disable
void spi_write(char _data) {
	t_buf[t_in++]=_data;
}

#pragma disable
void spi_writestr(char *t) {
	while(*t)
		spi_write(*t++);
	// add a carriage return.
	spi_write(0x0D); 
}

// called to read available bytes
char spi_read(void) {
	if (r_in!=r_out)
		return r_buf[r_out++];
	else
		return (-1);
}

related[edit]

Alternatives:

See the many alternatives at MMC.