PIC18F45K22CPUボードのKiCad5データ

PIC18F45K22CPUボードのKiCad Ver5のデータを公開します

KiCadデータはgithubの下記リンクに置いています。

KiCad5-pic18f45k22-v2cpubrd

回路図PDF

pic18f45k22-v21_sch

データとライセンス

KiCad Ver5.0用にアップデートしました、githubを使って公開しています。
ライセンスはオープンソースハードウェア扱いとし、オリジナル元の記載を忘れないようにしていただければ、使用上の制限は設けません。

PIC18F45K22CPUボードについて

広島県工業高校ものづくり検定の課題用に設計したCPUボードです。
部品には出来るだけ秋月電子で入手可能なものを使っています。

このCPUボードの特徴

最近のワンチップCPUは非常に使い勝手が良くなり、基板に載せて端子を入出力の相手に接続するだけで使えるようになりました。
この基板もCPUボードの機能としては、CPU端子をコネクタで接続できるようにしただけですが、教育用CPUボードとして工夫した点がいくつかあります。

1.電源供給とUSBシリアル通信機能を備えたUSBコネクタがある

USB機器が普及した今では、ダイソーでも、USB用電源アダプタやUSBケーブルを手に入れることが出来ますし、PCのUSBコネクタから電源を供給することも出来ます。
そのため、USBコネクタを電源供給用コネクタとして使うことで電源供給機器を用意する手間を最小化出来ると考えてUSBコネクタから電源供給が出来るようにしています。

また、組み込みプログラミングではprintf文を使って、プログラム動作中の情報をPCに表示することが大きなデバッグ効果を発揮します。
Ardinoがプログラミング入門用に広く受け入れられているのも、使い始めるときの敷居の低さと、シリアルモニタ、シリアルプロットというプログラム動作中の情報表示機能を備えているからです。
PICの場合、プログラム書き込み用ツールPicKitによるデバッガ機能を備えていますがシリアルモニタは出来ません。

PICはUSB通信をおこなう機能も備えていますが、入門者にUSB機能を使うためのプログラムを書かせるのは負担が大きすぎますし、割り込み関数の周期が不正確になるなどCPUの動作に影響を及ぼしてしまいます。

秋月でも入手できるFTDIのUSBシリアルを使えば、CPU側では単純なシリアル通信を行うだけでPCからは通信機能を持ったUSB機器として認識できるので、USBコネクタに電源供給とUSBシリアル通信機能を持たせて、教育効果を上げることを狙っています。

2.LEDで入出力ポートの状態を確認出来るようにしている

自分が撃ち込んだプログラムが思った通りに動作しない時、初心者はPCの画面だけを見て、「どこに問題があるんだろう?」と考え込んでしまうことがあります。
しかし、実際にはプログラムのせいではなく、コネクタの接続を間違っていたり、接触が悪かったりと、ハードウェア的要因によるトラブルの可能性もあります。

組み込みプログラミングに慣れてくれば、プログラムの確認と同時にオシロスコープ等を使ったハードウェアの確認が自然に出来るようになるものですが、初心者が少しでもハードウェアに意識を向けられるように、コネクタにつながる信号をLEDの点滅で確認できるようにしてあります。

また、限られた時間で課題に取り組む場合には、測定器などを準備する手間なくポートの状態を確認して、課題を解く時間を短縮できるという効果もあります。

KiCad用データの使い方

このサンプルデータをKiCadで開いて編集する方法については KiCad5サンプルデータの使い方 を参照してください

ものづくりコンテスト:サンプルプログラム

PIC18F45K22CPUボード用ヘッダファイル(mono_con.h)

ものづくりコンテストではポート定義のヘッダファイルに限り持ち込みが認められていて、下記のようなヘッダファイルを用意しておくとプログラム作成の時間短縮に役立ちます。

/* 
 * File: mono_con.h
 * Author: jsd
 *
 * Created on 2018/04/17, 14:14
 */

#ifndef MONO_CON_H
 #define MONO_CON_H

