PICマイコン(PIC16F1827)のリセット要因と使用例

組み込みエンジニア

こんにちは、ENGかぴです。

PICマイコンなどソフト開発していると何らかの要因でマイコンがリセットし調査が必要になることがあります。リセット要因の特定の一例としてPIC16F1827のブラウンアウト・ソフトウェア及びWDTエラーによるリセットの動作確認を行いました。

LCDはAQM1602XA-RN-GBW(秋月電子)を使用しています。

PIC16F1827のリセット

PIC16F1827のリセットについてMCCでレジスタの設定を行います。MCCの使い方については下記記事にまとめています。

MPLAB Code Configurator(MCC)の追加と使い方

今回はMCCを使用してSystem Module・TMR0・MSSP1の設定を行っています。LCDの動作は下記記事を流用しています。

PICマイコン(PIC16F1827)のI2C通信を実装する

System Moduleの設定

最初に共通事項であるSystem Moduleの設定を行います。MPLAB X IDEを起動しMCCのアイコンをクリックしてMCCを有効にします。

System Moduleの設定(MCC)

内部クロックを使用するためINTOSCを選択しています。クロック周波数は任意でもよいですが4MHz_Hzを選択しています。クロックを高速にするほど消費電流が増えてしまいます。

WDTの設定(MCC)
WDTの設定(MCC)

WDTの設定を行います。WDTが動作するタイミングは任意でよいですが4秒程度にしています。4秒CLRWDT()によってWDTがリフレッシュされなかった場合はWDTが動作してソフトウェアリセットがかかります。

WDTのモードが4種類ありますがWDTの動作を常にONしたいためWDT_enabledを選択しています。WDT_enabledを選択するとSWDTENビットの設定は無効になります。

CONFIG1及びCONFIG2の設定(MCC)
CONFIG1及びCONFIG2の設定(MCC)

ブラウンアウトリセットを使用するためCONFIG1のBORENビットでBrown-out Reset enabledを選択しています。電源立ち上げ時に遅延を持たせるためPWRTEビットをPWRT enabledを選択しています。WDTを使用するためWDTEビットをWDT enabledを選択しています。

ブラウンアウトリセットのリセット条件を2.5Vにするためhigh trip point selectedを選択しています。MCLRを使用するためMCLREをMCLR/VPP pin function is MCLRを選択しています。

リセットのコールドスタートとホットスタートの意味

コールドスタートとホットスタートの説明
コールドスタートとホットスタートの説明

電源をONしてマイコンの動作がスタートした場合はマイコンが動作したばかりなので熱を持っていないためコールドスタートと表現しています。

電源をONしている状態でマイコンがリスタートした場合はマイコンが動作していた状態なので熱を持っているためホットスタートと表現しています。

マイコンがユーザーが予期していない状態でリセット状態になると致命的な動作不良につながることから意図したリスタートでない限りリスタートの要因を調査する必要があります。

PICマイコンではリスタートしたときにリセット要因を特定する目安としてSTATUSレジスタPCONレジスタを確認してリセット時の状況を確認することができます。

if( PCONbits.nBOR == 0 && PCONbits.nPOR == 0 ){
    PCONbits.nPOR = 1;   //パワーオンリセットをクリア(発生すると0)
    PCONbits.nBOR = 1;   //ブラウンアウトリセットをセット(発生すると0)
    PCONbits.nRMCLR = 1;   //MCLRクリア(発生すると0) 
}
else{ //ホットスタート
    if( PCONbits.nBOR == 0){
        starttyp.bits.bor = 1; //ブラウンアウトリセットでリセット
        PCONbits.nBOR = 1;   //ブラウンアウトリセットをセット(発生すると0)
    }
    if( PCONbits.nPOR == 0 ){
        starttyp.bits.por = 1;
        PCONbits.nPOR = 1;   //パワーオンリセットをクリア(発生すると0)
    }
    if( PCONbits.nRI == 0){ 
        starttyp.bits.ri = 1;
        PCONbits.nRI = 1;
    }
    if( PCONbits.nRMCLR == 0){ 
        starttyp.bits.rmclr = 1;
        PCONbits.nRMCLR = 1;
    }       
    if( PCONbits.STKOVF == 1 ){ //スタックオーバー
        starttyp.bits.so = 1;
        PCONbits.STKOVF = 0;
    }
    if( PCONbits.STKUNF == 1 ){ //スタックアンダーフロー
        starttyp.bits.su = 1;
        PCONbits.STKUNF = 0;
    }
    starttyp.bits.hot = 1; //ホットスタートしたことをセット
}

電源をONしたときはPCONレジスタの/BORおよび/PORビットは0になります。BORとPORを使用する場合はユーザーで1をセットすることが必要なのでコールドスタート時に1をセットします。

