はじめに
自宅のオーブンの温度を測ろうと思ってM5Stack core2を使おうと思ったのですが,意外とM5Stack core2のSPIについての記事がないと思ったので書き留めておきます.
MAX31855とは
SPI接続で熱電対を利用して温度を測定することができるICです.
今回は秋月電子さんで買えるこちらのモジュールを使用しました.
端子はそれぞれ,
Vin->5V
3Vo->未接続
GND->GND
DO->MISO
CS->自分で設定したGPIO(今回は19)
CLK->SCK に接続します.
BUSのGPIOを整理する
まず,M5Stackとcore2でBUSに引き出されているGPIOが変更されているようです.
SPIのSSに指定しようとするピン番号に注意が必要です.
M5Stack Core2の公式ドキュメントページは以下.
SPIについて
サンプルコードなど色々調べた結果,どうやらBUSに引き出されているSPIのピンは「VSPI」モジュールのようです.
普段UARTばかり使っているので,受信関数は何だろうと疑問に思ったのですが,SPIはReceive_data = transfer(Send_data);
でデータの送受信を同時に行えるんですね.
SPIの使い方は,
#include <SPI.h> /*SPIヘッダーをインクルード*/
#define MAXCS 19 /*SSピンをGPIO19に*/
static const int spiClk = 1000000; /*クロックを1MHzに*/
SPIClass * thermo_spi = NULL /*SPIクラスを格納するポインタを準備*/
void setup(){
thermo_spi = new SPIClass(VSPI); /*インスタンスを生成*/
thermo_spi -> begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, MAXCS); /*指定したピンでSPI通信を確立*/
pinMode(MAXCS, OUTPUT); /*begin()でSSピンを指定しても自動でピンモードを設定してくれないので手動で設定する.*/
}
/*通信コード*/
void measurement(){
/* beginTransaction()でSPI通信の開始を宣言して,
* digitalWrite()でSSピンを手動で操作する.
* transfer()でデータの送受信を行い,
* digitalWrite()でSSピンを元に戻してから,
* endTransaction()でSPI通信を終了する. */
thermo_spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(MAXCS, LOW);
/*今回はMAX31855に合わせて32bit通信用関数を使用.
*もちろん普通の8bit通信のtransfer()を4回呼び出してもいい.
*MAX31855は受信機能はないので,送るデータは何でもいい. */
receive_data = thermo_spi->transfer32(0x32241608);
digitalWrite(MAXCS, HIGH);
thermo_spi->endTransaction();
}
出来上がったプログラムの画面はこんな感じです.
「℃」の表示をどうしようと考え,オー'o'とシー'C'を無理やりくっつければいいやと雑に考えた割には,それっぽいものを作ることができて満足しています.
バッテリーの残量表示もあったほうがいいな~と作った後から思い立ったのでさりげなく追加してあります(笑)
おわりに
今回初めてM5Stackを使いました.
コンパクトでケーシングもあって,ディスプレイもついていてすごく便利ですね.
SPIライブラリの中身を見たら,ちょうど32bit転送命令transfer32()
があったので使用してます.
関連リンク
SPIの理解には,こちらのページを参考にさせていただきました.
ソースコード
作ったプログラムのソース全文は以下です.
サンプリング周期を可変できるようにしようとか,考えていたけどやめたりしていてちょっと無駄な内容とかもありますが…
どなたかのお役に立つことを願って<(_ _)>
#include <SPI.h>
#include <M5Core2.h>
#include <Wire.h>
#include <AXP192.h>
// Define ALTERNATE_PINS to use non-standard GPIO pins for SPI bus
#ifdef ALTERNATE_PINS
#define VSPI_MISO 2
#define VSPI_MOSI 4
#define VSPI_SCLK 0
#define VSPI_SS 33
#define HSPI_MISO 26
#define HSPI_MOSI 27
#define HSPI_SCLK 25
#define HSPI_SS 32
#else
#define VSPI_MISO MISO //38
#define VSPI_MOSI MOSI //23
#define VSPI_SCLK SCK //18
#define VSPI_SS SS //4
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15
#endif
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define VSPI FSPI
#endif
static const int spiClk = 1000000; // 1 MHz
SPIClass * thermo_spi = NULL;
//thermocouple
#define MAXCS 19
double temperature;
int32_t receive_data=0;
void measuerment(){
//use the SPI buses
thermo_spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(MAXCS, LOW); //pull SS slow to prep other end for transfer
receive_data = thermo_spi->transfer32(0x32241608);
digitalWrite(MAXCS, HIGH); //pull ss high to signify end of data transfer
thermo_spi->endTransaction();
double internal = (recieve_data >> 4) & 0x7FFF;
internal *= 0.0625;
if((receive_data >> 4) & 0x800) internal *= -1;
if( receive_data & 0x80000000){
receive_data = 0xFFFFC000 | ((receive_data >> 18) & 0x00003FFF);
}else{
receive_data >>= 18;
}
temperature = receive_data;
temperature *= 0.25;
}
//timer interrupt
hw_timer_t *timer = NULL;
volatile int interruptCounter;
volatile bool update_admit = 0;
volatile uint16_t intervalTime=10;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer(){
portENTER_CRITICAL_ISR(&timerMux);
interruptCounter++;
update_admit = 1;
portEXIT_CRITICAL_ISR(&timerMux);
}
//battery
AXP192 power;
float bat=0;
#define LOW_BATTERY 3.65
#define LOW_THRESHOLD 0.53
void update_temperature(){
M5.Lcd.setTextSize(4);
M5.Lcd.drawFloat(temperature,2,10,30);
M5.Lcd.setCursor(270,30);
M5.Lcd.setTextSize(1);
M5.Lcd.print('o');
M5.Lcd.setTextSize(4);
M5.Lcd.print('C');
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(300,45);
if(interruptCounter % 2){
M5.Lcd.print('.');
}else{
M5.Lcd.print(' ');
}
M5.Lcd.setCursor(0, 0);
}
void setup() {
// put your setup code here, to run once:
M5.begin();
M5.Lcd.setTextSize(4);
//timer
timer = timerBegin(0,80,true);//1 micro sec
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 1000000, true);
timerAlarmEnable(timer);
//spi
thermo_spi = new SPIClass(VSPI);
thermo_spi->begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, MAXCS);
pinMode(MAXCS, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
M5.update();
if(update_admit){
measuerment();
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(240,0);
bat = power.GetBatVoltage();
bat = bat - LOW_BATTERY;
//M5.Lcd.printf("%3.2f V", power.GetBatVoltage());
if(bat < 0){
M5.Lcd.print("0 %%");
}else{
M5.Lcd.printf("%3.1f %%", (bat / LOW_THRESHOLD)*100);
}
update_temperature();
portENTER_CRITICAL_ISR(&timerMux);
update_admit = 0;
portEXIT_CRITICAL_ISR(&timerMux);
}
if(interruptCounter > (intervalTime-1)){
portENTER_CRITICAL_ISR(&timerMux);
interruptCounter=0;
portEXIT_CRITICAL_ISR(&timerMux);
}
delay(100);
}