Author Archives: admin

32ビットマイコン(PIC32MX210F016B)の動作確認

目的

32ビットマイコン用のMCC Harmonyを使ってベアメタル環境でLチカを行い、基本的な開発環境の使い方とマイコンの動作を確認する

ハードウェア実装

PIC32MX210F016BはDIP基板なのでブレッドボードに実装しました。図1に回路図を示します。今回のマイコンは動作電圧が2.3 V~3.6 Vなので5 Vを加えると壊れるので注意してください。

図1 PIC動作確認用回路(ピンの名称は省略しているので全てを正しく表現できていないので注意)

Vcapにはコンデンサを、AVddとAVssにはVddとGNDを接続しておかないとプログラムの書き込みがうまくいきませんでした。またデータシート上ではコンデンサを置く推奨箇所があったり使わない端子の処理をする様に指示がありますが最低限VcapとAVdd、AVss、Vdd、Vssだけ接続しておけばとりあえず動作します。誤動作だったり外来ノイズに対して弱くなるのできちんとした設計(処理)をするに越したことは無いと思います。

また図のPICの端子は記載以外にもピンの割り当て可能な機能がいくつもあるので詳しくはデータシートを参照してください。今回は表記を省略しています。

MCCで基本設定

Harmonyの使い勝手がさっぱりわからなかったためまずは簡単にタイマー割り込みを使ったLチカを作って試してみました。

MCC Harmonyは32ビットマイコン用のコードコンフィグレータでアプリケーション層(ソフトウェア的な処理)、ドライバー層(I2CとかUARTの制御群)、ペリフェラル層(UART、I2C、タイマーとかのハードウェア)を連携させて開発しやすくする便利ツールです。(私個人の雑な理解です)

16ビットマイコンと同じ様に32ビットマイコン(今回はPIC32MX210F016B)でプロジェクトを作成して、MCCが開いたら、Device Resourceをクリックして図の様にTMR1を追加します。Harmonyのシステムサービスを利用して便利な?(慣れないとタイマー割り込みで直接動作させる方がわかりやすいと思うこととシステムサービスを使うためにもいろいろと苦戦したため)開発を試してみようと思います。

図2 MCC Harmonyの起動画面
手順1 MCC HarmonyでTMR1を追加する

追加されたTMR1をクリックすると下図のようにペリフェラルのタイマー1が追加されます。次にSystemのTimeサービスも追加します。

手順2 MCC HarmonyでTIME System Serviceを追加する

そうすると下図の様にHarmony CoreとFreeRTOSを追加するかを順番に聞かれるのでCoreサービスはYesでFreeRTOSはNoを選択します。ちなみに余談ですが私の技量ではこのマイコンにFreeRTOSを組み込めませんでした。機能を削ってもコンパイラの最適化レベルを変更してもヒープメモリやスタックサイズを変えてビルドしてもスタックメモリが足りないかヒープメモリが足りないと怒られてうまくいきませんでした。このマイコンにはSRAMが4 kBあるのですがMCC Harmonyで提供されるFreeRTOSだと難しいのかもしれません。

次に追加されたTimeサービスのTMRにTMR1を接続します。ひし形のポートをドラッグすれば接続できます。その次にピン設定をするためにPluginsからPin Configurationをクリックします。

手順3 ピン割り当てを行うためPin Configurationを開く

PinTable(図3)、PinDiagram(図4)、PinSettings(図5)の三つが開くと思います。Diagramで視覚的にピンを割り当てもできればPinTableでピンごとに割り当て可能な機能を見ながら決めていくこともできます。PinTableでパッケージを変えるとピンの割り当て可能な箇所が変わるようです。また入出力やプルアップ、プルダウンはPinSettingsのみでできる様です。今回は手順4に示すようにRA2にGPIOピンを割り当ててDirectionをOutにしています。

図3 Pin Table設定 パッケージを選ぶとそのパッケージで割り当て可能なピンが変化する
図4 Pin Diagram 視覚的にピン設定を確認割り当てできる
図5 Pin Settingsの画面
手順4 Pin Configurationから任意のピンに機能を割り当てる

ピン設定からMCCのタブに切り替えてGenerateボタンを押します。そうすればMCCでコードが生成されます。

手順5 内容が良ければMCCタブのGenerateボタンをクリックしてコードを生成する

main.cやapp.cなどのプログラムを変更すると次回生成時にそのコードがそのまま出てきます。再利用可能なアプリケーションモジュールとして使っていくからそういった作りになっているのかなとは思っています。(アプリケーション名が過去に作ったものと一致するとその時のcファイルが開かれるみたいです)

MCCのデフォルト設定でコードを生成するとmain.cとapp.cファイルが生成されます。またmain.cにはSYS_TasksとSYS_Initialaize関数が、APP.CにはAPP_Initialize()とAPP_Tasks()が生成されるようです。(MCCの内容によって変わります。) APP.Cにはユーザー(開発者)がステートマシンで状態を管理するプログラムを書いていくみたいです。

※MCCでHarmony Core Serviceが読み込まれているとデフォルトでAPP.C(設定変更できます。)が生成されます。

アプリケーション名の変更方法

上図の様にCoreサービスをクリックすると右側に設定画面が表示されます。Generation Harmony Application Files内のApplication NameをAPPから好きな名前に変えることで変更できます。また複数のアプリケーションを作りたい場合はNumber of Applicationsを1から好きな数に変更すればその個数分のCファイルが生成されるようです。2個目、3個目も同様に名前も変更できます。また生成しない設定も可能でその際はGenerate Harmony Application Filesのチェックを外します。

Projectツリーから生成されたmain.cを開いて一度クリーンビルドを実行し、ビルドが成功することを確認します。

手順6 クリーンビルドの実行を行ってコード生成の結果を確認する

app.hとapp.cに下のコードを書いて、Runしました。使い方がわからない関数とか変数があればその参照しているヘッダファイルとかcファイルとかを読んでみると関数の使い方や定義などの説明が丁寧に書いてあったので、時間はかかるかもしれませんが、理解の助けになるかもしれません。

ちなみにヘッダーファイルは手順6の左上あたりにあるHeader Files内にアプリケーション名のファイルがあると思います。

下のコードは行数を抑えるために一部コメントを削除しています。またあまり良い書き方ではないかもしれないので参考程度にお願いします。

#include "app.h" //ヘッダファイルにappData(任意の名称で良い)の構造体やステート名称の定義などをする今回はapp.hだがMCC上で名称変更できます
#include "system/time/sys_time.h" //システムサービスのコールバック関数を利用するのに必要なので追記


APP_DATA appData;


// *****************************************************************************
// ****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************

/*******************************************************************************
  Function:
    void APP_Initialize ( void )

//時間経過フラグ
void TimeExpired(){
    appData.tmrexpired = true;
}

//初期化処理
void APP_Initialize ( void )
{
    appData.state = APP_STATE_INIT;
    appData.tmrexpired = false;  
}

    void APP_Tasks ( void )


//主な処理内容
void APP_Tasks ( void )
{

    switch ( appData.state )
    {
        /* Application's initial state. */
        case APP_STATE_INIT:
        {
            SYS_TIME_CallbackRegisterMS(TimeExpired, 0, 1000, SYS_TIME_PERIODIC); //1000ミリ秒でTimeExpired関数を呼び出す
            bool appInitialized = true;

            if (appInitialized)
            {

                appData.state = APP_STATE_SERVICE_TASKS;
            }
            break;
        }

        case APP_STATE_SERVICE_TASKS:
        {
            if(appData.tmrexpired == true){
                appData.tmrexpired = false; //時間のフラグが立った後は再度フラグをリセットしておく
                PORTAbits.RA2 = !PORTAbits.RA2;
            }
            break;
        }
           default:
        {
            break;
        }
    }
}
//APP.hの内容

#ifndef _APP_H
#define _APP_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "configuration.h"


typedef enum
{
    /* Application's state machine's initial state. */
    APP_STATE_INIT=0,
    APP_STATE_SERVICE_TASKS,
    /* TODO: Define states used by the application state machine. */

} APP_STATES;

typedef struct
{
    /* The application's current state */
    APP_STATES state;
    bool  tmrexpired; //追加
    /* TODO: Define any additional data used by the application. */

} APP_DATA;

void APP_Initialize ( void );

void APP_Tasks( void );

#endif /* _APP_H */

プログラムを書き込む前にPICKIT5のPower設定で3.25 Vの電圧を出力するようにしておきます。(デフォルトで3.25 Vになっているとは思いますが念のため)

実行結果

プログラムを実行すると動画のようになりました。

デバッグについて

今回、思ったような動作をしなかった時があったので原因を探るのにデバッグ機能を重宝しました。デバッグはPICKIT5のPGDとPGCをマイコン側のPGDx・PGCx(xは任意の番号)端子に接続してMCCで設定を行えばプログラムと本体の動作の確認ができました。書き込みはPGDx・PGCxの好きなポートにつなげばできるのですがデバッグは設定が必要です。図6のようにSystemをクリックしてDEVCFG0内のIn-Circuit Emmulator..(緑色のマーカー表示の箇所)をデバッグに使いたいポートに設定します。はじめはPGx1になっていてMCLRの隣にデバッグポートも持っていきたかったのでPGx3に変更しました。

図6 デバッグに使うポートの変更画面

図7の様に赤丸をクリックするとPICに書き込みをした後にそのままデバッグが始まります。

図7 デバッグを始める場合

図8の様に赤丸の部分でコードを1行ずつ進めたりデバッグを停止したり、最初からやり直したりできます。左の赤いボタンはデバッグ停止でその隣の黄色のボタン(灰色になっていますが…)は一時停止です。一時停止ボタンを押すことで紫色のリセットや緑のContinue、他ステップ実行やカーソル箇所でのプログラム実行を行えます。変数の状態は画面下側のVariablesタブなどを開くと確認することができます。

図8 デバッグ操作方法

終わりに

今回は32ビットマイコンでLチカを通してMPLAB Xのツールの使い方やプログラミング方法を確認しました。いろいろな試行錯誤を行って開発環境の使い方が多少なりともわかってきたように感じます。

32ビットマイコンになると16ビットマイコンよりも複雑でとっつきにくいですが、用途や実現したい内容によって選択ができるものが増えることは良いことだと思います。今回はLチカの動作確認だけですが、かなり苦戦したので(自分の技量の問題もありますが…)誰かの役に立てれば幸いです。ここまで読んでいただきありがとうございました。

UART通信の確認(PIC16F13145)

UARTで通信のテストを行ってみました。個人的な備忘録になります。

使用部品

MCCの設定

UART通信を行うためにMCCでUARTドライバーを追加しておきます。図1の様にAdd Component若しくはDrivers & Peripheralsをクリックし、検索欄にUARTを入力して図2の赤丸をクリックします。

図1 MCCのUART設定
図2 UARTの追加画面

UARTを追加すると図3の様にUART1が追加されるのでUART1をクリックして右側のツールバー内のInterrupt Drivenを有効にしておきます。ボーレート(設定値:9600)やデータサイズなど他の項目はデフォルトの値にしています。

図3 UART1の設定

次にピン入力設定を行います。図1の画面上にあるPinsかPin Package Viewタブから必要な割り当てを行います。今回はUARTを追加したので図3の下側にEUSART1の項目が追加されています。受信と送信の動作を確認したいのでRX1(受信用)をPORTBの5番(RB5)・TX1(送信用)をPORTBの6番(RB6)に割り当てました。また送信ボタン入力用にPORTAの0番(RA0)を入力とし、動作確認用にPORTCの0番(RC0)を出力(LED1)として割り当てています。

図4 ピンの入出力設定

設定が終わったらProject ResourceからGenerateします。

プログラム

main.cに下記のコードを記述しました。

#include <string.h>
#include "mcc_generated_files/system/system.h"
      
void sendhello();

void main(void)
{
    SYSTEM_Initialize();
    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;
 
    uint8_t data;
 
    while(1)
    {
        if(RA0 == 1){
                sendhello(); //送信ボタンを押したときに文字列を送信する
        }
          
        if(EUSART1_IsRxReady()){
                data = EUSART1_Read(); //入力値を読み込む
            for(uint8_t i = 0 ; i < data ; i++){
                LED1_Toggle();
                __delay_ms(100); //指定回数だけLEDを点滅させる
                LED1_Toggle();
                __delay_ms(500);
            }
                if(EUSART1_IsTxReady()){
                   EUSART1_Write(data); //入力値の確認用
                }
        }
    }    
}

void sendhello(){
    char msg[] = "Hello World\r\n";
        for(uint8_t i = 0; i < strlen(msg); i++)
        {
            if(EUSART1_IsTxReady()){
                EUSART1_Write(msg[i]); //LEDを点滅させながら文字を出力
            }
            LED1_SetHigh();
            __delay_ms(100);
            LED1_SetLow();
            __delay_ms(100);
        }
}

マイコンからパソコンへのUART通信はEUSART1_Write()で実装しています。パソコンからマイコンへはEUSART1_Read()で実装しています。EUSART1_Write()やEUSART1_Read()は1バイトずつ読み書きするためHello Worldのような文字列はfor文で1文字ずつ送信しています。受信に関しては、1文字(1バイト)のみなので1度だけ実行する形になっています。またUARTの動作確認が目的のためボタンのチャタリング対策は考慮していません。EUSARTの関数は下記リンクを参考にしました。

下記はMicroChip社のリファレンスになります。

参考:4.26.2 EUSART – Enhanced USART

組み立てた回路と外観

回路実装

図5のような回路を作りました。

図5 UART実験回路 スイッチ入力端子はプルダウンさせています

完成したものは図6に示します。

図6 実験回路外観

TeraTerm設定

そのままではCH340は使用できないのでまず下記メーカーサイトからドライバーを入手してインストールします。

ZIPファイルの中のSETUP.exeを実行してINSTALLを押すとドライバーが入ります。

CH341SER.ZIP – Nanjing Qinheng Microelectronics Co., Ltd.

図7 CH340のドライバインストール画面


TeraTermを起動する前にCH340をUSB接続します。TeraTermを起動すると接続先を聞かれるので画面下側のシリアルをクリックしてCH340と表示されているポートを選択します。

図8 TeraTerm 起動時の接続先設定画面

TeraTermの設定タブからシリアルポートをクリックすると図7の画面が表示されます。マイコン側(MPLAB XのMCC)のUART設定でボーレートや他の設定を変えている場合はTeraTerm側もそれに合わせて設定を変更します。通信設定が同じでないときちんと通信ができません。

図9 TeraTermのシリアルポート設定画面

TeraTermの端末画面(黒い画面)でマイコン側に設けたボタンを押すと端末画面にHello Worldが表示されると思います。またその画面でパソコンの任意のキーボードのキーを押すとそのキーに対応したASCIIコードがシリアルポートから送信されます。今回のプログラムでは指定回数だけマイコンのLEDを点滅させる動作をさせようとしたのですが上記の仕組みから意図した点滅動作か分かりづらくなりました。ASCIIコードは1バイトのため入力したキーとASCIIコードを照らし合わせれば意図したものかは判断できますが、折角なのでマクロ機能を使用して任意の数字(16進数)を送るようにしました。

調べてみるとTeraTermのマクロ機能を利用すればASCIIコードのデータではなく任意のデータを送れることが分かりました。マクロ機能を使用するために次の文字列をメモ帳に貼付して拡張子をttlにして保存しました。このデータをマイコン側に送ればLEDは5回点滅するはずです。

send $05
setdebug 2
図10 メモ帳の画面 コマンドを記述して保存する
図11 ttl形式でファイルを保存する

TeraTermでマクロを使用するにはコントロールからマクロをクリックして先ほど作成したttlファイルを開きます。するとコマンドが実行されます。

図12 マクロの実行方法
図13 図9で保存したマクロを開く画面

TeraTermの他のコマンドは下記が参考になると思います。

参考:TTL コマンドリファレンス

動作確認

ブレッドボード上のボタンを押すとTeraTerm上にHello Worldが表示され、マクロを実行するとブレッドボード上のLEDが5回点滅した後にTeraTerm上に05が出力されていることから、マイコンとパソコンで双方向に通信ができていることを確認できました。

動画の前半の動作はHelloWorldの送信で後半の動作はデータを受信した際の動作です。図13に実行結果の画面を示します。

図13 UARTの送受信動作の実行結果

以上の実験からUARTの送受信動作と基本的な取り扱い方の確認ができました。読んでいただきありがとうございました。

MCCのClassicを使いたかったがダメだった

目的

PIC 16F13145でSDカードの読み書きをしたかったのでMCCを使ってSDカードに対応させたかった。

実装方法

他の方の事例を調べると公式のライブラリにFATFSとSD/MMCがあるのでそれを組み込んでGenerateすれば、簡単にSDカードの読み書きできそうに見えた。(思っていた)

事前認識

32ビットマイコンはMCCのHarmonyが起動し、16ビットや8ビットマイコンはMCCのMelodyかClassicを選べるものだと思っていた。公式サイトでも普通に出来そうな雰囲気で書いてあるし、他の方はClassicを使って実装してたし…

MCCの挙動を確認してみる(MCC Core バージョン5.9.1の場合)

PIC 16F13145でプロジェクトを作成するとMCC Melodyが立ち上がるがどこを探してもMCC Classicに切り替える選択肢が無かった。MCCの古いバージョンを使ってClassicを使われておられる方もいらっしゃったのだが、何となく最新版を使いたいがために躊躇っていた。

FATFSだと容量の大きなSDカードだと対応できないとかでサポートしない方向なのかと思ってこのPICで試してみることを諦めた。どうやら32ビットマイコンならSDカードを使えるようにできるらしいことは調べて分かった。

そして別なPICを使って全く違うことを試してみようと思い、試しに買ってみたPIC 10F322用にプロジェクトを作成すると、普通にMCC Classicが立ち上がった。実にあっけなかった。

ここで16/8ビットの中でも古いマイコンはClassic、新しいマイコン16/8ビットはMelodyで32ビットマイコンはHarmonyが自動で選択されるのではと思った。

10F322・16F18313・16F13145・32MX210F016BでMCCの挙動を比較してみた。

16F13145は何事もなくMCC Melodyが起動した。Finishをクリックした後にMCCが起動しそのままMCCの設定画面に遷移した。

図1 PIC16F13145の新規プロジェクト作成の最終画面
図2 16F13145のMCC設定画面

10F322は何事もなくClassicが起動した。

図3 10F322の新規プロジェクト作成の最終画面
図4 10F322のMCC設定画面

16F18313は図5でFinishをクリックした後に図6の様に最初にMelodyかClassicのどちらにするか聞いてきた。

図5 16F18313のプロジェクト作成の最終画面
図6 16F18313のMCC初回起動時の画面
図7 図6でMelodyを選択後の画面

16F18313ではMCCのMelodyかClassicかを図6の画面で選択できるようになっているようだ。

32MX210F016Bは図8のFinishをクリックすると図9の様にHarmonyが起動した。

図8 32MX210F016Bの新規プロジェクト作成の最終画面
図9 32MX210F016BのMCC Harmonyの設定画面

まとめ

32ビットマイコンはMCC Harmony、16/8ビットマイコンはMCC Melody/Classicと漠然と認識していたが、PICの世代によって起動するMCCが異なることが分かった。また勝手な認識だが新しい世代の16/8ビットマイコンはMelody一択なのかなと思った。参考までにそれぞれのPICがいつごろから存在しているか調べてみた。

読んでいただきありがとうございました。誰かの参考になれば幸いです。

温湿度センサーSHT40Iによる室温・湿度の測定

目的

前回LCDで文字表示ができたのでI2C接続でセンサーから温度と湿度のデジタルデータを取得してその値を表示させてみました。

使用機器

回路図

前回のLCD表示に用いた回路に新たにI2C接続のSHT40Iセンサを配線しました。LCDはパラレル通信で温湿度センサはI2Cで通信しています。図1に回路図を示します。

図1 温湿度センサを追加した回路

デバイス設定

MCCを使用して図2の様に設定しました。

図2 MCCの設定画面
図3 AddComponent内の画面

前回のLCDのピン設定RC0~RC3とRS,Eの割り当てを行ったのと新たにAddComponentからI2C_Hostドライバーを追加しています。さらにMSSP1のSCL1とSDA1をPortBの6と4に割り当てています。またデフォルトでチェックが入っているかもしれませんがInterruptDrivenを有効にしておきます。設定が終わったらGenerateを押して生成します。

ソースコード

あまりきれいではないですが参考までにソースコードを載せます。以前のLCDのコードにI2C通信のコードを追記したものになります。またSHT40Iのデータシートに記載のあったサンプルプログラムを参考にデータ処理部分は流用しています。

#include "mcc_generated_files/system/system.h"
#include "stdio.h"

void lcd4_send(uint8_t code, uint8_t mode)
{
    LATC = (code & 0xF0) >> 4;  // RC0-RC3に出力
	if (mode == 0) 
		RS_SetHigh();   // 表示データ時
	else
		RS_SetLow();    // コマンドデータ時
	E_SetHigh();           
	E_SetLow();            
}

void lcd4_char(uint8_t chr){
	lcd4_send(chr, 0);	// 上位4ビット出力
	lcd4_send(chr<<4, 0);	// 下位4ビット出力
	__delay_us(50);		
}

void lcd4_ctl(uint8_t ctl){
	lcd4_send(ctl, 1);	// 上位4ビット出力
	lcd4_send(ctl<<4, 1);	// 下位4ビット出力
	__delay_ms(2);		
}

void lcd4_clear(){
	lcd4_ctl(0x01);
	__delay_ms(15);		
}

void lcd4_init(){
    __delay_ms(150);          
	lcd4_send(0x30, 1);	// 8bit mode set
	__delay_ms(5);
	lcd4_send(0x30, 1);	// 8bit mode set
	__delay_ms(1);
	lcd4_send(0x30, 1);	// 8bit mode set
	__delay_ms(1);
	lcd4_send(0x20, 1);	// 4bit mode set
	__delay_ms(1);
	lcd4_ctl(0x2E);		// DL=0 4bit mode
	lcd4_ctl(0x08);		// display off C=D=B=0
	lcd4_ctl(0x0D);		// display on C=D=1 B=0
	lcd4_ctl(0x06);		// entry I/D=1 S=0
	lcd4_ctl(0x02);		// cursor home
}
void lcd4_str(uint8_t *str){
    while(*str != 0){        
        lcd4_char(*str++); 
    }
}
int main(void)
{
    SYSTEM_Initialize();
    lcd4_init();
    INTCONbits.GIE = 1; //INTCONbits.GIEと
    INTCONbits.PEIE = 1;//INTCONbits.PEIEを記載しないと動きませんでした

    char temp[32],hum[32]; //文字列格納用
    uint8_t rx[6]; //受信データ格納用
    uint8_t cmd = 0xFD; //High Precisionコマンド
    uint16_t t_ticks, rh_ticks; //受信データ2バイト 結合用
    float t_degC, rh_pRH;
    while(1)
    {
        I2C1_Write(0x44, &cmd, 1); //コマンド送信
        while(I2C1_IsBusy());
        __delay_ms(10);
        I2C1_Read(0x44, rx, 6); //データ受信
        while(I2C1_IsBusy());

        t_ticks = rx[0] * 256 + rx[1]; //データ処理部分 SHT40Iのデータシート参照
        //checksum_t = rx[2]; //データシートに記載のチェックサム用の変数だが動作確認が目的なので使用していない
        rh_ticks = rx[3] * 256 + rx[4]; //1バイトずつ受信しているのでそれを2バイトのデータ列に変換
        //checksum_rh = rx[5]; //データシートに記載のチェックサム用の変数だが動作確認が目的なので使用していない
        t_degC = -45.0f + 175.0f * ((float)t_ticks/65535.0f); //小数に変換(温度)
        rh_pRH = -6.0f + 125.0f * ((float)rh_ticks/65535.0f); //小数に変換(湿度)
        
        if (rh_pRH > 100) //相対湿度が100 %を超えた場合
            rh_pRH = 100; 
        if (rh_pRH < 0) //相対湿度が0 %を下回った場合
            rh_pRH = 0;
        
        sprintf(temp, "Temp: %f", t_degC); //文字列変換
        sprintf(hum, "Hum: %f", rh_pRH);  //文字列変換

        lcd4_ctl(0x80);
        lcd4_str(temp);
        lcd4_ctl(0xC0);
        lcd4_str(hum);
        __delay_ms(1000);
        lcd4_clear();
        
    }    
}

動作確認

図4 動作確認が取れた時の環境

図4の様に実際に室温と湿度が数値として取得できていることが確認できます。

余談

I2Cの通信がうまくいかず3日近く悩みました。原因は、割り込みの許可をプログラムのほうで行っていなかっただけです。MCCでI2Cのドライバーを追加してInterrupt.cが生成されたので大丈夫だと思っていたのですが駄目でした。(Interruptって書いてあったら割り込みも自動でやってくれるもんだと思ってました甘く見てました)

あまり生成AIを使わずに自分の力でできたらと思って取り組んでいたのですが、一向に解決できないので彼の力も借りました。便利な時代になりましたね。

古いバージョンだと、自分で信号の制御を記述していたようですが新しいバージョンではそこが抽象化されて今回で言えばI2C1_Write関数だけで書き込みができるようになっているようです。

またI2Cの書き方でスレーブアドレスとレジスタアドレスそしてコマンドを送る形式の記述をよく見ていて、この温湿度センサではスレーブアドレスとコマンドしかデータシートに記述がなく...「レジスタアドレスって何?どう記述すればいいんだろうか...」と思っていたのですが、動作確認を通して同じI2Cでも物によってデータ形式が異なることを学びました。またパラレル通信では信号線にLEDを並列に入れても普通に動作していたのですがI2Cの場合は並列にLEDを入れるときちんと動作しなくなりました。動作確認のために入れていたのですがかえって悪化させていました。ここまで読んでいただきありがとうございました。

LCDの動作確認

LCD(ACM1602K-NLW-BBW)をPICで動作確認したのでその時の記録になります。今回は、8ビットモードと4ビットモードのそれぞれで動作確認を行いました。参考になれば幸いです。

使用部品と回路構成

以下に使用部品および図1にLCDの回路を示します。

  • PIC 16F13145
  • LCD ACM1602K-NLW-BLW(秋月電子で購入)
  • 金属皮膜抵抗2 kΩ,VoとGND間で接続 ※正しい方法ではない 図1、図2を参照
  • ブレッドボード
  • 配線
