Vol.930 7.Nov.2025

単3/単4サイズの1.5Vリチウム充電電池 「ハードウェアを安全に取り外し」アイコン MCP25625CANトランシーバー

L 単3/単4サイズの1.5Vリチウム充電電池

by fjk

 携帯テレビ・ラジオに単4ニッケル水素電池を使っているが、電圧が1.2Vで使用時間も短かった。最近、単4(又は単3)サイズで充電式のリチウム電池が発売されており、電圧が1.5Vで長持ちしそうなので、Amazonで「SANPIMA単4リチウム充電電池 1.5V 1300mWh×8個セット」(2,790円)をポチり。
 早速充電してみると、8個の内1個は1.3Vまでしか充電できなかったが、他の7個は1.5Vまで充電できた。電池8個とケース兼用充電器でこの価格、消耗品なので1/8の初期不良品は(低電圧でも使えそうなので)仕方ないのかな・・。
 さらに、マウス用に単3リチウム充電電池(同価格、8個共不良品無し)もクーポン有りで買っちゃった。


充電式リチウム電池とUSB-C(要2A)充電器
   
(リチウム電池)    (ニッケル水素電池)
満充電電池のレベルメーター表示が異なる


H 「ハードウェアを安全に取り外し」アイコンが表示されない

by fjk

 Windows コンピューターで 、USB メモリ等を適切に取り外さないと、データが失われる可能性があります。こんな時には、「ハードウェアを安全に取り外し」アイコンで、「取り外し」を選ぶと、取り出しが可能か(windows10では)メッセージで教えてくれました。
 しかし、Windows 11の初期設定では、このアイコンは通知領域に表示されていないため、接続した周辺機器を確認して取り外す場合には、隠れているアイコンを表示させる必要があります。
  1. 「設定/個人用設定/タスクバー」を選択
     
  2. 「その他のシステムトレイアイコン」をクリック
     
  3. 「ハードウェアを安全に取り出してメディアを取り出す」を「オン」にする。
【参考】
「ハードウェアを安全に取り外してメディアを取り出す」アイコンが表示されない場合


P PICでCAN通信(4) 〜MCP25625CANトランシーバー

by fjk

 前報(abc929)まで利用したCANモジュールはMCP2515+TJA1441ATを使用しているが、メーカーではMCP2515の使用を、現在は推奨していない。そこで、CANコントローラーとドライバーを1チップに収納したCANトランシーバーMCP25625秋月#112663、@400)を使ってみた【ノード6】。なお、MCP25625はSSOP28パッケージなので、DIP変換基板も用いた(手持ちのダイセン電子製を利用したが、秋月#110438、@50も利用可)。ここではPIC16F18326を使用したが、PIC16F18325でもOK(printfを使わなければもっと余裕)。
 MCP25625のコントーラ機能はMCP2515と同じなので、abc929で使用したライブラリーをそのまま使用することができる。
 また、CANだけでなく、I2Cを使ってADT7410Mによる温度データの取得と、AQM0802AによるLCD表示も行った(ADT7410の使い方はabc897などを参照)。温度データは1秒ごとに測定し、CANバスにそのデータをアスキー文字列で送信している。
 CANバスには、ノード6の他に、ノード1*、ノード2*ノード5(CANバスモニター)を接続(*は終端抵抗120Ω付き)。

【ハード】


ノード6回路図(PIC16F18326、MCP25625、ADT7410、AQM0802A)

