PIC24Fシリアル通信(I2C/SPI/UART)について

〔PICの動かせ方入門に戻る〕


このページではPIC24FJ64GB002で動作するI2C/SPI/UARTのライブラリについて書いて置きます。
尚、PIC24Fファミリのデバイスで利用可能と思われますがぁ....

ピンの構成図

MPLAB X V4.10MPLAB(R) XC16 C Compiler Version 1.30コンパイラ を使用しての記事です。

《I2C》

I2Cの基本話はこちらを参考にして下さい。
I2CのPIC24Fファミリー・日本語リファレンスマニュアルはこちらを参照下さい。
ただぁ、14頁のクロックレートの記事はデータシートの記事と異なっている様ですがぁ...

PIC24FのI2C特徴は
 ・独立したマスタロジックとスレーブロジック
 ・アドレスに関係なく、モジュールを全てのメッセージに応答させるバスリピータモード(IPM I)
  (スレーブ時のみですが、ジェネラルコールとは又、別な話の様です)

I2Cは、I2C1I2C2のモジュールが2個有ります。
I2CのSDA/SCLはピンに直接割り当てられています(PPS機能でピンを割当てる事は出来ません)、

ピンの代替え機能はI2C1のみ可能で、コンフィギュレーションビットの"I2C1SEL"で行います。
 I2C1SEL : I2C1で使用するピンの選択を行う
       SEC =ASDA1(RA0)/ASCL1(RA1)を使用  PRI =SDA1(RB9)/SCL1(RB8)を使用

I2C用プルアップ抵抗をPIC内蔵で行う場合はCNPU1/CNPU2レジスターで行います。
_CN21PUE= 1 ;    // RB9(SDA1)をプルアップする
_CN22PUE= 1 ;    // RB8(SCL1)をプルアップする
_CN6PUE= 1 ;     // RB2(SDA2)をプルアップする
_CN7PUE= 1 ;     // RB3(SCL2)をプルアップする
_CN2PUE= 1 ;     // RA0(ASDA1)をプルアップする
_CN3PUE= 1 ;     // RA1(ASCL1)をプルアップする

※ I2Cで使用するピン(SDA/SCL)はAD1PCFG/TRISxレジスターでデジタル入力にする必要が有ります

↓ここからI2Cライブラリのソースファイルをダウンロードして下さい。
skI2Clib.zip(16ビットMCU専用)

skI2Clib.h

I2C通信の関数ライブラリ用ヘッダファイルです。
"skI2Clib.c"を利用する場合に
#include "skI2Clib.h" をプログラムの先頭で記述しないとだめです。

デフォルトではI2C1を使う様になりますが、I2C2側を使う場合は、
"#define I2C_MSSP2_USE"を記述して下さい。(コメントになっているので外す)

skI2Clib.c

このライブラリはI2Cデバイス(RTC/EEPROM等)と接続を行う為の関数集です。
この関数集は”16ビットMCU専用”ですが、
24EP256MC202と24FJ64GB002でしか動作させていません。
dsPIC33E/PIC24EファミリとPIC24Fファミリのデバイスで使用出来ると思われますがぁ...

I2C通信の関数ライブラリの使い方を説明します。(マスターモードのみ対応)

InitI2C_Master(speed)
 I2C通信のマスターモードで初期化を行う処理です。
 I2C通信用の速度クロック周波数は100KHz/400KHzでの設定です、
  speed  : I2Cの通信速度を指定します。(0=100KHz 1=400KHz)
 I2CxBRGレジスタの参考値(0x1FFまで設定できます)
PIC24F PIC24E FCY 4MHz 8MHz 16MHz | 10MHz 20MHz 30MHz 40MHz 60MHz 70MHz 100KHz 0x27 0x4E 0x9D | 0x0B2 0x0C5 0x128 0x18B (0x1FFまでの設定となる) 400kHz 0x09 0x12 0x25 | 0x017 0x02F 0x047 0x05F 0x08F 0x0A7
  I2CxBRGレジスの設定は"#define FCY"を見てプログラムを切替える様にしています。
  60MHz以上での100KHz速度は、0x1FFまでなので使用には注意が必要です、
   60MHz/I2CxBRG=0x1FFと設定した場合は、約112KHz程の通信速度が出ます。

