SD card project part3 以STM32F407VG 寫入與讀取SD卡資料 my_diskio.c & my_diskio.h

在my_diskio程式裡我們實做SD卡的起使化,讀值、和寫值

我直接按照我程式編寫的順序介紹
首先標頭檔
#include "stdint.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_rcc.h"
#include "initsys.h"
#include "my_diskio.h"

--------------------------------------------------------------------------------------------------------

接下來是wait_for_R1_3_6_7()這個function
這是用來在發送完指令給SD卡後接SD卡repsponse,的程式因為不同command 會對應不同的response有R1~R7詳情請自己收尋一下SD卡的spec

void wait_for_R1_3_6_7(uint8_t *return_data){
uint8_t start_data;
uint8_t get_first_response=7;
const uint8_t data_size = 7;
uint8_t i;
uint8_t data[data_size]={0xff};
uint8_t data_rearranged[data_size-1];
while(data[0]==0xff){//wait for sd card to response
data[0]=SPI2_send(0Xff);
}
for(i=1;i<data_size;i++)
{
data[i]=SPI2_send(0Xff);
}
GPIO_SetBits(GPIOD,GPIO_Pin_8);
//because sd card response is not 8bite in a row so 
//we need to find whitch bite is the start bite
start_data=~data[0];
for(get_first_response=7;get_first_response>0;get_first_response--)
{
if((start_data>>get_first_response)==1)
break;
  }
for(i=0;i<data_size-1;i++){
return_data[i]=(data[i]<<(7-get_first_response))|(data[i+1]>>(get_first_response+1));
}
}

--------------------------------------------------------------------------------------------------------

SD卡的喚醒和起使程式圖片來源都在這個pdf內

sorce:http://users.ece.utexas.edu/~valvano/EE345M/SD_Physical_Layer_Spec.pdf

uint8_t SD_card_startup(){
uint8_t count=0;
uint8_t R7_data[6]={0};
uint8_t R3_data[6]={0};
uint8_t R1_data[6]={0};
uint8_t state=1;
uint8_t sdcard_busy;//not 0xff means busy

GPIO_SetBits(GPIOD,GPIO_Pin_8);
delay_ms(100);
//do software reset
SD卡的起使流程參考下圖,因為我用V2.0 standard capacity sd memory card所以做了下圖中間這條分支,此程式沒有判斷卡片的功能

使SD卡進入SPI通訊模式
http://users.ece.utexas.edu/~valvano/EE345M/SD_Physical_Layer_Spec.pdf

//send a CMD0 with CS low
GPIO_ResetBits(GPIOD,GPIO_Pin_8);
SPI2_send(0x40);
SPI2_send(0X00);
SPI2_send(0X00);
SPI2_send(0X00);
  SPI2_send(0X00);
SPI2_send(0X95);
wait_for_R1_3_6_7(R1_data);
GPIO_SetBits(GPIOD,GPIO_Pin_8);
  if(R1_data[0]!=0x01)return RES_ERROR;

若CMD8 valid 則回傳的第一byte 為0x01 否則直接retrun error
//do CMD8
GPIO_ResetBits(GPIOD,GPIO_Pin_8);
do{
sdcard_busy=SPI2_send(0XFF);
}while(sdcard_busy!=0xFF);
//if sd card not return 0xff then keep send 0xff
//this is means to wait for sd card untial it not busy 
SPI2_send(0x48);//CMD8 command token |0 start bit|1 transmission bit|command index 6bit|argument 32bit|CRC 7bit|1 end bit|
SPI2_send(0X00);
SPI2_send(0X00);
SPI2_send(0X01);//voltage supplied 2.7-3.6v
  SPI2_send(0XAA);//check pattern
SPI2_send(0X87);
wait_for_R1_3_6_7(R7_data);
if(R7_data[0]!=0x01){//CMD 8 NO response CMD8 vallid response =0x01
return RES_ERROR;
}
else{ //CMD 8 response
確定CMD8 valid後在check他return的patterm是不是等於我們給他的patterm (double check)
if(R7_data[4]==0xAA)//check CMD 8 patterm
{
patterm 正確後我直接跳過CMD58進入CMD55+ACMD41的循環,因為網路上很多人都是這樣做的測試後一樣可以workCMD55+ACMD41的循環要直到回傳值為0為止代表sd卡從閒置模式進入可以溝通的模式
while(state){
//do CMD55
GPIO_ResetBits(GPIOD,GPIO_Pin_8);
  do{
sdcard_busy=SPI2_send(0XFF);
}while(sdcard_busy!=0xFF);
SPI2_send(0xFF);
SPI2_send(0x77);
SPI2_send(0X00);
SPI2_send(0X00);
SPI2_send(0X00);
  SPI2_send(0X00);
SPI2_send(0X75);
wait_for_R1_3_6_7(R1_data);
  if(R1_data[0]!=0x01)return RES_ERROR;
//do ACMD 41
GPIO_ResetBits(GPIOD,GPIO_Pin_8);
do{
sdcard_busy=SPI2_send(0XFF);
}while(sdcard_busy!=0xFF);
SPI2_send(0xFF);
SPI2_send(0x69);
SPI2_send(0X00);
SPI2_send(0X00);
SPI2_send(0X00);
  SPI2_send(0X00);
SPI2_send(0XFF);
wait_for_R1_3_6_7(R3_data);//wait for R3
if(R3_data[0]==0){
state=0;
}
  }//end cmd55+acmd51 while
接下來做CMD58目的事要讀OCR Registor的第31個bit確定他結束開機程序了

//do CMD 58
GPIO_ResetBits(GPIOD,GPIO_Pin_8);
do{
sdcard_busy=SPI2_send(0XFF);
}while(sdcard_busy!=0xFF);
SPI2_send(0x7A);
SPI2_send(0X00);
SPI2_send(0X00);
SPI2_send(0X00);
  SPI2_send(0X00);
SPI2_send(0X75);
wait_for_R1_3_6_7(R3_data);//wait for R3
if(R3_data[1]==0X80){
USART_puts(USART2,"init sd success \r\n");
}
else{
USART_puts(USART2,"init sd failed cmd58 \r\n");
return RES_ERROR;
}
現在sd卡已經開機好可以把spi的頻率調到>400KHz
  //let spi to a higher freq
Spi2_Init(SPI_BaudRatePrescaler_2);//clock = 42mhz/2 =21mhz
CMD16設定待會讀值存值的BLOCK大小,存值必須為512Byte讀值可以1~512byte,我這裡就設512不再更改
// CMD 16 SET_BLOCKLEN
GPIO_ResetBits(GPIOD,GPIO_Pin_8);
do{
sdcard_busy=SPI2_send(0XFF);
}while(sdcard_busy!=0xFF);
SPI2_send(0x50);
SPI2_send(0X00);
SPI2_send(0X00);
SPI2_send(0X02);
  SPI2_send(0X00);//set block size to 512byte muse be or cant write
SPI2_send(0X01);
wait_for_R1_3_6_7(R1_data);//wait for R1
  if(R1_data[0]==0x00){
USART_puts(USART2,"sd card Init success cmd16 \r\n");
return RES_OK;
}
else{
USART_puts(USART2,"sd card Init failed cmd16 \r\n");
return RES_ERROR;
}
}//if cmd 8 check patterm value 
else{//cmd 8 check patterm value failure
USART_puts(USART2,"Unusable sd card \r\n");
return RES_ERROR;
}
}//else
}

