勝手な電子工作・・

勝手なオリジナル電子工作に関する記事を書きます

快速マイコンの Teensy4.xを使ってみる

f:id:a-tomi:20210319203954j:plain

米国のPJRC最新マイコン開発ボードである、Teensy4.0と4.1を試しています。高速・大容量でとてもいいと思います。

パフォーマンスはCoreMarkのベンチマークで圧倒的に高速。

f:id:a-tomi:20210319204558j:plain

中国生まれのESPを圧倒ですね。折柄、米中対立で?・・^^;

この小さなPCBによくもCortex-M7を搭載したものだと思います。しかも安いし。ただし、PJRCで直接買えば正価で安いのですが、EU向け以外は送料がかなりかさみます。日本ではRobotShop.comで買えば、日本法人があるのでそこそこの価格で入手できます(その他ではとんでもなく高価なのにびっくり)。とはいえ、いまだに品切れになりやすい状態。主な仕様は次のとおりですが、とにかく凄い。

f:id:a-tomi:20210319204908j:plain

通常は600MHzで動かしますが、オーバークロックで動かせばなんと1GHzを超える速度が可能です(その場合は冷却が必須ですが)。

使っていたTeensy3.5と比べても長足の進歩。ここ数年間はこの世界はESPに席巻されていたようでもあるし、これは大歓迎ですね。高速信号をとらえたりする用途もこれのみで楽にできそうです。

f:id:a-tomi:20210319205341j:plain

ハードウェアの作りの品質の良さは袋にまで及び "Assembled in USA"と誇らしげ(?)に書かれているのもいい感じですね。

次のPJRCのHP

https://www.pjrc.com/

から製品の情報をたどれば、使い方のチュートリアル等もしっかりあり、英語ではありますがとてもわかりやすく書かれています。

そして、ある時期のモデルからはArduino-IDEでの開発となりましたので楽ちんです。対応ライブラリーがまだ少ないのが少し残念ではありますが、勝手な電子工作としてはひるまずに使いたいマイコンです。

この記事ではTeensy4.0について簡単に紹介したいと思います。

f:id:a-tomi:20210319210820j:plain

これにIDEでプログラミングする際に、実行速度が指定できます。

f:id:a-tomi:20210319210913j:plain

0.9GHzからはクーリングが必須と書いてありますね。

600MHzだってハンパない速さです。これでどういう発熱をするのかを測ってみました。実行開始から10分後の発熱状況は次です。

f:id:a-tomi:20210319211146j:plain

室温25℃の机上で紙の上に置いて測りましたが、最高温度は46.7℃にちゃんと収まっています。ヘダーピンをつけてない状態ですが、つけてそれなりのソケットに刺せばさらに下がると思われ、安心しました。

このPCBは裏にもオプションがつけられるようになっています。

f:id:a-tomi:20210319211828j:plain

そのためヘダーピンをさっさととり付けるのがはばかられ、最初のうちはヘダーピンをつけずに接続してテストしていました(簡単・確実にブレッドボードにつける方法を考えついて^^。そのうちに別記事でご紹介しようと思います)。

まずは20x4のキャラクターLCD表示をしようとしましたが、Liquide Crystal_I2Cでコンパイルエラーがでます。その解決に回り道するのは時間が勿体ない^^;

I2Cデバイスの多くは、データシートをみながらライブラリーなしでコーディングしても簡単です。この場合は、前に次の記事に書いた通りで、確実に動くプロトコルがわかっています。

https://a-tomi.hatenablog.com/entry/2020/12/21/211852

そこに書いたとおり、20x4のI2C-4bit-LCDは、最小限で次のプロトコルを使えば簡単に動きます。

f:id:a-tomi:20210319213117j:plain

ついでにRTC(リアルタイムクロック)もつけますが、そもそもTeensyのこのモデルにはRTCが最初からついています。しかしそのクロックの維持のためにPCB自体にバッテリーをつけておく必要があるので、当面はそれを使わず単体のRTCをつなぎます。今回はRTCを読み込むだけですが、時間を更新する際の簡単な方法は、前に次の記事に書きました。

