RP2040にTM1637を接続する

最終更新日:2022/4/20

RP2040に表示機能を追加します。まずは時刻表示を実現するためにTM1637が搭載された7セグ4桁のLEDボード(モデル名不明)を接続し動作確認したいと思います。

配線ですが、Groveのデジタルコネクタを利用します。Pinアサインを設定すれば融通が利くのですが、SPI0にNICを接続することを検討しておりGPIO20,21はちょっと避け、今回はGPIO18,19に接続します。

Grove Groveケーブル配色 XH4P
@ GND   B GND
A VCC   C 5V
B D2   A DIO
C D1   @ SCK

インクルードするライブラリはSeeed-Studioのものが良さそうです。
https://github.com/Seeed-Studio/Seeed_Grove_4Digital_Display_g

/*
 * 2022年3月23日 T.Wanibe
 * for 4-Digit Display
 * 最大2093056バイトのフラッシュメモリのうち、スケッチが59208バイト(2%)を使っています。
 * 最大262144バイトのRAMのうち、グローバル変数が11156バイト(4%)を使っていて、ローカル変数で250988バイト使うことができます。
 */
#include        "TM1637.h"
#define         CLK     18
#define         DIO     19
int             loopCount       = 0;
int             count           = 0;
int8_t          NumTab[]        = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};      //0~9,A,b,C,d,E,F
int8_t          ListDisp[4];
TM1637          tm1637(CLK,DIO);
//----------------
void setup(){
        Serial.begin(115200);
        while (!Serial) {
                ;                       // シリアルポートが接続するのを待ちます。 ネイティブUSBポートにのみ必要
        }
        pinMode(LED_BUILTIN, OUTPUT);
        digitalWrite(LED_BUILTIN, HIGH);
        tm1637.init();
        tm1637.set(BRIGHT_TYPICAL);     //BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
        Serial.println(F("EndSetup"));
}
//----------------
void loop(){
        if(loopCount % 2){
                digitalWrite(LED_BUILTIN, HIGH); 
        }else{
                digitalWrite(LED_BUILTIN, LOW);
        }
        unsigned char   i       = 0;
        unsigned char   count   = 0;
        delay(150);
        while(count < 16){
                i = count;
                count ++;
                //if(count == sizeof(NumTab)) count = 0;
                for(unsigned char BitSelect = 0;BitSelect < 4;BitSelect ++){
                        ListDisp[BitSelect] = NumTab[i];
                        i ++;
                        if(i == sizeof(NumTab)) i = 0;
                }
                tm1637.display(0,ListDisp[0]);
                tm1637.display(1,ListDisp[1]); 
                tm1637.display(2,ListDisp[2]);
                tm1637.display(3,ListDisp[3]);
                delay(300);
        }
        Serial.println(loopCount++);
}


時刻表示をするためにはRTCとTimer割込を追加する必要があります。
まずは、適当な基準時刻をRTCにセットし、更新される時刻を7セグ4桁のLEDに表示します。

/*
 * 2022年3月24日 T.Wanibe
 * Timeというサンプルスケッチの動作確認を行う Earle F. Philhower、III氏によるスケッチ
 * 
 * 現在時刻の設定と印刷の簡単なデモンストレーション
 * 最初の時間は、RTC、NTP、またはユーザーから取得する必要があります
 * Earle F. Philhower、IIIによってパブリックドメインにリリースされました<earlephilhower@yahoo.com>
 * 最大2093056バイトのフラッシュメモリのうち、スケッチが64172バイト(3%)を使っています。
 * 最大262144バイトのRAMのうち、グローバル変数が11372バイト(4%)を使っていて、ローカル変数で250772バイト使うことができます。
 */
