STM32F103を使ってみる

SPI2とSPIのRemap


Arduino STM32で使えそうなSD-CARD Libraryはいくつかあります。
@ https://github.com/stm32duino/STM32SD
A https://github.com/greiman/SdFat
B https://github.com/jbeynon/sdfatlib

探せばもっとあるかもしれません。
@とBはサンプルスケッチのコンパイルで、コンパイルエラーとなります。
Aのライブラリが安定して動きます。
そこで、このライブラリを使って、SPI2の使い方と、SPIのRemapを紹介します。


SPI2を使う

EthernetモジュールやSPI-TFTのライブラリは、SPI標準ポート(PA4 PA5 PA6 PA7)を使います。
SPI2を使うとSPI標準ポートを空けることができるので、SDカードをEthernetモジュールやSPI-TFTと一緒に使うには好都合で す。
以下のコードでSPI2(PB12 PB13 PB14 PB15)に繋いだSD-CARD内のファイル一覧を表示します。
#include <SPI.h>
#include <SdFat.h>  // https://github.com/greiman/SdFat

// Use second SPI port
/*
 * MOSI --- PB15
 * MISO --- PB14
 * SCK ---- PB13
 * CS ----- PB12
 */
SPIClass SPI_2(2);
SdFat sd(&SPI_2);
// SdFatEX sd(&SPI_2);

const uint8_t SD_CS = PB12;   // chip select for sd

//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------



void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.println("SdFat_ListDir_STM32");

  //Serial.print(F("FreeStack: "));
  //Serial.println(FreeStack());

  // initialize the second card
  //if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) {
  if (!sd.begin(SD_CS)) {
    sd.initError("sd:");
  }

  sd.ls(LS_DATE | LS_SIZE | LS_R);
}

void loop() {
}



上の例ではls関数を使ってお手軽に済ませていますが、ls関数を使わずに自力でやる場合は以下のようになります。
結果はls関数と同じになります。
/*
 * Example use of two SPI ports on an STM32 board.
 * Note SPI speed is limited to 18 MHz.
 */
#include <SPI.h>
#include <SdFat.h>      // https://github.com/greiman/SdFat

// Use second SPI port
/*
 * MOSI --- PB15
 * MISO --- PB14
 * SCK ---- PB13
 * CS ----- PB12
 */
SPIClass SPI_2(2);
SdFat sd(&SPI_2);
// SdFatEX sd(&SPI_2);

const uint8_t SD_CS = PB12;   // chip select for sd

// Number of files found.
uint16_t n = 0;

// Max of ten files since files are selected with a single digit.
const uint16_t nMax = 100;

// Position of file's directory entry.
uint16_t dirIndex[nMax];


//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------

/*
 * Function to print all timestamps.
 */
void printTimestamps(SdFile& f) {
  dir_t d;
  if (!f.dirEntry(&d)) {
    return;
  }

  //Serial.print("Creation: ");
  //f.printFatDate(d.creationDate);
  //Serial.print(" ");
  //f.printFatTime(d.creationTime);

  //Serial.print("Modify: ");
  f.printFatDate(d.lastWriteDate);
  Serial.print(" ");
  f.printFatTime(d.lastWriteTime);

  //Serial.print("Access: ");
  //f.printFatDate(d.lastAccessDate);
  Serial.print(" ");
}

void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.println("SdFat_ListDir");

  // initialize the second card
  //if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) {
  if (!sd.begin(SD_CS)) {
    sd.initError("sd2:");
  }

  // List files in root directory.
  if (!dirFile.open("/", O_READ)) {
    sd.errorHalt("open root failed");
  }
  while (n < nMax && file.openNext(&dirFile, O_READ)) {

    // Skip directories and hidden files.
    if (!file.isSubDir() && !file.isHidden()) {

      // Save dirIndex of file in directory.
      dirIndex[n] = file.dirIndex();
#if 0
      // Print the file number and name.
      Serial.print(n);
      Serial.write(' ');
      file.printName(&Serial);
      Serial.println();
#endif
      n++;
    }
    file.close();
  }

  for(int i=0;i<n;i++) {
    if (!file.open(&dirFile, dirIndex[i], O_READ)) {
      sd.errorHalt(F("open"));
    }
    //file.timestamp(&file);
    printTimestamps(file);

    //Serial.print("open ok ");
    char buf[60];
    uint32_t fsz = file.fileSize();
    char fname[20];
    file.getName(fname,20);
    sprintf(buf,"%10d %s",fsz,fname);
    //Serial.println("fname[" + String(i) + "]=" + String(fname));
    Serial.println(buf);
    file.close();
    Serial.flush();
  }
}

void loop() {
}


SPIをRemapして使う

こちらに BluePillのPinOutが公開されています。
これを見るとPA15 PB3 PB4 PB5がSPIとして使えそうです。

こち らでSPIの Remapの方法が紹介されています。
以下のスケッチでSPIのピンをRemapして使用することができます。
SCK PA5→PB3
MISO PA6→PB4
MOSI PA7→PB5
SS PA4→PA15

#include <SPI.h>
#include <SdFat.h>  // https://github.com/greiman/SdFat

// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
// Use second SPI port
SdFat sd;
//SdFatEX sd(2);
SdFile file;
//const uint8_t SD_CS = PB12;   // chip select for sd2
const uint8_t SD_CS = PA15;   // chip select for sd

//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------



void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.println("SdFat_ListDir_STM32");