ans = I2C_Start(adrs,rw)
 スレーブにスタートコンディションを発行する処理です。
  adrs  :スレーブのアドレスを指定します
  rw     :スレーブに対する動作の指定をします
       0 = スレーブに書込みなさい要求  1 = スレーブに送信しなさい要求
  ans   :0 = 正常 1 = 異常(相手からACKが返ってこない)

ans = I2C_rStart(adrs,rw)
 スレーブにリピート・スタートコンディションを発行する処理です。
  adrs  :スレーブのアドレスを指定します
  rw     :スレーブに対する動作の指定をします
       0 = スレーブに書込みなさい要求  1 = スレーブに送信しなさい要求
  ans   :0 = 正常 1 = 異常(相手からACKが返ってこない)

I2C_Stop()
 スレーブにストップコンディションを発行する処理です。

ans = I2C_Send(dt)
 スレーブにデータを1バイト送信する処理です。
  dt     :送信するデータを指定します
  ans   :0 = 正常 1 = 異常(相手からACKが返ってこない又はNOACKを返した)
 例)slave_adrsへの2バイト送信
   ans = I2C_Start(slave_adrs,RW_0);  // スタートコンディションを発行する
   if (ans == 0) {
        I2C_Send(dt) ;                // 1バイト目を送信する
        I2C_Send(dt) ;                // 2バイト目を送信する
   } else 異常 ;
   I2C_Stop() ;                       // ストップコンディションを発行する
c = I2C_Receive(ack)
 スレーブからデータを1バイト受信する処理です。
  ack    :スレーブからデータを受信した後に返答するデータを指定します
       0 = ACKをスレーブに返す 1 = NOACKをスレーブに返す(受信データが最後なら1)
  c       :受信した1バイトのデータを返す
 例)slave_adrsから2バイト受信する
   ans = I2C_Start(slave_adrs,RW_1) ;  // スタートコンディションを発行する
   if (ans == 0) {
        dt1 = I2C_Receive(ACK) ;       // 1バイト目を受信する
        dt2 = I2C_Receive(NOACK) ;     // 2バイト目を受信する
   } else 異常 ;
   I2C_Stop() ;                        // ストップコンディションを発行する
ans = I2C_SlaveRead(slv_adrs,reg_adrs,*data,kosu)
 スレーブのデバイスから指定個数のデータを読み込む処理です。
 センサー等のデバイスのレジスターから値を読み取ります
 マルチマスターに対応しています。
  slv_adrs  :スレーブのデバイスアドレスを指定します(7bitで指定)
  reg_adrs :読出すデータのレジスターアドレスを指定します
        連続的に読出す場合は、読出すレジスターの先頭アドレスを指定
  *data     :読出したデータの格納先を指定します
  kosu      :読出すデータのバイト数を指定します
   ans       :0=正常 1=異常(相手からACKが返ってこない)
        -1=他のマスターとのバス衝突が発生してリトライオーバー

ans = I2C_SlaveWrite(int slv_adrs,char reg_adrs,unsigned char *data,char kosu)
 スレーブのデバイスに指定個数のデータを書き込む処理です。
 センサー等のデバイスのレジスターに値を書き込みます
 マルチマスターに対応しています。
  slv_adrs  :スレーブのデバイスアドレスを指定します(7bitで指定)
  reg_adrs :書出すデータのレジスターアドレスを指定します
        連続的に書出す場合は、書出すレジスターの先頭アドレスを指定
  *data     :書出すデータの格納先を指定します
  kosu      :書出すデータのバイト数を指定します
   ans       :0=正常 1=異常(相手からACKが返ってこない)
        -1=他のマスターとのバス衝突が発生してリトライオーバー