#include        <time.h>
#include        <sys/time.h>
#include        "TM1637.h"
#define         CLK     18
#define         DIO     19
struct          timeval tv;
struct          tm *t;
time_t          now;
char            buff[80];
int             hour,minute,second;
int             loopCount;
int8_t          TimeDisp[] = {0x00,0x00,0x00,0x00};
TM1637          tm1637(CLK,DIO);
//---------------
void setup() {
        Serial.begin(115200);
        while(!Serial);                         //シリアルオープンを待つ
        tv.tv_sec = 1642734855;                 // 1611198855   = Jan 21, 2021  3:14:15AM ...RPi Pico Release;
                                                // 1642734855   = 2022/3/24 00:00:00
                                                //GPSから時刻を読み取る
        tv.tv_usec = 0;
        settimeofday(&tv, nullptr);             //RTCに時刻をセットする
        pinMode(LED_BUILTIN, OUTPUT);
        digitalWrite(LED_BUILTIN, HIGH);
        tm1637.init();
        tm1637.set(BRIGHT_TYPICAL);             //BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
        Serial.println(F("EndSetup"));
}
//---------------
void loop() {
        time(&now);
        t=localtime(&now);
        hour    =       t->tm_hour;
        minute  =       t->tm_min;
        second  =       t->tm_sec;
        sprintf(buff,"%02d:%02d:%02d", hour,minute,second);
        if(!second || !loopCount){
                TimeDisp[0] = hour / 10;
                TimeDisp[1] = hour % 10;
                TimeDisp[2] = minute / 10;
                TimeDisp[3] = minute % 10;
        }
        if(++loopCount % 2){
                digitalWrite(LED_BUILTIN, HIGH); 
                tm1637.point(POINT_ON);
        }else{
                digitalWrite(LED_BUILTIN, LOW);
                tm1637.point(POINT_OFF);
        }
        tm1637.display(TimeDisp);
        Serial.println(buff);
        delay(1000);
}

期待通りの結果になるはずです。


GPSから取得する時間をRTCにセットし、更新される時刻を7セグ4桁のLEDに表示します。
※最終的にはSTM32miniShieldで構築したGPS-NTPserverプロジェクトをRP2040に移植したいと思っています。その前段過程作業となります。

RP2040には内部時計(RTC)があります。このクロック精度が悪いです。200ppmという結果が出ています。
この値は一般的なRTCchipの10倍のブレです。多分STM32F1よりも悪いようです。
200ppmズレているとなると1時間に1回GPSによる時刻校正をしたいところです。
また、内部RTCのアクセス方法も要検討ですね。表示のためにTimer割込で1秒毎に表示更新したいです。

/*
 * 2022年3月24日 T.Wanibe
 * UARTに接続したGPSから *GxRMC のセンテンスを解析して時刻情報を取得しRTCにセットする
 * GPSの解析はライブラリを使わず独自のモノにする。
 * Timeというサンプルスケッチの動作確認を行う Earle F. Philhower、III氏によるスケッチ
 * 
 * 現在時刻の設定と印刷の簡単なデモンストレーション
 * 最初の時間は、RTC、NTP、またはユーザーから取得する必要があります
 * Earle F. Philhower、IIIによってパブリックドメインにリリースされました<earlephilhower@yahoo.com>
 * 最大2093056バイトのフラッシュメモリのうち、スケッチが71828バイト(3%)を使っています。
 * 最大262144バイトのRAMのうち、グローバル変数が11784バイト(4%)を使っていて、ローカル変数で250360バイト使うことができます。
 */