ブレッドボード(BB)配線状況
★Xtalのリード線は細く、そのままBBに刺すと
 緩いので、L型ピンヘッダーにハンダ付けして
 2.54mmピッチのピンにした。
 [Xtalは秋月#117135(@150/5)使用]

【ソフト】  MCC設定


Pin Module

System Module

TMR0 Module

MSSP1(SPI) Module

MSSP2(I2C) Module

EUSART Module

Interrupt Module

Exitint

ノード6のプログラムリスト

【テスト結果】

@ノード6で測定した温度をCANで送信(1行目はADTの16進数生データ)
Aノード5(CANバスモニター)はASCモードで、温度データ取得・表示
Bノード1は温度データを16ビット整数として取得・表示

CANテスト風景全体(CANケーブルは青黄のツイストペアー)

【トラブルと注意点】

  1. これだけのセットとなると、1本のUSBバスパワーだけでは電力不足となり、各ユニット毎に電源を供給する必要がある。また、動作安定用として、バイパスコンデンサや電解コンデンサを追加した。
  2. 当初、ノード6の温度測定は正常に動作したが、何故かCAN通信ができなかった。マニュアルを詳細に読むと、MCP25625のSTBY端子をLow(Gnd)にする必要があることが判り、解決した。
  3. 電源投入後、ノード2がフリーズ(LEDが点滅しない)することがあった。どうもCANバスエラーのようで、全てのCANノードをバスに接続後、再起動すると正常に動作した。


≪≪ ノード6 ≫≫   ▼abc930-18326.c(zip)

/***********************************(abc930-18326.c)*****
 *      CAN test (MCP25625)         <ノード6>        *
 ********************************************************/

#include "myProject.h"
#include "myFunction.h"
#include "mcp_can_dfs.h"
#include "skMCP25xx.h"
#include "i2c_LCD_lib.h"

#define  ADT_ADR    0x48            // ADT I2Cアドレス

#define  EU_BFSIZE  120
#define  MBF_SIZE    64             // メッセージ用BFサイズ

/***** グローバル変数 ***************************/
//--- タイマー用変数
uint8_t  TFlg;                      // タイマーフラグ

//-----  EUSARTシリアル用
char     RBuf[EU_BFSIZE];           // 受信文字Buffer
uint8_t  SFlg;                      // 受信フラグ

//---- 汎用データ処理用
char     Msg[MBF_SIZE];             // コメント等送信用文字列

//---- CAN用共通変数の宣言
char     flagRecv = 0 ;             // CAN受信有無フラグ
uint8_t  R_len = 0 ;                // CAN受信データサイズ
char     RcvBuf[9];                 // CAN受信データ
uint8_t  S_len = 0 ;                // CAN送信データサイズ
char     SndBuf[9];                 // CAN送信データ
uint8_t  T_Snd = 1;                 // 温度送信フラグ

/************************************************
  *  タイマ0 Callback関数 (1秒周期割り込み)      *
 ************************************************/
void TMR0_Process(void){
    TFlg = 1;                       // タイマーフラグセット
}

//----  MCP2515のINTピン割り込み
void IOCAF2_Process(void){
     flagRecv = 1 ;                 // CAN割込有りフラグセット
}

/************************************************/
//---- 温度データの計算
uint16_t calc_Temp(char *bf, uint16_t tDat){
    uint16_t tp;
    if(tDat & 0x8000){                      // 負数なら
        tp = ((8192 - (tDat >> 3)) * 10) / 16;
        sprintf(bf,"-%2d.%1dC", tp / 10, tp % 10);
    }else{                                  // 正数
        tp = ((tDat >> 3) * 10) / 16;
        sprintf(bf,"%3d.%1dC", tp / 10, tp % 10);
    }
    return tp;
}

//---- 温度データの表示 ----
uint16_t prt_Temp(uint16_t tDat){
    uint16_t  tp;
    sprintf(Msg,"Tmp:%04X",tDat);
    EU_Write(Msg);
    LCD_clr(); LCD_str(Msg);
    tp = calc_Temp(SndBuf, tDat);
    EU_Puts(SndBuf);
    LCD_cursor(1,1); LCD_str(SndBuf);
    return tp;
}

//--- 温度データの取得
uint16_t get_ADT7410(void){
    uint16_t temp;
    I2C2_ReadNBytes(ADT_ADR,(uint8_t *)&temp,2); // 温度データ読込
    temp = (temp<<8) | ((uint8_t)(temp>>8)); // 上下バイトデータ入替
    prt_Temp(temp);
    return temp;
}

/*------------------------------------------------*
 *  CAN受信の処理                                 *
 *------------------------------------------------*/
void CAN_Receive(void){
    union {  unsigned char c[2] ;
             unsigned int  i ;
          }   data ;
    unsigned long id ;

    while (CAN_MSGAVAIL == mcp_checkReceive()) {
        // 受信したメッセージを読み込む,
        mcp_readMsgBuf(&R_len, (uint8_t *)RcvBuf);
        // データフレームなら処理する
        if (mcp_isRemoteRequest() == 0) {
            id = mcp_getCanId() ;
            switch (id) {
                case 0x123 :
                    data.c[0] = RcvBuf[0] ;
                    data.c[1] = RcvBuf[1] ;
                    sprintf(Msg,"%03X:%8d",(uint16_t)id,data.i);
                    EU_Puts(Msg);       //LCD_str(buf);
                    break ;
                default :               // その他の識別子ID
                    LCD_str(" Unknown") ;
                    break ;
            }
        }
    }
}

//---- CAN送信実施(標準、フレーム送信済み待ち)
/*		uint32_t  id:	11ビット識別子
 *		uint8_t	*buf:	送信するデータ列アドレス
 *		uint8_t  len:	送信するバイト数* 
 *-----------------------------------------*/
void CAN_Send(uint32_t id, uint8_t *buf, uint8_t n){
    uint8_t res;
    if(n == 0)
        res = mcp_sendMsgBuf(id,CAN_STDID,CAN_RMTFRM,0,buf,1) ;
    else
        res = mcp_sendMsgBuf(id,CAN_STDID,CAN_DTFRM,n,buf,1) ;
    sprintf(Msg,"%x:%d",id,res);
    EU_Puts(Msg);
}

/*******************************************************
 *    Main application
 *******************************************************/
void main(void)
{
    char cmd;
    uint8_t *p;
    // initialize the device
    SYSTEM_Initialize();
    SSP1CON1bits.SSPEN = 1;
    LCD_init();

    TMR0_SetInterruptHandler(TMR0_Process); // Timer0 Callback関数
    IOCAF2_SetInterruptHandler(IOCAF2_Process);	// Pin割込CallBack

    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    // MCP2515によるCAN通信の初期化
    // CANバス通信速度=100Kbps  MCP25625のクロック=16MHz
    while (CAN_OK != mcp_begin(CAN_100KBPS,MCP_16MHz)) {
         LCD_str("InitFail") ;
         while(1) ;                 // 終了
    } 
    LCD_str("Init OK!") ;
    // MASK0->Filter0->RXB0(オーバフローでRXB1)のみ使用します。
    mcp_init_Mask(0, 0, 0x3ff) ;    // 全て受付る
    mcp_init_Filt(0, 0, 0x123) ;    // ID:0x123のみ受け取る
    // MCP2515のINTピン割り込みの設定
    IOCAN2 = 1 ;                    // RA2で立下げエッジ割込み
    IOCAF2 = 0 ;                    // RA2割込フラグをクリア
    IOCIF  = 0 ;                    // 割込フラグをクリア
    IOCIE  = 1 ;                    // 状態変化割り込みを有効に
		
    EU_Puts("--- Ready\n"); // LCD_str("Ready");

    while (1){
        if(TFlg){                   // タイマー割込有り?
            TFlg = 0;
            get_ADT7410();
            S_len = (uint8_t)strlen(SndBuf);
            if(T_Snd)               // 温度送信モードなら
                CAN_Send(0x123, (uint8_t *)SndBuf, S_len);
        }
        if(flagRecv) {              // CAN割込有り?
            flagRecv = 0 ;
            CAN_Receive();          // CAN受信データ取得
        }
        if(SFlg){                   // シリアル入力有り?
            EU_Puts(RBuf);
            cmd = RBuf[0];
            switch(cmd){
                case 'T': T_Snd ^= 1;
                          break;    // 温度送信モード変更
                case 'R': CAN_Send(0x123,p,0);
                          break;    // CANリモート出力
                case 'S': CAN_Send(0x149,(uint8_t *)(RBuf+1),1);
                          break;    // 一文字CAN送信
            }
            SFlg = 0;
       }
    }
}
/*********** end of file ********************/
<シリアルコマンド>
:  温度データを「CAN送信する/しない」を変更
:  id = 0x123 でリモートフレーム送信
str: コマンドに続く1文字を id = 0x149 で送信

【その他のプログラム】

mcp_can_dsf.h(zip)
skMCP25xx.c/h(zip)
myProject.h(zip)
myFunction.c/h(zip)
i2c_LCD_lib.c/.h(zip)
※ EUSARTの設定についてはabc917等を参照


【おまけ】(11/9追加)
 str コマンドで、1文字だけでなく、(最大8文字の)文字列をCAN送信できるようにした。
 
   ▼abc930-18326S.zip(解凍したファイル名[abc930-18326.c]は同じなので、置き換えること)

● 追加・変更したところを赤文字で記載。
≪≪ ノード6(S) ≫≫  

//---  <ここまで省略(追加・変更なし)>

/*******************************************************
 *    Main application
 *******************************************************/
void main(void){
    char cmd;
    uint8_t *p, n;
    // initialize the device
    SYSTEM_Initialize();
    SSP1CON1bits.SSPEN = 1;

//---  <途中省略(追加・変更なし)>

        if(SFlg){                                       // シリアル入力有り?
            EU_Puts(RBuf);
            cmd = RBuf[0];
            p = (uint8_t *)RBuf;
            n = (uint8_t)strlen(RBuf) - 1;
            if(n > 8) n = 8;
            switch(cmd){			
                case 'T': T_Snd ^= 1;            break;	// 温度送信モード変更
                case 'R': CAN_Send(0x123,p,0);   break; // リモート送信
                case 'S': CAN_Send(0x149,p+1,n); break; // 文字列CAN送信
            }
            SFlg = 0;
        }
    }
}
/*********** end of file ********************/