--------------------------------------------------------------------------------------------------------

wait_for_R1_rwdata() 這個function 更wait_for_R1_3_6_7()不同的地方是他不會改變SPI CS Pin的狀態要多這個function的目的是因為待會做sd卡讀值和存值時不是指令給完後通訊就終結,回有來回確認的動作,所以SPI CS Pin不能再接到repsponse後就從低電位拉到高電位,終止通訊

void wait_for_R1_rwdata(uint8_t *R1_data){
uint8_t start_data;
uint8_t get_first_response=7;
const uint8_t data_size = 2;
uint8_t i;
uint8_t data[data_size];
do{
data[0]=SPI2_send(0Xff);
}while(data[0]==0xff);

for(i=1;i<data_size;i++)
{
data[i]=SPI2_send(0Xff);
}
//because sd card response is not 8bite in a row so 
//we need to find whitch bite is the start bite
start_data=~data[0];
for(get_first_response=7;get_first_response>0;get_first_response--)
{
if((start_data>>get_first_response)==1)
break;
  }
//now get_first_response is the start bite
for(i=0;i<data_size-1;i++){
R1_data[i]=(data[i]<<(7-get_first_response))|(data[i+1]>>(get_first_response+1));
}
}

--------------------------------------------------------------------------------------------------------

read_sd_data_to_buff()這個function可以把sd卡的data獨出來,由於我前面的設定block=512所以會有512byte長度的data block再加上start block 和最後2byte的CRC所以要讀515byte


uint8_t read_sd_data_to_buff(uint8_t* buff,uint16_t offset, uint16_t count){
  uint16_t i;
uint8_t start_data;
  uint8_t data[515];
uint8_t get_first_response=7;
uint16_t buffindex=0;
do{
data[0]=SPI2_send(0xFF);
}while(data[0]==0xFF);

for(i=1;i<515;i++)
{
data[i]=SPI2_send(0Xff);
}
GPIO_SetBits(GPIOD,GPIO_Pin_8);
start_data=~data[0];
for(get_first_response=7;get_first_response>=0;get_first_response--)
{
if((start_data>>get_first_response)==1)
break;
  }
start_data=0xFE|data[0]>>get_first_response;
for(i=0;i<512;i++){
buff[buffindex]=(data[i]<<(8-get_first_response))|(data[i+1]>>get_first_response);
buffindex++;
}
return RES_OK;

}

--------------------------------------------------------------------------------------------------------

讀取SD卡內之資料主程式


uint8_t SD_data_read(uint8_t* buff,uint32_t sector,uint16_t offset,uint16_t count){
uint32_t add;
uint8_t R1_data[6];
add=sector*512;
uint8_t sdcard_busy;//not 0xff means busy
//CMD 17 READ_SINGLE_BLOCK
GPIO_ResetBits(GPIOD,GPIO_Pin_8);

do{

sdcard_busy=SPI2_send(0XFF);
}while(sdcard_busy!=0xFF);

SPI2_send(0x51);
SPI2_send(add>>24);
SPI2_send(add>>16);
SPI2_send(add>>8);
  SPI2_send(add);
SPI2_send(0X01);
wait_for_R1_rwdata(R1_data);
if(R1_data[0]==0x00){
接序上面的子程式將data讀到buff變數中
return read_sd_data_to_buff(buff,offset, count);//wait for data_tokens and then save data to buff
}
else {
USART_puts(USART2,"Read sd card data fail 01 \r\n");
return RES_ERROR;
}
}

--------------------------------------------------------------------------------------------------------

Data Response Token 是將data寫入sd卡後sd卡會回傳的確認值

wait_for_data_response()用來偵測確認值
uint8_t wait_for_data_response(){
uint16_t long_rece_data;
uint8_t get_first_response=7;
const uint8_t data_size = 2;
uint8_t data[data_size];
uint8_t data_rearranged[data_size-1];
do{
data[0]=SPI2_send(0XFF);
}while(data[0]==0xFF);
data[1]=SPI2_send(0Xff);
long_rece_data=data[0]<<8|data[1];
GPIO_SetBits(GPIOD,GPIO_Pin_8);
for(get_first_response=1;get_first_response<=11;get_first_response++)
{
if((long_rece_data<<get_first_response&0x5000)==0x5000){
return 0x05;
break;
}
else if((long_rece_data<<get_first_response&0xB000)==0xB000){
return 0x0B;
break;
}
else if((long_rece_data<<get_first_response&0xD000)==0xD000){
return 0x0D;
break;
}
}
  return 0xff;
}

--------------------------------------------------------------------------------------------------------

寫data進入sd卡主程式



uint8_t SD_data_write(const uint8_t* buff,uint32_t sc){

uint16_t i=0;

uint32_t add;

uint8_t sdcard_busy;

add=sc*512;

uint8_t data_response;

uint8_t R1_data[6];

// CMD 24 WRITE_BLOCK

GPIO_ResetBits(GPIOD,GPIO_Pin_8);

do{

sdcard_busy=SPI2_send(0XFF);

}while(sdcard_busy!=0xFF);


SPI2_send(0x58);

SPI2_send(add>>24);

SPI2_send(add>>16);

SPI2_send(add>>8);

  SPI2_send(add);

SPI2_send(0X01);

wait_for_R1_rwdata(R1_data);//wait for R1

  if(R1_data[0]==0x00)

{
SPI2_send(0xFE);//single block write Start Block Token
for(i=0;i<512;i++)
SPI2_send(buff[i]);//send buffer
SPI2_send(0xFF);
SPI2_send(0xFF);// send crc (don't care)
data_response=wait_for_data_response();//wait for R1 data response token
GPIO_SetBits(GPIOD,GPIO_Pin_8);
  if(data_response==0x05){
USART_puts(USART2,"Data send success \r\n");
return RES_OK;
}
else if(data_response==0x0B){
USART_puts(USART2,"Data rejected due to a crc error \r\n");
return RES_ERROR;
}
else if(data_response==0x0D){
USART_puts(USART2,"Data rejected due to a write error \r\n");
return RES_ERROR;
}
else if(data_response==0xff)
{
USART_puts(USART2,"Data send error \r\n");
return RES_ERROR;
}
}
else{
GPIO_SetBits(GPIOD,GPIO_Pin_8);
USART_puts(USART2,"Data send error CMD 24\r\n");
return RES_ERROR;
}
}

---------------------------------------------------------------------------------

my_diskio.h

#ifndef _MY_DISKIO_H_
#define _MY_DISKIO_H_
#include <stm32f4xx.h>
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_flash.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_gpio.h"
#define RES_OK 0
#define RES_ERROR 1
void wait_for_R1_3_6_7(uint8_t *return_data);
uint8_t SD_card_startup(void);
void wait_for_R1_rwdata(uint8_t *R1_data);
uint8_t read_sd_data_to_buff(uint8_t* buff,uint16_t offset, uint16_t count);
uint8_t SD_data_read(uint8_t* buff,uint32_t sector,uint16_t offset,uint16_t count);

uint8_t wait_for_data_response();
uint8_t SD_data_write(const uint8_t* buff,uint32_t sc);

#endif

留言

這個網誌中的熱門文章

freeRTOS Deleting a Task

Interrupt Management

在ubuntu中編寫c語言