#ifdef __cplusplus
 extern "C" {
 #endif

// LED G, B, R
 #define LEDG LATDbits.LATD1
 #define LEDB LATDbits.LATD5
 #define LEDR LATEbits.LATE2

// BUZZER
 #define BZ LATEbits.LATE0
 
 // Latch
 #define LT LATDbits.LATD2
 
 // 7seg 
 #define SEG7L LATBbits.LATB0
 #define SEG7R LATBbits.LATB1
 
 #define SEG7(d) (LATC=(d))
 #define SEG7_a LATCbits.LATC0
 #define SEG7_b LATCbits.LATC1
 #define SEG7_c LATCbits.LATC2
 #define SEG7_d LATCbits.LATC3
 #define SEG7_e LATCbits.LATC4
 #define SEG7_f LATCbits.LATC5
 #define SEG7_g LATCbits.LATC6
 #define SEG7_dp LATCbits.LATC7

// Stepping motor
 #define STM(d) (LATC=(d)&0x0f)

// DC motor
 #define DCM(d) (LATC=(((d)<<6)&0xc0)
 
 // SW status
 #define ENCA (PORTAbits.RA3)
 #define ENCB (PORTAbits.RA4)
 #define SW2 (PORTAbits.RA5==0)
 #define SW1 (PORTAbits.RA6==0)

#ifdef __cplusplus
 }
 #endif

#endif /* MONO_CON_H */

割り込みの使い方サンプルプログラム(プロジェクトkadaiのmain.c)

下のプログラムは割り込みを使うためのサンプルソースです。

プログラムの最初に ヘッダファイルmono_con.hを読み込むための#include文を追加します。

#include "mcc_generated_files/mcc.h"

#include "mono_con.h"

10mS 毎に呼び出される割り込み関数inttmr0()でスイッチの読み込みなどの処理をおこないます。 関数名inttmr0()をmain関数の最初で登録して割り込みを有効にすることで割り込み関数が使えるようになります。 割り込み関数を使う上での注意事項  関数内で宣言するローカル変数は割り込み関数を抜けると記憶されないので変数はグローバル(外部)変数か、関数内で宣言する場合はstatic宣言を使います。  割り込み関数の中で無限ループや長い時間待ちを使ってはいけません。出来るだけ早く終わるように処理を記述します。

/* 10ms毎に起動するタイマ割り込み関数
 *  mainの最初で登録することで周期的に呼ばれる
 * 入力処理と制御をここに記述
 * ※割り込み関数使用上の注意
 * 1.関数は出来るだけ早く抜けること(無限ループ厳禁)
 * 2.変数はグローバル変数(関数の外に書く)にするかstatic宣言をつけること
 */
int tmr1s,tmr10ms;  // これはグローバル変数
uint16_t tmr0_s,tmr0_e;    // デバッグ用の変数
void inttmr0(void)
{
    static int i;
    tmr0_s = TMR0_ReadTimer(); // デバッグ用、課題処理には不要
    
    // 課題1回答例 ここから
    if( SW2 && SW1 ){
        DCM(2);
        printf("dcm(2) PORTA=%02x,LATC=%02x\n",PORTA,LATC);
        LEDG = 1;
    }else if( !SW2 && SW1 ){
        DCM(1);
        printf("dcm(1) PORTA=%02x,LATC=%02x\n",PORTA,LATC);
        LEDB = 1;
    }else{
        DCM(0);
        LEDG = 0;
        LEDB = 0;
    }
    // ラッチの立ち上がりでモータドライバに信号が伝わる
    LT = 0;
    __delay_us(10); // パルスを観測し易くするために10us待つ 
    LT = 1;
    // 課題1回答例 ここまで
            
    // tmr10msをインクリメントし100になったらtmr1sをカウントアップ
    if( 100 <= ++tmr10ms ){ 
        // この部分は1秒ごとに実行
        tmr1s++;
        tmr10ms = 0;
    }
    
    tmr0_e = TMR0_ReadTimer(); // デバッグ用:課題処理には不要
}

tmr01msは100us毎に呼び出される割り込み関数です ブザーを大きな音で鳴らすためには1KHz以上の周波数でON/OFFを繰り返す 必要があるので10KHzの周期にしています。 tmr1_s, tmr1_eには割り込み開始時と終了時のタイマカウンタの値が 入り、printfで表示するとどのくらいの余裕があるかを確認できます。

/* 0.1ms毎に起動するタイマ割り込み関数
 * 出力処理をここに記述
 * ※関数使用上の注意はtmr0と同じ
 */
int tmr01ms;        // これはグローバル変数
uint16_t tmr1_s,tmr1_e;    // デバッグ用の変数

void inttmr1(void)
{
    static int t;   // スタティック変数
    
    tmr1_s = TMR1_ReadTimer(); // デバッグ用、課題処理には不要
    
    // 0.1ms * 9000 = 0.9秒毎に '*' を出力
    // mainのprintfを邪魔する様子を観察するために書いてある
    if( (tmr10ms==0)&&(45 <= ++tmr01ms) ){
        tmr01ms = 0;
        printf("*");
    }  
    tmr1_e = TMR1_ReadTimer(); // デバッグ用、課題処理には不要
}