【おまけ(2)】(11/18追加)
 USB接続なしでも操作できるよう、以下のスイッチを押すと、以下の動作をするようにした。

  • 青スイッチ(RA3)を押すと、温度送信モードを変更
  • 白スイッチ(RA0)を押すと、id=149に、押すたびに、'0'と'1'文字を交互にCAN送信。
 ▼abc930-18326W.zip(解凍したファイル名[abc930-18326.c]は同じなので、置き換えること)
 
● 追加・変更したところを赤文字で記載。
≪≪ ノード6(W) ≫≫  

//---  <ここまで省略(追加・変更なし)>

/*******************************************************
 *    Main application
 *******************************************************/
void main(void){
    uint8_t d[] = { 0x30, 0x40 };
    char cmd;

//---  <途中省略(追加・変更なし)>

        if(flagRecv) {
            flagRecv = 0 ;                  // 割り込みフラグをクリア
            CAN_Receive();                  // CAN受信データを取得・処理
        }
        if(IO_RA3_GetValue()==0){           // RA3が押されていたら
            T_Snd ^= 1;                     // 温度送信モード変更
            while(IO_RA3_GetValue() == 0);  // キーが離れるまで待つ
            __delay_ms(100);
        }
        if(SW0_GetValue()==0){              // SW0が押されていたら
            d[0] ^= 1;                      // ビット0を反転
            CAN_Send(0x149,d,1);            // データ1文字CAN送信
            while(SW0_GetValue() == 0);     // キーが離れるまで待つ
            __delay_ms(100);
        }
        if(SFlg){                           // シリアル入力有り?


//---  <以後追加・変更なし>

PIN_Module[RA3を汎用入力に]
 
  RA3(MCLR)を汎用入力にする方法はabc928を参照


【おまけ(3)】(11/21追加)
 CAN送信IDを変更できるよう、Xコマンドを追加した。

hhh
hhhは0x無しの16進数
 ▼abc930-18326X.zip(解凍したファイル名[abc930-18326.c]は同じなので、置き換えること)
 
● 追加・変更したところを赤文字で記載。
≪≪ ノード6(X) ≫≫  

//---  <ここまで省略(追加・変更なし)>

//---- CAN用共通変数の宣言
char     flagRecv = 0 ;                 // CAN受信有無フラグ
uint8_t  R_len = 0 ;                    // CAN受信データサイズ
char     RcvBuf[9];                     // CAN受信データ
uint8_t  S_len = 0 ;                    // CAN送信データサイズ
char     SndBuf[9];                     // CAN送信データ
uint8_t  T_Snd = 1;                     // 温度送信フラグ
uint32_t S_id = 0x149;                  // 送信id


//---  <途中省略(追加・変更なし)>

            switch(cmd){			
                case 'T': T_Snd ^= 1;             break; // 温度送信モード変更
                case 'R': CAN_Send(S_id,p,0);     break; // リモートCAN送信
                case 'S': CAN_Send(S_id,p+1,n);   break; // 文字列CAN送信
                case 'X': S_id = my_xtol(RBuf+1); break; // 送信id設定
            }
            SFlg = 0;
        }
    }
}
/*********** end of file ********************/


※プログラムのリストをハイライト無しのスタイルで見る場合はここをクリック


※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。


単3/単4サイズの1.5Vリチウム充電電池 「ハードウェアを安全に取り外し」アイコン MCP25625CANトランシーバー