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();
}

inserted by FC2 system