I2Cスレーブアドレスについて

このライブラリは7ビットアドレスのみ対応です。
アドレス設定  アドレスは7ビットで表します、左図の1〜7ビットです。
 0ビット目はR/Wでこれはスレーブに対する読書き指示ビットです。
 R/W=0 : 書き込み要求です(スレーブは受信モード)
 R/W=1 : 読み込み要求です(スレーブは送信モード)


割り込み処理について

このライブラリを使うには"main.c"で以下の様に割込み許可を行います。
  _IPL = 0 ;      // CPUの割込み優先レベルを設定(優先レベル0以上を許可)
尚、割込み優先レベルを変更したい場合は、InitI2C_Master( )関数内の"I2C_MIP = 5"を変更します。

(参考回路)

I2C回路図  I2C接続小型LCDを使った実態配線図です、
 電源は3.3V。

 アナログ専用電圧AVDD(28番)/AVSS(27番)は
 VDD(13番)/VSS(8・19番)と同じ電源に接続
 しています。

 内蔵のレギュレータ用にVCAP端子(20番)に、10uFの
 コンデンサーを取付けVSSに落とす必要が有ります。

1番ピンはMCLR専用なのでリセット回路を取付けないのなら1K-10KΩ抵抗でVDDに接続します。
I2C1(SDA1/SCL1)を使い、I2C接続小型LCDモジュールに接続しています。
また、I2Cのプルアップ抵抗はPIC内蔵プルアップを利用しています。

I2C接続小型LCDモジュールと"skI2CLCDlib"についての詳しい話は、12F1822での記事ですがこちら
参照下さい。

参考回路のサンプルソースファイルは↓ここからダウンロードして下さい。
LCD.zip
 LCD.c・・・・・・・・・・・本体のソースプログラム
 skI2CLCDlib.c・・・・I2C接続LCDライブラリ関数ソースファイル
 skI2CLCDlib.h・・・・I2C接続LCDライブラリ用ヘッダファイル

後は、上記の"skI2Clib"と一緒にコンパイルします。

《SPI》

SPIの基本話はこちらを参考にして下さい。
SPIのPIC24Fファミリー・日本語リファレンスマニュアルはこちらを参照下さい。

PIC24FのSPI特徴は
 ・送受信バッファで8レベルのFIFO拡張バッファモードが設定可能。(本ライブラリは未対応)
 ・SPIの転送データ幅が8ビット(通常)と16ビットで選択可能。(本ライブラリは8ビット)
 ・基本的なフレーム化SPIxプロトコルモードをサポートしています。(本ライブラリは未対応)

SPIは、SPI1SPI2のモジュールが2個有ります。
SPIの使用するピンSDI1(2)/SDO1(2)/SCK1(2)は、PPS機能で割り付けないと使えません。
例えば、下記の様に記述します。
 #include <xc.h>
 #include <PPS.h>                  // Pin Re-Mapping peripheral library

 #pragma config IOL1WAY = OFF      // 周辺機器のピン割り付けは何度でも変更出来る様に許可

   PPSUnLock ;
     iPPSInput(IN_FN_PPS_SDI1,IN_PIN_PPS_RP9) ;       // RB9(SDI1)
     iPPSOutput(OUT_PIN_PPS_RP7,OUT_FN_PPS_SCK1OUT) ; // RB7(SCK1)
     iPPSOutput(OUT_PIN_PPS_RP8,OUT_FN_PPS_SDO1) ;    // RB8(SDO1)
   PPSLock ;
※ SPIで使用するピンSDI1/2はAD1PCFG/TRISxレジスターでデジタル入力にする必要が有ります。
  (SCK/SDOはデジタル出力です)
※ マスターでSSピンを使う場合は、デジタルピンの何れでも良いが、初期化時にHIGHにします。
※ スレーブでSSピンを使う場合は、PPSで割り付けてから使用します。
※ "#include <PPS.h>"のライブラリを使う場合はこちらを参照し、インストールを行います。