/*
 * CS ----- PA15
 * SCK ---- PB3
 * MISO --- PB4
 * MOSI --- PB5
 */

  afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY); // release PB3 and PB5
  afio_remap(AFIO_REMAP_SPI1); // remap SPI1  
  gpio_set_mode(GPIOB, 3, GPIO_AF_OUTPUT_PP);
  gpio_set_mode(GPIOB, 4, GPIO_INPUT_FLOATING);
  gpio_set_mode(GPIOB, 5, GPIO_AF_OUTPUT_PP);  

  // initialize the second card
  if (!sd.begin(SD_CS, SD_SCK_MHZ(18))) {
  //if (!sd.begin(SD_CS)) {
    sd.initError("sd:");
  }

  sd.ls(LS_DATE | LS_SIZE | LS_R);
}

void loop() {
}

ただしRemapしてもPA5 PA6 PA7を普通のディジタルポートとして使うことができません。
以下のスケッチではこれらのポートのLチカが正しく動きません。
#include <SPI.h>
#include <SdFat.h>  // https://github.com/greiman/SdFat


// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
// Use second SPI port
SdFat sd;
//SdFatEX sd(2);
SdFile file;
//const uint8_t SD_CS = PB12;   // chip select for sd2
const uint8_t SD_CS = PA15;   // chip select for sd

//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------

int pins[] = { PA4, PA5, PA6, PA7 };
int numpins;


void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.println("SdFat_ListDir_STM32");


/*
 * CS ----- PA15
 * SCK ---- PB3
 * MISO --- PB4
 * MOSI --- PB5
 */

  afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY); // release PB3 and PB5
  afio_remap(AFIO_REMAP_SPI1); // remap SPI1  
  gpio_set_mode(GPIOB, 3, GPIO_AF_OUTPUT_PP);
  gpio_set_mode(GPIOB, 4, GPIO_INPUT_FLOATING);
  gpio_set_mode(GPIOB, 5, GPIO_AF_OUTPUT_PP);  

  // initialize the second card
  if (!sd.begin(SD_CS, SD_SCK_MHZ(18))) {
  //if (!sd.begin(SD_CS)) {
    sd.initError("sd:");
  }


  numpins=sizeof(pins)/4;
  Serial.print("numpins=");
  Serial.println(numpins);
  for (int i=0;i<numpins;i++) {
    pinMode(pins[i], OUTPUT);
    digitalWrite(pins[i], LOW);
  }

}

void loop() {
  static int pin=0;

  sd.ls(LS_DATE | LS_SIZE | LS_R);

  Serial.print("pin=");
  Serial.println(pins[pin]);
  for (int i=0;i<numpins;i++) {
    digitalWrite(pins[i], LOW);
  }
  digitalWrite(pins[pin], HIGH);
  pin++;
  if (pin == numpins) pin=0;
  delay(1000);

}

どうも、SDカードライブラリ内部で、毎回これらのピンのモードを変えてしまうようです。
以下のようにSDカードのアクセスが終わるたびに、ピンモードを変えてやれば、
PA5 PA6 PA7を普通のディジタルポートとして使うことができます。
#include <SPI.h>
#include <SdFat.h>  // https://github.com/greiman/SdFat


// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
// Use second SPI port
SdFat sd;
//SdFatEX sd(2);
SdFile file;
//const uint8_t SD_CS = PB12;   // chip select for sd2
const uint8_t SD_CS = PA15;   // chip select for sd

//------------------------------------------------------------------------------
// print error msg, any SD error codes, and halt.
// store messages in flash
#define errorExit(msg) errorHalt(F(msg))
#define initError(msg) initErrorHalt(F(msg))
//------------------------------------------------------------------------------

int pins[] = { PA4, PA5, PA6, PA7 };
int numpins;


void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.println("SdFat_ListDir_STM32");


/*
 * CS ----- PA15
 * SCK ---- PB3
 * MISO --- PB4
 * MOSI --- PB5
 */

  afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY); // release PB3 and PB5
  afio_remap(AFIO_REMAP_SPI1); // remap SPI1  
  gpio_set_mode(GPIOB, 3, GPIO_AF_OUTPUT_PP);
  gpio_set_mode(GPIOB, 4, GPIO_INPUT_FLOATING);
  gpio_set_mode(GPIOB, 5, GPIO_AF_OUTPUT_PP);  
  //Serial.print(F("FreeStack: "));
  //Serial.println(FreeStack());


  // initialize the second card
  if (!sd.begin(SD_CS, SD_SCK_MHZ(18))) {
  //if (!sd.begin(SD_CS)) {
    sd.initError("sd:");
  }

#if 0
  numpins=sizeof(pins)/4;
  Serial.print("numpins=");
  Serial.println(numpins);
  for (int i=0;i<numpins;i++) {
    pinMode(pins[i], OUTPUT);
    digitalWrite(pins[i], LOW);
  }
#endif
}

void loop() {
  static int pin=0;

  sd.ls(LS_DATE | LS_SIZE | LS_R);

  numpins=sizeof(pins)/4;
  Serial.print("numpins=");
  Serial.println(numpins);
  for (int i=0;i<numpins;i++) {
    pinMode(pins[i], OUTPUT);
    digitalWrite(pins[i], LOW);
  }

  Serial.print("pin=");
  Serial.println(pins[pin]);
  for (int i=0;i<numpins;i++) {
    digitalWrite(pins[i], LOW);
  }
  digitalWrite(pins[pin], HIGH);
  pin++;
  if (pin == numpins) pin=0;
  delay(1000);

}



いろいろRemapして使う

こ ちらにAlternate Function Input/Output(AFIO)関数が公開されています。

afio_remap()を使うと幾つかの機能をRemapすることができます。

一部のボードでは PA15 PB3 PB4 はデフォルトでJTAGにアサインされています。
afio_cfg_debug_ports(AFIO_DEBUG_NONE)
でJTAG機能を無効にすることができます。
JTAG機能を無効にすると、PA15 PB3 PB4 がGPIOとして使えるようになります。
JTAG機能を無効にしてもPA11 PA12 は使えません。

続く...