図1 4ビットモード時と8ビットモード時の回路図 右上の回路図はコントラスト調整用の半固定抵抗をVoに取り付けた場合のものを示しています。
LCDのピンアサイン
図2 LCDのピンアサイン(出典:https://akizukidenshi.com/goodsaffix/ACM1602K-NLW-BBW_VER1.pdf)

8ビットモードではデータ線はDB0~DB7とPICの対応する出力端子を接続し、4ビットモードではDB4~DB7までのデータ線とPICの対応する出力端子を接続しています。またコントラスト調整を行わないと文字がきちんと表示されないため半固定抵抗が必要です。今回は購入し忘れたため適当な抵抗を接続したところきちんと表示できたので正しくない方法(データシートの指示と違うという意味で)でやっています。

LCDの制御方法の説明
図3 LCDの制御仕様 (出典:https://akizukidenshi.com/goodsaffix/ACM1602K-NLW-BBW_VER1.pdf)
LCDの初期化手順の説明
図4 LCDの初期化手順の説明 (8ビットモード)
図5 LCDの初期化手順の説明(4ビットモード)

図4、図5は図2と図3のデータシートとは別のサイトのものです。

図4、図5の出典:ST7066U

LCDは文字を表示させるだけなので(書き込みのみ使用)5番ピンをGNDに接続しています。

MCCでRSとEをそれぞれRB5ピンとRB6ピンに出力、また4ビットモードではRC0ピンからRC3ピン、8ビットモードではRC0ピンからRC7ピンまでを出力として設定しています。

ソースコード

あまり参考になるかわかりませんがLCDを動作できた時のそれぞれのソースコードを記載しておきます。データシートによると例えば初期化の処理などでビットの状態がどちらでもよいときがあり(データシートではXで記載)、この時そのビットは1にすればよいか0にすればよいかわからず、おそらくどちらかの状態に固定していればいいのかもしれませんが…

//4ビットモードで動作させた場合
void lcd4_send(uint8_t code, uint8_t mode)
{
    LATC = (code & 0xF0) >> 4;  // RC0-RC3に出力
	if (mode == 0) 
		RS_SetHigh();   // 表示データ時
	else
		RS_SetLow();    // コマンドデータ時
	E_SetHigh();           
	E_SetLow();            
}

void lcd4_char(uint8_t chr){
	lcd4_send(chr, 0);	// 上位4ビット出力
	lcd4_send(chr<<4, 0);	// 下位4ビット出力
	__delay_us(50);		
}

void lcd4_ctl(uint8_t ctl){
	lcd4_send(ctl, 1);	// 上位4ビット出力
	lcd4_send(ctl<<4, 1);	// 下位4ビット出力
	__delay_ms(2);		
}

void lcd4_clear(){
	lcd4_ctl(0x01);
	__delay_ms(15);		
}

void lcd4_init(){
    __delay_ms(150);          
	lcd4_send(0x30, 1);	// 8bit mode set
	__delay_ms(5);
	lcd4_send(0x30, 1);	// 8bit mode set
	__delay_ms(1);
	lcd4_send(0x30, 1);	// 8bit mode set
	__delay_ms(1);
	lcd4_send(0x20, 1);	// 4bit mode set
	__delay_ms(1);
	lcd4_ctl(0x2E);		// DL=0 4bit mode
	lcd4_ctl(0x08);		// display off C=D=B=0
	lcd4_ctl(0x0D);		// display on C=D=1 B=0
	lcd4_ctl(0x06);		// entry I/D=1 S=0
	lcd4_ctl(0x02);		// cursor home
}
void lcd4_str(uint8_t *str){
    while(*str != 0){        
        lcd4_char(*str++); 
    }
}
void main(void)
{
    SYSTEM_Initialize();       
    lcd4_init();                 
    lcd4_clear(); 
    while(1)                    
    {
        lcd4_ctl(0xC0);
        lcd4_str("Test LCD!!");
        __delay_ms(1000);
    }
}
//8ビットモードで動作させた場合
void lcd_send(uint8_t code, uint8_t mode)
{
    LATC = code;  // RC0-RC7に出力
	if (mode == 0)          
		RS_SetHigh();   //データ表示時
	else
		RS_SetLow();    // コマンド時
	E_SetHigh();            
	E_SetLow();            
    __delay_us(40);
}
void lcd_print(uint8_t *str){
    while(*str != 0){
        lcd_send(*str++, 0);
    }
}

void lcd_clear(){
  lcd_send(0x01, 1);      // 消去コマンド
  __delay_ms(15);
}

void lcd_init(){
    __delay_ms(150);           
    lcd_send(0x38, 1);	// FunctionSet 
    __delay_ms(5);
    lcd_send(0x38, 1);	// FunctionSet
    __delay_ms(1);
    lcd_send(0x38, 1);	// FunctionSet
    __delay_ms(1);
  lcd_send(0x08, 1);	// Display On/Off Control
    __delay_ms(1);
    lcd_send(0x0D, 1);
    __delay_ms(1);
    lcd_clear();
    lcd_send(0x06, 1);		// EntryMode Set I/D=1 S=0
    __delay_ms(1);
    lcd_send(0x02, 1);		// Return Home
}

void main(void)
{
    SYSTEM_Initialize();       
    lcd_init();                 
    lcd_clear();              
    while(1)                
    {
        lcd_send(0xC0, 1);
        lcd_print("Test LCD!!");
        __delay_ms(1000);
    }
}

動作確認

図6 4ビットモードでの動作状態
図7 8ビットモードでの動作状態

図7と図8を見るとデータ線が4本少なくなるだけでも見た目が多少なりともすっきりしていることがわかるかと思います。プログラムに関しては8ビットモードのほうがそのままデータを出力すればよいので多少簡単になっていることがわかると思います。

まとめ

4ビットモード 8ビットモードでLCDの文字表示動作の確認ができました。余談ですがLCDの初期化に失敗していると表示が変になったり(LCDの上半分だけ全て真っ白表示)、delay関数による待ち時間が短く高速にデータが送られると、文字表示が欠落したり、意図した文字と違う文字が表示されたり意図しない箇所に表示されたり挙動がおかしくなっていました。

今回はここまでになります。最後まで読んでいただきありがとうございました。

PIC16F13145の動作確認メモ

目的

PICマイコンの取り扱い方を理解する。

部品リスト

今回、用意した部品は以下の通りです。

  • Microchip PICKIT5
  • Microchip PIC16F13145
  • AE AKIPIC ライト
  • ブレッドボード
  • 配線
  • LED
  • 抵抗 1/4 W, 1 kΩ

ソースコード作成とPICへの書き込み

PICにプログラムを書き込むためにはPIC本体の他にライタとプログラム自体を作成する開発環境が必要になります。Microchipから公式に開発環境が提供されているのでそれを導入します。今回は省略して、プロジェクト作成からのメモになります。

ファイルからNewprojectをクリックする。

上の画像の画面が出てくるのでそのままNextをクリック

PICの型式を聞いてくるので自分が使うPICの型をDevice欄に打ち込むか選択する

コンパイラを選ぶ画面になるのでXC8をクリックしてNextをクリック

プロジェクト名を決めてFinishをクリックする。

勉強のためであれば一度すべて自分で設定していくべきですが、今回は動作確認をして大まかに使い方を知りたいのでMCCを使って楽をします。(便利だし)図5がMCCの画面になります。

Application BuilderやPin Package Viewを見るとPICの構成設定が視覚的にできることがわかると思います。Pin Package ViewでPICの入出力設定ができます。今回は10番ピン(RB7)を出力ピンと(GPIOのoutput PORTB 7の南京錠をクリックしてロック表示に変えると割当される)します。画面右側のパネルに設定した箇所の情報が表示されていると思います。 画面をスクロールしてCustom Nameの名前をわかりやすいものに変えます。(変えなくてもよい)今回はLEDにしました。(図6)

Application Builderではコンフィグレーションビットやクロックの設定に加えコンポーネントを追加することで作りたい機器にあったセットアップを簡単に実現できるようです。(図7)必要な機能だけを使用しさらに効率的な動作をするためにはコンフィグレーションビットやクロックなどを理解しきちんと設定すべきです。デフォルトでもとりあえず動作するので特に触らずに行きます。(色んな人に怒られそうなので先に謝っておきますごめんなさい。)

図7の画面の上側に青色に白文字でGenerateがあるのでそれをクリックすればコンフィグレーションビットやクロック、ピンの設定が生成されます。

図8の様にmain.cにLEDの点滅動作を記述する。図9の様にPICをライタにセットして図8の上側のRun Main Projectをクリックすることでコンパイルが実行されてコンパイルに成功するとそのままPICに書き込みされる。

上手くいかなかったところ

最初にプログラムをPICに書き込もうとしたところVDDが供給されていないとエラーが出てしまいました。PICKIT5ではそれ自体でVDDを供給できるはずなのになぜと思ってしまいました。調べていくとライタ自体の設定を変えて自分で指定しないければ電源が供給されないようでした。図9で矢印のProject PropertiesからPICKIT5をクリックしてOption categoriesからPowerを選びます。Power target circuit from PIC kit 5のボックスにチェックを入れます。そのチェック欄の下に電圧設定があるのでPICごとに合った適切な電圧を入力します。(不適切な電圧を加えると最悪の場合PICが壊れる恐れがあるので注意)

このマイコンは5.5 VまでVDDに電圧を印加しても大丈夫なようなので5.0 Vの電圧を加える設定にして書き込みを試したところ上手くいきました。

動作確認

まとめ

簡単なプログラムを実行することで開発環境の使い方とPICの動作確認を行うことができました。最後まで読んでいただきありがとうございました。

初投稿

このサイトでは私の趣味で作ったものをまとめていこうと思います。

私がふとした時に振り返るためまたは理解を深めていくために作っていくものではありますが、こんな拙い記事でも誰かの役に立つこともあるかもしれないと思い、投稿していこうと思います。

私の勘違いや理解不足・勉強不足で間違いがあるかもしれませんので鵜呑みにせず参考程度に読んでいただければ幸いです。