↓ここからSPIライブラリのソースファイルをダウンロードして下さい。
skSPIlib.zip(16ビットMCU専用)

skSPIlib.h

SPI通信を行う関数のヘッダファイルです。
"skSPIlib.c"を利用する場合に
#include "skSPIlib.h" をプログラムの先頭で記述して下さい。

デフォルトではSPI1を使う様になりますが、SPI2側を使う場合は、
"#define SPI_MSSP2_USE"を記述して下さい。(コメントになっているので外す)

skSPIlib.c

このライブラリはSPI通信を行う為の関数集です。
この関数集は”16ビットMCU専用”ですが、
24EP256MC202と24FJ64GB002でしか動作させていません。
dsPIC33E/PIC24EファミリとPIC24Fファミリのデバイスで使用出来ると思われますがぁ...

SPI通信を行う関数の使い方を説明します。(マスターモードのみ対応)

SPI_Init(mode,divider,sdo)
 SPIモードの設定と初期化を行う処理
 マスターモード、8ビット通信、セカンダリプリスケール 1:1で設定しています。
 セカンダリプリスケールは、SPI_setClockDivider( )関数で変更可能です。
  mode   :SPIの転送モードを設定します(クロック極性とクロック位相の組み合わせ)
        SPI_MODE1 = クロック極性(0:LOW) クロック位相(0:アイドル0Vで、0V->5Vに変化で転送)
        SPI_MODE0 = クロック極性(0:LOW) クロック位相(1:アイドル0Vで、5V->0Vに変化で転送)
        SPI_MODE3 = クロック極性(1:HIGH) クロック位相(0:アイドル5Vで、5V->0Vに変化で転送)
        SPI_MODE2 = クロック極性(1:HIGH) クロック位相(1:アイドル5Vで、0V->5Vに変化で転送)
  divider :SPIの通信速度を設定します(プライマリプリスケール)
        SPI_PPRE_DIV4 = 4:1  (FCY/4)/セカンダリプリスケール
        SPI_PPRE_DIV16 = 16:1 (FCY/16)/セカンダリプリスケール
        SPI_PPRE_DIV64 = 64:1 (FCY/64)/セカンダリプリスケール
  sdo     :(未使用:8ビット版ライブラリとの互換性の為に残しています)

ans = SPI_setDataMode(mode)
 SPIの転送モードの変更を行う処理
 mode値はSPI_Init( )関数を参照。

ans = SPI_setClockDivider(divider1,divider2)
 SPIの通信速度の変更を行う処理
 通信速度 = (FCY/プライマリプリスケール)/セカンダリプリスケール
 プライマリプリスケール 1:1とセカンダリプリスケール 1:1の組み合わせは避ける
  divider1 :プライマリプリスケールを設定します
        3=1:1  2=4:1  1=16:1  0=64:1
  divider2 :セカンダリプリスケールを設定します
        7=1:1  6=2:1  5=3:1  4=4:1  3=5:1  2=6:1  1=7:1  0=8:1

ans = SPI_transfer(dt)
 SPI通信でのデータ送信とデータ受信を行う処理
  dt  :8ビットの送信するデータを指定します
  ans:8ビットの受信したデータを返します

(参考回路)

SPI回路図  7セグメントLEDシリアルドライバモジュールを
 使った実態配線図です。
 回路の電源は3.3V。

 アナログ専用電圧AVDD(28番)/AVSS(27番)は
 VDD(13番)/VSS(8・19番)と同じ電源に接続
 しています。

 内蔵のレギュレータ用にVCAP端子(20番)に、10uFの
 コンデンサーを取付けVSSに落とす必要が有ります。

1番ピンはMCLR専用なのでリセット回路を取付けないのなら1K-10KΩ抵抗でVDDに接続します。
SPI1を使っていますので、 RB9:SDI1(18番)/RB8:SDO1(17番)/RB7:SCK1(16番)にPPS機能で
割り付けています。

