LPC810メモ:SPI interface(SDカードを読み書き)
SDカードに書き込みを行うサンプルですが、ファイルシステムは無視して書き込むので、使ったカードは再フォーマットしないとPCからアクセスできなくなると思われます。もし試そうとしてくれる方がいましたら、必ず不要なSDカードを使ってください。
SPI通信の例として、SDカードを読み書きする例。
前提とする環境はこちら→LPC810メモ 共通の準備
- 本例の動作。電源を入れると、以下を順に行う。
- SDカードの最後のブロックに、1,2,3,1,2,3...というデータを書き込む。
- 同じブロックからデータを読み込む
- 読み込んだ値に応じた回数のLEDを点滅させる。つまり、1回、2回、3回、1回、2回、3回...と点滅する。
サンプルコード
#include "LPC8xx.h" // 成功するまで粘るコマンドのための回数定義 #define TIMEOUT 5000 // 失敗したここに飛ばす。 // 引数の回数だけLED点滅させておく。 static void error(uint8_t e) { while(1) { for(uint8_t i = 0; i < e * 2; i++) { LPC_GPIO_PORT->NOT0 |= (1<<0); for(volatile int j = 0; j < 100000; j++) {} } for(volatile int i = 0; i < 300000; i++) {} } } static bool sdhc; // SDHC/XCの場合にtrue(途中で設定) static uint32_t sdBlocks; // ブロックの数(途中で設定) // read/write時のアドレス指定(sdhcだとblock単位) static uint32_t sdAdress(uint32_t block) { return block * (sdhc ? 1 : 512); } // CSにつなぐ1番pin(GPIO5)をHIGHにする、LOWにする static void csSet() { LPC_GPIO_PORT->SET0 |= (1<<5); }; static void csClear() { LPC_GPIO_PORT->CLR0 |= (1<<5); }; // 1byte読み書き // UM10601 29.3.5 Transmit and receive a byte to/from slave 0 // の例を真似。 static uint8_t spiWrite(uint8_t arg = 0xff) { // UM10601 17.6.3 SPI Status register // TXRDY(Transmitter Ready flag)が立つまで待つ while(~LPC_SPI0->STAT & (1 << 1)); // UM10601 17.6.7 SPI Transmitter Data and Control register // 1バイト書き込み LPC_SPI0->TXDATCTL = arg // TXDAT = 送信するデータ | (8 - 1) << 24 // LEN = 8bit送信 | (1 << 20); // EOT = 1 (data stallの発生を防ぐのに必要だとか) // UM10601 17.6.3 SPI Status register // RXRDY(Receiver Ready flag)が立つまで待つ while(~LPC_SPI0->STAT & (1 << 0)); // UM10601 17.6.6 SPI Receiver Data register // 読み込み結果を返す return LPC_SPI0->RXDAT; } // SDカードにコマンド送信 // skipはR1byteの後に何バイト読むか(R7なら4byteなど)。ただし読むだけで無視する。 static uint8_t sdCmd(uint8_t cmd, uint32_t arg = 0, int skip = 0, uint8_t crc=0) { csClear(); spiWrite(0x40 | cmd); spiWrite(arg >> 24); spiWrite(arg >> 16); spiWrite(arg >> 8); spiWrite(arg >> 0); spiWrite(crc); for (int i = 0; i < TIMEOUT; i++) { int response = spiWrite(); if (!(response & 0x80)) { for (int j = 0; j < skip; j++) { spiWrite(); } csSet(); spiWrite(); return response; } } csSet(); spiWrite(); return 0xff; // タイムアウト } // データパケット読み込み static void sdReadPacket(uint8_t *buffer, int length) { csClear(); // データトークンを待つ for (int i = 0;; i++) { if(i > TIMEOUT) { error(5); }; if(spiWrite() == 0xFE) { break; }; } for (int i = 0; i < length; i++) { buffer[i] = spiWrite(); } // データ読み込み spiWrite(); // CRC spiWrite(); csSet(); spiWrite(); } // 指定されたブロックから512byte読み込み static void sdRead(uint8_t *buffer, uint32_t block_number) { // CMD17 if (sdCmd(17, sdAdress(block_number)) != 0) { error(6); } sdReadPacket(buffer, 512); } // 指定されたブロックに512byte書き込み static void sdWrite(uint8_t *buffer, uint32_t block_number) { // CMD24 if (sdCmd(24, sdAdress(block_number)) != 0) { return; } csClear(); spiWrite(0xFE); // データトークン for (int i = 0; i < 512; i++) { spiWrite(buffer[i]); } // データ読み込み spiWrite(); // CRC spiWrite(); // レスポンストークン if ((spiWrite() & 0x1F) != 0x05) { // 成功 csSet(); spiWrite(); return; } // なんらか失敗(0以外が戻るまで待つ) for (int i = 0;; i++) { if(i > TIMEOUT) { error(7); }; if(spiWrite() != 0) { break; }; } csSet(); spiWrite(); } // SDカード初期化 void sdInitialize() { // SDカードが立ち上がってないかもしれないので少しだけ待つ for(volatile int i = 0; i < 5000; i++) {} // UM10601 17.6.10 SPI Divider register LPC_SPI0->DIV = 120 - 1; // 12MHz / 120 = 100KHzに設定 // SPIをmasterとして有効化 // UM10601 17.6.1 SPI Configuration register LPC_SPI0->CFG = (1 << 0) // ENABLE = 1 | (1 << 2); // MASTER = 1 // SDカード初期化のためにCSをHIGHにして0xffを送信(クロック74回以上) csSet(); for(int i = 0; i < 16; i++) { spiWrite(); } // 16byte送って、都合128クロック if(sdCmd(0,0,0,0x95) != 1) { error(2); }; // CMD0(リセット) // CMD8の結果が1(idle state, illegal commandではない)ならSDHC/XCとみなす。 // 0x01 << 8は3.3v、0xaaはcheck pattern(なぜ0xaaなのかは知らない) sdhc = (sdCmd(8, 0x01aa, 4, 0x87) == 1) ; // ACMD41(初期化) for (int i = 0;; i++) { if(i > TIMEOUT) { error(3); }; sdCmd(55); if (sdCmd(41, sdhc ? (1 << 30) : 0) == 0) { break; }; // 成功 } sdCmd(16, 512); // CMD16(ブロック長を512byteに設定。なくても良い気がする。) LPC_SPI0->DIV = 12 - 1; // 12MHz / 12 = 1MHzに設定 // CMD9(CSDを読んで、カードのブロック(セクタ)の数を求める) if (sdCmd(9) != 0) { error(4); } uint8_t csd[16]; sdReadPacket(csd, 16); if(sdhc) { // CSD Version 2.0 uint32_t c_size = (csd[7] & 0x3f) << 16 | csd[8] << 8 | csd[9]; // [69:48] device size sdBlocks = (c_size + 1) * 1024; } else { // CSD Version 1.0 uint32_t c_size = (csd[6] & 0x3) << 10 | (csd[7] << 2) | (csd[8] & 0xc0) >> 6; // [73:62] device size uint32_t c_size_mult = (csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7; // [49:47] device size multiplier uint32_t read_bl_len = (csd[5] & 0xf); // [83:80] max. read data block length uint32_t block_len = 1 << read_bl_len; uint32_t mult = 1 << (c_size_mult + 2); uint32_t blocknr = (c_size + 1) * mult; uint32_t capacity = blocknr * block_len; sdBlocks = capacity / 512; } } int main(void) { // GPIOの設定 1,8番pinを出力に設定しておく // 1番はSDカードのCSにつなぐ // 8番はLEDにつなぐn LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); LPC_GPIO_PORT->DIR0 |= (1<<0) | (1<<5); // LED消灯 LPC_GPIO_PORT->CLR0 |= (1<<0); // 「UM10601 17.3 Basic configuration」の手順で初期化 // SPI0に対してclockを有効化(bit11。bit12はspi1なので関係ない) // In the SYSAHBCLKCTRL register, set bit 11 and 12 (Table 30) to // enable the clock to the register interface. LPC_SYSCON->SYSAHBCLKCTRL |= (1<<11); // Clear the SPI0/1 peripheral resets using the PRESETCTRL register (Table 19). LPC_SYSCON->PRESETCTRL &= ~(0x1<<0); LPC_SYSCON->PRESETCTRL |= (0x1<<0); // SWMの設定 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7); /* Pin Assign 8 bit Configuration */ LPC_SWM->PINASSIGN3 = 0x04ffffffUL; // SPI0_SCK ->2番pin LPC_SWM->PINASSIGN4 = 0xffff0302UL; // SPI0_MISO->3番、SPI0_MOSI->4番 LPC_SWM->PINENABLE0 = 0xffffffffUL; // 固定pin機能はreset含めて無効化(1番使いたいから) // SDカード初期化 sdInitialize(); // 読み書き用のbufferを用意。データを適当に入れる(頭から順に1,2,3,1,2,3....) uint8_t buffer[512]; for(int x = 0; x < 512; x++) { buffer[x] = x % 3 + 1; } // SDカードの最後のセクタにbufferの内容を書き込む sdWrite(buffer, sdBlocks-1); // 読み込み用のデータをゼロ初期化 for(int x = 0; x < 512; x++) { buffer[x] = 0; } // SDカードの最後のセクタからbufferの内容を読み込む sdRead(buffer, sdBlocks-1); // buffer[x]の値に応じた回数だけLED点滅 for(int x = 0; x < 512; x++) { for(int j = 0; j < buffer[x] * 2; j++) { LPC_GPIO_PORT->NOT0 |= (1<<0); for(volatile int i = 0; i < 200000; i++) {} } for(volatile int i = 0; i < 1000000; i++) {} } return 0 ; }
接続
- 左型の緑色のは、秋月で買ったSDカードスロットDIP化モジュール(250円)
- SDカードとの結線は下記
LPC810 | SDカード |
---|---|
1番pin(GPIO5) | CS |
2番pin(SPI0_SCK) | CLK |
3番pin(SPI0_MISO) | SDO |
4番pin(SPI0_MOSI) | SDI |
- LEDは8番pin
- 6,7番pin(Vdd,GND)は0.1μFのコンデンサ。
- 実行後のSDカードの中身をHxDで確認した結果
- 8MBのSDカード
- 最後のSector(=Block)13278に、01,02,03...と書き込まれている。
- 8MBのSDカード
-
- 8GBのSDHCカード
- 最後のSector(=Block)15278079に、01,02,03...と書き込まれている。
- 8GBのSDHCカード