Top Page PIC24FV32KA302でバイナリカウンターを試す
自作・実験工程のメモとして
09.Apr.2020


 二つ前の「STM32F103でOCXO Calibratorを作る(1)」と、一つ前の「STM32F103でOCXO Calibratorを作る(2)」で、10MHz OCXOの較正器づくりに続けて失敗していますが、そのまま続けても埒が明かないので視点を変えて別の物を試してみることにしました。
 較正器の入力段は秋月電子の周波数カウンターの回路を、ほぼそのまま使わせていただいていますが、昨年9月に追加された最新の「8桁周波数カウンターキット」にはPIC24FV32KA302を使って、PICだけで50MHzまで測定できるように記載されています。
そこで、同じように秋月電子からPIC24FV32KA302(DIP-28)を取り寄せ、試してみることにしました。

 PICは4年ぐらい前にMikroCで8bitのPIC18F2423を使ったのが最後で、今回は16bitの「PIC24FV32KA302」(以下、PIC24FVと略す)を初めて使うこともあり、開発環境の整備から手探りで始めました。
Arduino系のCPUに慣れてきているせいで、戸惑うことが多々ありましたが、それなりに見込みが立ちましたので、掲載いたします。


  STM32F103でOCXO Calibratorを作る(3) を掲載しました。

▼約50MHz(50.1MHzを入力)をGATE時間1秒で計測中の「PIC24FV32KA302」カウンター

▼OCXO(10MHz)をGATE時間1000秒で計測中の「PIC24FV32KA302」カウンター

小数点は入れていませんが、1/1000 でHzとなります。
0.030Hz(30mHz)程度ズレているのが分かります。

Edit by bluegriffon3.1
表記文字の校正を較正に変更しました。

1.開発環境の整備

 PICの16bit系開発環境ということで、MPLAB X IDEをWindows10のPCにインストールすることから始めました。
8bit系の「MikroC PRO for PIC」のライセンスは生きており、大きなプログラムを作ることもできますが、8bit系では桁数の大きな整数を扱う限界があり、16bit系では64bitまで整数が扱えることと、処理速度の関係から16bit系からのスタートです。


■.MPLAB X IDE 他のインストール

表に現れるショートカットは、のこの2つです。

▼インストール順(途中、前後しているかもしれません)
1.MPLAB® X IDE v5.35 (IPE含む)
2.MPLAB XC16 Compiler v1.50 (場所:\Program Files\Microchip\xc16\v1.50 ) 
3.MPLAB XC16 Compiler Part-Support Patch v1.50   ( xc16\v1.50 <= v1.50 フォルダーを指定して) 
4.Peripheral Libraries (PLIBS)   ( xc16\v1.50 <= v1.50 フォルダーを指定して) 
5.Legacy PIC24 MCU & dsPIC DSC Peripheral Library
  (場所:\Program Files\Microchip\xc16\v1.50\support\peripheral_24F にインストール )  
6.MPLAB® Code Configurator (MCC)プラグインをインストール
7.PICKIT3ファームウェアーアップデート

■.PIC24FV32KA302 基板の準備
1.通常はブレッドボードを使うと思いますが、接触不良等を心配してサンハヤトの基板(ICB-86G)に、ICのピンと同じ数のピンソケットを付けています。
2.左側は、GATE回路用の74HC00です。
3.手前からの線(5本)は、PICKIT3に繋がっています。
4.赤いつまみ付ジャンパーピンは、PIC24FVのリセット回路用コンデンサ切替です。
5.ボード上の右手前に「Lチカ」用のLED、奥にはI2C用のピンソケットを載せています。
6.その他電源は親基板OCXO Calibrator(以下、較正器)からとっています。

2.PIC24FV32KA302 実験の回路図

 回路図は、ある程度目途がついてきたときのもので、最初は別の接続だったものを修正しています。
今のPICは、水晶発振子が要らないようになっているので、周辺回路は簡単なものになりました。

■.回路図
(クリックでPDFファイルが開きます。)
1.このまま基板を作ることは無いので、上面には較正器との接続情報と74HC00によるGATE回路を記述しています。
2.左下は、PICのワンサイズ小さめな(20pin)のシンボルも載せたままにしています。
3.試用するときは、較正器側の74HC590は抜いた状態で、そのICに追加したピンソケットから取り出し、GATE制御等はSTM32F103側でそのまま受け持つ予定です。