#define TIMER_INTERRUPT_DEBUG         1
#define _TIMERINTERRUPT_LOGLEVEL_     4
#include        <time.h>
#include        <sys/time.h>
#include        "TM1637.h"
#include        "RPi_Pico_TimerInterrupt.h"
#define         CLK             18
#define         DIO             19
#define         DELIMITER       ","
#define         debug           1
#define         TIMER0_INTERVAL_MS      1000
struct          timeval         tv;
struct          timezone        tz;
struct          tm              *tt;                                     //STM32dinoとRP2040の構造体が異なる
time_t          now;
char            buff[80];
int             year            = 2022;
byte            month           = 3;
byte            day             = 24;
byte            hour            = 13;
byte            minute          = 00;
byte            second          = 00;
int             loopCount;
int8_t          TimeDisp[] = {0x00,0x00,0x00,0x00};
char            chkHader[]      = "RMC";                        //このバージョンから3文字に変更
float           timezone        = 9.0;                          //TimeZone 日本は+9です。
const uint8_t   daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };//const or compiler complains
const unsigned long seventyYears = 2208988800UL;                // NTP時刻(1900)とUNIX時刻(1970)のオフセット(2208988800秒)70年間の秒数
String          nmea;
tm              tmm;
/*
        構造体 tm はtime.h の中で宣言され、以下の情報を含みます.STM32dinoとRP2040の構造体が異なる
        struct tm {
          int tm_sec;      // 秒 [0-61] 最大2秒までのうるう秒を考慮
          int tm_min;      // 分 [0-59]
          int tm_hour;     // 時 [0-23]
          int tm_mday;     // 日 [1-31]
          int tm_mon;      // 月 [0-11] 0から始まることに注意
          int tm_year;     // 年 [1900からの経過年数]
          int tm_wday;     // 曜日 [0:日 1:月 ... 6:土]
          int tm_yday;     // 年内の通し日数 [0-365] 0から始まることに注意
          int tm_isdst;    // 夏時間が無効であれば 0
        };
*/
long            gLoopCount      = 3;                                    //1時間毎に更新予定 最初は1分後に更新したくて初期値を調整
bool            GpsEnable       = false;
unsigned long   baseMilis       = millis();
bool            toggle0         = false;
bool            started         = false;
TM1637          tm1637(CLK,DIO);
RPI_PICO_Timer  ITimer0(0);                                             //Init RPI_PICO_Timerは、0?15の疑似ハードウェアタイマーを使用できます。
//-------------タイマ割込で呼ばれるルーチン
bool    TimerHandler0(struct repeating_timer *t){ 
        if (!started){
                started = true;
                pinMode(LED_BUILTIN, OUTPUT);
        }
#if (TIMER_INTERRUPT_DEBUG > 0)
        Serial.print(F("ITimer0 called, millis() = ")); Serial.println(millis());
#endif
        time(&now);
        tt      =       localtime(&now);
        hour    =       tt->tm_hour;
        minute  =       tt->tm_min;
        second  =       tt->tm_sec;
        sprintf(buff,"\n%04d/%02d/%02d %02d:%02d:%02d", tt->tm_year,tt->tm_mon+1,tt->tm_mday,hour,minute,second);
        Serial.println(buff);
        TimeDisp[0] = hour / 10;
        TimeDisp[1] = hour % 10;
        TimeDisp[2] = minute / 10;
        TimeDisp[3] = minute % 10;
        if(toggle0)     tm1637.point(POINT_ON);
        else            tm1637.point(POINT_OFF);
        tm1637.display(TimeDisp);
        //タイマー割り込みはピンLED_BUILTINを切り替えます
        digitalWrite(LED_BUILTIN, toggle0);
        toggle0 = !toggle0;
        return true;
}
//---------------NTP since 1900/01/01 retouch2020/12/25
static unsigned long int numberOfSecondsSince1900Epoch(uint16_t y, uint8_t m, uint8_t d, uint8_t h, uint8_t mm, uint8_t s,float timezone) {
        //2036年問題が存在する。unsigned long intの桁溢れが発生
        //桁溢れが発生したら受信側が対応する必要がある。サーバ側では64bit化するべきだが動かなくなる。
        //timezoneをfloat扱いにした
        int leapAdjustment = 0;
        if (y >= 1970) {
                y -= 1970;
                leapAdjustment  = 2;                            // LM: 1970 was NOT a leap year!
        }
        uint16_t days = d;
        for (uint8_t i = 1; i < m; ++i)         days += pgm_read_byte(daysInMonth + i - 1);
/*
        if (m > 2 && y % 4 == 0)                ++days;
*/
        if (m > 2 && (y + leapAdjustment) % 4 == 0)     ++days; // LM: Weak but okay for the present
        days += 365 * y + (y + 3) / 4 - 1;
        long offsetTime = long((float(h)- timezone) * 3600.0);
        return days*24L*3600L + offsetTime + mm*60L + s + seventyYears;
}
//------------------
bool readSensor(void){
        if (Serial1.available()) {
                Serial1.readStringUntil('\n');                  //読み捨てて先頭から読めるようにする
                nmea = Serial1.readStringUntil('\n');
#if debug
                Serial.println(nmea);
#endif
                //$GNRMC,022757.000,A,3430.62587,N,13316.66175,E,0.63,144.24,231220,,,A,V*02
                String  nmeames = nmea.substring(3,6);           // $GP***
                int     count   = 0;
                while(!nmeames.equals(chkHader)){
                       nmea     = Serial1.readStringUntil('\n');
                       nmeames  = nmea.substring(3,6);          // $GP***
                       //digitalWrite(LED2, count++%2);
#if debug
                       Serial.print(nmea);Serial.print(F("\t"));Serial.println(nmeames);
#endif
                        if(count>30){
                                //digitalWrite(LED1, HIGH);
                                break;
                        }
                }
                int     nextPos = 0;
                if ( nmeames.equals(chkHader)){                                 // get UTC
                        nextPos         = nmea.indexOf(DELIMITER);//int indexOf(char ch, unsigned int fromIndex) const;
                        String shh      = nmea.substring(nextPos+1,nextPos+3);
                        String smm      = nmea.substring(nextPos+3,nextPos+5);
                        String sss      = nmea.substring(nextPos+5,nextPos+7);
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip UTC
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip Status
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip 緯度
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip 北緯か南緯か
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip 経度
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip 東経か西経か
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip 地表における移動の速度[knot]
                        nextPos         = nmea.indexOf(DELIMITER,nextPos+1);    //skip 地表における移動の真方位[deg]
                        String sdd      = nmea.substring(nextPos+1,nextPos+3);
                        String smn      = nmea.substring(nextPos+3,nextPos+5);
                        String syy      = nmea.substring(nextPos+5,nextPos+7);
                        Serial.println("20" + syy + smn + sdd + shh + smm + sss);
                        tt->tm_year     = syy.toInt() + 2000 - 1970;
                        if ((tt->tm_year < 31) || (tt->tm_year > 100)){               //2001~2079をサポート
                                return false;
                        }else{
                                tt->tm_hour      = shh.toInt();
                                hour            = tt->tm_hour;
                                tt->tm_min       = smm.toInt();
                                minute          = tt->tm_min;
                                tt->tm_sec       = sss.toInt();
                                second          = tt->tm_sec;
                                tt->tm_mday      = sdd.toInt();
                                day             = tt->tm_mday;
                                tt->tm_mon       = smn.toInt();
                                month           = tt->tm_mon;
                                year            = syy.toInt() + 2000;
                                return true;
                        }
                }else{
                        //digitalWrite(LED1, HIGH);
                        return false;
                }
        }
        return false;                   //要らないはずだけどコンパイルエラー回避のため
}
//------------------GPSにアクセスし、取得に成功したら内部時間を更新
bool ReadGPS(void){
        bool valid      = readSensor();
        Serial.println(valid);
        if(valid) {
                if(gLoopCount>60 | !GpsEnable){
                        //rtclock.setTime(tmm);                 // 時刻の設定 一時間毎に更新
                        tv.tv_sec       = numberOfSecondsSince1900Epoch(year,month,day,hour,minute,second,-9.0);
                        tv.tv_usec      = 0;
                        Serial.println((unsigned long)(tv.tv_sec));
                        settimeofday(&tv, nullptr);             //RTCに時刻をセットする
                        baseMilis  = millis();                  //20201224追加
                        gLoopCount = 1;
                        GpsEnable      = true;
                }
                valid      = true;
        }else{
                delay(100); 
                if(gLoopCount%60)      Serial.print(F("."));
                else{
                        Serial.println(F("."));
                        //GpsEnable      = false; 
                }
        }
        if(gLoopCount > 0x7FFFFFF0)     gLoopCount = 1;
        return valid;
}
//---------------
void setup() {
        Serial.begin(115200);
        //while(!Serial);                         //シリアルオープンを待つ
        tv.tv_sec = 1611198855;                 // 1611198855   = Jan 21, 2021  3:14:15AM ...RPi Pico Release;
                                                // 1642734855   = 2022/3/24 00:00:00
                                                //GPSから時刻を読み取る
        tv.tv_usec = 0;
        settimeofday(&tv, nullptr);             //RTCに時刻をセットする
        pinMode(LED_BUILTIN, OUTPUT);
        digitalWrite(LED_BUILTIN, HIGH);
        tm1637.init();
        tm1637.set(BRIGHT_TYPICAL);             //BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
        Serial.print(F("\nStarting TimerInterruptTest on ")); Serial.println(BOARD_NAME);
        Serial.println(RPI_PICO_TIMER_INTERRUPT_VERSION);
        Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz"));
        //マイクロ秒単位の間隔
        if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)){
                Serial.print(F("Starting ITimer0 OK, millis() = ")); Serial.println(millis());
        }else{
                Serial.println(F("Can't set ITimer0. Select another freq. or timer"));
        }
        Serial.flush(); 
        Serial.println(F("\nStart UART0"));
        Serial1.begin(9600);
        Serial.println(F("EndSetup"));
}
//---------------
void loop() {
        if (Serial1.available()){
                bool valid      = ReadGPS();
        }
}



戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。