WDTによるリスタートやRESET()によってソフトウェアリセットがかかった場合はホットスタートになるためPCONレジスタのビットを確認してリセットの要因を格納します。

PCONレジスタ以外のSTATUSレジスタで確認するビットは/TOと/PDになりますが/PDはスリーブを使用しないため/PDは変化しないため集約してビットを立てて確認しています。

リセットの動作を確認する

リセットの要因のうちWDTによるリセット・RESET()によるリセット・ブラウンアウトリセット・/MCLRピンによるリセット動作を確認します。リセット要因は下記の構造体で対象のビットを立てて発生したことを確認します。

typedef union{
    uint8_t byte;
    struct{
        uint8_t dmy  :1; //LSB
        uint8_t hot  :1;
        uint8_t so  :1;
        uint8_t su  :1;
        uint8_t rmclr  :1;
        uint8_t ri  :1;
        uint8_t bor  :1;
        uint8_t por :1; //MSB
    }bits;
}START_TYP;

動作確認用の回路図

リセットの動作確認の回路図
リセット動作確認の回路図

SW1を押すとWDTCLR()がコールされないようになり4秒程度経過するとWDTが働いてリセットがかかるようにします。SW2を2秒間押し続けるとRESET()をコールしてソフトウェアリセットがかかるようにします。

/MCLRの確認はVDDに接続している配線をGND(DC5Vのマイナス側)に接続するとリセットがかかります。VR1の可変抵抗で電圧を減圧していくとPICマイコンの電源が低下していきブラウンアウトリセットがかかります。

WDT及びRESET()によるリセット

if( DiData[1].di == 1){
    CLRWDT(); //WDTをリフレッシュ
}
if( timreswait == TIME_UP){
    timreswait = TIME_OFF;
    RESET(); //ソフトウェアリセット
}

SW1を押すとDiData[1].diが0になるためCLRWDT()がコールされなくなりWDTがリフレッシュされない期間が経過するとリセット条件となりリスタートします。SW2を長押しして2秒経過するとRESET()によってソフトウェアリセットがかかります。

WDTとRESET()の動作確認
WDTとRESET()の動作確認

SW1を長押するとWDTによるリセットがかかりHOTスタートになります。その後SW2を長押しするとRESET()関数がコールされHOTスタートとなりますが、PCONレジスタの/RIビットがセットされているためRESET()によるリスタートであることが分かります。

LCDの一段目のSTART TYPEの値は34(10進数)であり、2進数に置き換えてSTART_TYP構造体を確認するとhotビットとriビットが立っていることが分かります。

/MCLRピンの確認

/MCLRピンはLアクティブなのでMCLRピンがLの時はマイコンにリセット状態になります。マイコンを動作させるためにはMCLRピンをHにする必要があります。/MCLRピンがHとなりリセットが解除されプログラムが動作します。

MCLRの動作確認
MCLRの動作確認

電源が切断されていないリセットになるためホットスタートとなりPCONレジスタの/RMCLRビットが0になっているためMCLRピンによるリセットであることが分かります。

LCDの一段目のSTART TYPEの値は18(10進数)であり、2進数に置き換えてSTART_TYP構造体を確認するとhotビットとrmclrビットが立っていることが分かります。

ブラウンアウトリセットの確認

ブラウンアウトリセットは電源電圧がブラウンアウトリセット電圧よりも低くなった時にマイコンのリセットをかけるものです。PIC16F1827のブラウンアウトリセット電圧は約2.5Vもしくは2.0Vになっています。

System設定で2.5Vにしているため電源が2.5V付近になるとブラウンアウトリセットがかかります。ブラウンアウトリセットの確認は電源に可変抵抗VR1で分圧した電圧を印加することで確認します。

BORの動作確認
BORの動作確認

電源がOFFしていないリセットになるためホットスタートとなりPCONレジスタの/BORビットが0になっているためブラウンアウトリセットであることが分かります。

START TYPE横の値は十進数で66であるため2進数に置き換えた時START_TYP構造体のhotビットとborビットが立っていることが分かります。

ブラウンアウトリセット電圧を使用すると数百uA消費電流が増えてしまいます。系統電源が取れる場合は消費電流の増加は気になりませんが、エナジーハーベストなどの微弱な電源である場合、数百uAの消費電流は大きなものになります。

外付けのリセットICを使用して/MCLRピンをコントロールすることで2.5V付近でリセットがかかる仕組みを検討したほうが良いこともあります。

ブラウンアウトリセットよりもリセットICの方が良い場合
ブラウンアウトリセットよりもリセットICの方が良い場合

例ではTPS3839G25(TI製)を使用していますが、低消費で150nA程度となるのでブラウンアウトリセットよりも低消費でリセットとなります。