3.プログラムの練習

  久しぶりのPICで、どう手を付けて良いのか分からず、とりあえず定番の「Lチカ」から始めました。
幸い日本語データシート「PIC24FV32KA304データシート」がMicrochipから用意されていたので、それを見ましたが良くわからず手探り状態でした。
そんな中でも、諸先輩がNet上に公開されている情報が、とても参考になりました。(欲を言えばもう少し詳しく・・・)

(1)最初は「Lチカ」から

「Lチカ」ソースコード 
/*
 * File:   main.c
 * Author: jlb.jp
 *  PIC24FV32KA302
 *
 * project name A0_Lchika_24FV32KA
 *
 * ■■■■■■■■■■■■■■■■■■■
 * 1秒ごとにPB5のLEDがチカチカする。
 * ■■■■■■■■■■■■■■■■■■■
 *
 * 2020/04/03:以後は変更しない。
 *
 */

// delay関数を使うのに必要。クロック周波数÷2が命令実行サイクル
#define FCY 4000000  // クロック周波数FOSC(8MHz)÷2

#include <xc.h>
#include <libpic30.h> // __delay_ms()が定義されている

// コンフィギュレーションの設定 PIC24FV32KA302 Configuration Bit Settings
#pragma config FNOSC = FRC  // Oscillator Select (Fast RC Oscillator (FRC))
#pragma config FWDTEN = OFF  // Watchdog Timer Enable (Watchdog Timer is disabled)
#pragma config ICS = PGx1  // Comm Channel Select (Emulator EMUC1/EMUD1 pins are shared with PGC1/PGD1)

int main(void) {
    ANSB = 0x0000; // PORTBアナログ入力なし
    TRISBbits.TRISB5 = 0; // output PB5
    while (1) {
        if (LATBbits.LATB5 == 0) {
            LATBbits.LATB5 = 1; // LED off (high)
        } else {
            LATBbits.LATB5 = 0; // LED on (low)
        }
        __delay_ms(500);
    }
    return 1;
}
RB5を使った「Lチカ」を最初に動かしてみました。
PICと言えばコンフィグレーション・ビットの設定が有ったなと、データシートを眺めて決めきれず、取りあえずNet上の公開情報を真似ています。


(2)LCDへの表示実験


使ったLCDは、アイテンドの[ATD1602CPB]で「FRAT」製作の時に使ったものです。
I2Cアドレス3Fで、PIC24FVが5V駆動の関係で、3.3V用の物を止む無く5Vで使用しました。

「I2C LCDへの表示」MCC画面キャプチャー





「I2C LCDへの表示」ソースコード 
/**
  Generated main.c file from MPLAB Code Configurator(MCC)を使用した。

  @Company
    Microchip Technology Inc.

  @File Name
    main.c

  @Summary
    This is the generated main.c using PIC24 / dsPIC33 / PIC32MM MCUs.

  @Description
    This source file provides main entry point for system initialization
    and application code development.
    Generation Information :
        Product Revision  :  PIC24 / dsPIC33 / PIC32MM MCUs - 1.166.1
        Device            :  PIC24FV32KA302
    The generated drivers are tested against the following:
        Compiler          :  XC16 v1.41
        MPLAB               :  MPLAB X v5.30
 *
 * 2020/04/04:最終編集後、変更しない。
 *
 * I2C でLCDを動かす
 *
 * LCD は、アイテンドの「基板付きI2CキャラクタLCDモジュール(16x2) [ATD1602CPB]」
 * 参考にしたのは、以下のURL (2020/04/04現在)
 * http://fujiharagiken.hatenablog.com/entry/2017/10/21/124650
 *
 * 参照は、「組込み系システム開発室by藤原技研M」のブログです。

 */

/*
    (c) 2020 Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS.
 */

/**
  Section: Included Files
 */
// delay関数を使うのに必要。クロック周波数÷2が命令実行サイクル
#define FCY 16000000  // クロック周波数FOSC(32MHz)÷2

#include "mcc_generated_files/system.h"

