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);
//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;
}
//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卡內之資料主程式
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
留言
張貼留言