main関数では割り込み関数を登録してタイマー割り込みを開始する処理だけを記述して 課題の処理は全て割り込み関数でおこないます。

void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

/* 次のコメントを除いた4行は新しく追加する必要があります。 登録関数とタイマースタート関数の名前は生成されたソースで確認することが出来ます。*/

    // タイマー0割り込み関数を登録 (tmr0.c の関数を呼ぶ)
    TMR0_SetInterruptHandler(inttmr0);
    // タイマー1割り込み関数を登録 (tmr1.c の関数を呼ぶ)
    TMR1_SetInterruptHandler(inttmr1);
    // タイマー0をスタート (tmr0.c の関数を呼ぶ)
    TMR0_StartTimer();
    // タイマー1をスタート (tmr1.c の関数を呼ぶ)
    TMR1_StartTimer();

/* 割り込みを有効にするために次の2行のコメントを外して有効にします。*/

   
    // If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts
    // If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global and Peripheral Interrupts
    // Use the following macros to:

    // Enable the Global Interrupts:この下の行をコメントアウトし有効にする
    INTERRUPT_GlobalInterruptEnable();

    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();

    // Enable the Peripheral Interrupts:この下の行をコメントアウトし、有効にする
    INTERRUPT_PeripheralInterruptEnable();

    // Disable the Peripheral Interrupts
    //INTERRUPT_PeripheralInterruptDisable();

/* ここにはデバッグ用のprintf文を書いていますが 課題の処理ではメイン関数の無限ループは空のままで構いません。*/

    // メインループはデバッグ用のprintfだけを書く
    // 課題の処理には必要無し
    while (1)
    {
        // 1秒毎にインクリメントされるtmr1sの値を表示
        // tmr0_s : tmr0割り込み開始時のタイマー値  
        // tmr0_e : tmr0割り込み終了時のタイマー値 
        //    ※0になると次の割り込みが始まる  
        // tmr1_s : tmr1割り込み開始時のタイマー値  
        // tmr2_e : tmr1割り込み開始時のタイマー値 
        //    ※0になると次の割り込み開始  
        if( tmr10ms == 0 ){
            printf("tmr1s=%d, tmr0_s=%u, tmr0_e=%u, tmr1_s=%u, tmr1_e=%u\n",
                    tmr1s, 0xffff-tmr0_s, 0xffff-tmr0_e, 0xffff-tmr1_s, 0xffff-tmr1_e);
        }  
    }
}

課題1回答例 (MCCが生成したコメントを除く)

ポイント 条件文で SW1 == 0 と書く代わりに !SW1 と記述している  教科書的には等号・不等号をきちんと書いた方が良いが、 プログラムを書いている時に等号==を代入の記号=と間違えて記述する場合があり、 気が付きにくいバグとなるので後者の記述にした方が有利である。

#include "mcc_generated_files/mcc.h"

#include "mono_con.h"

/* 10ms毎に起動するタイマ割り込み関数
 */

void inttmr0(void)
{
    if( !SW2 && SW1 ){ // SW2:OFF  SW1:ON
        DCM(2);
    }else if( SW2 && SW1 ){ // SW2:ON SW1:ON
        DCM(1);
    }else{
        DCM(0);
    }
    // ラッチの立ち上がりでモータドライバに信号が伝わる
    LT = 0;
    __delay_us(10); // パルスを観測し易くするために10us待つ 
    LT = 1;
}

/* 10ms毎に起動するタイマ割り込み関数
 * 出力処理をここに記述
 * 課題1では必要ないが、プロジェクトの初期化部分を共通化するために空の関数を記述
 */
void inttmr1(void)
{

}

void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

    // タイマー0割り込み関数を登録 (tmr0.c の関数を呼ぶ)
    TMR0_SetInterruptHandler(inttmr0);
    // タイマー1割り込み関数を登録 (tmr1.c の関数を呼ぶ)
    TMR1_SetInterruptHandler(inttmr1);
    // タイマー0をスタート (tmr0.c の関数を呼ぶ)
    TMR0_StartTimer();
    // タイマー1をスタート (tmr1.c の関数を呼ぶ)
    TMR1_StartTimer();

    // Enable the Global Interrupts:この下の行をコメントアウトし有効にする
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts:この下の行をコメントアウトし、有効にする
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
  
    }
}