#include "i2c1.h"       // エラーが出るのでmcc_generated_files/より移動
// 以下もコンパイルは通るが警告>が出るので記述
#include <libpic30.h> // __delay_ms()に必要
#include <stdio.h>      // sprintf()に必要
#include <string.h>     // strlen()に必要
#include <ctype.h>
#include "p24FV32KA302.h"   // ANSB TRISBbits. 等に必要

// =================================================

#define I2CLCD_AQM0802A 0x3E   // 3FのLCDでも3Eと記述

/**
 * コマンド書き込み
 * @param device_address    :I2Cデバイスのアドレス
 * @param controlByte   :コントロールバイト
 * @param cmdData   :コマンド
 * @return
 */
uint8_t I2C1_write(uint8_t device_address, uint8_t controlByte, uint8_t cmdData) {
    I2C1_MESSAGE_STATUS status = I2C1_MESSAGE_PENDING;
    uint8_t write_buffer[2];

    write_buffer[0] = controlByte;
    write_buffer[1] = cmdData;

    I2C1_MasterWrite(write_buffer, 2, device_address, &status);
    int cnt = 0;

    while (status == I2C1_MESSAGE_PENDING && cnt++ < 10) {
        __delay_us(100);
    }
    return (status == I2C1_MESSAGE_COMPLETE);
}

/**
* LCDの指定アドレスに表示する
* @param str: 表示したい文字列
* @param num:表示バイト数
* @param LcdAddr LCDのDDRAM表示アドレス
*/
void WireLcdDisplay(char *str, int num, uint8_t LcdAddr) {
    uint8_t dt[65]; //16文字まで対応 64文字に拡大
    int i;
    I2C1_MESSAGE_STATUS status = I2C1_MESSAGE_PENDING;

    // Set DDRAM address コマンドをセットする
    dt[0] = 0x40;

    // 文字列をアスキーコードに変換して配列に格納する
    for (i = 0; i < num; i++) {
        dt[i + 1] = (uint8_t) str[i];
    }

    // Slaveへ制御コード:0x00
    // コマンド:0x80 + DDRAMのアドレス
    I2C1_write(I2CLCD_AQM0802A, 0x00, 0x80 | LcdAddr);
    I2C1_MasterWrite(dt, num + 1, I2CLCD_AQM0802A, &status);
    int cnt = 0;

    while (status == I2C1_MESSAGE_PENDING && cnt++ < 10) {
        __delay_us(100);
    }
    __delay_ms(1); // 追加して呼び出し側(main)に記述しない。
}

// これによって出力されるデータは以下の通りになります
// 【I2C1_writeによって出力されるデータ】
// 0x7C :LCDのアドレス
// 0x00 :コントロールバイト(RS=0:コマンド)※次はコマンドを送る
// 0x80 :コマンドバイト(SetDDRAMaddress + DDRAMアドレス)
//
// 【I2C1_MasterWriteによって出力されるデータ】
 // 0x7C :LCDアドレス
// 0x40 :コントロールバイト(RS=1:データ)※次からデータを送る
// 表示データ(num個)

/**
 * LCD の初期化処理
 */
void WireLcdInit(void) {
    I2C1_MESSAGE_STATUS status = I2C1_MESSAGE_PENDING;
    uint8_t sendData[ ] = {0x38, 0x39, 0x14, 0x70, 0x56, 0x6C, 0x38, 0x0C, 0x01};

    I2C1_MasterWrite(sendData, sizeof (sendData) + 1, I2CLCD_AQM0802A, &status);
    int cnt = 0;

    while (status == I2C1_MESSAGE_PENDING && cnt++ < 10) {
        __delay_us(100);
    }
    __delay_ms(1); // 追加して呼び出し側(main)に記述しない。
}

// これによって出力されるデータは以下の通りになります
// 【I2C1_MasterWriteによって出力されるデータ】
// 0x7C :LCDアドレス
// 0x38, 0x39, 0x14, 0x70, 0x56, 0x6C, 0x38, 0x0C, 0x01 :コマンド

/**
* LCD表示処理サンプル(事前にWireLcdInit ()を呼び出しておくこと)
*/
void LcdDisp(void) {
    char str1[ ] = "Kitchen ";
    char str2[ ] = "Timer   ";

    //------[ 表示(1行目)]------
    WireLcdDisplay(str1, 8, 0x00);
    //------[ 表示(2行目)]------
    WireLcdDisplay(str2, 8, 0x40);
}