リセットICを使用すると部品とコストが増えてしまうため電源の容量がシビアでない限りはブラウンアウトリセットで十分です。

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/examples/i2c1_master_example.h"

#define TIME_OFF -1         //タイマーを使用しない場合
#define TIME_UP 0           //タイムアップ
#define DI_NUM 4            //DIフィルタのサンプリング数
#define DI_NO  2
#define	CNT_INIT_MAX 10     //10ms×10 = 100ms
#define TIME_FILTER_MAX 1
#define LCD_TIM_MAX 10
#define RES_MAX 200

#define SLAVE_ADRS 0x3E
#define LINE1_ADRS 0x40
#define LINE2_TOP (0x40 +0x80)
#define FUNC1_SET 0x38
#define FUNC2_SET 0x39
#define INT_OSC 0x14
#define CONST_SET 0x77 //0x73
#define PWR_ICON_SET 0x54 //0x56
#define FOLLOWER_SET 0x6C
#define CLR_DISP 0x01
#define DISP_ONOFF_SET 0x0C

typedef struct{
	uint8_t    wp;
	uint8_t    buf[ DI_NUM ];
    uint8_t    di;
}FILT_DATA;

typedef union{
    uint8_t byte;
    struct{
        uint8_t dmy  :1; //LSB
        uint8_t hot  :1;
        uint8_t so  :1;
        uint8_t su  :1;
        uint8_t rmclr  :1;
        uint8_t ri  :1;
        uint8_t bor  :1;
        uint8_t por :1; //MSB
    }bits;
}START_TYP;

uint8_t CntInit;	//初期化時のみ使用
FILT_DATA DiData[DI_NO];
int8_t timfilter;   //DIフィルタ起動
int16_t timlcd;
int16_t timreswait = TIME_OFF;
uint8_t btncnt;
uint8_t btncntInit;
bool btn1hold;
uint8_t initmoji[2][16] ={"PIC16F1827-RES","         Ver1.00"};
START_TYP starttyp;

void mainTimer(void);
void mainApp(void);
void DiFilterInit(void);
void DiFilter(void);
void LcdInit(void);
void DspLine2Top(void);
void DspClear(void);
void DispSet(void);
void SysStart(void);