課題2回答例

main関数は課題1と同じなので省略しています ポイント  シーケンシャルな処理をするためにステートマシンを使っています。  ステートマシンとはswitch文と状態変数を使って割り込みが呼ばれるたびに  状態変数で処理内容を選択する書き方です。  慣れると複雑なシーケンスでも簡単に記述できます。  スイッチがONした瞬間をとらえるためにSWの状態を記憶しておいて  次のタイミングでスイッチがONでと記憶した状態がOFFの時にスイッチON時の処理をしています。

#include "mcc_generated_files/mcc.h"
#include "mono_con.h"

/* 10ms毎に起動するタイマ割り込み関数
 */
void inttmr0(void)
{
    static int sw1; // SW1の状態を記憶する変数 ※static宣言が必要
    static int state=0; // ステートマシン用変数
    
    switch(state){
        case 0: // 消灯
            LEDR = LEDG = LEDB = 0;  // 全部消灯
            if( SW2 )state = 1; // トグルSWがONになったら 1 へ
            break;
        case 1:  // 黄色
            LEDR = LEDG = 1;        // 緑と赤を点灯(黄色)
            if( !SW2 ) state = 0;   // トグルSWがOFFになったら 0 へ
            if( SW1 && !sw1 ){      // タクトSWの立ち上がりで2へ
                LEDR = LEDG = 0;
                state = 2;
            }
            break;
        case 2: // 緑点灯
            LEDG = 1;
            if( !SW2 ) state = 0;
            if( SW1 && !sw1 ){
                LEDG = 0;
                state = 3;
            }
            break;
        case 3: // 青点灯
            LEDB = 1;
            if( !SW2 ) state = 0;
            if( SW1 && !sw1 ){
                LEDB = 0;
                state = 4;
            }
            break;
        case 4: // 赤点灯
            LEDR = 1;
            if( !SW2 ) state = 0;
            if( SW1 && !sw1 ){
                LEDR = 0;
                state = 2;
            }
            break;
        default:
            state = 0;
            break;
    }
    sw1 = SW1;
    // printf文は無くても良いし、あっても課題の動作には影響が無い
    printf("state=%d,SW1=%d,sw1=%d\n",state,SW1,sw1);

}

/* 10ms毎に起動するタイマ割り込み関数:関数名は任意
 *  mainの最初で登録することで周期的に呼ばれる
 * 出力処理をここに記述
 * ※関数使用上の注意はtmr0と同じ
 */
void inttmr1(void)
{

}

課題3回答例

ポイント  エンコーダ信号もスイッチ信号と同じく変化したタイミングでだけ処理をおこなうのがポイントです。  使っているエンコーダはA相の信号がHighからLowになった時でないと方向判別が出来ない仕様です。  割り込みを使った7SEGの時分割表示はパターンを覚えればとても簡単です。

#include "mcc_generated_files/mcc.h"
#include "mono_con.h"

/* 10ms毎に起動するタイマ割り込み関数
 */
int seg7R = 16, seg7L = 16;
int ct = 0;  // `エンコーダカウント

void inttmr0(void)
{
    static int encA; // SW1の状態を記憶する変数 ※static宣言が必要
    
    // ロータリーエンコーダの信号でct をカウントアップ・ダウン
    if( !ENCA && encA ){ // ENCAがHighからLowになった時
        if( ENCB ){
            ct++;
            if( 20 < ct ) ct = 20;
        }else{
            ct--;
            if( ct < -5 ) ct = -5;
        }
    }
    encA = ENCA; // 次のために記憶
    
    if( !SW2 ) ct = 0;
    
    // 7SEGに表示
    if( 0 <= ct ){
        seg7L = (ct/10) % 10; // seg7L に ctの上位桁を入れる
        seg7R = ct % 10;      // seg7R にctの下位桁を入れる
    }else{
        seg7L = 17;
        seg7R = -ct % 10;      // seg7R にctの下位桁を入れる        
    }
        
}

/* 10ms毎に起動するタイマ割り込み関数:関数名は任意
 *  mainの最初で登録することで周期的に呼ばれる
 * 出力処理をここに記述
 * ※関数使用上の注意はtmr0と同じ
 */
/*****************************************************************
  7SEG LED の表示データ
 ****************************************************************/