7セグメントLEDシリアルドライバモジュールについての話と関数の使い方はこちらを参照下さい。

参考回路のサンプルソースファイルは↓ここからダウンロードして下さい。
SevenSEG.zip
 SevenSEG.c・・・・・・・・・・本体のソースプログラム

 後は、上記の"skSPIlib"と一緒にコンパイルします。

 動作は、起動させると3秒後に0-9の数値表示を1秒毎に繰り返します。

《UART》

UARTの基本話はこちらを参考にして下さい。
UARTのPIC24Fファミリー・日本語リファレンスマニュアルはこちらを参照下さい。

PIC24FのUART特徴は
 ・UxCTSおよびUxRTSピンによるハードウェアフロー制御(本ライブラリは未対応)
 ・偶数/奇数、又はパリティ無しと1/2ストップビットのオプション設定可能
 ・16MIPSで1Mbps〜15bpsのbaudレートレンジ
 ・送受信バッファに4段のFIFOバッファ
 ・診断用ループバックモードをサポート
 ・IrDAエンコーダ/ デコーダロジックをサポート
 ・LINバスをサポート
 ・アドレス検出付き9ビットモードのサポート

UART、UART1UART2のモジュールが2個有ります。
UARTの使用するピンU1RX/U1TXとU2RX/U2TXは、PPS機能で割り付けないと使えません。
例えば、下記の様に記述します。
 #include <xc.h>
 #include <PPS.h>                  // Pin Re-Mapping peripheral library

 #pragma config IOL1WAY = OFF      // 周辺機器のピン割り付けは何度でも変更出来る様に許可

   PPSUnLock ;
     iPPSInput(IN_FN_PPS_U1RX,IN_PIN_PPS_RP8) ;    // RB8(RX1)
     iPPSOutput(OUT_PIN_PPS_RP9,OUT_FN_PPS_U1TX) ; // RB9(TX1)
   PPSLock ;
※ UARTで使用するピンU1RX/U2RXはAD1PCFG/TRISxレジスタでデジタル入力にする必要が有ります
※ "#include <PPS.h>"のライブラリを使う場合はこちらを参照し、インストールを行います。

↓ここからUARTライブラリのソースファイルをダウンロードして下さい。
skUARTlib.zip(16ビットMCU専用)

skUARTlib.h

USART機能にアクセスする為のライブラリ用のヘッダファイルです。
"skUARTlib.c"を利用する場合に
#include "skUARTlib.h" をメインプログラムの先頭で記述して下さい。

下記の様に記述されています、32MHz以外のFCYクロックに変える場合は変更します。
#ifndef FCY
  // Unless already defined assume 32MHz(16MIPS) system frequency
  #define FCY 16000000UL      // Fosc/2(16MIPS)
  #include 
#endif
受信バッファのサイズを変更する場合は下の行を変更して下さい。
(但し、UART_BUFFER_SIZE - 1 の個数だけ受信可能です、それ以上は読み捨てられます。)

#define UART_BUFFER_SIZE  64  // USARTの受信バッファサイズ

デフォルトではUART1を使う様になりますが、UART2側を使う場合は、
"#define UART2_USE"を記述して下さい。(コメントになっているので外す)
このライブラリでは、UART1又はUART2の何方か一方のみ利用可能です。

パリティビットについて

通常の通信パラメータは"Data=8bit Parity=none Stop=1bit Flow=none"となります。
この設定は、"skUARTlib.h"の"#define UART_8N1"を生かします。
でぇ、パリティ(奇数)有りは"Data=8bit Parity=0dd Stop=1bit Flow=none"となります。
この設定は、"skUARTlib.h"の"#define UART_8O1"を生かします。
又、Parity=Evenの偶数パリティは"#define UART_8E1"を生かします。

パリティビット等のシリアル通信書式についてはこちらを参照下さい。