/* Main application */
void main(void)
{
    SYSTEM_Initialize();
    TMR0_SetInterruptHandler(mainTimer);
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
    
    SysStart();  //スタートの状態判断
    DiFilterInit();
    LcdInit();
    timreswait = TIME_OFF;
    
    while (1)
    {
        mainApp();
        DiFilter();        
    }
}
/* メインアプリ処理 */
void mainApp(void){
    
    if( DiData[1].di == 1){
        CLRWDT();
    }
    
    if( DiData[0].di == 1){
        timreswait = TIME_OFF;
    }
    else{
        if( timreswait == TIME_OFF ){
            timreswait = RES_MAX;
        }
    }
    
    if( timlcd == TIME_UP ){
        timlcd = LCD_TIM_MAX;
        DispSet(); //LCD表示部分をセット
    }
    
    if( timreswait == TIME_UP){
        timreswait = TIME_OFF;
        RESET();
    }
}
/* タイマ管理関数 */
void mainTimer(void){
            
    if( timlcd > TIME_UP ){
        --timlcd;
    }
    if( timfilter > TIME_UP ){
        --timfilter;
    }
    if( timreswait > TIME_UP ){
        --timreswait;
    }
}
// DIフィルタの初期化 //
void DiFilterInit(void){
	
    CntInit = CNT_INIT_MAX;
	while(CntInit > 0){  //0になるまでフィルタを実施
		DiFilter();         //DIフィルタ処理
        __delay_ms(10);     //10ms遅延させてDIフィルタ処理
        CntInit--;
		timfilter = TIME_UP;
	}    
}
//--------SWのDIフィルタ----------------------------
void DiFilter(void){
    uint8_t i;
    uint8_t no;
    bool    boo;

    if( timfilter == TIME_UP ){
        timfilter = TIME_FILTER_MAX;

        for( no = 0; no < DI_NO; no++){
            switch(no){
                case 0: //PB2
                    DiData[no].buf[ DiData[no].wp ] = IO_RB2_GetValue();
                    break;
                case 1: //PB3
                    DiData[no].buf[ DiData[no].wp ] = IO_RB3_GetValue();
                    break;
            }
            boo = true;
            for( i = 1; i < DI_NUM; i++){
                if( DiData[no].buf[i-1] != DiData[no].buf[i] ){
                    boo = false;
                    break;
                }
            }

            if( boo ){ ////比較して一致したら値を採用
                 DiData[no].di = DiData[no].buf[ 0 ]; 
            }

            if( ++DiData[no].wp >= DI_NUM){
                    DiData[no].wp = 0;
            }
        }
    }
}
/* LCDの初期化*/
void LcdInit(void){
    uint8_t i;
    
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, FUNC1_SET ); //8ビットバス・2LINE表示
    __delay_us(40);
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, FUNC2_SET ); //拡張コマンド
    __delay_us(40);
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, INT_OSC ); //内部周波数調整
    __delay_us(40);    
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, CONST_SET ); //コントラスト1
    __delay_us(40);    
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, PWR_ICON_SET );//コントラスト2
    __delay_us(40);   
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, FOLLOWER_SET );//フォロワー制御
    __delay_ms(250);
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, FUNC1_SET ); //拡張コマンドをオフ
    __delay_us(40);
    
    DspClear();
    
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, DISP_ONOFF_SET );
    __delay_us(40);
    
    I2C1_WriteNBytes( SLAVE_ADRS,&initmoji[0][0], sizeof(initmoji[0]));
    
    DspLine2Top();

    for( i = 0; i < sizeof(initmoji[1]); i++ ){
        I2C1_Write1ByteRegister(SLAVE_ADRS,LINE1_ADRS,initmoji[1][i]);
    }
    __delay_ms(2000);
    DspClear();   
}
/* LCDのカーソル移動 */
void DspLine2Top(void){
    
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, LINE2_TOP );
    __delay_us(40);    
}
/* LCDの表示初期化*/
void DspClear(void){
  
    I2C1_Write1ByteRegister(SLAVE_ADRS, 0x00, CLR_DISP );
    __delay_us(40);
}
/* DispSet function add */
void DispSet(void){
    uint8_t i;
    uint8_t dsp[16];

    sprintf(dsp,"START TYPE:%u   ", starttyp.byte);
    for( i = 0; i < sizeof(dsp); i++ ){
        I2C1_Write1ByteRegister(SLAVE_ADRS,LINE1_ADRS,dsp[i]);
    }
  
    DspLine2Top();
    
    if( starttyp.byte == 0 ){
        sprintf(dsp,"COLD START      ");
    }
    else if( starttyp.bits.por == 1 ){
        sprintf(dsp,"RESTART: POR    ");
    }
    else if( starttyp.bits.bor == 1){
        sprintf(dsp,"RESTART: BOR    ");
    }
    else if( starttyp.bits.ri == 1){
        sprintf(dsp,"RESTART: RES    ");
    }   
    else if( starttyp.bits.rmclr == 1 ){
        sprintf(dsp,"RESTART: RMCLR  ");
    }
    else if( starttyp.bits.hot == 1 ){
        sprintf(dsp,"RESTART: HOT    ");
    }
    else{
        sprintf(dsp,"RESTART: OTHER  ");
    }

    for( i = 0; i < sizeof(dsp); i++ ){
        I2C1_Write1ByteRegister(SLAVE_ADRS,LINE1_ADRS,dsp[i]);
    } 
}
/* リセット要因確認処理 */
void SysStart(void){
    
    if( PCONbits.nBOR == 0 && PCONbits.nPOR == 0 ){
        PCONbits.nPOR = 1;   //パワーオンリセットをクリア(発生すると0)
        PCONbits.nBOR = 1;   //ブラウンアウトリセットをセット(発生すると0)
        PCONbits.nRMCLR = 1;   //MCLRクリア(発生すると0) 
    }
    else{ //ホットスタート
        if( PCONbits.nBOR == 0){
            starttyp.bits.bor = 1; //ブラウンアウトリセットでリセット
            PCONbits.nBOR = 1;   //ブラウンアウトリセットをセット(発生すると0)
        }
        if( PCONbits.nPOR == 0 ){
            starttyp.bits.por = 1;
            PCONbits.nPOR = 1;   //パワーオンリセットをクリア(発生すると0)
        }
        if( PCONbits.nRI == 0){ 
            starttyp.bits.ri = 1;
            PCONbits.nRI = 1;
        }
        if( PCONbits.nRMCLR == 0){ 
            starttyp.bits.rmclr = 1;
            PCONbits.nRMCLR = 1;
        }       
        if( PCONbits.STKOVF == 1 ){ //スタックオーバー
            starttyp.bits.so = 1;
            PCONbits.STKOVF = 0;
        }
        if( PCONbits.STKUNF == 1 ){ //スタックアンダーフロー
            starttyp.bits.su = 1;
            PCONbits.STKUNF = 0;
        }
        starttyp.bits.hot = 1; //ホットスタートしたことをセット
    }
}
/* End of File */

本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていないと使用できません。

関連リンク

PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

Code Camp完全オンラインのプログラミング個人レッスン【無料体験】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました