概要
MSP430F149は、Texas Instrumentsが提供する16ビットRISC超低消費電力マイコンである。
最大8[MHz]動作、60[KB]フラッシュメモリ、2[KB] RAMを搭載し、1.8[V] ~ 3.6[V]の広い電源電圧範囲で動作する。
MSP430F149はUSARTモジュールを2チャネル搭載しており、それぞれUARTモードとSPIモードを切り替えて使用できる。
このうち1チャネルをSPIマスターモードで動作させることにより、SDカードとのSPI通信が可能となる。
FatFsモジュールのディスクI/O層をMSP430F149のSPIドライバ上に実装することで、SDカード上のFATファイルシステムに対する読み書きを実現できる。
ただし、MSP430F149のRAMは2[KB]しかないため、FatFsをそのまま使用するとメモリが不足する。
FatFsのTINYモード (FF_FS_TINY=1) を有効にすることが必須であり、これにより、FIL構造体のサイズが約552バイトから約40バイトに削減される。
TINYモードでは全てのオープンファイルがFATFS構造体内の単一セクタバッファを共有するため、
1ボリューム・1ファイル構成で約604バイトのRAMでFatFsを動作させることが可能となる。
MSP430F149は3.3[V]で動作させるのが一般的であり、SDカードの動作電圧 (2.7[V] ~ 3.6[V]) と一致する。
そのため、レベルシフタを使用せずにMSP430F149とSDカードを直結できるメリットがある。
MSP430F149の仕様
MSP430F149は、Texas Instrumentsが提供する16ビットRISC超低消費電力マイコンである。
FatFsとSDカードを組み合わせたデータロギングシステム等、組み込みアプリケーションに広く使用されている。
下表に、主要な仕様を示す。
| 項目 | 仕様 |
|---|---|
| CPU | 16ビットRISC (MSP430) |
| 最大動作周波数 | 8[MHz] |
| 電源電圧 | 1.8[V] ~ 3.6[V] (通常3.3[V]) |
| フラッシュメモリ | 60[KB] + 256バイト (情報メモリ) |
| RAM | 2[KB] (0x0200 ~ 0x09FF) |
| パッケージ | QFP-64 |
| I/Oピン | 48本 |
RAM 2[KB]という制約は、FatFsのTINYモード有効化が必須となる主な理由である。
TINYモードを有効にすることで、FIL構造体のサイズを約552バイトから約40バイトに削減できる。
詳細な設定方法は マイコン - FatFsの設定 を参照のこと。
下表に、MSP430F149のメモリマップを示す。
| アドレス範囲 | サイズ | 用途 |
|---|---|---|
| 0x0000 ~ 0x00FF | 256バイト | ペリフェラルレジスタ (8ビットバス) |
| 0x0100 ~ 0x01FF | 256バイト | ペリフェラルレジスタ (16ビットバス) |
| 0x0200 ~ 0x09FF | 2[KB] | RAM |
| 0x1000 ~ 0x10FF | 256バイト | 情報メモリ (フラッシュ) |
| 0x1100 ~ 0xFFFF | 60[KB]強 | メインフラッシュメモリ |
下表に、MSP430F149に搭載されているペリフェラルの概要を示す。
| ペリフェラル | 説明 |
|---|---|
| Timer_A | 3チャネルCC、16ビットタイマ |
| Timer_B | 7チャネルCC、16ビットタイマ |
| ADC12 | 12ビットSAR ADC、8チャネル |
| コンパレータ_A | アナログ比較 |
| USART0 | UART/SPI兼用 (P3.1=SIMO, P3.2=SOMI, P3.3=UCLK) |
| USART1 | UART/SPI兼用 (P3.6=TXD, P3.7=RXD) |
FatFsのSDカード接続にはUSART0をSPIモードで使用する。
SPIインターフェース
USART-SPIモードの概要
MSP430F149のUSARTは、UCTLレジスタのSYNCビットを1に設定することでSPIモードに切り替えることができる。
SPI機能を有効化するためのレジスタ設定を以下に示す。
| 項目 | 説明 |
|---|---|
| USART0のSPI有効化 | ME1レジスタのUSPIE0ビット (ビット6) を1に設定する。 |
| USART1のSPI有効化 | ME2レジスタのUSPIE1ビット (ビット4) を1に設定する。 |
3ピンモードでは、STE (CS) を使用せずにSIMO、SOMI、UCLKの3線で通信する。
FatFsのSDカード接続では、CSをGPIOで手動制御するため、3ピンモードを使用する。
MSP430F149のSPI信号名は一般的なSPI信号名と異なる名称を使用している。
下表に、対応関係を示す。
| MSP430F149の信号名 | 一般的な信号名 | 説明 |
|---|---|---|
| SIMO | MOSI | マスタ出力 / スレーブ入力 |
| SOMI | MISO | マスタ入力 / スレーブ出力 |
| UCLK | CLK | クロック信号 |
| STE | CS | スレーブ選択 (チップセレクト) |
SPI関連レジスタ
下表に、USART0のSPI動作に関わる主要レジスタを示す。
| レジスタ名 | アドレス | 説明 |
|---|---|---|
| UCTL0 | 0x0070 | USARTモード制御レジスタ (SYNC、MM、CHARビット等) |
| UBR00 | 0x0072 | ボーレートレジスタ (下位バイト) |
| UBR10 | 0x0073 | ボーレートレジスタ (上位バイト) |
| UTCTL0 | 0x0074 | 送信制御レジスタ (CLKSビット等でクロック源選択) |
| URCTL0 | 0x0075 | 受信制御レジスタ |
| UTXBUF0 | 0x0076 | 送信バッファ |
| URXBUF0 | 0x0077 | 受信バッファ |
| IE1 | 0x0000 | 割り込み有効レジスタ1 (URXIE0、UTXIE0ビット) |
| IFG1 | 0x0002 | 割り込みフラグレジスタ1 (URXIFG0、UTXIFG0ビット) |
| ME1 | 0x0004 | モジュール有効レジスタ1 (USPIE0ビット) |
SPIクロック設定
SPIのクロック周波数は、MCLK (8[MHz]) をUBRレジスタの値で分周して設定する。
SDカードの初期化シーケンスでは、クロック周波数を100[kHz] ~ 400[kHz]以下に設定する必要がある。
初期化完了後に高速クロックに切り替える。
| UBR値 | クロック周波数 | 用途 |
|---|---|---|
| 0 (= 1) | 8[MHz] | 通常動作時の最大速度 |
| 2 | 4[MHz] | 安定動作が必要な場合 |
| 8 | 1[MHz] | 中速動作 |
| 80 | 100[kHz] | SDカード初期化時 |
SPI送受信の仕組み
MSP430F149のSPI送受信の手順を以下に示す。
| 項目 | 説明 |
|---|---|
| 送信 | UTXBUFレジスタにデータを書き込むと、シフトレジスタに転送されてSIMOピンから出力される。 |
| 受信 | 同時にSOMIピンからデータが入力され、URXBUFレジスタから読み出せる。 |
| データ順序 | MSBファーストでデータが転送される。 |
SDカードのハードウェア接続
電圧レベルと接続の基本方針
MSP430F149は3.3[V]動作であり、SDカードの動作電圧 (2.7[V] ~ 3.6[V]) と一致するため、レベルシフタは不要である。
下表に、MSP430F149とSDカードのピン接続を示す。
| MSP430F149ピン | 機能 | SDカードピン | 説明 |
|---|---|---|---|
| P3.1 | USART0 SIMO | DI (ピン2) | マスタ --> SDカード |
| P3.2 | USART0 SOMI | DO (ピン7) | SDカード --> マスタ |
| P3.3 | USART0 UCLK | CLK (ピン5) | クロック信号 |
| P1.0 (任意) | GPIO出力 | CS (ピン1) | チップセレクト (アクティブLow) |
| VCC (3.3[V]) | 電源 | VDD (ピン4) | SDカード電源 |
| GND | グラウンド | VSS (ピン3, 6) | GND |
プルアップ抵抗
下表に、プルアップ抵抗の要否を示す。
| ピン | プルアップ | 説明 |
|---|---|---|
| DO (MISO) ピン | 10[kΩ]プルアップ必要 | SDカードのDO出力はオープンドレイン構成のため、外部プルアップが必須である。 |
| SIMO (MOSI) ピン | 不要 | MSP430F149のプッシュプル出力で駆動するため、プルアップは不要である。 |
| UCLK (CLK) ピン | 不要 | プッシュプル出力のため、プルアップは不要である。 |
| CS ピン | 不要 | GPIOのプッシュプル出力で制御する。 |
回路接続の要点
回路設計の要点を以下に示す。
- MSP430F149の3.3[V]電源からSDカードのVDDに接続する。
- DO (MISO) ラインには3.3[V]ラインから10[kΩ]のプルアップ抵抗を接続する。
- SDカードのCSピンはP1.0に接続し、初期状態でHighに設定する。
- デカップリングコンデンサとして、SDカードのVDD-VSS間に0.1[uF]セラミックコンデンサを配置する。
SPIドライバの実装
ヘッダファイルの定義
SPIドライバのヘッダファイル spi.h を以下に示す。
CSピンの定義と、SPIドライバが提供する関数のプロトタイプを定義する。
#ifndef SPI_H
#define SPI_H
#include <msp430.h>
#include <stdint.h>
// CSピン定義 (P1.0を使用する場合の例)
#define SD_CS_PORT P1OUT
#define SD_CS_DDR P1DIR
#define SD_CS_PIN BIT0
// CSピン制御マクロ
#define SD_CS_LOW() (SD_CS_PORT &= ~SD_CS_PIN)
#define SD_CS_HIGH() (SD_CS_PORT |= SD_CS_PIN)
// 関数プロトタイプ
void SPI_Init(void);
uint8_t SPI_SendByte(uint8_t data);
uint8_t SPI_ReceiveByte(void);
void SPI_SetSpeedHigh(void);
void SPI_SetSpeedLow(void);
#endif // SPI_H
SPI初期化関数
USART0をSPIマスターモードで初期化する SPI_Init() 関数の実装例を以下に示す。
SWRST (ソフトウェアリセット) ビットを設定した状態でレジスタを設定して、最後にSWRSTを解除してUSARTを有効化する手順となる。
#include "spi.h"
void SPI_Init(void)
{
// SWRSTビット設定 (リセット状態でレジスタを設定する)
U0CTL |= SWRST;
// SPIモード設定: SYNC=1 (同期), MM=1 (マスタ), CHAR=1 (8ビット)
U0CTL |= SYNC | MM | CHAR;
U0CTL &= ~(LISTEN | I2C); // リスンモード無効、I2Cモード無効
// クロック設定 (初期化時は低速: 100[kHz])
// MCLK (8[MHz]) / 80 = 100[kHz]
U0TCTL |= CKPH | SSEL1; // CLKSビット: SMCLK選択、クロック位相設定
UBR00 = 80; // ボーレート下位バイト
UBR10 = 0; // ボーレート上位バイト
UMCTL0 = 0; // モジュレーション不要
// USART0のSPI機能を有効化
ME1 |= USPIE0;
// ポート設定 (P3.1 = SIMO, P3.2 = SOMI, P3.3 = UCLK)
P3SEL |= BIT1 | BIT2 | BIT3; // P3.1, P3.2, P3.3をUSART機能に設定
P3DIR |= BIT1 | BIT3; // SIMO, UCLKを出力に設定
P3DIR &= ~BIT2; // SOMIを入力に設定
// CS用GPIOの設定 (P1.0)
SD_CS_DDR |= SD_CS_PIN; // CSピンを出力に設定
SD_CS_HIGH(); // 初期状態はCSをHighに設定
// SWRSTを解除してUSARTを有効化
U0CTL &= ~SWRST;
// 送受信割り込みは使用しない (ポーリング方式)
IE1 &= ~(URXIE0 | UTXIE0);
}
SPI送受信関数
1バイトの送受信を行う SPI_SendByte() および SPI_ReceiveByte() 関数の実装例を以下に示す。
UTXBUFレジスタへの書き込みで送信を開始して、URXBUFレジスタから受信データを読み出す。
// 1バイト送受信 (全二重)
// 引数: data=送信データ
// 戻り値: 受信データ
uint8_t SPI_SendByte(uint8_t data)
{
// 送信バッファが空になるまで待機
while (!(IFG1 & UTXIFG0));
// データを送信バッファに書き込む
TXBUF0 = data;
// 受信バッファにデータが入るまで待機
while (!(IFG1 & URXIFG0));
// 受信データを返す
return RXBUF0;
}
// ダミーバイト送信でデータを受信する
// 戻り値: 受信データ
uint8_t SPI_ReceiveByte(void)
{
return SPI_SendByte(0xFF);
}
SPI速度変更関数とCS制御マクロ
SDカード初期化後に高速クロックに切り替える SPI_SetSpeedHigh() および SPI_SetSpeedLow() 関数の実装例を以下に示す。
速度変更時はSWRSTを設定してからUBRレジスタを変更して、SWRSTを解除する手順が必要である。
// 高速クロックに設定 (8[MHz])
// SDカード初期化完了後に呼び出す
void SPI_SetSpeedHigh(void)
{
U0CTL |= SWRST;
UBR00 = 1; // 8[MHz] / 1 = 8[MHz]
UBR10 = 0;
UMCTL0 = 0;
U0CTL &= ~SWRST;
}
// 低速クロックに設定 (初期化用: 100[kHz])
void SPI_SetSpeedLow(void)
{
U0CTL |= SWRST;
UBR00 = 80; // 8[MHz] / 80 = 100[kHz]
UBR10 = 0;
UMCTL0 = 0;
U0CTL &= ~SWRST;
}
SDカード初期化シーケンス
初期化フロー
SDカードをSPIモードで初期化する手順を以下に示す。
| ステップ | 内容 | 説明 |
|---|---|---|
| ステップ1 | 電源投入後の安定待ち | 電源投入後、最低1[ms]以上待機してからSPIクロックを送出する。 |
| ステップ2 | 74クロック以上の送出 (CS = HIGH) | CS=HIGH状態でダミークロックを74クロック以上送出して、SDカードをSPIモードに移行させる。 実装上は10バイト (80クロック) 送出する。 |
| ステップ3 | CMD0 (リセット) | CS=LOWにしてCMD0 (ソフトウェアリセット) を送信する。 R1レスポンスが0x01 (Idle State) であれば正常にSPIモードに移行している。 |
| ステップ4 | CMD8 (インターフェース条件確認) | CMD8を送信してSDカードのバージョンを確認する。 正常な応答があればSDv2、エラーであればSDv1またはMMCとして扱う。 |
| ステップ5 | ACMD41ループ (初期化) | CMD55 + ACMD41を繰り返し送信して初期化完了を待つ。 R1レスポンスが0x00になれば初期化完了である。 |
| ステップ6 | CMD58 (OCR読み出し) | SDv2の場合にCMD58でOCRを読み出し、CCSビットでブロックアドレス対応か判定する。 |
| ステップ7 | 高速クロック切替 | 初期化完了後に高速クロック (8[MHz]) に切り替える。 |
下表に、初期化シーケンスの詳細を示す。
| ステップ | 操作 | 期待するレスポンス | 補足 |
|---|---|---|---|
| 1 | 電源投入 --> 1[ms]以上待機 | - | 電源安定のために必要 |
| 2 | CS=HIGH、74クロック以上出力 | - | SDカードをSPIモードに移行させる |
| 3 | CMD0送信 | 0x01 (Idle State) | CRCが必要 (0x95) |
| 4 | CMD8送信 (引数: 0x1AA) | 0x01 + 4バイト応答 | SDv2判定、CRC必要 (0x87) |
| 5 | ACMD41ループ (SDv2: 引数HCS=1) | 0x00 (Ready) | 最大1秒程度待機 |
| 6 | CMD58送信 (OCR読み出し) | 0x00 + 4バイトOCR | CCSビットでアドレス方式確認 |
| 7 | 高速クロックに切り替え | - | 初期化完了後に実施 |
SDカードコマンド送信関数
SDカードへのコマンド送信とR1レスポンス受信を行う SD_SendCmd() 関数の実装例を以下に示す。
CMD0とCMD8のみ有効なCRCが必要であり、その他のコマンドはダミーCRCを使用する。
// カードタイプ定義
#define CT_MMC 0x01 // MMCカード
#define CT_SD1 0x02 // SDカード Ver.1
#define CT_SD2 0x04 // SDカード Ver.2
#define CT_SDC (CT_SD1 | CT_SD2)
#define CT_BLOCK 0x08 // ブロックアドレス対応カード
// SDカードコマンド定義
#define CMD0 (0) // リセット
#define CMD8 (8) // インターフェース条件送信
#define CMD9 (9) // CSD読み出し
#define CMD12 (12) // 転送停止
#define CMD17 (17) // シングルブロック読み込み
#define CMD18 (18) // マルチブロック読み込み
#define CMD24 (24) // シングルブロック書き込み
#define CMD25 (25) // マルチブロック書き込み
#define CMD41 (41) // SDカード初期化 (ACMD41)
#define CMD55 (55) // アプリケーションコマンド
#define CMD58 (58) // OCR読み出し
// SDカードコマンド送信
// 引数: cmd = コマンド番号 (上位ビットが1の場合はACMD), arg=引数
// 戻り値: R1レスポンスバイト
static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg)
{
uint8_t n, res;
// ACMD (アプリケーションコマンド) の場合はCMD55を先行送信
if (cmd & 0x80) {
cmd &= 0x7F;
res = SD_SendCmd(CMD55, 0);
if (res > 1) return res;
}
// カードがReadyになるまで待機
SD_CS_HIGH();
SPI_SendByte(0xFF);
SD_CS_LOW();
SPI_SendByte(0xFF);
// コマンドパケット送信 (6バイト)
SPI_SendByte(0x40 | cmd); // スタートビット + コマンドインデックス
SPI_SendByte((uint8_t)(arg >> 24)); // 引数バイト3 (MSB)
SPI_SendByte((uint8_t)(arg >> 16)); // 引数バイト2
SPI_SendByte((uint8_t)(arg >> 8)); // 引数バイト1
SPI_SendByte((uint8_t)arg); // 引数バイト0 (LSB)
// CRC送信 (CMD0とCMD8のみ有効なCRCが必要)
n = 0x01; // ダミーCRC (ストップビットのみ)
if (cmd == CMD0) n = 0x95; // CMD0のCRC
if (cmd == CMD8) n = 0x87; // CMD8のCRC
SPI_SendByte(n);
// CMD12の場合はダミーバイトを送信
if (cmd == CMD12) SPI_SendByte(0xFF);
// R1レスポンス受信 (最大10バイト待機)
n = 10;
do {
res = SPI_SendByte(0xFF);
} while ((res & 0x80) && --n);
return res;
}
SDカード初期化関数
SDカードの初期化シーケンス全体を実行する SD_Init() 関数の実装例を以下に示す。
static volatile DSTATUS Stat = STA_NOINIT; // ディスクステータス
static uint8_t CardType; // カードタイプ
// カードのBusy解除待ち
// 戻り値: 1=Ready、0=タイムアウト
static int wait_ready(void)
{
uint8_t d;
uint16_t timeout = 5000;
do {
d = SPI_SendByte(0xFF);
timeout--;
} while ((d != 0xFF) && timeout);
return (d == 0xFF) ? 1 : 0;
}
// SDカード初期化
// 戻り値: ディスクステータス (0=成功, STA_NOINIT=失敗)
DSTATUS SD_Init(void)
{
uint8_t n, cmd, ty, ocr[4];
uint16_t tmr;
// 電源投入後の安定待ち (1ms以上)
for (tmr = 1000; tmr; tmr--) {
__delay_cycles(8000); // 約1ms (MCLK=8MHz)
}
// CS=HIGH状態で74クロック以上送出 (SDカードをSPIモードに移行)
SD_CS_HIGH();
for (n = 10; n; n--) {
SPI_SendByte(0xFF); // 10バイト x 8クロック = 80クロック
}
ty = 0;
// CMD0: カードをアイドル状態にリセット
if (SD_SendCmd(CMD0, 0) == 1) {
// CMD8: SDカードv2の確認 (引数: VHS=1 (2.7-3.6V), Check Pattern=0xAA)
if (SD_SendCmd(CMD8, 0x1AA) == 1) {
// SDv2: OCRの応答 (4バイト) を受信
for (n = 0; n < 4; n++) ocr[n] = SPI_SendByte(0xFF);
if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
// 電圧範囲確認OK: ACMD41で初期化ループ
for (tmr = 1000; tmr && SD_SendCmd(0x80 | CMD41, 1UL << 30); tmr--) {
__delay_cycles(8000);
}
// CMD58: OCRを読み出してブロックアドレス対応確認
if (tmr && SD_SendCmd(CMD58, 0) == 0) {
for (n = 0; n < 4; n++) ocr[n] = SPI_SendByte(0xFF);
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
}
}
}
else {
// SDv1またはMMC: ACMD41で初期化ループ
if (SD_SendCmd(0x80 | CMD41, 0) <= 1) {
ty = CT_SD1;
cmd = 0x80 | CMD41; // SDC: ACMD41を使用
}
else {
ty = CT_MMC;
cmd = 1; // MMC: CMD1を使用
}
for (tmr = 1000; tmr && SD_SendCmd(cmd, 0); tmr--) {
__delay_cycles(8000);
}
// ブロック長を512バイトに設定
if (!tmr || SD_SendCmd(16, 512) != 0) ty = 0;
}
}
CardType = ty;
// CSをHighに戻してIDLE状態にする
SD_CS_HIGH();
SPI_SendByte(0xFF);
if (ty) {
// 初期化成功: 高速クロックに切り替え
SPI_SetSpeedHigh();
Stat &= ~STA_NOINIT;
}
else {
Stat = STA_NOINIT;
}
return Stat;
}
ディスクI/O層の実装
ヘルパー関数とマクロ定義
ディスクI/O層で使用するヘルパー関数の実装例を以下に示す。
diskio.c のヘルパー関数部分であり、データブロックの受信・送信を担当する。
#include "diskio.h"
#include "spi.h"
#include <stdint.h>
// データブロック受信
// 引数: buff = 受信バッファ、btr = 受信バイト数
// 戻り値: 1 = 成功、0 = 失敗
static int rcvr_datablock(BYTE *buff, UINT btr)
{
uint8_t token;
uint16_t timeout = 2000;
// データトークン待機 (0xFE)
do {
token = SPI_SendByte(0xFF);
timeout--;
} while ((token == 0xFF) && timeout);
if (token != 0xFE) return 0; // データトークンエラー
// データ受信
do {
*buff++ = SPI_SendByte(0xFF);
*buff++ = SPI_SendByte(0xFF);
} while (btr -= 2);
// CRC破棄 (2バイト)
SPI_SendByte(0xFF);
SPI_SendByte(0xFF);
return 1;
}
// データブロック送信
// 引数: buff = 送信バッファ、token = データトークン (0xFE: 通常, 0xFD: 終端)
// 戻り値: 1 = 成功、0 = 失敗
static int xmit_datablock(const BYTE *buff, BYTE token)
{
uint8_t resp;
uint16_t cnt = 512;
// カードReadyまで待機
if (!wait_ready()) return 0;
// データトークン送信
SPI_SendByte(token);
if (token != 0xFD) {
// データ送信 (512バイト)
do {
SPI_SendByte(*buff++);
SPI_SendByte(*buff++);
} while (cnt -= 2);
// ダミーCRC (2バイト)
SPI_SendByte(0xFF);
SPI_SendByte(0xFF);
// データレスポンス受信
resp = SPI_SendByte(0xFF);
if ((resp & 0x1F) != 0x05) return 0; // 書き込み受理確認
}
return 1;
}
disk_status関数とdisk_initialize関数
FatFsが要求する disk_status() および disk_initialize() 関数の実装例を以下に示す。
disk_initialize() 関数は、内部でSD_Init()を呼び出してSDカードを初期化する。
// ドライブステータス取得
// 引数: pdrv = 物理ドライブ番号 (0のみサポート)
// 戻り値: ディスクステータス
DSTATUS disk_status(BYTE pdrv)
{
if (pdrv != 0) return STA_NOINIT; // 物理ドライブ0のみサポート
return Stat;
}
// ドライブ初期化
// 引数: pdrv=物理ドライブ番号
// 戻り値: ディスクステータス
DSTATUS disk_initialize(BYTE pdrv)
{
if (pdrv != 0) return STA_NOINIT;
SPI_Init(); // SPIドライバを初期化
SD_Init(); // SDカードを初期化
return Stat;
}
disk_read関数
FatFsが要求する disk_read() 関数の実装例を以下に示す。
シングルブロック読み込みはCMD17、マルチブロック読み込みはCMD18を使用する。
// セクタ読み込み
// 引数: pdrv = 物理ドライブ番号、buff = 読み込みバッファ、sector = 開始セクタ番号、count = 読み込みセクタ数
// 戻り値: RES_OK = 成功、RES_ERROR = 失敗
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count)
{
uint32_t sect = (uint32_t)sector;
if (pdrv != 0 || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
// バイトアドレス変換 (ブロックアドレス対応でないカードはセクタ x 512)
if (!(CardType & CT_BLOCK)) sect *= 512;
if (count == 1) {
// シングルブロック読み込み: CMD17
if ((SD_SendCmd(CMD17, sect) == 0) && rcvr_datablock(buff, 512)) {
count = 0;
}
}
else {
// マルチブロック読み込み: CMD18
if (SD_SendCmd(CMD18, sect) == 0) {
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
SD_SendCmd(CMD12, 0); // 転送停止
}
}
SD_CS_HIGH();
SPI_SendByte(0xFF);
return (count ? RES_ERROR : RES_OK);
}
disk_write関数
FatFsが要求する disk_write() 関数の実装例を以下に示す。
シングルブロック書き込みはCMD24、マルチブロック書き込みはCMD25を使用する。
// セクタ書き込み
// 引数: pdrv = 物理ドライブ番号、buff = 書き込みデータ、sector = 開始セクタ番号、count = 書き込みセクタ数
// 戻り値: RES_OK = 成功、RES_ERROR = 失敗
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count)
{
uint32_t sect = (uint32_t)sector;
if (pdrv != 0 || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (Stat & STA_PROTECT) return RES_WRPRT;
if (!(CardType & CT_BLOCK)) sect *= 512;
if (count == 1) {
// シングルブロック書き込み: CMD24
if ((SD_SendCmd(CMD24, sect) == 0) && xmit_datablock(buff, 0xFE)) {
count = 0;
}
}
else {
// マルチブロック書き込み: CMD25
if (CardType & CT_SDC) SD_SendCmd(0x80 | 23, count); // ACMD23: 事前消去
if (SD_SendCmd(CMD25, sect) == 0) {
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) count = 1; // 終端トークン送信
}
}
SD_CS_HIGH();
SPI_SendByte(0xFF);
return (count ? RES_ERROR : RES_OK);
}
disk_ioctl関数 と get_fattime関数
FatFsが要求する disk_ioctl() および get_fattime() 関数の実装例を以下に示す。
RTCを搭載していない環境では、get_fattime() は固定のタイムスタンプを返す。
// デバイス制御コマンド実行
// 引数: pdrv = 物理ドライブ番号、cmd = 制御コマンド、buff = データバッファ
// 戻り値: RES_OK = 成功、RES_ERROR = 失敗
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{
DRESULT res;
uint8_t n,
csd[16];
uint32_t csize;
if (pdrv != 0) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
res = RES_ERROR;
switch (cmd) {
case CTRL_SYNC:
// 書き込み完了待ち
SD_CS_LOW();
if (wait_ready()) res = RES_OK;
break;
case GET_SECTOR_COUNT:
// 総セクタ数の取得
if ((SD_SendCmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) {
// SDv2 CSD
csize = csd[9] + ((uint16_t)csd[8] << 8) + ((uint32_t)(csd[7] & 0x3F) << 16) + 1;
*(DWORD*)buff = csize << 10;
}
else {
// SDv1 CSD
n = (csd[5] & 0x0F) + ((csd[10] & 0x80) >> 7) + ((csd[9] & 0x03) << 1) + 2;
csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 0x03) << 10) + 1;
*(DWORD*)buff = csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE:
// セクタサイズ (常に512バイト)
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
// 消去ブロックサイズ
*(DWORD*)buff = 128;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
SD_CS_HIGH();
SPI_SendByte(0xFF);
return res;
}
// タイムスタンプ用時刻取得 (RTCなし環境では固定値を返す)
// ビットフォーマット:
// [31:25] = 年 - 1980
// [24:21] = 月 (1-12)
// [20:16] = 日 (1-31)
// [15:11] = 時 (0-23)
// [10:5] = 分 (0-59)
// [4:0] = 秒 / 2 (0-29)
DWORD get_fattime(void)
{
return ((DWORD)(2024 - 1980) << 25) // 年: 2024
| ((DWORD)1 << 21) // 月: 1月
| ((DWORD)1 << 16) // 日: 1日
| ((DWORD)0 << 11) // 時: 0時
| ((DWORD)0 << 5) // 分: 0分
| ((DWORD)0 >> 1); // 秒: 0秒
}
参考リンク
- TI MSP430F149 製品ページ
- MSP430F149のデータシート、アプリケーションノート
- FatFs公式ページ
- FatFsモジュールのダウンロードとドキュメント
- FatFs Application Note
- ディスクI/O層の移植ガイド
- 関連ページ
- マイコン - FatFs - FatFsモジュールの概要、API一覧、ディスクI/Oインターフェースの説明
- マイコン - FatFsの設定 - ffconf.hの設定項目詳細とMSP430F149向け推奨設定
- MSP430F149 - FatFsの応用 - アプリケーション実装例とトラブルシューティング