// =================================================
// 追加関数  jlb.jp
// 表示位置を指定して LCD に表示する
// ll = 行:0 or 1   ,cc 桁:0 to 15
// =================================================
void WireLcd_lxc_Print(uint8_t ll, uint8_t cc, char* txt) {
    uint8_t ca, lc;
    int ln = strlen(txt);
    if (ln > 16) ln = 16;
    ca = cc;
    switch (ll) {
        case 1:
            lc = 0x40;
            break;
        default:
            lc = 0x00;
    }
    if (ca > 15) ca = 15;
    lc += ca;

    WireLcdDisplay(txt, ln, lc);
}

// =================================================
// 追加関数  jlb.jp
// 画面表示をクリヤーする
//
// =================================================
void WireLcd_Clr(void) {
    char sptext[17] = "                ";
    WireLcdDisplay(sptext, 16, 0x00);
    WireLcdDisplay(sptext, 16, 0x40);
}


int gMode;
int gCount;
char temptxt[80];

/*
                         Main application
 */
int main(void) {
    // initialize the device
    SYSTEM_Initialize();

    int loopCnt = 0;
    ANSB = 0x0000; // PORTBアナログ入力なし 0 = デジタル入力バッファを有効にする
    TRISBbits.TRISB5 = 0; // output PB5
    __delay_ms(40);

    WireLcdInit();  // LCD初期化

    WireLcd_lxc_Print(0, 0, "teast  JLB");
    WireLcd_lxc_Print(1, 1, "2020/04/03 ");
    __delay_ms(2000);


    while (1) {
        WireLcd_Clr(); // 画面表示をクリヤーする

        if (loopCnt > 1000) {
            loopCnt = 0;
        } else {
            loopCnt++;
        }

        if (LATBbits.LATB5 == 0) {
            LATBbits.LATB5 = 1; // LED off (high)
            WireLcd_lxc_Print(0, 0, "PB5 LED OFF"); // LCDにLEDの状態を文字で表示
        } else {
            LATBbits.LATB5 = 0; // LED on (low)
            WireLcd_lxc_Print(0, 0, "LED On ABCD EFGHIJKxyz123");
        }

        sprintf(temptxt, "%4d", loopCnt);
        WireLcd_lxc_Print(1, 3, temptxt);   // 2行目の4桁目から表示

        __delay_ms(500); // 500ms

    }
    return 1;
}

ブログ「組込み系システム開発室by藤原技研M」の【キッチンタイマーの製作(3)I2CでLCDの制御】を参考にさせていただきました。(感謝)
この記事はちょっと衝撃的?で、MPLAB Code Configurator(以下、MCC)というものを初めて知りました。
MCCは、CPUの周波数や周辺デバイスを画面で設定していくと、コンフィグレーション・ビットだけでなく周辺デバイスのドライバ(的な物:ライブラリのような)ものまで吐き出してくれます。
各デバイスの操作は、MCCが吐き出した「デバイス名.h」「デバイス名.c」 を確認・追記する必要は有りますが、難しい設定が省略されます。
これ(MCC)が有れば、PICの開発がすごく楽になると思いますし、機会が有れば積極的に使っていきたいと感じました。


(3)UARTの確認


PICから直接PCに送っても良かったのですが、接続線が最小になる上記の組み合わせで実験しました。

「U2TXによる送信」MCC画面キャプチャー
System Module については、前 (2)LCDへの表示実験 と同じ。


「U2TXによる送信」ソースコード 
/**
  Generated main.c file from MPLAB Code Configurator

  @Company
    Microchip Technology Inc.

  @File Name
    main.c

  @Summary
    This is the generated main.c using PIC24 / dsPIC33 / PIC32MM MCUs.

  @Description
    This source file provides main entry point for system initialization and application code development.
    Generation Information :
        Product Revision  :  PIC24 / dsPIC33 / PIC32MM MCUs - 1.166.1
        Device            :  PIC24FV32KA302
    The generated drivers are tested against the following:
        Compiler          :  XC16 v1.41
        MPLAB               :  MPLAB X v5.30
 *
 *
 * U2TX UART のテスト
 *
 *
 *
*/