const unsigned char SEG7D[] = {      // 7セグメントLEDの表示データ
    0b00111111,                         //  0 = 0
    0b00000110,                         //  1 = 1
    0b01011011,                         //  2 = 2
    0b01001111,                         //  3 = 3
    0b01100110,                         //  4 = 4
    0b01101101,                         //  5 = 5
    0b01111101,                         //  6 = 6
    0b00100111,                         //  7 = 7
    0b01111111,                         //  8 = 8
    0b01101111,                         //  9 = 9
    0b01110111,                         // 10 = A
    0b01111100,                         // 11 = b
    0b00111001,                         // 12 = C
    0b01011110,                         // 13 = d
    0b01111001,                         // 14 = E
    0b01110001,                         // 15 = F
    0b00000000,                         // 16 = 消灯
    0b01000000,                         // 17 = -(マイナス)
    0b01010100,                         // 18 = OFF(冖)
    0b00001000,                         // 19 = ON (_)
};

void inttmr1(void)
{
     static int selseg, t;
    
    // 7SEG 表示処理
    if( selseg ){ // 右の7SEG点灯
        SEG7R = 1;
        SEG7L = 0;
        SEG7(SEG7D[seg7R]);       
    }else{        // 左の7SEG点灯
        SEG7R = 0;
        SEG7L = 1;
        SEG7(SEG7D[seg7L]);
    }
    selseg = !selseg; // フラグを反転する
    
    // ブザーを鳴らす
    if( ct == -5  ){
        if( 2 <= ++t ){
            t = 0;
            BZ = !BZ;
        }
    }    
}

課題4回答例

ポイント エンコーダのカウントと7SEG表示は課題3と同じでカウンタの範囲を±3に制限するだけです。  割り込みを使うとステッピングモータ駆動も簡単に追加することが出来ます。  ステッピングモータ駆動信号と7SEG表示信号が重なっているため、 割り込み関数の中で最初に駆動信号をラッチしてから表示信号をセットするように注意しなければなりません。  順序が逆の場合は7SEGの表示がでたらめになります。  その理由を考えてみるのはハードを理解するうえで良い勉強になるでしょう。

/* 10ms毎に起動するタイマ割り込み関数
 */
int seg7R = 16, seg7L = 16;
int ct = 0;
int stmct = 0; // ステッピングモータ回転指令カウンタ

void inttmr0(void)
{
    static int sw1,encA; // SW1の状態を記憶する変数 ※static宣言が必要
    static int stmpos = 0, tgtpos = 0;
    static int state = 0;
    
    // ロータリーエンコーダの信号でct をカウントアップ・ダウン
    if( !ENCA && encA ){ // ENCAがHighからLowになった時
        if( ENCB ){
            ct++;
            if( 3 < ct ) ct = 3;
        }else{
            ct--;
            if( ct < -3 ) ct = -3;
        }
    }
    encA = ENCA; // 次のために記憶
    if( !SW2 ) ct = 0;

    // ステッピングモーター制御
    if( SW1 && !sw1 ) tgtpos = ct*120; // 目標位置
    sw1 = SW1;
    
    switch(state){
        case 0: // 目標位置との差分に指令値をセット
            if( tgtpos != stmpos ){
                stmct = tgtpos - stmpos;
                stmpos = tgtpos;
                state = 1;
            }
            break;
        case 1: // ステッピングモーターが止まるまで待つ
            if( stmct == 0 ) state = 0;
            break;
        default:
            state = 0;
            break;                
    }
    if( tgtpos != stmpos ){
        
    }
        
    // 7SEGに表示
    if( 0 <= ct ){
        seg7L = (ct/10) % 10; // seg7L に ctの上位桁を入れる
        seg7R = ct % 10;      // seg7R にctの下位桁を入れる
    }else{
        seg7L = 17;
        seg7R = -ct % 10;      // seg7R にctの下位桁を入れる        
    }
        
    //printf("stmct=%d\n",stmct);
}

/* 10ms毎に起動するタイマ割り込み関数:関数名は任意
 *  mainの最初で登録することで周期的に呼ばれる
 * 出力処理をここに記述
 * ※関数使用上の注意はtmr0と同じ
 */
/*****************************************************************
  7SEG LED の表示データ
 ****************************************************************/
