I2CエクスパンダIC PCF8574



 I2C通信を使ってIOポートを増やすI2CエクスパンダというICがあります。いくつかのメーカーが製造しており、様々な型番が出回っています。その中で古くからあるポピュラーなPCF8574を使ってみました。基板に組み上がった製品がAITENDOから出ていましたので、この製品を使った実験をしてみました。2021年1月時点では在庫なしの状態になっていますが、秋月電子からもDIP形式のものが販売されています。


◎ピン接続

 SCL:M12のP1_6に接続 2kΩの抵抗でプルアップ
 SDA:M12のP1_0に接続 2kΩの抵抗でプルアップ

 A0 :GNDに接続
 A1 :GNDに接続
 A2 :GNDに接続
 (スレーブアドレスは、出力時:0x40  入力時:0x41)

 P0 :LED0に接続
 P1 :LED1に接続
 P3 :SWに接続
 



 PCF8574Nの型番の製品はアドレスピンA0、A1、A2の接続をVCC、GNDに接続することによってスレーブアドレスを変更することができます。スレーブアドレスは、0x40〜0x47の8通りに設定することが可能です。つまり、同じI2Cの線上にに8ビットのI/Oポートを8個増設することが可能となります。

 また、後ろにPCF8574ANのようにAがつく型番の製品は異なるスレーブアドレスの型番の製品で、0x70〜0x77の8通りを設定することが可能ですので、さらに8個の8ビットのI/Oポートを増設することが可能となります。NとAの型番の製品をあわせると、何と、16個の8ビットポートを使うことが可能となります。


◎PCF8574のポートにデータを出力する関数
 P0〜P7が8ビット入出力ポートになります。データを出力する場合は、I2C通信の手順に従って、まず、PCF8574のスレーブアドレス(0x40)を転送しアクナレッジ信号を受信待機した後、出力データを書き込むだけです。

