ARMアーキテクチャ用のSTM32およびmikroCコンパイラの最初のステップ-パート4-HD4478に基づくI2C、pcf8574、およびLCD接続

マイクロコントローラに接続されたさまざまなマイクロ回路でよく使用される、広く普及したi2cインターフェイスの操作に、次の記事を捧げたいと思います。



I2Cは、2つの物理的な接続(共通ワイヤに加えて)で動作するバスです。 インターネットについてはかなり多くのことが書かれており、 Wikipediaにも良い記事があります。 さらに、バス操作アルゴリズムについては、 ここで非常に明確に説明されています 。 つまり、バスは2線式の同期バスです。 バスには最大127個のデバイスを同時に配置できます(デバイスアドレスは7ビットです。後でこれに戻ります)。 以下は、MKをマスターとして、デバイスをi2cバスに接続する典型的な図です。







i2cの場合、すべてのデバイス(マスターとスレーブの両方)はオープンドレイン出力を使用します。 簡単に言えば、彼らはバスを地上に引くことしかできません。 プルアップ抵抗により、高いバスレベルが提供されます。 これらの抵抗の値は、通常4.7〜10 kOhmの範囲で選択されます。 i2cは、デバイスを接続する物理回線に対して十分な感度があるため、大容量の接続(たとえば、長く細いケーブルまたはシールド付きケーブル)を使用する場合、この容量の影響により信号フロントが「ぼやけ」、バスの通常の動作に干渉する可能性があります。 プルアップ抵抗が小さいほど、この静電容量は信号エッジの応答に影響を与えませんが、i2cインターフェイスの出力トランジスタにはより多くの負荷がかかります。 これらの抵抗の値は、特定の実装ごとに選択されますが、2.2 kOhms未満にしないでください。そうしないと、バスで動作するデバイスの出力トランジスタを単純に焼き付けることができます。



バスは、SDA(データライン)とSCL(クロック信号)の2つのラインで構成されています。 マスターデバイスは 、通常はMKであるバスをクロッキングしています。 SCLが高い場合、情報はデータバスから読み取られます。 SDAは、クロック信号が低い場合にのみ変更できます。 SCLレベルが高い場合、 START信号が生成されるとSDAの信号が変化し(SCLレベルが高いとSDAの信号がHighからLowに変化します)、 STOP -SCLレベルが高いとSDAの信号はLowからHighに変化します。



それとは別に、i2cではアドレスは7ビットの数値で指定されると言われるべきです。 8-最下位ビットはデータ転送の方向を示します0-スレーブがデータを送信することを意味し、1-受信します。 。 要するに、i2cを使用するためのアルゴリズムは次のとおりです。





STM 32には、ハードウェアで実装されたi2cバストランシーバーがあります。 MKには、このようなモジュールが2つまたは3つ存在する場合がありますが、その構成には、使用されるMKのリファレンスで説明されている特別なレジスタが使用されます。



MicroCでは、i2c(および周辺機器)を使用する前に、適切に初期化する必要があります。 これを行うには、次の機能を使用します(ウィザードとしての初期化):



I2Cn_Init_Advanced(unsigned long : I2C_ClockSpeed, const Module_Struct *module);
      
      







まずバスのフリーネスをチェックします。これには、 I2Cn_Is_Idle()関数があります。 バスが空いている場合は1、交換中の場合は0を返します。



次に、 STARTシグナルを生成します。



 I2Cn_Start();
      
      





ここで、 nはマイクロコントローラーの使用済みi2cモジュールの番号です。 この関数は、バスでエラーが発生した場合は0を返し、すべてが正常であれば1を返します。



データをスレーブに転送するには、次の関数を使用します。



 I2Cn_Write(unsigned char slave_address, unsigned char *buf, unsigned long count, unsigned long END_mode);
      
      







スレーブからデータを読み取るには、次の関数を使用します。



 I2Cn_Read(char slave_address, char *ptrdata, unsigned long count, unsigned long END_mode);
      
      







MKに何かを接続してみましょう。 まず、 広く普及しているPCF8574(A)チップ。 これは、i2cバスを制御する入出力ポートエクステンダーです。 この超小型回路には、物理​​的な入出力ポートである内部レジスタが1つだけ含まれています。 つまり、バイトが彼女に渡されると、彼はすぐに彼女の結論にさらされます。 そこからバイトを読み取ると(読み取りフラグ、 RESTERTシグナル STARTアドレスを送信し、データを読み取り、最終的にSTOPシグナルを生成します)、出力に論理状態が反映されます。 データシートに従ってチップを接続します。



画像