/*
    (c) 2020 Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS.
*/

/**
  Section: Included Files
*/
// delay関数を使うのに必要。クロック周波数÷2が命令実行サイクル
#define FCY 16000000  // クロック周波数FOSC(32MHz)÷2

#include "mcc_generated_files/system.h"

// #include "i2c1.h"       // エラーが出るのでmcc_generated_files/より移動
// 以下もコンパイルは通るが警告>が出るので記述
#include <libpic30.h> // __delay_ms()に必要
#include <stdio.h>      // sprintf()に必要
#include <string.h>     // strlen()に必要
#include <ctype.h>
#include "p24FV32KA302.h"   // ANSB TRISBbits. 等に必要
#include "uart2.h" // 警告が出るのでmcc_generated_files/より移動
// =================================================

// 送信するテキスト
char txtext[] = "ABabcdefg 1234567890 CDEF*|HI\r\n";

// 追加した関数はこれだけ。
void Serialprint(char *outtext) {
    int8_t i, n;
    uint8_t uwd;
    n = strlen(outtext);
    for (i = 0; i < n; i++) {
        uwd = outtext[i];
        UART2_Write(uwd);
    }
}


/*
                         Main application
 */
int main(void) {
    // initialize the device
    SYSTEM_Initialize();

    ANSB = 0x0000; // PORTBアナログ入力なし 0 = デジタル入力バッファを有効にする
    TRISBbits.TRISB5 = 0; // output PB5

    while (1) {
        // シリアル送信 一行だけ
        Serialprint(txtext);

       
        // 動作状態を目で見るためのLチカ
        if (LATBbits.LATB5 == 0) {
            LATBbits.LATB5 = 1; // LED off (high)
        } else {
            LATBbits.LATB5 = 0; // LED on (low)
        }

        __delay_ms(2000); // 2000ms

    }
    return 1;
}


このU2TXを使ったやり方の前に、Net上の別のサンプルを参考にして実験してみました。
それは、動くことは動きますが、何故か5文字以上を送ると文字化け・乱れが発生するようになり、MCCを使って再試行したものです。
文字列を送信できるように追加した関数一つと、その関数を1秒ごとに実行する一行( Serialprint(txtext); )だけで、難なく送信することができました。(Arduino並みの簡単さです)



(4)TIMER1(T1CK)による周波数カウント

実は、TIMER1(16bit)の前に欲張って32bitで使えるTIMER2+TIMER3を実験しましたが、最高周波数が7MHz(安定的には5MHz程度)までしかカウントできず諦めかけました。
しかし秋月の「8桁周波数カウンターキット」は同じPIC24FVを使用して50MHzまで測定可能と説明書に書いてあります。
 そこでよく見ると、カウント入力をTIMER1に入れていることに気づき、試しにT1CK端子(RB9)に入れてやると50MHzを超えてカウントしています。
60MHzもカウントしますが時々カウントモレが有り、上限50MHzまでなら自作でも大丈夫なようです。

「TIMER1によるカウンタ」MCCの画面キャプチャー
System Module については、前出 (2)LCDへの表示実験 と同じです。




「TIMER1によるカウンタ」フォルダーのイメージ
赤枠のファイルは、コンパイル中に警告(warning)が出るため「mcc_generalated_files」フォルダーからコピーしました。

「TIMER1によるカウンタ」ソースコード 
/**
  Generated main.c file from MPLAB Code Configurator

  @Company
    Microchip Technology Inc.

  @File Name
    main.c

  @Summary
    This is the generated main.c using PIC24 / dsPIC33 / PIC32MM MCUs.

  @Description
    This source file provides main entry point for system initialization and application code development.
    Generation Information :
        Product Revision  :  PIC24 / dsPIC33 / PIC32MM MCUs - 1.166.1
        Device            :  PIC24FV32KA302
    The generated drivers are tested against the following:
        Compiler          :  XC16 v1.41
        MPLAB               :  MPLAB X v5.30
 *
 * *
 *
 * クロックをTIMER1でカウントして、I2C LCDにその数を表示する。
 *
 * 
 * LCD は、アイテンドの「基板付きI2CキャラクタLCDモジュール(16x2) [ATD1602CPB]」
 * 参照は、「組込み系システム開発室by藤原技研M」のブログです。(2020/04/04現在)
 *
 * この方法で、TIMER1は最高周波数が50MHz以上まで伸びる。
 * しかも、入力レベルも50MHz(-4dBm程度まで)カウントする。
 * 2020/04/05
 *
 *
 *
 */