回路不要のかんたんなRTCライター:Arduinoで - 勝手な電子工作・・

今回のスケッチは次です(2021.3.20追記:LCDへのEdit方法を少し改善したV02に差し替えておきます)。

/************************************************************
       Teensy 4.0 RTC and LCD Test via I2C - V02
         using Wire library (i2c_t3 is no more compatible)
         (c) 2021 Akira Tominaga, All rights reserved.
        Revisions:
          Initial version V00; 3/6, 2021
          V01; LCD interfaces to Roll-your-own      3/7,2021
          V02: writeString instead of writeLine    3/20,2021
 ************************************************************/
#include "Wire.h"       // Teensy Wire library
#define sclP 19         // SCL pin 19
#define sdaP 18         // SDA pin 18
#define aLCD 0x27       // LCD I2C addr
#define aRTC 0x68       // RTC I2C addr
// for LCD I2C 20x4
#define wT 12           // microsec between bytes to LCD
#define fL 20           // full-length for one line
// for Real Time Clock DS3231
byte  vR[8];            // values in RTC format
int vI[8] = { 0, 0, 0, 0, 0, 0, 0, 0};  // RTC data area
#define mdI 0           // Initial mode
#define mds 1           // ss mode
#define mdm 2           // mm mode
#define mdh 3           // hh mode
#define mdW 4           // WW=Day-of-Week mode
#define mdD 5           // DD mode
#define mdM 6           // MM mode
#define mdY 7           // YY mode
char  strYMDhms[20];    // editting area for RTC values

void setup() { // *** Teensy 4.x setup() ***
  Wire.setSCL(sclP);
  Wire.setSDA(sdaP);
  Wire.begin();
  Serial.begin(9600);
  while (!Serial) {}
  Serial.println(F("\nStarting"));
  iL();                 // initialize LCD
  Serial.println(F("LCD initialized"));
  // write strings to LCD               // *V02
  wS(0, 0, F("* Teensy 4.x Test *"));   // *V02
  wS(0, 1, F("with Real Time Clock"));  // *V02
  wS(3, 3, F("by Akira Tominaga"));     // *V02
}

void loop() { // *** Teensy 4.x loop() ***
  getTime();            // get time
  sC(1, 2);             // set cursor (1,2)
  for (uint8_t m = 0; m < 19; m++) {
    wC(strYMDhms[m]) ; // write characters
  }
  delay(100);
}

/********************************************************
     User defined fumctions
 ********************************************************/
// LCD - application program interfaces
// *** iL() *** initialize LCD ***
void iL(void) {
  wA(0x00);
  delay(100);
  wA(0x34);
  wA(0x30);
  delay(5);
  wA(0x34);
  wA(0x30);
  delayMicroseconds(200);
  wA(0x34);
  wA(0x30);
  delayMicroseconds(200);
  wA(0x24);
  wA(0x20);
  delayMicroseconds(200);
  wA(0x24);
  wA(0x20);
  wA(0x84);
  wA(0x80);
  delayMicroseconds(200);
  wA(0x04);
  wA(0x00);
  wA(0xC4);
  wA(0xC0);
  delayMicroseconds(150);
  wA(0x04);
  wA(0x00);
  wA(0x14);
  wA(0x10);
  delay(2);
  wA(0x04);
  wA(0x00);
  wA(0x64);
  wA(0x60);
  delayMicroseconds(150);
  wA(0x08);
  delay(1);
}
// *** wS(column,row,string) *** write String (x,y,string) ***
void wS(uint8_t col, uint8_t row, String strC) { // *V02
  sC(col, row);         // set cursor to (x,y)
  uint8_t sLen = strC.length();
  Serial.print("("); Serial.print(col); Serial.print(",");
  Serial.print(row); Serial.print(") "); Serial.println(strC);
  if (col + sLen > fL) {
    Serial.println(F("*** Too long & truncated."));
    sLen = fL - col;    // shorten sLen to avoid line-over 
  }
  for  (uint8_t nChr = 0; nChr < sLen; nChr++) {
    wC(strC.charAt(nChr));
  }
}
// *** sC(column,line) *** set cursor to (x,y) ***
void sC(uint8_t Col, uint8_t Line) {
  uint8_t Pos;
  switch (Line) {
    case 0: Pos = 128 + Col; break;
    case 1: Pos = 192 + Col; break;
    case 2: Pos = 148 + Col; break;
    case 3: Pos = 212 + Col; break;
    default: Serial.println("Err for LCD line");
      while (true) {}
  }
  wA((Pos & 0xF0) | 0x0C);
  wA((Pos & 0xF0) | 0x08);
  wA(((Pos << 4) & 0xF0) | 0x0C);
  wA(((Pos << 4) & 0xF0) | 0x08);
  delayMicroseconds(wT);
}
// *** wC(char) *** write a character to LCD ***
void wC(byte Chr) {
  wA((Chr & 0xF0) | 0x0D);
  wA((Chr & 0xF0) | 0x09);
  wA(((Chr << 4) & 0xF0) | 0x0D);
  wA(((Chr << 4) & 0xF0) | 0x09);
  delayMicroseconds(wT);
}
// *** cS() *** clear LCD screen ***
void cS() {
  wA(0x0C);
  wA(0x08);
  wA(0x1C);
  wA(0x18);
  delay(1);
}
// for LCD control
// *** wA(byte) *** write an absolute byte to LCD ***
void wA(byte Abs) {
  Wire.beginTransmission(aLCD);
  Wire.write(Abs);
  Wire.endTransmission();
}

// Real Time Clock -  application program interfaces
// *** getTime() *** get RTC MMDD & hhmm into strMDhm
void getTime(void) {
  rRTC();             // read RTC device
  cR2I();             // convert RTC-format to Integers
  sprintf(strYMDhms, "20%02d-%02d-%02d %02d:%02d:%02d", vI[mdY], vI[mdM], vI[mdD], vI[mdh], vI[mdm], vI[mds]);
}
// for RTC control
// *** rRTC() *** read Real-Time-Clock
void rRTC(void) {
  Wire.beginTransmission(aRTC);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(aRTC, 7);
  while (Wire.available() < 7) {}  // wait for data
  for (int i = 1; i < 8; i++) {
    vR[i] = Wire.read();
  }
  Wire.endTransmission();
}
// *** cR2I() *** convert RTC-format to Integers
void cR2I(void) {
  vI[mds] = ((vR[mds] & B01110000) / 16) * 10 + (vR[mds] & B00001111);
  vI[mdm] = ((vR[mdm] & B01110000) / 16) * 10 + (vR[mdm] & B00001111);
  vI[mdh] = ((vR[mdh] & B00100000) / 32) * 20 + ((vR[mdh] & B00010000) / 16) * 10 + (vR[mdh] & B00001111);
  vI[mdW] = vR[mdW];
  vI[mdD] = ((vR[mdD] & B01110000) / 16) * 10 + (vR[mdD] & B00001111);
  vI[mdM] = ((vR[mdM] & B00010000) / 16) * 10 + (vR[mdM] & B00001111);
  vI[mdY] = ((vR[mdY] & B11110000) / 16) * 10 + (vR[mdY] & B00001111);
}
// *** End of program ***

 ブレッドボードの配線は主にI2Cだけですが、3.3V専用のTeensy4.0は5Vトレランシーがありません。よってI2C 信号レベルコンバーターをつけます。また、SCL, SDAを外部の4.7kオームでそれぞれプルアップします。次の写真のとおりです。

f:id:a-tomi:20210319222336j:plain

 I2Cプロトコルは相手デバイス(I2Cスレーブ)の都合上、Defaultのボーレート100kHzで動かしていますが、その他の実行部分の速さ(Loop内実行所用時間の短さ)は、当然とはいえびっくりするほど速いです。念のためにI2Cの様子を覗くと、次のように時間的な乱れ全くなしでキッチリと動いています。

f:id:a-tomi:20210319223213j:plain

 

今後、速度や容量が要る用途には、これをどんどん使っていこうと思います。

以上、ごく簡単な紹介でしたが、なんらかのお役に立てば幸いです。

 

©2021 Akira Tominaga, All rights reserved.