skUARTlib.c

このライブラリはUART通信を行う為の関数集です。
この関数集は”16ビットMCU専用”ですが、
24EP256MC202と24FJ64GB002でしか動作させていません。
dsPIC33E/PIC24EファミリとPIC24Fファミリのデバイスで使用出来ると思われますがぁ...

送受信相手がUARTに対応していればこの関数集は利用可能です。

UARTにアクセスを行う関数の使い方を説明します。

InitUART(rx, tx, brg)
 USART通信の初期化を行う処理
  rx :(未使用:8ビット版ライブラリとの互換性の為に残しています)
  tx :(未使用:8ビット版ライブラリとの互換性の為に残しています)
  brg :ボーレートを指定します(通信速度になります)
     速度が9600ボーレートなら9600と指定します。

  8ビット版ライブラリとボーレートの指定方法が異なります。

UART_Send(dt,num)
 相手に指定した個数のデータを送信する処理
  dt  :送信するデータを格納した配列を指定します
  num:送信するデータの個数を指定します。

UART_Write(dt)
 相手に1バイトのデータを送信する処理
  dt  :送信するデータを指定します

ans = UART_Available( )
 受信したデータの個数を返す処理
  ans:受信したデータの個数を返します

ans = UART_Read( )
 受信したデータを1バイト読み込む処理
  ans:受信したデータを返します、0xffffを返したら受信データは空です
 例)
   char dt ;

   if (UART_Available() != 0) {
        dt = UART_Read() ;    // 1バイトデータを受信する
        UART_Write(dt) ;      // 1バイトデータを送信する
   }
UART_Flush( )
 受信バッファをクリアする処理
 受信バッファにデータを受信していてもこの関数を実行させればデータはなくなります。

割り込み処理について

このライブラリを使うには"main.c"で以下の様に割込み許可を行います。
  _IPL = 0 ;      // CPUの割込み優先レベルを設定(優先レベル0以上を許可)
尚、割込み優先レベルを変更したい場合は、InitUART( )関数内の"UEIP/UTXIP/URXIP"を変更します。

skUART2lib.h *1)
skUART2lib.c

"skUARTlib"はUART1/UART2の何れかを利用する用に出来ていましたが、 "skUART2lib"は"skUARTlib"をUART2専用にした物ですので使い方は同じです。
ですので、"skUARTlib"でUART1にして"skUART2lib"でUART2を使用する事が出来ます。

skUARTlib.zip に含めて置きます。

(参考回路)

USARRT回路図  FT234X使用の超小型USBシリアル変換
 モジュール
を使った実態配線図です。
 モジュール側の5V端子は接続しません、
 外部から3.3Vを供給します。
 アナログ専用電圧AVDD(28番)/AVSS(27番)は
 VDD(13番)/VSS(8・19番)と同じ電源に接続
 しています。

 内蔵のレギュレータ用にVCAP端子(20番)に、10uFの
 コンデンサーを取付けVSSに落とす必要が有ります。

1番ピンはMCLR専用なのでリセット回路を取付けないのなら1K-10KΩ抵抗でVDDに接続します。
UART1の端子はRB8:RX1(17番)/RB9:TX1(18番)にPPS機能で割り付けています。

USBシリアル変換モジュール関連の記事は、12F1822での内容ですがこちらを参考にして下さい。

参考回路のサンプルソースファイルは↓ここからダウンロードして下さい。
testUART.zip
 testUART.c・・・・・・・・・・・本体のソースプログラム

後は、上記の"skUARTlib"と一緒にコンパイルします。

動作はパソコンのターミナルソフト等からデータを送ります、
PICは1バイト毎に受信し、受信データに+1をたして返します。
例えば、キーボードの"1"を押せば、1(0x31)+1 = 2(0x32)なので"2"が返ります。



"skUART2lib"を追加(*1) 2020/05/13


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2020 Shigehiro Kimura All Rights Reserved.