/*
    (c) 2020 Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS.
 */

/**
  Section: Included Files
 */
// delay関数を使うのに必要。クロック周波数÷2が命令実行サイクル
#define FCY 16000000  // クロック周波数FOSC(32MHz)÷2

#include "mcc_generated_files/system.h"

#include "i2c2.h"       // エラーが出るのでmcc_generated_files/より移動
// 以下もコンパイルは通るが警告が出るので記述
#include <libpic30.h> // __delay_ms()に必要
#include <stdio.h>      // sprintf()に必要
#include <string.h>     // strlen()に必要
#include <ctype.h>
#include "p24FV32KA302.h"   // ANSB TRISBbits. 等に必要
#include "tmr1.h"
#include "ext_int.h"

// =================================================

#define I2CLCD_AQM0802A 0x3E   // 3FのLCDでも3Eと記述

// タイマー関連のグローバル変数 ui64overf
uint64_t ui64overf = 0;
uint16_t ui32count = 0; //
uint8_t countBusy = 0;
uint8_t CountOk = 0;


int gMode;
int gCount;
char temptxt[80];

// 警告が出るので以下に記述した。
void TMR1_JOB(void);

/**
 * コマンド書き込み
 * @param device_address    :I2Cデバイスのアドレス
 * @param controlByte   :コントロールバイト
 * @param cmdData   :コマンド
 * @return
 */
uint8_t I2C2_write(uint8_t device_address, uint8_t controlByte, uint8_t cmdData) {
    I2C2_MESSAGE_STATUS status = I2C2_MESSAGE_PENDING;
    uint8_t write_buffer[2];

    write_buffer[0] = controlByte;
    write_buffer[1] = cmdData;

    I2C2_MasterWrite(write_buffer, 2, device_address, &status);
    int cnt = 0;

    while (status == I2C2_MESSAGE_PENDING && cnt++ < 10) {
        __delay_us(100);
    }
    return (status == I2C2_MESSAGE_COMPLETE);
}

// ここから、LCD関係の記述

/**
 * LCDの指定アドレスに表示する
 * @param str: 表示したい文字列
 * @param num:表示バイト数
 * @param LcdAddr LCDのDDRAM表示アドレス
 */
void WireLcdDisplay(char *str, int num, uint8_t LcdAddr) {
    uint8_t dt[65]; //16文字まで対応 64文字に拡大
    int i;
    I2C2_MESSAGE_STATUS status = I2C2_MESSAGE_PENDING;

    // Set DDRAM address コマンドをセットする
    dt[0] = 0x40;

    // 文字列をアスキーコードに変換して配列に格納する
    for (i = 0; i < num; i++) {
        dt[i + 1] = (uint8_t) str[i];
    }

    // Slaveへ制御コード:0x00
    // コマンド:0x80 + DDRAMのアドレス
    I2C2_write(I2CLCD_AQM0802A, 0x00, 0x80 | LcdAddr);
    I2C2_MasterWrite(dt, num + 1, I2CLCD_AQM0802A, &status);
    int cnt = 0;

    while (status == I2C2_MESSAGE_PENDING && cnt++ < 10) {
        __delay_us(100);
    }
    __delay_ms(1); // 追加して呼び出し側(main)に記述しない。
}

// これによって出力されるデータは以下の通りになります
// 【I2C2_writeによって出力されるデータ】
// 0x7C :LCDのアドレス
// 0x00 :コントロールバイト(RS=0:コマンド)※次はコマンドを送る
// 0x80 :コマンドバイト(SetDDRAMaddress + DDRAMアドレス)
//
// 【I2C2_MasterWriteによって出力されるデータ】
// 0x7C :LCDアドレス
// 0x40 :コントロールバイト(RS=1:データ)※次からデータを送る
// 表示データ(num個)