const unsigned char SEG7D[] = {      // 7セグメントLEDの表示データ
    0b00111111,                         //  0 = 0
    0b00000110,                         //  1 = 1
    0b01011011,                         //  2 = 2
    0b01001111,                         //  3 = 3
    0b01100110,                         //  4 = 4
    0b01101101,                         //  5 = 5
    0b01111101,                         //  6 = 6
    0b00100111,                         //  7 = 7
    0b01111111,                         //  8 = 8
    0b01101111,                         //  9 = 9
    0b01110111,                         // 10 = A
    0b01111100,                         // 11 = b
    0b00111001,                         // 12 = C
    0b01011110,                         // 13 = d
    0b01111001,                         // 14 = E
    0b01110001,                         // 15 = F
    0b00000000,                         // 16 = 消灯
    0b01000000,                         // 17 = -(マイナス)
    0b01010100,                         // 18 = OFF(冖)
    0b00001000,                         // 19 = ON (_)
};

/* ステッピングモーター相励磁データ
 *  STMD[stmidx]の形でstmidxを0,1,2,3,0,1,2,3,...とすると
 *  ここに設定したパターンが順番に呼び出される
 * それをモータードライバーに接続されたポートに出力してモーターを回す
*/
const unsigned char STMD[] = {
    // oooo にモーターが回転する相励磁パターンを入れる
	0b0001, // 0 A 励磁 (A相だけ1にする)
	0b0010, // 1 B 励磁 (B相だけ1)
	0b0100, // 2 /A 励磁(/A相だけ1)
	0b1000, // 3 /B 励磁(/B相だけ1)
};

void inttmr1(void)
{
   static int selseg, t, stmidx;
    
    //  ステッピングモーター駆動
    //  7SEGの信号と共通(時分割)のため先にステッピングモーター信号をセット
    SEG7R = SEG7L = 0;  // ちらつき防止のため7SEGGをOFFにする
    if( 50 <= ++t ){ // ステッピングモータの速度調整
        t = 0;
        if( 0 < stmct ){
            if( --stmidx < 0 ) stmidx = 3; // 時計回り
            stmct--;
        }else if( stmct < 0 ){
            if( 3 < ++stmidx ) stmidx = 0; // 反時計回り
            stmct++;
        }
        STM(STMD[stmidx]);  // 相励磁信号セット
        LT = 0;        // 相励磁信号をラッチ
        LT = 1;
    }
#if 1
    // 7SEG 表示処理
    if( selseg ){ // 右の7SEG点灯
        SEG7R = 1;
        SEG7L = 0;
        SEG7(SEG7D[seg7R]);       
    }else{        // 左の7SEG点灯
        SEG7R = 0;
        SEG7L = 1;
        SEG7(SEG7D[seg7L]);
    }
    selseg = !selseg; // フラグを反転する
#endif
}

ものづくりコンテスト:MPLAB-X 使い方のヒント

MPLAB-Xは機能が豊富な故に時々訳の分からないトラブルで悩むことがあります。
以下はよくあるトラブルとその対処法についての備忘録です。

1.PicKit3から書き込みが出来なくなる

 MPLAB-XとPicKit3を使っていてよくあるのは書き込みが出来ないというトラブルです。
 プログラム書き込みは下の赤丸で囲まれたボタンを使います、一度書き込みがうまくいくと左の緑のボタンでも書き込みはできますがプロジェクトを切り替えた時など書き込みが出来ない場合があります。

2.書き込んだプログラムが期待通りの動作をしない

 MPLAB-Xは同時に複数のプロジェクトを開けるので複数の課題を解くには便利ですが、うっかり別の課題のプログラムを編集してしまったり書き込むプログラムを間違ったりして期待通りの動作をしなことがあります。
 プログラムのコンパイルが出来て書き込みも出来ているように見えるのにプログラムが思い通りに動かない場合はprojectウィンドウで該当のプロジェクトを選び、

右クリックで表示されるメニューで Set as Main project を選択した後に上図で緑の丸で囲まれたボタンをクリックしてプロジェクトの再構築をしてからプログラムの書き込みをおこないます。
 それでもプログラムが期待通りの動作をしない場合は一度MPLAB-Xを終了してPicKit3も接続し直すと正常に動作する場合があります。

3.プロジェクトをコピーした後にエラーが出る

MPLAB-Xは右クリックで表示されるメニューからプロジェクトをコピーして新しいプロジェクトを作ることが出来ます。

この機能を使うとプロジェクトの初期化設定がそのまま新しいプロジェクトに反映されるので次の課題を解く時間を節約することが出来ます。
ただし、コピーしたプロジェクトをコンパイル&書き込みしようとすると下のエラーで先に進めない場合があります。

この時には一度下図のところでCustomizeを選択して再設定をおこなうことで正常にコンパイルが通るようになります。