米国のPJRC最新マイコン開発ボードである、Teensy4.0と4.1を試しています。高速・大容量でとてもいいと思います。
パフォーマンスはCoreMarkのベンチマークで圧倒的に高速。
中国生まれのESPを圧倒ですね。折柄、米中対立で?・・^^;
この小さなPCBによくもCortex-M7を搭載したものだと思います。しかも安いし。ただし、PJRCで直接買えば正価で安いのですが、EU向け以外は送料がかなりかさみます。日本ではRobotShop.comで買えば、日本法人があるのでそこそこの価格で入手できます(その他ではとんでもなく高価なのにびっくり)。とはいえ、いまだに品切れになりやすい状態。主な仕様は次のとおりですが、とにかく凄い。
通常は600MHzで動かしますが、オーバークロックで動かせばなんと1GHzを超える速度が可能です(その場合は冷却が必須ですが)。
使っていたTeensy3.5と比べても長足の進歩。ここ数年間はこの世界はESPに席巻されていたようでもあるし、これは大歓迎ですね。高速信号をとらえたりする用途もこれのみで楽にできそうです。
ハードウェアの作りの品質の良さは袋にまで及び "Assembled in USA"と誇らしげ(?)に書かれているのもいい感じですね。
次のPJRCのHP
から製品の情報をたどれば、使い方のチュートリアル等もしっかりあり、英語ではありますがとてもわかりやすく書かれています。
そして、ある時期のモデルからはArduino-IDEでの開発となりましたので楽ちんです。対応ライブラリーがまだ少ないのが少し残念ではありますが、勝手な電子工作としてはひるまずに使いたいマイコンです。
この記事ではTeensy4.0について簡単に紹介したいと思います。
これにIDEでプログラミングする際に、実行速度が指定できます。
0.9GHzからはクーリングが必須と書いてありますね。
600MHzだってハンパない速さです。これでどういう発熱をするのかを測ってみました。実行開始から10分後の発熱状況は次です。
室温25℃の机上で紙の上に置いて測りましたが、最高温度は46.7℃にちゃんと収まっています。ヘダーピンをつけてない状態ですが、つけてそれなりのソケットに刺せばさらに下がると思われ、安心しました。
このPCBは裏にもオプションがつけられるようになっています。
そのためヘダーピンをさっさととり付けるのがはばかられ、最初のうちはヘダーピンをつけずに接続してテストしていました(簡単・確実にブレッドボードにつける方法を考えついて^^。そのうちに別記事でご紹介しようと思います)。
まずは20x4のキャラクターLCD表示をしようとしましたが、Liquide Crystal_I2Cでコンパイルエラーがでます。その解決に回り道するのは時間が勿体ない^^;
I2Cデバイスの多くは、データシートをみながらライブラリーなしでコーディングしても簡単です。この場合は、前に次の記事に書いた通りで、確実に動くプロトコルがわかっています。
https://a-tomi.hatenablog.com/entry/2020/12/21/211852
そこに書いたとおり、20x4のI2C-4bit-LCDは、最小限で次のプロトコルを使えば簡単に動きます。
ついでに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オームでそれぞれプルアップします。次の写真のとおりです。
I2Cプロトコルは相手デバイス(I2Cスレーブ)の都合上、Defaultのボーレート100kHzで動かしていますが、その他の実行部分の速さ(Loop内実行所用時間の短さ)は、当然とはいえびっくりするほど速いです。念のためにI2Cの様子を覗くと、次のように時間的な乱れ全くなしでキッチリと動いています。
今後、速度や容量が要る用途には、これをどんどん使っていこうと思います。
追記:最近の世界のGoogle検索量を調べると、マイコン開発ボードではESP32の人気がなんとArduino Unoよりも高い状態となっています。
ESP32はWiFiやBluetoothを内蔵しているだけでなく、専用IDEなど開発環境が急速に充実しましたし、Arduino IDEへの対応も安定してきたのがその大きな理由かと推測。Teensy4.xも今後ぜひ躍進してほしいと思います。普及は、WiFi等の組込みというよりも、むしろ開発環境の進展次第かと勝手に思います。
以上、ごく簡単な紹介でしたが、なんらかのお役に立てば幸いです。
©2021 Akira Tominaga, All rights reserved.