/**
 * LCD の初期化処理
 */
void WireLcdInit(void) {
    I2C2_MESSAGE_STATUS status = I2C2_MESSAGE_PENDING;
    uint8_t sendData[ ] = {0x38, 0x39, 0x14, 0x70, 0x56, 0x6C, 0x38, 0x0C, 0x01};

    I2C2_MasterWrite(sendData, sizeof (sendData) + 1, I2CLCD_AQM0802A, &status);
    int cnt = 0;

    while (status == I2C2_MESSAGE_PENDING && cnt++ < 10) {
        __delay_us(100);
    }
    __delay_ms(1); // 追加して呼び出し側(main)に記述しない。
}

// これによって出力されるデータは以下の通りになります
// 【I2C2_MasterWriteによって出力されるデータ】
// 0x7C :LCDアドレス
// 0x38, 0x39, 0x14, 0x70, 0x56, 0x6C, 0x38, 0x0C, 0x01 :コマンド

/**
 * LCD表示処理サンプル(事前にWireLcdInit ()を呼び出しておくこと)
 */
void LcdDisp(void) {
    char str1[ ] = "Kitchen ";
    char str2[ ] = "Timer   ";

    //------[ 表示(1行目)]------
    WireLcdDisplay(str1, 8, 0x00);
    //------[ 表示(2行目)]------
    WireLcdDisplay(str2, 8, 0x40);
}

// =================================================
// 追加関数  jlb.jp
// 表示位置を指定して LCD に表示する
// ll = 行:0 or 1   ,cc 桁:0 to 15
// =================================================

void WireLcd_lxc_Print(uint8_t ll, uint8_t cc, char* txt) {
    uint8_t ca, lc;
    int ln = strlen(txt);
    if (ln > 16) ln = 16;
    ca = cc;
    switch (ll) {
        case 1:
            lc = 0x40;
            break;
        default:
            lc = 0x00;
    }
    if (ca > 15) ca = 15;
    lc += ca;

    WireLcdDisplay(txt, ln, lc);
}

// =================================================
// 追加関数  jlb.jp
// 画面表示をクリヤーする
//
// =================================================
void WireLcd_Clr(void) {
    char sptext[17] = "                ";
    WireLcdDisplay(sptext, 16, 0x00);
    WireLcdDisplay(sptext, 16, 0x40);
}

// ここまでは、LCD関係の記述

// 外部信号割り込み

// =================================================
// 追加関数  jlb.jp
// GATE(INT1)外部割込みのイニシャライズ関数
// 立ち上り指定なので、GATEのOFF 時に立ち上がる信号(~GON)を使用する。
// ext_intc に連携する。
// ■■ project > properties > xc16gcc で Use64-bit doubleを指定した。
// =================================================

void EXT_INT1_Initialize(void) {
    EX_INT1_InterruptFlagClear();
    EX_INT1_PositiveEdgeSet();
    EX_INT1_InterruptEnable();
}

// =================================================
// 追加関数  jlb.jp
// GATE(INT1)外部割込みの処理関数
// GATEがOFF 時に、処理する。
// CountOk = 1 をセットする。
// ext_intc に連携する。
// EXT_INT1 と INT0 の■2つ■を指定すると ext_int.c に
//  void __attribute__ ((weak)) EX_INT0_CallBack(void)
// {
//    // Add your custom callback code here
// }
// が出現する。そちらに記述か?
// =================================================

void EX_INT1_CallBack(void) {
    // if (TRISBbits.TRISB14 == 0) { // 念のためビットを確認する。
    TMR1_Stop(); // タイマーストップ
    CountOk = 1; // カウント完了フラグをON(1)
    ui32count = TMR1_Counter16BitGet(); // 32bit 読み出し。
    ui64overf = ui64overf << 16; // オーバーフローを32bitシフト
    ui64overf = ui64overf + ui32count; // 合計カウント数
    TMR1_Counter16BitSet(0); // カウンタークリヤー
    TMR1_Start(); // タイマースタート

}

void EX_INT0_CallBack(void) {
    TMR1_Stop(); // タイマーストップ
    TMR1_Counter16BitSet(0); // カウンタークリヤー
    ui32count = 0;
    ui64overf = 0;
    TMR1_Start(); // タイマースタート
}


