|
|
View previous topic :: View next topic |
Author |
Message |
irilias
Joined: 18 Aug 2012 Posts: 5
|
PIC16F877A, supporting MMC/SD cards |
Posted: Fri Apr 05, 2013 7:46 am |
|
|
hi, i'm new to PIC microcontrollers and will be using one (pic16f877a) to develop a project using an MMC/SD card.
i found a piece of code that initializes an MMC card into SPI mode, and sets the block length, and i wanted to add SD support to it. so i did my research and i ended up simply adding the ACMD41 command required for SD to initialize. my reasoning was as follows :
Set card select high
Send 80 SPI clock cycles (done by writing 0xFF 10 times)
Set card select low
Send CMD0 [0x400000000095]
R1 = 0x01 (idle)
Send CMD55 [0x770000000065] (1st part of ACMD41)
R1 = 0x01 (idle if SD, time out if MMC)
Send CMD41 [0x6900000000E5] (2nd part of ACMD41)
R1 = 0x00 (SD card initialized successfuly, otherwise time out "MMC")
Send CMD1 [0x4100000000FF]
R1 = 0x00 (MMC card initialized successfuly)
and then CMD16 for setting the block length
here's the code :
Code: |
/*************************** MMC/SD Init ***********************************/
/* Initialises the MMC/SD into SPI mode and sets block size, returns 0 on success */
int mmc_init()
{
int i,y,z;
SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED);
*0x94 |= 0x40; // set CKE = 1 - clock idle low
*0x14 &= 0xEF; // set CKP = 0 - data valid on rising edge
OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
for(i=0;i<10;i++) // initialise the MMC/SD card into SPI mode by sending clks on
{
SPI_WRITE(0xFF);
}
OUTPUT_LOW(PIN_C2); // set SS = 0 (on) tells card to go to spi mode when it receives reset
SPI_WRITE(0x40); // send reset command
SPI_WRITE(0x00); // all the arguments are 0x00 for the reset command
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x95); // precalculated checksum as we are still in MMC/SD mode
puts("Sent go to SPI\n\r");
if(mmc_response(0x01)==1) return 1;
// if = 1 then there was a timeout waiting for 0x01 from the mmc/sd
puts("Got response from MMC/SD\n\r");
// sending ACMD41, first CMD55, then CMD41. Check for time out
y = 0;
while((y< 255) && (mmc_response(0x01)==1)) // must keep sending command
{
SPI_WRITE(0x77); // send command 55
SPI_WRITE(0x00); // all the arguments are 0x00 for command 55
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x65); // checksum is no longer required but in some cases it is
y++;
}
if(mmc_response(0x01)==1) return 1;
// if = 1 then there was a timeout waiting for 0x01 from the mmc/sd
z=0 ;
while((z< 255) && (mmc_response(0x00)==1)) // must keep sending command
{
SPI_WRITE(0x77); // send command 41
SPI_WRITE(0x00); // all the arguments are 0x00 for command 41
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0xE5); // checksum is no longer required but in some cases it is
z++;
}
if(mmc_response(0x01)==1) return 1;
// if = 1 then there was a timeout waiting for 0x01 from the mmc/sd
i=0;
while((i < 255) && (mmc_response(0x00)==1)) // must keep sending command if response
{
SPI_WRITE(0x41); // send mmc command one to bring out of idle state
SPI_WRITE(0x00); // all the arguments are 0x00 for command one
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0xFF); // checksum is no longer required but we send 0xFF
i++;
}
if(i >= 254) return 1; // if >= 254 then there was a timeout waiting for 0x00 from the mmc
puts("Got out of idle response from MMC\n\r");
OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
SPI_WRITE(0xFF); // extra clocks to allow mmc to finish off what it is doing
OUTPUT_LOW(PIN_C2); // set SS = 0 (on)
SPI_WRITE(0x50); // send mmc command 16
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x02); // high block length bits - 512 bytes
SPI_WRITE(0x00); // low block length bits
SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF
if((mmc_response(0x00))==1) return 1;
OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
puts("Got set block length response from MMC\n\r");
return 0;
}
/************************* MMC/SD get response *****************************/
/**** Repeatedly reads the MMC/SD until we get the response we want or timeout ****/
int mmc_response(unsigned char response)
{
unsigned long count = 0xFFFF;
// 16bit repeat, it may be possible to shrink this to 8 bit but there is not much point
while(SPI_READ(0xFF) != response && --count > 0);
if(count==0) return 1; // loop was exited due to timeout
else return 0; // loop was exited before timeout
}
/**************************************************************************/
|
my questions are :
- is the code correctly written or could some more modifications be made to smoothen things a little more?
- if it's correct, what type of memory card and size do you recommend (from your experience of course) that will probably function best with
this code? [ go with the SD (sandisk, Transcend ..) , or just stick with the MMC ]
any answers would be much appreciated, thank you. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Fri Apr 05, 2013 8:54 am |
|
|
DO you intend writing to the SD/MMC card?
If so, the PIC16F877, or for that matter any PIC16F, is a poor choice becasue of the SD/MMC card's requirement that writes be performed in 512 byte blocks. The PIC16F just does not have enough RAM nor the appropriate memory structure to support this. It would be simpler if you used a processor such as the PIC18F4620.
I do a lot of development work with SD/MMC based loggers and virtually all of my new developments requiring SD/MMC support are with the PIC24/dsPIC33 or PIC32 processors. Note that CCS does not currently offer a compiler for the PIC32 family. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Fri Apr 05, 2013 8:59 am |
|
|
First, there's no point commenting on the code before we know what is your hardware setup?
The reason I ask, is that everyone incorrectly assumes they can just wire up an '877 to an SD card.
Please report back with either a picture (hosted of another site) or good word description of the setup.
Generally speaking, any card <=2GB should be used.
hth
jay |
|
|
irilias
Joined: 18 Aug 2012 Posts: 5
|
|
Posted: Fri Apr 05, 2013 11:13 am |
|
|
asmallri wrote: | DO you intend writing to the SD/MMC card?
If so, the PIC16F877, or for that matter any PIC16F, is a poor choice becasue of the SD/MMC card's requirement that writes be performed in 512 byte blocks. The PIC16F just does not have enough RAM nor the appropriate memory structure to support this. It would be simpler if you used a processor such as the PIC18F4620.
I do a lot of development work with SD/MMC based loggers and virtually all of my new developments requiring SD/MMC support are with the PIC24/dsPIC33 or PIC32 processors. Note that CCS does not currently offer a compiler for the PIC32 family. |
unfortunately, at the time being i can't get my hands on another uC and i just have to make my way around the limited RAM. any ideas on how to enhance the code, i even tried adding SDHC support, but that was too much for my brain... i mean this is my first project with MMC/SD
Last edited by irilias on Fri Apr 05, 2013 11:38 am; edited 1 time in total |
|
|
irilias
Joined: 18 Aug 2012 Posts: 5
|
|
Posted: Fri Apr 05, 2013 11:19 am |
|
|
temtronic wrote: | First, there's no point commenting on the code before we know what is your hardware setup?
The reason I ask, is that everyone incorrectly assumes they can just wire up an '877 to an SD card.
Please report back with either a picture (hosted of another site) or good word description of the setup.
Generally speaking, any card <=2GB should be used.
hth
jay |
sorry, i forgot to include the schematic.. anyway, i'm trying to use a PIC16F877A to access an MMC/SD card. The memory card is interfaced to the uC via SPI, no file system is used here. http://imageshack.us/photo/my-images/189/sanstitre3dc.jpg/ |
|
|
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Fri Apr 05, 2013 1:34 pm |
|
|
Hardware is incorrect.
RC4, a Schmit trigger input, requires a minumum of .8*Vdd.
The specs are in the datasheet(table 1.2) and (Electrical 15.2)
For this PIC, .8*5.00 is 4.00 volts
The SD card can only output a maximum of 3 volts.
result. PIC never sees a '1' from the card.
Workable options include
1) selecting a 3volt rated PIC xxLFyyyz
2) additon of 'logic level translating' ic
3) selecting I/O pins that are TTL capable
The choice, as always, is up to you the designer !!
No amount of good software can work properly on bad hardware.
Also as pointed out earlier, serious lack of RAM ! Cheaper, newer, better PICs are available that will work.
hth
jay |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sat Apr 06, 2013 3:12 am |
|
|
There are more hardware problems:
- The 5V PIC output uses a resistor divider to go down to 3.3V, that's ok. The other way is wrong!The output of the MMC is 3.3V but now going through a resistor divider, making it even lower voltage.
- The two data lines from/to the MMC card need pull-up resistors, 10k will do. But this will only work when the voltage dividers are gone.
Do more investigations into your hardware. It can be done and many (wrong) examples can be found on the internet. I don't have the time right now to point you in the right direction so you'll have to do the searching yourself. |
|
|
irilias
Joined: 18 Aug 2012 Posts: 5
|
|
Posted: Sat Apr 06, 2013 8:20 am |
|
|
Thank you temtronic and ckielstra for your thoroughly answers.
i guess it's probably best to go with a logic level translating IC (maybe the 74 series 125) as it's the easiest and the most reliable solution, as for the pull up resistors, i did a little bit of research and i think it's essential to add one to CS (SS pin), as for the other pins adding pull up res is a good idea in case of a hot swap implementation..i'm just saying that, because i'm probably going to use permanent connections.
anyways i hope i'm doing this right ... here's the schematic
[img] http://imageshack.us/photo/my-images/826/picmeminterface.jpg/ [/img]
now my questions are :
- would you recommend any other level translating IC ?
- in this example [url] http://www.ljcv.net/picnet1/picnet1-ds0.pdf [/url], they were using 100 ohms resistors between the (SD-CS, SDO/RC5, SCK/RC3) and the logic level translating IC, what's their purpose? should i add them to the schematic?
thank you again for your replies |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Sat Apr 06, 2013 8:33 am |
|
|
The 100r resistors can be used to reduce any 'ringing' or noise as SPI is a fast data transfer. If you have room add them, they cannot hurt, cost just pennies and may actually help!
The 74act125 is a great solution. The last link to the PDF show a well thought out product.
You really shouldn't use the '877. Old, now 'obsolete', it has very limited RAM and other peripherals. Consider something newer, cheaper, pin compatible. I've settled on the 18F46K22 though there should be a 16F series you might choose.
hth
jay |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sat Apr 06, 2013 10:58 am |
|
|
The pull-up resistors are not only there for hot swapping, but also to have a defined situation at power-up. The PIC processor starts up with all I/O pins configured as inputs, this means that without pull-up (or pull-down) resistors the input pins of the MMC/SD cards will be floating at an undefined level. You don't want this, so that's why at least a pull-up for CS is required.
Then, there is another reason for the Data Out (D0 or DOut) to require a 10k pull-up: the MMC/SD card starts up in what is called the MMC-bus mode. In this mode the DOut pin is Open Collector driven, i.e. it can not generate an output voltage by itself and depends on a pull-up resistor. Only after you have sent the command to switch to SPI mode this pin will become actively driven and the pull-up is no longer required. But, for those first few commands to work correctly you can't leave the pull-up out! |
|
|
nailuy
Joined: 21 Sep 2010 Posts: 159
|
|
Posted: Sat Apr 06, 2013 3:40 pm |
|
|
The simplest solutions are the best.
put the MMC at the 3.6V it should work up to 3.63 V (3,3V+10%)
put PIC16F877 at the 4.5V (data sheet page 125) or low 4V.
betweend MMC and PIC put resistors 1K.
so at 4.5V*0,8=3,6V for logic high as 3,6V MMC
Use this, is more simple and work well.
Best regards |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Sat Apr 06, 2013 4:11 pm |
|
|
Best solution is to use an LF series of PIC which will work at 3 volts,doesn't require any 'interface' chips and has over 512 bytes of RAM.
Having '3 volt' PICs allows easy battery power.
Using '3 volt' PIC means no additional interface chips
MMC/SD cards transfer data in 512 byte 'chunks'.
Trying to use an '877 at the low end can have serious problems related to clock speed..which can be made worse by 'nonhuman' temperatures(little too cold, little too hot).
hth
jay |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Sun Apr 07, 2013 12:29 am |
|
|
temtronic wrote: | Best solution is to use an LF series of PIC ... |
??? A better solution would be to use a PIC24/dsPIC - native 3.3 volt operation, high speed, more RAM, true linear address space for the RAM, much better suited to SD/MMC style applications. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
irilias
Joined: 18 Aug 2012 Posts: 5
|
|
Posted: Sun Apr 07, 2013 1:16 am |
|
|
Thank you guys for clearing that up, i'll add the 100 resistors and a 10k pull-up to the Dout pin of the MMC to ensure it starts up correctly once in MMC-bus mode. and i'll probably go for an LF series of PIC or a 5V PIC with enough RAM.
now since the hardware setup is correct, would you please consider taking a look at the code, it was originally written for MMC memory cards, and i tried adding SD support by using the ACMD41 (CMD55+CMD41) command required for it to initialize.
Set card select high
Send 80 SPI clock cycles (done by writing 0xFF 10 times)
Set card select low
Send CMD0 [0x400000000095]
R1 = 0x01 (idle)
Send CMD55 [0x770000000065] (1st part of ACMD41)
R1 = 0x01 (idle if SD, time out if MMC)
Send CMD41 [0x6900000000E5] (2nd part of ACMD41)
R1 = 0x00 (SD card initialized successfuly, otherwise time out "MMC")
Send CMD1 [0x4100000000FF]
R1 = 0x00 (MMC card initialized successfuly)
and then CMD16 for setting the block length
and i also did some other changes that i thought was necessary :
- i deleted the SPI_SS_DISABLED, because the slave select pin is only present in the slave
- the MMC initialization process has to be done with the clock at 400khz or less, so i used SPI_CLK_DIV_64 instead of SPI_CLK_DIV_4 to get
the clock down to 125khz. (i'm using an 8Mhz xtal)
here's the code :
Code: |
/*************************** MMC/SD Init ***********************************/
/* Initialises the MMC/SD into SPI mode and sets block size, returns 0 on success */
int mmc_init()
{
int i,y,z;
SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_64);
*0x94 |= 0x40; // set CKE = 1 - clock idle low
*0x14 &= 0xEF; // set CKP = 0 - data valid on rising edge
OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
for(i=0;i<10;i++) // initialise the MMC/SD card into SPI mode by sending clks on
{
SPI_WRITE(0xFF);
}
OUTPUT_LOW(PIN_C2); // set SS = 0 (on) tells card to go to spi mode when it receives reset
SPI_WRITE(0x40); // send reset command
SPI_WRITE(0x00); // all the arguments are 0x00 for the reset command
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x95); // precalculated checksum as we are still in MMC/SD mode
puts("Sent go to SPI\n\r");
if(mmc_response(0x01)==1) return 1;
// if = 1 then there was a timeout waiting for 0x01 from the mmc/sd
puts("Got response from MMC/SD\n\r");
// sending ACMD41, first CMD55, then CMD41. Check for time out
y = 0;
while((y< 255) && (mmc_response(0x01)==1)) // must keep sending command
{
SPI_WRITE(0x77); // send command 55
SPI_WRITE(0x00); // all the arguments are 0x00 for command 55
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x65); // checksum is no longer required but in some cases it is
y++;
}
if(mmc_response(0x01)==1) return 1;
// if = 1 then there was a timeout waiting for 0x01 from the mmc/sd
z=0 ;
while((z< 255) && (mmc_response(0x00)==1)) // must keep sending command
{
SPI_WRITE(0x77); // send command 41
SPI_WRITE(0x00); // all the arguments are 0x00 for command 41
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0xE5); // checksum is no longer required but in some cases it is
z++;
}
if(mmc_response(0x01)==1) return 1;
// if = 1 then there was a timeout waiting for 0x01 from the mmc/sd
I=0 ;
while((i < 255) && (mmc_response(0x00)==1)) // must keep sending command if response
{
SPI_WRITE(0x41); // send mmc command one to bring out of idle state
SPI_WRITE(0x00); // all the arguments are 0x00 for command one
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0xFF); // checksum is no longer required but we send 0xFF
i++;
}
if(i >= 254) return 1; // if >= 254 then there was a timeout waiting for 0x00 from the mmc
puts("Got out of idle response from MMC\n\r");
OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
SPI_WRITE(0xFF); // extra clocks to allow mmc to finish off what it is doing
OUTPUT_LOW(PIN_C2); // set SS = 0 (on)
SPI_WRITE(0x50); // send mmc command 16
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x02); // high block length bits - 512 bytes
SPI_WRITE(0x00); // low block length bits
SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF
if((mmc_response(0x00))==1) return 1;
OUTPUT_HIGH(PIN_C2); // set SS = 1 (off)
puts("Got set block length response from MMC\n\r");
return 0;
}
/************************* MMC/SD get response *****************************/
/**** Repeatedly reads the MMC/SD until we get the response we want or timeout ****/
int mmc_response(unsigned char response)
{
unsigned long count = 0xFFFF;
// 16bit repeat, it may be possible to shrink this to 8 bit but there is not much point
while(SPI_READ(0xFF) != response && --count > 0);
if(count==0) return 1; // loop was exited due to timeout
else return 0; // loop was exited before timeout
}
/**************************************************************************/
|
my questions :
- am i doing this right?, is the code correctly written or could some more modifications be made ?
- how can i crank the clock speed back up to a 2Mhz once the initialization sequence is done ? |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|