usrlib-sd.h
SD cardのドライバルーチンのためのヘッダファイルで、ポート番号とかコマンド番号とかエラーコードとかを定義しています.
#include "stm8s.h"
おなじみのライブラリのインクルードです.
// hardware definitions
#define SD_SPI_CLK CLK_PERIPHERAL_SPI
#define
SD_SPI_SCK_GPIO_PORT
GPIOC //
GPIOC
#define
SD_SPI_SCK_PIN
GPIO_PIN_5 // PC5
#define SD_SPI_MISO_GPIO_PORT GPIOC // GPIOC
#define
SD_SPI_MISO_PIN
GPIO_PIN_7 // PC7
#define SD_SPI_MOSI_GPIO_PORT GPIOC // GPIOC
#define
SD_SPI_MOSI_PIN
GPIO_PIN_6 // PC6
#define
SD_CS_GPIO_PORT
GPIOE //
GPIOE
#define
SD_CS_PIN
GPIO_PIN_5 // PE5
#define
SD_DETECT_GPIO_PORT
GPIOE //
GPIOE
#define
SD_DETECT_PIN
GPIO_PIN_2 // PE2
これらは、GPIOのポートの定義をしています.
回路図と一致するように定義しているわけです.
もしも、STM8S105C6じゃなくて、別のラインナップを使うのであれば、ここをカスタマイズすればOKです.
// SD card parameters
#define SD_PRESENT ((uint8_t)0x01)
#define SD_NOT_PRESENT ((uint8_t)0x00)
SD cardがある/なしのbitパターンを定義しています.
SD card挿入SWはPG0に接続しましたから、bit0にそのSWの影響が生じるわけです.
#define SD_BLOCK_SIZE 0x200
SD cardはblockが512BYTEです.
#define SD_DUMMY_BYTE 0xFF
あとで解説しますけど、SD cardに対する用事が特にないけれどもダミークロックを送信したい時には仕方ないので何かデータを送信せざるを得ないので、そのときはFFを送信します.
// SD work area
extern uint8_t SDstr[SD_BLOCK_SIZE]; // SD card write buffer 512bytes
SDのread/writeをするためのバッファで、512バイト.
// SD commands
#define SD_CMD_GO_IDLE_STATE 0 /*!< CMD0 = 0x40 */
#define SD_CMD_SEND_OP_COND 1 /*!< CMD1 = 0x41 */
#define
SD_CMD_SEND_CSD
9 /*!< CMD9 = 0x49 */
#define
SD_CMD_SEND_CID
10 /*!< CMD10 = 0x4A */
#define SD_CMD_STOP_TRANSMISSION 12 /*!< CMD12 = 0x4C */
#define SD_CMD_SEND_STATUS 13 /*!< CMD13 = 0x4D */
#define SD_CMD_SET_BLOCKLEN 16 /*!< CMD16 = 0x50 */
#define SD_CMD_READ_SINGLE_BLOCK 17 /*!< CMD17 = 0x51 */
#define SD_CMD_READ_MULT_BLOCK 18 /*!< CMD18 = 0x52 */
#define SD_CMD_SET_BLOCK_COUNT 23 /*!< CMD23 = 0x57 */
#define SD_CMD_WRITE_SINGLE_BLOCK 24 /*!< CMD24 = 0x58 */
#define SD_CMD_WRITE_MULT_BLOCK 25 /*!< CMD25 = 0x59 */
#define
SD_CMD_PROG_CSD
27 /*!< CMD27 = 0x5B */
#define SD_CMD_SET_WRITE_PROT 28 /*!< CMD28 = 0x5C */
#define SD_CMD_CLR_WRITE_PROT 29 /*!< CMD29 = 0x5D */
#define SD_CMD_SEND_WRITE_PROT 30 /*!< CMD30 = 0x5E */
#define SD_CMD_SD_ERASE_GRP_START 32 /*!< CMD32 = 0x60 */
#define SD_CMD_SD_ERASE_GRP_END 33 /*!< CMD33 = 0x61 */
#define SD_CMD_UNTAG_SECTOR 34 /*!< CMD34 = 0x62 */
#define SD_CMD_ERASE_GRP_START 35 /*!< CMD35 = 0x63 */
#define SD_CMD_ERASE_GRP_END 36 /*!< CMD36 = 0x64 */
#define SD_CMD_UNTAG_ERASE_GROUP 37 /*!< CMD37 = 0x65 */
#define
SD_CMD_ERASE
38 /*!< CMD38 = 0x66 */
SD cardにいろいろなコマンドを送ってwriteしたりreadしたりするので、そのコマンド番号の定義です.
このサンプルプログラムで使うのは、0,1,17,24だけです.
それだけ知ってりゃ読み書きできるってことです.
// Start Data tokens
#define SD_START_DATA_SINGLE_BLOCK_READ 0xFE /*!< Data token start byte, Start Single Block Read */
#define SD_START_DATA_MULTIPLE_BLOCK_READ 0xFE /*!< Data token start byte, Start Multiple Block Read */
#define SD_START_DATA_SINGLE_BLOCK_WRITE 0xFE /*!< Data token start byte, Start Single Block Write */
#define SD_START_DATA_MULTIPLE_BLOCK_WRITE 0xFD /*!< Data token start byte, Start Multiple Block Write */
#define SD_STOP_DATA_MULTIPLE_BLOCK_WRITE 0xFD /*!< Data toke stop byte, Stop Multiple Block Write */
SD cardにコマンドを送りますと、SD cardは「ああそうwriteね」などと気づくわけです.
で、SD cardは、データが来るのを待ち構えるわけです.
そのときに、データの始まりの合図がFEです.
そのFEをstart data tokenと呼びます.
いろいろと定義してありますけど、このサンプルプログラムで使うのはFEだけです.
FEがstart data tokenだというのはSD card formatで決められているので、FEを別の値にカスタマイズするのは不可能です.
// SD responses and error flags
#define SD_RESPONSE_NO_ERROR 0x00
#define SD_IN_IDLE_STATE 0x01
#define SD_ERASE_RESET 0x02
#define SD_ILLEGAL_COMMAND 0x04
#define SD_COM_CRC_ERROR 0x08
#define SD_ERASE_SEQUENCE_ERROR 0x10
#define SD_ADDRESS_ERROR 0x20
#define SD_PARAMETER_ERROR 0x40
#define SD_RESPONSE_FAILURE 0xFF
コマンドに対するSD cardの返値の定義です.
SD cardでは、返値のことをresponseと呼びます.
このサンプルプログラムでケアしているのは0と1ぐらいです.
// Data response error flags
#define
SD_DATA_OK
0x05
#define SD_DATA_CRC_ERROR 0x0B
#define SD_DATA_WRITE_ERROR 0x0D
#define SD_DATA_OTHER_ERROR 0xFF
データに対するSD cardの返値です.コマンド返値とは別です.
このサンプルプログラムでは5をケアしています.
5だけは憶えておきましょう.データを正しく受け取ったら5だと.
/*----- FAT16 function -----*/
extern u32 SD_size_byte; SDカードにwriteしたトータルサイズバイト.ただし0からカウント
extern u8 SD_full; SDカードが満杯になったフラグ
u16 SDstr_ptr(void); 512BYTEのワークエリアの末尾を示すポインタ 0-511
u32 SD_block(void); ブロックの末尾を示すポインタ 504-4194303
u8 SD_block_cnt(void); 1クラスタ=64ブロックです.クラスタ内におけるブロック末尾ポインタ 0-63
u16 SD_cluster(void); クラスタの末尾を示すポインタ 2-65535
u16 SD_FAT_block(void); FATの末尾ブロックを示すポインタ 6-471
u16 SD_FAT_block_ptr(void); 末尾のFATブロック内におけるFATポインタ 0-510
void SD_write_sequencial_init(void); シーケンシャルライトを初期化
void SD_write_sequencial(u8* wdata, char v); シーケンシャルライトルーチン
void SD_format_2GB(void); 2GBのSDカードをformat
/*----- High layer function -----*/
void SD_DeInit(void);
SD cardのインターフェースのためのIO PORTを解放するルーチンです.
uint8_t SD_Init(void);
SD cardのインターフェースのためのIO PORTを設計するルーチンです.
uint8_t SD_Detect(void);
SD cardが挿入されているかどうかをチェックするルーチンです.
ちなみに、このサンプルプログラムでは、ライトプロテクトはケアしてません.
uint8_t SD_ReadBlock(uint8_t* pBuffer, uint32_t ReadAddr);
block読みルーチン
uint8_t SD_WriteBlock(uint8_t* pBuffer, uint32_t WriteAddr);
block書きルーチン
void SD_DumpBlock(void);
read/writeバッファであるSDstrをUARTにダンプ表示します
/*----- Medium layer function -----*/
void SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc);
コマンドを送信するルーチン
uint8_t SD_GetResponse(uint8_t Response);
コマンドレスポンスをチェックするルーチン.
SD cardから引数のレスポンスが返ってくるまでループし続けるのが特徴です.
timeout機能もあります.
uint8_t SD_GetDataResponse(void);
データレスポンスをチェックするルーチン.
uint8_t SD_GoIdleState(void);
SD cardをSPIモードにするルーチン.初期化時に使います.
/*----- Low layer function -----*/
uint8_t SD_WriteByte(uint8_t byte);
SD cardに1BYTE送信するルーチン.SPIにアクセスします.
uint8_t SD_ReadByte(void);
SD cardから1BYTE受信するルーチン.SPIにアクセスします.
void SD_LowLevel_DeInit(void);
SPIを解放するルーチンです.
void SD_LowLevel_Init(void);
SPIを設定するルーチンです.
#define SD_CS_LOW() GPIO_WriteLow(SD_CS_GPIO_PORT, SD_CS_PIN)
#define SD_CS_HIGH() GPIO_WriteHigh(SD_CS_GPIO_PORT, SD_CS_PIN)
SD cardのCSをassert(LOW)、deassert(HIGH)するルーチンです.
usrlib-sd.c
SD cardのドライバルーチンです.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "stm8s.h"
#include "usrlib-uart.h"
#include "usrlib-sd.h"
ヘッダファイルを読みます.
//***************************************************
//***************************************************
// FAT16 functions
//***************************************************
//***************************************************
// SD card format 2GB
uint8_t SDstr[SD_BLOCK_SIZE+1]; ブロックread/writeのワークエリア512BYTE
void SD_format_2GB(void) 2GBのSDカードをformatします
{
int i;
// BPB (block 0-5) 最初にBPBをformatします
for(i=0;i<SD_BLOCK_SIZE;i++) SDstr[i]=0;
SDstr[0] =0xeb; // BLOCK 0
:
中略
:
SDstr[63]=0xc9;
ここではBPBの先頭64BYTEを下記のようにします.
eb 3c 90 4d 53 44 4f 53 35 2e 30 00 02 40 06 00 | .<.MSDOS5.0..@..
02 00 02 00 00 f8 e9 00 3f 00 ff 00 00 00 00 00 | ........?.......
00 10 3a 00 80 00 29 29 f0 4a ec 4e 4f 20 4e 41 | ..:...)).J.NO NA
4d 45 20 20 20 20 46 41 54 31 36 20 20 20 33 c9 | ME FAT16 3ノ
SD_WriteBlock(SDstr, 0); ワークエリアをSDカードのブロック0にwriteします
for(i=0;i<SD_BLOCK_SIZE;i++) SDstr[i]=0; ワークエリアをクリア
SD_WriteBlock(SDstr, (uint32_t)(1*SD_BLOCK_SIZE)); ブロック1にwrite
SD_WriteBlock(SDstr, (uint32_t)(2*SD_BLOCK_SIZE)); ブロック2にwrite
SD_WriteBlock(SDstr, (uint32_t)(3*SD_BLOCK_SIZE)); ブロック3にwrite
SD_WriteBlock(SDstr, (uint32_t)(4*SD_BLOCK_SIZE)); ブロック4にwrite
SD_WriteBlock(SDstr, (uint32_t)(5*SD_BLOCK_SIZE)); ブロック5にwrite
SerialPutString("Wrote block 0-5 (BPB)\r\n"); メッセージを表示
// FAT (block 6-471) つぎにFATをformatします.
for(i=0;i<SD_BLOCK_SIZE;i++) SDstr[i]=0; ワークエリアをクリア
SDstr[0]=0xF8; 先頭をF8FFFFFFにします
SDstr[1]=0xFF;
SDstr[2]=0xFF;
SDstr[3]=0xFF;
SD_WriteBlock(SDstr, (uint32_t)(6*SD_BLOCK_SIZE)); ワークエリアをSDカードのブロック6にwriteします
SerialPutString("Wrote block 6 (FAT)\r\n"); メッセージを表示
// RDE (block 472-1d8) つぎにRDEをformatします.RDEはブロック472〜503までですが、ここでは先頭の472だけをformatします.
for(i=0;i<SD_BLOCK_SIZE;i++) SDstr[i]=0; read/writeバッファクリア
SDstr[0] ='S';
:
中略
:
SDstr[32+0x1F]=0;
53 44 43 41 52 44 20 20 20 20 20 08 00 00 00 00 | SDCARD ←SDカードのvolume nameです
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
4c 4f 47 20 20 20 20 20 54 58 54 20 18 00 00 00 | LOG TXT ←log.txtファイル名です
00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 | ................
SD_WriteBlock(SDstr, (uint32_t)(472*SD_BLOCK_SIZE)); ワークエリアをSDカードのブロック472にwriteします
UART_PutString("Wrote block 472 (RDE)\r\n"); メッセージを表示
return;
}
// SD card parameter calculation functions 下記の関数は、SDカードのパラメータを計算する関数です.
extern u32 SD_size_byte; SDカードにwriteしたトータルサイズバイト.ただし0からカウント
u16 SDstr_ptr(void) {return (u16)( SD_size_byte%512);} 512BYTEのワークエリアの末尾を示すポインタ 0-511
u32 SD_block(void) {return (u32)((SD_size_byte>>9)+504);} ブロックの末尾を示すポインタ 504-4194303
u8 SD_block_cnt(void) {return (u8) ((SD_size_byte>>9)%64);} 1クラスタ=64ブロックです.クラスタ内におけるブロック末尾ポインタ 0-63
u16 SD_cluster(void) {return (u16)(((SD_block()-504)>>6)+2);} クラスタの末尾を示すポインタ 2-65535
u16 SD_FAT_block(void) {return (u16)((SD_cluster()>>8)+6);} FATの末尾ブロックを示すポインタ 6-471
u16 SD_FAT_block_ptr(void) {return (u16)((SD_cluster()%256)*2);} 末尾のFATブロック内におけるFATポインタ 0-510
extern u8 SD_full; SDカードが満タンであるフラグ
extern char UARTstrTX[80]; UARTのバッファ
void SD_PutString_init(void) この関数を文字列をSDカードにwriteする関数を初期化します
{
SDstr[SD_BLOCK_SIZE]=0; read/writeバッファの末尾=0
SD_size_byte=0xFFFFFFFF; // 0-2GB SDカードにwriteしたトータルバイト数をクリア
SD_full=0; // SD card full flag SDカード満タンフラグクリア
}
// wdata: write data (If wdata is empty then flush buffer)
// v: "v" verbose mode
void SD_PutString(u8* wdata_, char v) この関数は、引数の文字列をSDカードにwriteします.
{
int j,k;
char wdata[120]; 文字列をコピーするバッファ
u8* p;
strcpy(wdata,wdata_); 文字列をコピー
p=wdata;
if(SD_full) SDカードが満タンだったらreturnします
{
UART_PutString("SD card full\r\n");
return; // SD card full ?
}
// null wdata --> flush data
if(wdata[0]==0) wdataが空文字だったら、ワークエリアに残った文字列をSDカードにwriteします
{
UART_PutString("\r\nSD flushing\r\n"); UARTへ表示
// data flush
if(SDstr_ptr()==511)UART_PutString("\r\n"); ワークエリアのポインタ=511だったらすでにSDカードにwrite済みなのでなにもしない
else SDカードに書くべきデータがワークエリアに残っている
{
SD_WriteBlock(SDstr,(uint32_t)(SD_block()*SD_BLOCK_SIZE)); SDカードにワークエリアをwriteする
sprintf(UARTstr,"EndCLUSTER %d EndBLOCK %d TotalSIZE
%04x%04x[BYTE],
",SD_cluster(),(u16)SD_block(),(u16)(((SD_size_byte+1)&0xFFFF0000)>>16),(u16)((SD_size_byte+1)&0xFFFF)); UARTへ表示
UART_PutString(UARTstr); UARTへ表示
}
// update FAT
SD_ReadBlock(SDstr,(uint32_t)((u32)SD_FAT_block()*SD_BLOCK_SIZE)); 次にFATを更新するので、FATの末尾ブロックをワークエリアにreadします.
SDstr[SD_FAT_block_ptr()] =0xFF; FATにFFFFを書きます
SDstr[SD_FAT_block_ptr()+1]=0xFF; FATにFFFFを書きます
SD_WriteBlock(SDstr,(uint32_t)((u32)SD_FAT_block()*SD_BLOCK_SIZE)); FATをwriteします
UART_PutString("FAT updated, "); UARTへ表示
// update RDE
SD_size_byte++; write済みBYTEはゼロから数えているのでファイルサイズを表すために+1します
SD_ReadBlock(SDstr,(uint32_t)(472*SD_BLOCK_SIZE)); RDEをreadします
SDstr[32+0x1C]=(u8)( SD_size_byte&0xFF); RDEにファイルサイズを書きます
SDstr[32+0x1D]=(u8)((SD_size_byte&0xFF00)>>8); RDEにファイルサイズを書きます
SDstr[32+0x1E]=(u8)((SD_size_byte&0xFF0000)>>16); RDEにファイルサイズを書きます
SDstr[32+0x1F]=(u8)((SD_size_byte&0xFF000000)>>24); RDEにファイルサイズを書きます
SD_WriteBlock(SDstr,(uint32_t)(472*SD_BLOCK_SIZE)); RDEをwriteします
UART_PutString("RDE updated\r\n"); UARTへ表示
}
// set data to SDstr
for(j=0;j<strlen(wdata);j++) 以下ではワークエリアにwdataを格納してゆき、ワークエリアが満杯になったらSDカードにwriteします
{
SD_size_byte++; ファイルサイズを+1
SDstr[SDstr_ptr()]=*p++; ワークエリアの末尾にwdataを1文字格納
if(SDstr_ptr()==511) もしもワークエリアの末尾ポインタ=511になって満杯になっていたら、、、、
{
// write to SD card
SD_WriteBlock(SDstr,(uint32_t)(SD_block()*SD_BLOCK_SIZE)); ワークエリアをSDカードにwriteする
// update FAT
SD_ReadBlock(SDstr,(uint32_t)((u32)SD_FAT_block()*SD_BLOCK_SIZE)); FATを更新するためreadします
SDstr[SD_FAT_block_ptr()] =(u8)((SD_cluster()+1)&0xFF); FAT末尾にクラスタ番号を追記
SDstr[SD_FAT_block_ptr()+1]=(u8)(((SD_cluster()+1)&0xFF00)>>8); FAT末尾にクラスタ番号を追記
SD_WriteBlock(SDstr,(uint32_t)((u32)SD_FAT_block()*SD_BLOCK_SIZE)); ワークエリアをFATブロックにwriteします
// clr work area
for(k=0;k<SD_BLOCK_SIZE;k++) SDstr[k]=0; 次のwdataのためにワークエリアをクリア
// display
if(SD_block_cnt()==0) UARTへクラスタ番号を表示
{
sprintf(UARTstr,"CLUSTER %d\r\n",SD_cluster()); UARTへクラスタ番号を表示
UART_PutString(UARTstr); UARTへクラスタ番号を表示
}
if(v=='v') verbose modeなら詳細を表示する
{
sprintf(UARTstr,"BLK[%d/%d]
SizeB[%04x%04x]
FAT[%d/%d]\r\n",(u16)SD_block(),SD_block_cnt(),(u16)(((SD_size_byte+1)&0xFFFF0000)>>16),(u16)((SD_size_byte+1)&0xFFFF),SD_FAT_block(),SD_FAT_block_ptr());
UART_PutString(UARTstr);
}
// SD full ?
if(SD_cluster()>=65530) クラスタが65530になったらSDカードを満杯とします
{
UART_PutString("SD card is full\r\n"); UARTへ表示
SD_full=1; SDカード満杯フラグを立てる
}
}
}
return;
}
//***************************************************
//***************************************************
// BLOCK READ/WRITE functions
//***************************************************
//***************************************************
uint8_t SD_ReadBlock(uint8_t* pBuffer, uint32_t ReadAddr) 指定のblockを読んで512BYTEのバッファにデータを格納します.
{
uint32_t i = 0;
uint8_t rvalue = SD_RESPONSE_FAILURE;
GPIO_WriteLow(GPIOD, GPIO_PIN_0); // LED on アクセスLED
SD_CS_LOW(); CSをassertします
// Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block
SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, ReadAddr, 0xFF); CMD17を送信します
// Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */
if (!SD_GetResponse(SD_RESPONSE_NO_ERROR)) コマンドレスポンスがノーエラー00になるまでwaitします.
{
// wait data token(0xFE) to signify the start of the data
if (!SD_GetResponse(SD_START_DATA_SINGLE_BLOCK_READ)) データ先頭であることを示すFEが来るまでwaitします.
{
// Read 512 BYTE data
for (i = 0; i < SD_BLOCK_SIZE; i++) 512BYTEを一気に受信します
{
*pBuffer = SD_ReadByte();
pBuffer++;
}
// Get CRC bytes (not really needed by us, but required by SD)
SD_ReadByte(); このサンプルプログラムではCRCを使いませんけど、SD cardがCRCを2BYTE送信してくるので空読みします.
SD_ReadByte();
rvalue = SD_RESPONSE_NO_ERROR;
}
}
SD_CS_HIGH(); CSをdeassertします
SD_WriteByte(SD_DUMMY_BYTE); SD cardへダミークロックを8発送信します.
GPIO_WriteHigh(GPIOD, GPIO_PIN_0); アクセスLED
return rvalue;
}
uint8_t SD_WriteBlock(uint8_t* pBuffer, uint32_t WriteAddr) 指定のblockへ512BYTEのバッファのデータを書きます.
{
uint32_t i = 0;
uint8_t rvalue = SD_RESPONSE_FAILURE;
GPIO_WriteLow(GPIOD, GPIO_PIN_0); アクセスLED
SD_CS_LOW(); CSをassertします
// Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write a block
SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, WriteAddr, 0xFF); CMD24を送信します
// Check command response R1 (0x00: no errors)
if (!SD_GetResponse(SD_RESPONSE_NO_ERROR)) コマンドレスポンスがノーエラー00になるまでwaitします
{
SD_WriteByte(SD_DUMMY_BYTE); ダミークロックを8発送ります
SD_WriteByte(0xFE); データ先頭であることを示すFEを送ります
for (i = 0; i < SD_BLOCK_SIZE; i++) // Send data
{
SD_WriteByte(*pBuffer); 512BYTEのデータを一気に送信します
pBuffer++;
}
SD_ReadByte(); // Send CRC
SD_ReadByte();
このサンプルプログラムではCRCを使いませんが、SD cardの仕様なのでCRCもどきを2BYTE送信します.
送信とは言っても、ReadByteをコールしているのはなんでかというと、SPIが全2重モードで動いているので、clockを8発生成するという意味ではWriteでもreadでも同じってことなのです.
// Read data response
if (SD_GetDataResponse() == SD_DATA_OK)
データレスポンスを読んでチェックします.SD_GetDataResponse()の中ではtimeoutとかごにょごにょと処理しています.
{
rvalue = SD_RESPONSE_NO_ERROR;
}
}
SD_CS_HIGH(); // SD CS deassert
SD_WriteByte(SD_DUMMY_BYTE); SD cardへダミークロックを8発送信します.
GPIO_WriteHigh(GPIOD, GPIO_PIN_0); アクセスLED
return rvalue;
}
void SD_DumpBlock(void) read/writeバッファであるSDstrをUARTにダンプ表示します
{
int i,k;
for(i=0;i<SD_BLOCK_SIZE;i=i+16)
{
char ascii[3], str[17];
UARTstr[0]=0; // null string
str[16]=0;
for(k=0;k<=15;k++)
{
sprintf(ascii,"%02x ",SDstr[i+k]);
strcat(UARTstr,ascii);
str[k]=SDstr[i+k];
if(str[k]<32)str[k]=46;
if(str[k]==0x7F)str[k]=46;
if((str[k]&0xF0)==0x80)str[k]=46;
if((str[k]&0xF0)==0x90)str[k]=46;
if((str[k]&0xF0)==0xE0)str[k]=46;
if((str[k]&0xF0)==0xF0)str[k]=46;
}
strcat(UARTstr," | ");
strcat(UARTstr,str);
strcat(UARTstr,"\r\n");
UART_PutString(UARTstr);
}
}
//***************************************************
//***************************************************
// BASIC READ/WRITE functions
//***************************************************
//***************************************************
uint8_t SD_WriteByte(uint8_t Data)
{
// Wait until the transmit buffer is empty
while (SPI_GetFlagStatus(SPI_FLAG_TXE) == RESET) {}
SPI_SendData(Data);
// Wait to receive a byte
while (SPI_GetFlagStatus(SPI_FLAG_RXNE) == RESET) {}
return SPI_ReceiveData();
}
SD cardに1BYTE送信するルーチンです.
これはSTM8SのSPIを直接操作するLOWレベル関数です.
じつは、このソースを読んで、なんじゃこりゃと思ったわたしです.
なぜなら、transmitなのに、なぜかrecieveしているからです.
SPIの仕様をよーく読んで理解したことはこうでした.
●SPIの全2重モードで動いている
●なので、transmitとrecieveが必ず同時に生じる
●動作シーケンスはこうなっている
SPI_FLAG_TXEが空表示になるまで待つ
→ SPIのレジスタに1BYTE書く
→ SPIが1BYTE送信する
→ SPIが裏で送信と同時に受信する
→ SPI_FLAG_RXNEが空表示になるまで待つ
→ SPIのレジスタから1BYTE空読みする
uint8_t SD_ReadByte(void)
{
uint8_t Data = 0;
// Wait until the transmit buffer is empty
while (SPI_GetFlagStatus(SPI_FLAG_TXE) == RESET) {}
SPI_SendData(SD_DUMMY_BYTE);
// Wait until a data is received
while (SPI_GetFlagStatus(SPI_FLAG_RXNE) == RESET) {}
Data = SPI_ReceiveData();
return Data;
}
SD cardから1BYTE受信するルーチンです.
これはSTM8SのSPIを直接操作するLOWレベル関数です.
このルーチンも、全2重ゆえにダミーデータを送信して、目的のデータを受信しています.
動作シーケンスはこうなっています.
SPI_FLAG_TXEが空表示になるまで待つ
→ SPIのレジスタにダミーデータ FF を書く
→ SPIが1BYTE送信する
→ SPIが裏で送信と同時に受信する
→ SPI_FLAG_RXNEが空表示になるまで待つ
→ SPIのレジスタから1BYTE読む
//***************************************************
//***************************************************
// INITIALIZATION functions
//***************************************************
//***************************************************
void SD_LowLevel_Init(void)
{
// Enable SPI clock
CLK_PeripheralClockConfig(SD_SPI_CLK, ENABLE);
// Set the MOSI,MISO and SCK at high level
GPIO_ExternalPullUpConfig(SD_SPI_SCK_GPIO_PORT, (GPIO_Pin_TypeDef)(SD_SPI_MISO_PIN | SD_SPI_MOSI_PIN | \
SD_SPI_SCK_PIN), ENABLE);
// SD_SPI Configuration
SPI_Init( SPI_FIRSTBIT_MSB, SPI_BAUDRATEPRESCALER_4, SPI_MODE_MASTER,
SPI_CLOCKPOLARITY_HIGH, SPI_CLOCKPHASE_2EDGE,
SPI_DATADIRECTION_2LINES_FULLDUPLEX,
SPI_NSS_SOFT, 0x07);
SPI_Cmd( ENABLE); // SD_SPI enable
// Set MSD ChipSelect pin in Output push-pull high level
GPIO_Init(SD_CS_GPIO_PORT, SD_CS_PIN, GPIO_MODE_OUT_PP_HIGH_SLOW);
}
SD cardのためのLOWレベル設定です.SPIの設定が主です.
CLK_PeripheralClockConfig()は、SPIへclockを供給しています.
GPIO_ExternalPullUpConfig()は、MISO,MOSI,SCKをプルアップしています.SPIは味噌とか模試とか変な名前ですね.
SPI_Init()の引数のメジャーな設定は、MSB first、マスター、clock極性、全2重 です.
この他にマイナーな設定は、
・SPI_BAUDRATEPRESCALER_4 はプリスケ4分周ですから、源発信16MHz/4=4MHzがSD cardに供給されます.clock周波数上限がSD cardの都合で決まっていると思いますけど、よく知りません.
・SPI_CLOCKPHASE_2EDGE はおまじない的にこのままにしてください.
・SPI_NSS_SOFT はおまじない的にこのままにしてください.CSをhardwareで制御するのではなく、softで明示的に制御するという意味です.
・最後の引数の0x07は、CRCの生成多項式の設定ですが、このサンプルプログラムではCRCを使いませんので、設定しても無視されています.
uint8_t SD_Init(void)
{
uint32_t i = 0;
SD_LowLevel_Init(); // Initialize SD_SPI
SD_CS_LOW(); // SD chip select assert
for (i = 0; i <= 9; i++) // wait 80 clocks cycles
{
SD_WriteByte(SD_DUMMY_BYTE); // Send dummy byte 0xFF
}
// SD initialized and set to SPI mode properly */
return (SD_GoIdleState());
}
SD cardを使うための設定です.
最初にSD_LowLevel_Init()でSPIを設定し、その後にSD cardをSPIモードにするというのがおおまかな仕事です.
まず、CSをassert(LOW)にします.
つぎに、SD cardのformatの決まりで、80clock待つことになっているので、ダミーデータFFを10ヶ送信して80clockをSDに与えています.
-----SD_writeByte()では受信もしているので160clockになっているんじゃないのという疑問は間違いです-----
SD_GoIdleState()をコールします.
uint8_t SD_GoIdleState(void)
SD cardをSPIモードに入れる処理です.やや複雑です.
この複雑さを理解するのがSD cardの処理のクセを理解することになると思います.
{
SD_CS_LOW(); CS=assertにしていますが、すでにassertされているので無駄って言えば無駄な処理です.
// Send CMD0 (SD_CMD_GO_IDLE_STATE) to put SD in SPI mode
SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0, 0x95); CMD0を送信します.CMD0はIDLE stateになれというコマンドです.
// Wait for In Idle State Response (R1 Format) equal to 0x01
if (SD_GetResponse(SD_IN_IDLE_STATE))
CMD0へのコマンドレスポンスは1BYTEだけ返ってきます.IDLE状態なら01が返ってきます.
SD_GetResponse()の中ではtimeout処理とかごにょごにょやってます.
{
// No Idle State Response: return response failure
return SD_RESPONSE_FAILURE;
}
//---------Activates the card initialization process-----------
do
do-whileで囲われた処理をレスポンスがノーエラーになるまで何度も繰り返します.
こういうところがSD cardドライバのめんどくさいところです.
実機動作では、1度では失敗し、2度目で成功します.なんでだろ?
{
SD_CS_HIGH(); // SD CS deassert
SD_WriteByte(SD_DUMMY_BYTE); // dummy 8 clocks
ダミーバイトを送信している理由は、SD cardが必要とする8発のclockを発生するためです.送受信のためではありません.
ここも厄介なところです.ダミーバイト送信の前にCSをdeassertしていますが、必ずしもそうする必要はなく、CS assertのままでも良いしいです.
SD_CS_LOW(); // SD CS assert
// Send CMD1 (Activates the card process) until response equal to 0x0
SD_SendCmd(SD_CMD_SEND_OP_COND, 0, 0xFF);
CMD1を送信しますが、1度で成功するかどうかは保証の限りではありません.
// Wait for no error Response (R1 Format) equal to 0x00
}
while (SD_GetResponse(SD_RESPONSE_NO_ERROR));
成功するまで何度でもやります.わたしの経験では2度目で成功します.
SD_CS_HIGH(); // SD CS deassert
SD_WriteByte(SD_DUMMY_BYTE); Gここでもダミークロックを8発送ります.
return SD_RESPONSE_NO_ERROR;
}
void SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc)
{
uint32_t i = 0x00;
uint8_t Frame[6];
Frame[0] = (uint8_t)(Cmd | 0x40); // Construct byte 1
Frame[1] = (uint8_t)(Arg >> 24); // Construct byte 2
Frame[2] = (uint8_t)(Arg >> 16); // Construct byte 3
Frame[3] = (uint8_t)(Arg >> 8); // Construct byte 4
Frame[4] = (uint8_t)(Arg); // Construct byte 5
Frame[5] =
(Crc);
// Construct CRC: byte 6
for (i = 0; i < 6; i++)
{
SD_WriteByte(Frame[i]); // Send the Cmd bytes
}
}
コマンドを送信するルーチンです.
コマンドフォーマットはこのように決まっています.
●全部で6バイト
●byte0がコマンド番号
●byte1〜4が32bitのコマンド引数で、たとえばwriteアドレスだったりする
●byte5がCRC.ただし、SPIモードではデフォルトでCRCは使わない設定なので何を送信しても無視される.
uint8_t SD_Detect(void)
{
__IO uint8_t status = SD_PRESENT;
// Check GPIO to detect SD
if (GPIO_ReadInputData(SD_DETECT_GPIO_PORT) & SD_DETECT_PIN)
{
status = SD_NOT_PRESENT;
}
return status;
}
SD cardが挿入されているかどうかをチェックするために、PG0のbit0のLOW/IHGHをチェックしています.
uint8_t SD_GetDataResponse(void) データレスポンスをチェックするルーチンです.
{
uint32_t i = 0;
uint8_t response = 0, rvalue = 0;
while (i <= 64) 64バイト受信してもまともなレスポンスが返ってこなかったら、timeoutでexitするためのチェックです.
{
response = SD_ReadByte(); // Read response
response &= 0x1F; // Mask unused bits
とりあえず1BYTE受信して、不要なbitを無視するために1FでANDします.
switch (response)
{
case SD_DATA_OK: その結果が05であれば正常なので、breakします
{
rvalue = SD_DATA_OK;
break;
}
case SD_DATA_CRC_ERROR: return SD_DATA_CRC_ERROR; なんらかのエラーであればreturnします
case SD_DATA_WRITE_ERROR: return SD_DATA_WRITE_ERROR; なんらかのエラーであればreturnします
default:
{
rvalue = SD_DATA_OTHER_ERROR; なんらかのエラーであればreturnします
break;
}
}
// Exit loop in case of data ok
if (rvalue == SD_DATA_OK) break;
i++;
}
while (SD_ReadByte() == 0); 正常であったら、さらにデータが00になるまでwaitしつづけます
return response;
}
uint8_t SD_GetResponse(uint8_t Response) コマンドレスポンスをチェックするルーチンです.
引数Responseと同一のコマンドレスポンスが返ってくるまで待ちます
{
uint32_t Count = 0xFFF; FFF回受信しても所望のコマンドレスポンスが来なかったらtimeoutします
// Check if response is got or a timeout is happen
while ((SD_ReadByte() != Response) && Count) FFF回受信しても所望のコマンドレスポンスが来なかったらtimeoutします
{
Count--;
}
if (Count == 0) // timeout occured
{
return SD_RESPONSE_FAILURE; timeout
}
else // Good response
{
return SD_RESPONSE_NO_ERROR; good return
}
}
void SD_LowLevel_DeInit(void) SPIを解放します.
{
SPI_Cmd(DISABLE); // SD_SPI disable
// SD_SPI Peripheral clock disable
CLK_PeripheralClockConfig(SD_SPI_CLK, DISABLE);
// Configure SD_SPI pins: SCK
GPIO_Init(SD_SPI_SCK_GPIO_PORT, SD_SPI_SCK_PIN, GPIO_MODE_IN_FL_NO_IT);
// Configure SD_SPI pins: MISO
GPIO_Init(SD_SPI_MISO_GPIO_PORT, SD_SPI_MISO_PIN, GPIO_MODE_IN_FL_NO_IT);
// Configure SD_SPI pins: MOSI
GPIO_Init(SD_SPI_MOSI_GPIO_PORT, SD_SPI_MOSI_PIN, GPIO_MODE_IN_FL_NO_IT);
// Configure SD_SPI_CS_PIN pin: SD Card CS pin
GPIO_Init(SD_CS_GPIO_PORT, SD_CS_PIN, GPIO_MODE_IN_FL_NO_IT);
// Configure SD_SPI_DETECT_PIN pin: SD Card detect pin
GPIO_Init(SD_DETECT_GPIO_PORT, SD_DETECT_PIN, GPIO_MODE_IN_FL_NO_IT);
}
void SD_DeInit(void) SPIを解放します.
{
SD_LowLevel_DeInit();
}