// ここから、タイマー

/*
 * タイマーがオーバーフローしたときの処理関数
 * tmr1.c に記述する。
 * void __attribute__ ((weak)) TMR1_CallBack(void)
 * {
 *    // Add your custom callback code here
 *       ui64overf++; // オーバーフローカウンタを +1する。
 * }
 * と記述した。
 *
 */
void TMR1_JOB(void) {
    ui64overf++; // オーバーフローカウンタを +1する。
}

/*
                         Main application
 */
int main(void) {
    // initialize the device
    SYSTEM_Initialize(); // 外部割込み INT1の初期化 タイマー初期化を含む

    // int loopCnt = 0;
    ANSB = 0x0000; // PORTBアナログ入力なし 0 = デジタル入力バッファを有効にする
    TRISBbits.TRISB5 = 0; // output PB5
    __delay_ms(40);
    WireLcdInit(); // LCD初期化

    // MCCの TMR1 ==> Registers ==> 「Register:PR1」で設定可   
    TMR1_Period16BitSet( 0xFFFF);       // TIMER1(16bit)用
   
    TMR1_Start(); //タイマースタート

    WireLcd_lxc_Print(0, 0, "teast TIMER 1");
    WireLcd_lxc_Print(1, 1, "2020/04/05 ");
    __delay_ms(2000);

    while (1) {

        if (CountOk == 1) {
            WireLcd_Clr(); // 画面表示をクリヤーする
            WireLcd_lxc_Print(0, 0, "PIC24FV.. TMR1");
            sprintf(temptxt, "%13lld", ui64overf); // %lld を付けるlong long OK
            strcat(temptxt, "/G");
            WireLcd_lxc_Print(1, 0, temptxt); // 2行目の4桁目から表示
            ui64overf = 0;


            if (LATBbits.LATB5 == 0) {
                LATBbits.LATB5 = 1; // LED off (high)
            } else {
                LATBbits.LATB5 = 0; // LED on (low)
            }

            CountOk = 0; // カウント完了状態をクリヤー
        }

    }
    return 1;
}

/*
    if (LATBbits.LATB5 == 0) {
        LATBbits.LATB5 = 1; // LED off (high)
    } else {
        LATBbits.LATB5 = 0; // LED on (low)
    }
 */



/**
 End of File
 */


当初I2CはI2C1を、TIMERはT2CKを使っていましたが、MCCで変更をして展開すると、わずかな修正で動きました。
まだ、正しく記述できていないところもあると思いますが、とりあえず動いたので主テーマである較正器の見込みが出てきました。


4.PIC24FV と STM32F103 の連携

 ここまでくると、PIC24FVを使ってカウンターを作り直すこともできるように思いますが、「2.42"OLED」に表示することやフォント(u8g2)が使用できるか等が新たな課題となるため、勿体ない使い方ですが PIC24FVSTM32F103 を連携させることにしました。
 連携といってもデータの引き渡しは片方向ですので、最初はI2Cで引き渡しを考えましたが、STM32F103 側に外付けのEEPROMを付けていることもあり、PIC24FV をI2Cスレーブとして動かすやり方が理解できていません。
そこで、上記3項の「プログラムの練習」の内容を組み合わせて、PIC24FV 側からUARTの送信で引き渡すことにしました。

■.PIC24FV 側でLCDに表示させながら較正器へ引き渡し
上のLCDはケースの上に裸で置いたらショートするので、ビニール袋に入れて両画面を写しました。
転送速度は、9600bps/38400bpsの二つを試しましたが、問題無く通ります。

このPIC24FVに取り掛かる前に、「STM32マイコン徹底入門 (TECH I Processor)  川内康雄氏(著) 」という本をAmazonから取り寄せて、STM32F103のクロック端子も試そうとしました。
しかし、STM32duinoで使えるライブラリの使い方が良く分からず、カウンターとして使われている事例も発見できなかったため、PICに取り掛かったという次第です。

以降は、プログラムの記述と回路を整理して「STM32F103でOCXO Calibratorを作る(3)」完結編?を予定しています。


9.その他気づいたこと

(1)その他




99.追記用

大幅な改定・追記用