超小型回路アドレスは、端子A0、A1、A2の状態から形成されます。 PCF8574チップの場合、アドレスは0100A0A1A2になります。 (たとえば、A0、A1、A2にはそれぞれ高レベルがあり、マイクロ回路のアドレスは0b0100 111 = 0x27になります)。 PCF8574A - 0111A0A1A2の場合、配線図ではアドレス0b0111 111 = 0x3Fになります。 たとえば、A2がグランドに接続されている場合、 PCF8574Aのアドレスは0x3Bになります。 1つのi2cバスで合計16個のチップ、それぞれ8個のPCF8574AとPCF8574を同時にハングさせることができます。



何かを転送してi2cバスを初期化し、PCF8574に転送してみましょう。



 #define PCF8574A_ADDR 0x3F //c  PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) { I2C1_Start(); //   START I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); //  1      STOP } char PCF8574A_reg; //      PCF8574 void main () { I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); //  I2C delay_ms(25); //   PCF8574A_reg.b0 = 0; //   PCF8574A_reg.b1 = 1; //    while (1) { delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; //   I2C_PCF8574_WriteReg (PCF8574A_reg); //  PCF8574  } }
      
      





プログラムをコンパイルして実行すると、LEDが交互に点滅することがわかります。

LEDをカソードでPCF8574に接続しただけではありません。 問題は、ロジック0が出力に供給されると、マイクロチップは正直に出力をグランドに引き込みますが、論理1を供給すると、100μAの電流源を介して+電源に接続します。 つまり、「正直な」論理1は出力で取得できません。 また、100μAのLEDは点灯しません。 これは、PCF8574の出力を追加のレジスタなしで入力に設定するために行われます。 出力レジスタ1に書き込むだけで(実際、Vddでレッグのステータスを設定します)、単純にそれをグラウンドに短縮できます。 現在のソースでは、I / Oエクスパンダーの出力ステージが「燃え尽きる」ことはありません。 脚が地面に引っ張られている場合、地球の電位がその上にあり、論理0が読み取られます。脚が+に引っ張られている場合、論理1が読み取られます。一方で、それは単純ですが、他方では、これらの超小型回路で作業するときは常にこれを覚えておく必要があります



画像






拡張チップのピンの状態を読み取ってみましょう。



 #define PCF8574A_ADDR 0x3F //c  PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) { I2C1_Start(); //   START I2C1_Write(PCF8574A_ADDR, &wData, 1, END_MODE_STOP); //  1      STOP } void I2C_PCF8574_ReadReg(unsigned char rData) { I2C1_Start(); //   START I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); //  1      STOP } char PCF8574A_reg; //     PCF8574 char PCF8574A_out; //       PCF8574 char lad_state; //     void main () { I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); //  I2C delay_ms(25); //   PCF8574A_reg.b0 = 0; //    PCF8574A_reg.b1 = 1; //    PCF8574A_reg.b6 = 1; //   6  7  . PCF8574A_reg.b7 = 1; while (1) { delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); //    CF8574 I2C_PCF8574_ReadReg (PCF8574A_out); //   CF8574 if (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; //   1  (6     CF8574  0,  /  ) if (~PCF8574A_out.b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; //   2   2  } }
      
      





ボタンをクリックして、LEDをオンまたはオフにします。 チップにはINT出力もあります。 I / Oエクスパンダのターミナルの状態が変わるたびに、インパルスが形成されます。 これをMKの外部割り込み入力に接続することにより(外部割り込みの設定方法とそれらの操作方法については、以下の記事のいずれかで説明します)。



ポートエクスパンダーを使用して、キャラクターディスプレイを接続します。 それらの多くはありますが、それらのほとんどすべては、 HD44780コントローラーチップとそのクローンに基づいて構築されています。 たとえば、LCD2004ディスプレイを使用しました。









データシートとHD44780コントローラーは、インターネットで簡単に見つけることができます。 ディスプレイをPCF8574に、彼女をそれぞれSTM32に接続します。







HD44780は、パラレルゲートインターフェイスを使用します。 データはピンEで8(1サイクルあたり)または4(2サイクル)のゲートパルスで送信されます (立ち下がりエッジでディスプレイコントローラーによって読み取られ、1から0に移行します)。 RS出力は、ディスプレイにデータ( RS = 1 )(実際にASCIIコードから表示される文字)またはコマンド( RS = 0 )を送信しているかどうかを示します。 RWは、データ転送、書き込み、または読み取りの方向を示します。 通常、ディスプレイにデータを書き込むため、( RW = 0 )。 抵抗R6はディスプレイのコントラストを制御します。 コントラストをグラウンドまたは電源に調整して入力を接続することはできません。そうしないと、何も表示されません。 。 VT1は、MKコマンドに従ってディスプレイのバックライトをオンまたはオフにするために使用されます。 MicroCには、パラレルインターフェースでこのようなディスプレイを操作するためのライブラリがありますが、通常、ディスプレイに8脚を費やすのは高価なので、ほとんどの場合、PC8574を使用してこのような画面を操作します。 (誰か興味があるなら、並列インターフェースを介してMicroCに組み込まれたHD44780に基づくディスプレイの操作についての記事を書きます。)交換プロトコルはそれほど複雑ではありません(4データ行を使用し、2サイクルで情報を送信します)。タイミングチャート:



ディスプレイにデータを転送する前に、サービスコマンドを送信してデータを初期化する必要があります。 (データシートに記載されている、ここでは最も使用されているもののみを示します)









このインジケーターを頻繁に使用する必要があるため、プラグインライブラリ「i2c_lcd.h」を作成します。 これを行うには、 Project Manegerの Header Filesフォルダーを右クリックし、 Add New Fileを選択します 。 ヘッダーファイルを作成します。



 #define PCF8574A_ADDR 0x3F //c  PCF8574 #define DB4 b4 //   PCF8574   #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 //  #define displenth 20 //       static unsigned char BL_status; //     (/) void lcd_I2C_Init(void); //     PCF8574 void lcd_I2C_txt(char *pnt); //     ,  -     void lcd_I2C_int(int pnt); //       ,  -   void lcd_I2C_Goto(unsigned short row, unsigned short col); //     ,  row -  ( 1  2  4    )  col - ( 1  displenth)) void lcd_I2C_cls(); //   void lcd_I2C_backlight (unsigned short int state); //  (  1   -   0  )
      
      





次に、関数について説明します。Sourcesフォルダーを右クリックしてProject Manegerに移動し、 Add New Fileを選択します 。 ファイル「i2c_lcd.c」を作成します。



 #include "i2c_lcd.h" //  - char lcd_reg; //      PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) //    i2c   PCF8574 { I2C1_Start(); I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); } void LCD_COMMAND (char com) //     { lcd_reg = 0; // 0    lcd_reg.BL = BL_status.b0; //       ,    lcd_reg.DB4 = com.b4; //     4     lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; lcd_reg.EN = 1; // .   1 I2C_PCF8574_WriteReg (lcd_reg); //   PCF8574,      delay_us (300); //  lcd_reg.EN = 0; //    0,    I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = com.b0; //    4   lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); } void LCD_CHAR (unsigned char com) //   (ASCII  ) { lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; //        1  RS lcd_reg.DB4 = com.b4; //   4   lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg.EN = 0; // .   0,    I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; lcd_reg.DB4 = com.b0; //   4   lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); } void lcd_I2C_Init(void) { I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); //  I2c    delay_ms(200); lcd_Command(0x28); //    4    delay_ms (5); lcd_Command(0x08); //     delay_ms (5); lcd_Command(0x01); //  delay_ms (5); lcd_Command(0x06); //       delay_ms (5); lcd_Command(0x0C); //      delay_ms (25); } void lcd_I2C_txt(char *pnt) //     { unsigned short int i; //     char tmp_str[displenth + 1]; //  ,   1    ,      v  NULL ASCII 0x00 strncpy(tmp_str, pnt, displenth); //       displenth    for (i=0; i<displenth; i++) { if (tmp_str[i] == 0) break; //  NULL , і   LCD_CHAR(tmp_str[i]); //     } } void lcd_I2C_int(int pnt) //     { char tmp_str[8]; //  unsigned short i, j; IntToStr(pnt,tmp_str); //   ,  6  + NULL  while (tmp_str[0]==32) { for (i=0; i<7; i++) { tmp_str[i]=tmp_str[i+1]; //   (ASCII  32) tmp_str[6-j]=0; } j++; } lcd_I2C_txt (tmp_str); //    } void lcd_I2C_Goto(unsigned short row, unsigned short col) //     { col--; //      0,    1  switch (row) { case 1: lcd_Command(0x80 + col); //     break; case 2: lcd_Command(0x80 + col + 0x40); break; case 3: lcd_Command(0x80 + col + 0x14); break; case 4: lcd_Command(0x80 + col + 0x54); break; } } void lcd_I2C_cls() //  { lcd_Command(0x01); delay_ms (5); } void lcd_I2C_backlight (unsigned short int state) //     { lcd_reg = 0; BL_status.b0 = state.b0; //  ,            lcd_reg.BL = state.b0; I2C_PCF8574_WriteReg (lcd_reg); delay_ms (1); }
      
      





次に、メイン関数を使用して、新しく作成したライブラリをファイルに接続します。



 #include "i2c_lcd.h" //  - unsigned int i; //   void main() { lcd_I2C_Init(); //  lcd_I2C_backlight (1); //  lcd_I2C_txt ("Hellow habrahabr"); //    while (1) { delay_ms(1000); lcd_I2C_Goto (2,1); //  1  2  lcd_i2c_int (i); //    i++; //   } }
      
      







すべてが正しく組み立てられると、インジケーターにテキストが表示され、カウンターが1秒ごとに増加します。 一般的に、複雑なものはありません:)



次の記事では、引き続きi2cプロトコルとそれに対応するデバイスを扱います。 EEPROM 24XXメモリとMPU6050加速度計/ジャイロスコープの使用を検討してください。












All Articles