void IO_write(unsigned char add,unsigned char data) ・・・引数はアドレス、データ
{          
  unsigned char idt;      
  start(); ・・・I2Cスタートコンディション
  do{      
    idt=I2C_write(add) ・・・PC8574のスレーブアドレスを送る
  }while(idt!=0); ・・・ACK信号を待つ
  I2C_write(data); ・・・出力データを送る
  stop(); ・・・I2Cストップコンディション
 



◎PCF8574のポートからデータを受信する関数
 ポートからデータを入力する場合は、I2C通信の手順に従って、まず、PCF8574のスレーブアドレス(0x40)を転送しアクナレッジ信号を受信待機した後、読み込み時のスレーブアドレス 0x41(0x40+0x01)を送った後、I2C_read()関数を使って入力データを受け取る。

unsigned char IO_read(unsigned char add) ・・・引数はアドレス
{          
  unsigned char idt,a;      
  start(); ・・・I2Cスタートコンディション
  do{      
    idt=I2C_write(add) ・・・PC8574のスレーブアドレスを送る
  }while(idt!=0); ・・・ACK信号を待つ
  add |=0x01; ・・・読み込み時のスレーブアドレス0x41
  start(); ・・・I2Cスタートコンディション
  I2C_write(add) ・・・読み込み時のスレーブアドレスを送る
  a=I2C_read() ・・・IOより読み込む
  stop(); ・・・I2Cストップコンディション
  return a; ・・・読み込んだ値を返す
    


 複数のPCF8574を使った場合、あまり配線を長くとると動作しない現象が出ました。配線を短くすると正常に動作するようになりました。また、Acknowledge信号を受信待機する部分をコメントアウトしたところ、正常に動作します。信号のタイミングがうまく取れないのかもしれません。回路的に取り出す電流が少なくLEDの点滅がはっきりしない場合は、トランジスタを使い電流を増幅すると良いようです。


 プログラムは、メイン関数の”EM_I2C_8574.c”、関数”I2C_ctrl.c”、ヘッダーファイル”I2C_ctrl.h”の3つから構成されています。プログラムは、PCF8574のP0、P1に接続したLEDを順番に点滅させて、PCF8574のP3に接続したSWの値を読み取り、その結果を同じI2Cに接続されたLCDに表示させています。


◎メインプログラム EM_I2C_8574.c

01:/***********************************************************************/
02:/* I2CエキスパンダIC PCF8574を使う */
03:/* FILE :EM_I2C_8574.c */
04:/* DATE :Sat, Jul 26, 2014 */
05:/* DESCRIPTION :main program file. */
06:/* CPU GROUP :M12A */
07:/* Programmed By S.Noritomo */
08:/* This file is generated by Renesas Project Generator (Ver.4.19). */
09:/* NOTE:THIS IS A TYPICAL EXAMPLE. */
10:/***********************************************************************/
11:#include "sfr_r8m12a.h"  /* R8C/M12A SFRの定義ファイル */
12:#include "I2C_ctrl.h"
13:
14:void main(void)
15:{
16:  unsigned char x;
17:  init();
18:  lcd_init();      // LCD初期化
19:  LCD_cursor(1,0);
20:  LCD_str("PCF8574 テスト");
21:  wait_ms(1000); 22:  while(1){
23:    IO_write(ADD0,0x01); // No1-PCF8547のp0-LEDをON
24:    wait_ms(500);
25:    IO_write(ADD0,0x02); // No1-PCF8547のp1-LEDをON
26:    wait_ms(500);
27:
28:    IO_write(ADD0,0x08); // No1のp3ピンを入力モードにする
29:    x=IO_read(ADD0 ) & 0x08;// 入力ポートの値を読み込む
30:    x >>= 3;       // p3のビットを最下位ビットにシフト
31:    x &= 0x01;
32:    LCD_cursor(2,0);
33:    LCD_str("No1-P3 = ");
34:    LCD_data(x+0x30);   // p3の値を表示 SW ON=1 OFF=0
35:  }
36:}


◎ヘッダーファイル I2C_ctrl.h

01:/***********************************************************************/
02:/* FILE :I2C_ctrl.h */
03:/***********************************************************************/
04:#define    S_SCL    p1_6=1// I2Cクロック p1_6を1にする
05:#define    C_SCL    p1_6=0// I2Cクロック p1_6を0にする
06:#define    S_SDA    p1_0=1// I2Cデータ p1_0を1にする
07:#define    C_SDA    p1_0=0// I2Cデータ p1_0を0にする
08:#define    SDA_IN    pd1_0=0// p1_0を入力ポートに設定
09:#define    SDA_OUT   pd1_0=1// p1_0を出力ポートに設定
10:#define    SDA_data   p1_0 // データポートをp1_0に設定
11:#define    AD      0x7c // LCDのスレーブアドレス 0B 0111 110+0(c/w)
12:#define    CLOCK    20000
13:#define    ADD0     0x40 // No1のPCF8574書き込み時のアドレス
14:#define    ADD2     0x42 // No2のPCF8574書き込み時のアドレス
15:#define    ADD4     0x44 // No3のPCF8574書き込み時のアドレス
16:
17:/*======================================*/
18:/*プロトタイプ宣言           */
19:/*======================================*/
20:void wait_us(unsigned long usec);// us単位のタイマ
21:void wait_ms(unsigned long msec);// ms単位のタイマ
22:void delay(void);        // 6usの遅延関数
23:void start(void);        // I2C開始
24:void stop(void);        // I2Cストップ
25:void ack(void);         // ACKNOWLEDGE信号受信
26:void H_CLK(void);        // クロックHigh
27:void L_CLK(void);        // クロックLow
28:void lcd_init(void);       // lcd初期化関数
29:int I2C_write(unsigned char data);// 書き込みの基本関数
30:unsigned char I2C_read(void);  // 読み込みの基本関数
31:void LCD_cmd(unsigned char cmd); // 液晶へのコマンド出力関数
32:void LCD_data(unsigned char data);// 液晶への1文字出力関数
33:void LCD_str(char* ptr);     // 液晶への文字列表示関数
34:void LCD_clear(void);       // 液晶のクリャー関数
35:// カーソル位置設定関数 line(行数):1 or 2 cursor(左端からの位置):0〜f
36:void LCD_cursor(unsigned char line,unsigned char cursor);
37:void init(void);
38:void IO_write(unsigned char add,unsigned char data);// PCF8574のIOからの出力1バイト
39:unsigned char IO_read(unsigned char add); // PCF8574からの読み込み


◎コントロール関数 I2C_ctrl.c

01:/***************************************************************/
02:/*FILE:I2C_ctrl.c                       */
03:/***************************************************************/
04:/*インクルード             */
05:/*======================================*/
06:#include "sfr_r8m12a.h"    /* R8C/M12A SFRの定義ファイル */
07:#include "I2C_ctrl.h"
08:/*======================================*/
09:/*プロトタイプ宣言           */
10:/*======================================*/
11:/* I/O 初期化関数 */
12:void init(void)
13:{
14:  int i;
15:  prc0  = 1;   // プロテクトレジスタ解除
16:  hocoe  = 1;  // 高速オンチップオシレータ発振開始
17:  for(i=0;i<50;i++);// 発振安定まで待つ
18:  hscksel = 1;  // 高速オンチップオシレータ選択ビットを1にする
19:  scksel = 1;  // システム基準クロックを高速にする
20:  prc0  = 0;  // プロテクトレジスタ禁止
21:  pd1   = 0x41;// p1_0,p1_6 出力設定 0100 0001
22:}
23:/******************************************************/
24:/*液晶初期化関数                     */
25:/*******************************************************/
26:void lcd_init(void)
27:{
28:  wait_ms(100);
29:  LCD_cmd(0x38);// Function set 8bit 21Line Normal mode
30:  wait_us(30);
31:  LCD_cmd(0x39);// Function set 8bit 21Line Extended mode
32:  wait_us(30);
33:  LCD_cmd(0x14);// Internal OSC Frequency 183Hz BIAS 1/5
34:  wait_us(30);
35:  LCD_cmd(0x70);// Contrast set 0x70:2.7V 〜 0x30:3.0v
36:  wait_us(30);
37:  LCD_cmd(0x56);// Power Icon Contrast control
38:  wait_us(30);
39:  LCD_cmd(0x6c);// Follow control 3.3v
40:  wait_ms(400);
41:  LCD_cmd(0x38);// Set normal mode
42:  wait_us(30);
43:  LCD_cmd(0x0c);// Display ON
44:  wait_us(30); 45:  LCD_cmd(0x01);// Clear Display
46:  wait_ms(10);
47:}
48:/********************************************************/
49:/* タイマ本体  us単位のwait関数 */
50:/********************************************************/
51:void wait_us(unsigned long usec)
52:{
53:  usec = (usec * CLOCK) / 45000;
54:  while(usec --);{}
55:}
56:/********************************************************/
57:/* タイマ本体  ms単位のwait関数 */
58:/********************************************************/
59:void wait_ms(unsigned long msec)
60:{
61:  unsigned int i;
62:  for(i=0;i 63:    wait_us(1000);
64:  }
65:}
66:/*********************************************************/
67:/* 6usの遅れ関数                    */
68:/*********************************************************/
69:void delay(void)
70:{
71:  unsigned char i;
72:  for(i=0;i<5;i++){}
73:}
74:/********************************************************/
75:/* 開始条件                      */
76:/********************************************************/
77:void start(void)
78:{
79:  SDA_OUT;
80:  H_CLK();
81:  S_SDA;
82:  delay();
83:  C_SDA;
84:  delay()
; 85:  L_CLK();
86:  delay();
87:}
88:/******************************************************/
89:/* 終了条件                     */
90:/******************************************************/
91:void stop(void)
92:{
93:  SDA_OUT;
94:  L_CLK();
95:  C_SDA;
96:  delay();
97:  H_CLK();
98:  delay();
99:  S_SDA;
100: delay();
101:}
102:/*******************************************************/
103:/* クロックをHighにする */
104:/*******************************************************/
105:void H_CLK(void)
106:{
107:  S_SCL;
108:  delay();
109:}
110:/*******************************************************/
111:/* クロックをLowにする                  */
112:/*******************************************************/
113:void L_CLK(void)
114:{
115:  C_SCL;
116:  delay();
117:}
118:/*******************************************************/
119:/* ACKNOWLEDGE                    */
120:/*******************************************************/
121:void ack(void)
122:{
123:  SDA_IN;
124:  S_SCL;
125:  delay();
126:  C_SCL;
127:  delay();
128:  SDA_OUT;
129:}
130:/**********************************************************/
131:/* 書き込みの基本関数 */
132:/* 引数 data 8ビットデータ */
133:/* 戻り値 ack */
134:/**********************************************************/
135:int I2C_write(unsigned char data)
136:{
137:  int i;
138:  int ack;
139:  SDA_OUT;       // データラインを出力にする
140:  for(i=0;i<8;i++){  // 1ビト毎にクロックと共に送信
141:    if(data & 0x80){// ビットの値がHigh(=1)だったら
142:      S_SDA;  // データラインをHigh(=1)にする
143:    }
144:    else{
145:      C_SDA;  // ビットの値がLow(=0)だったらデータラインをLow
146:    }
147:    delay();
148:    H_CLK();// クロックを作る
149:    L_CLK();// 送信
150:    data<<=1;// 次のビットヘシフト
151:  }      // 8ビット繰り返す
152:  SDA_IN;   // データラインを入力モードにする
153:  H_CLK();  // クロック信号を作る
154:  ack=SDA_data;// クロックHighの状態でp1_0の値をackに入れる
155:  L_CLK();  // クロックLow
156:  SDA_OUT;
157:  delay();
158:  return ack; // ACK信号を返す
159:}
160:/******************************************************/
161:/* 読み込み基本関数                 */
162:/* 引数 なし                    */
163:/* 戻り値 読み込んだ8ビットデータ            */
164:/******************************************************/
165:unsigned char I2C_read(void)
166:{
167:  int i;
168:  unsigned char data;
169:  data=0;
170:  SDA_IN;
171:  for(i=0;i<8;i++){
172:    data<<=1;
173:    S_SCL;
174:    delay();
175:    data |=(SDA_data);
176:    C_SCL;
177:    delay();
178:  } 179:  H_CLK();
180:  S_SDA;
181:  delay();
182:  C_SDA;
183:  L_CLK();
184:  return data;
185:}
186:/***************************************************/
187:/* 液晶へのコマンド出力関数             */
188:/***************************************************/
189:void LCD_cmd(unsigned char cmd)
190:{
191:  start();// スタートコンディ゙ション
192:  I2C_write(AD);// アドレス指定
193:// C0+RS+000000 C0=1:連続で通信可 C0=0:stopコンディションで終了
194:// RS=0:次のバイトでコントロールコード送信可能
195:// I2C_write(0x80);// 次はコマンド出力を指定 連続
196:  I2C_write(0x00);// 次はコマンド出力を指定 連続ではない
197:  I2C_write(cmd);// コマンド送信出力
198:  stop();// ストップコンディション
199:  wait_us(50);
200:// delay();
201:}
202:/******************************************************/
203:/* 液晶への1文字出力関数              */
204:/******************************************************/
205:void LCD_data(unsigned char data)
206:{
207:  start();// スタートコンディ゙ション
208:  I2C_write(AD);// アドレス指定
209:// C0+RS+000000 C0=1:連続で通信可 C0=0:stopコンディションで終了
210:// RS=1:次のバイトでデータ出力を指定
211:// I2C_write(0xc0);// 次はデータ出力を指定 連続
212:  I2C_write(0x40);// 次はデータ出力を指定 連続ではない
213:  I2C_write(data);// 1文字データ出力
214:  stop();// ストップコンディション
215:  wait_us(50);
216:// delay();
217:}
218:/******************************************************/
219:/* 液晶への文字列表示関数              */
220:/******************************************************/
221:void LCD_str(char* ptr)
222:{
223:  while(*ptr !=0){
224:    LCD_data(*ptr++);
225:  }
226:}
227:/******************************************************/
228:/* 液晶画面のクリャー関数                */
229:/******************************************************/
230:void LCD_clear(void)
231:{
232:  LCD_cmd(0x01);
233:}
234:/******************************************************/
235:/* 液晶のカーソル位置設定関数            */
236:/******************************************************/
237:void LCD_cursor(unsigned char line,unsigned char cursor)
238:{
239:  if(line == 1){
240:    LCD_cmd(0x80 | cursor);
241:  }
242:  else{
243:    LCD_cmd(0xc0 | cursor);
244:  }
245:}
246:/*********************************************/
247:/* PC8574のポート にデータを出力する関数    */
248:/*********************************************/
249:void IO_write(unsigned char add,unsigned char data)
250:{
251:  unsigned char idt;
252:  start();// I2Cスタートコンディション
253:  do{
254:    idt=I2C_write(add);// PC8574のスレーブアドレスを送る
255:  }while(idt !=0);// ACK信号を待つ
256:  I2C_write(data);// 出力データを送る
257:  stop();// I2Cストップコンディション
258:}
259:/**********************************************/
260:/* PC8574のポートからデータを受け取る関数    */
261:/**********************************************/
262:unsigned char IO_read(unsigned char add)
263:{
264:  unsigned char idt,a;
265:  start();// I2Cスタートコンディション
266:  do{
267:    idt=I2C_write(add);// PC8574のスレーブアドレスを送る
268:  }while(idt != 0);// ACK信号を待つ
269:  add |= 0x01;// 読み込み時のスレーブアドレスにする(0x41=0100 0001)
270:  start();// I2Cスタートコンディション
271:  I2C_write(add);// 読み込み時のアドレスを送る
272:  a = I2C_read();// IOより読み込む
273:  stop();// I2Cストップコンディション
274:  return a;
275:}



プロジェクトファイルのダウンロード



メニューに戻る