2021年7月10日土曜日

HX711をI2Cから読めるようにしてみた

 Orange Pi PCでうまくHX711が使えなかったので、やっぱりSBCとかで使うにはI2Cのほうが便利ということでHX711をI2C経由で使えるようにしてみた。
やっぱりこういうデバイスはGPIOで頑張るよりちょっとしたマイコンで読んでもらったほうが楽勝な気がする…SBC側のCPU負荷も減るんじゃないかな。

今回はとりあえずAttiny202を使ってHX711のデータを連続で読みに行って、読んだ値をI2C用のレジスタに格納しておいてI2Cからはスレーブでレジスタを読みに来ればいい感じにしてみた。

HX711は10spsモードだからといって一定周期で読もうとすると変なデータになってしまったりするので調べてみるとナゾの一定周期パルスが出ている影響らしい?
とりあえず適当に対策してみたら10spsモードのHX711とAttiny202を繋いで連続で読みに行くと平均177msかかってる。一定周期パルスの2倍の周期でしか読めなかったけど、とりあえず用途的には十分なので良し。
I2C側は割り込みだし、loop内はHX711にだけ注力していればいいので簡単に対策できた。

I2Cでレジスタを読む形のスレーブデバイスを作る部分はちょっと前に作成したコードがそのままattiny202でも使えた。それにHX711を読み込むところを追加したぐらい…

#include <Arduino.h>
#include <Wire.h>

#define DAT 1
#define CLK 0
#define SLAVE_ADDRESS 0x60

#define DISABLE_INTERNAL_PULLUP

//使用するレジスタ範囲指定
#define I2C_REG_ROW 1
#define I2C_REG_COL 4
uint8_t I2C_REG[I2C_REG_ROW][I2C_REG_COL];

//読み書きするレジスタアドレス
uint8_t REG_SELECTED;

//i2c受信イベント
void receiveEvent(int _length) {
  //1byte目を読み込む(レジスタアドレス指定)
  REG_SELECTED = Wire.read();
  uint8_t startRow = REG_SELECTED >> 4;
  uint8_t startCol = (REG_SELECTED & 0x0F);

  while (Wire.available() > 0) {
    if (startRow < I2C_REG_ROW && startCol < I2C_REG_COL) {
      I2C_REG[startRow][startCol] = Wire.read();
    } else {
      Wire.read();
    }
    ++startCol;
  }
}

void requestEvent() {
  uint8_t startRow = REG_SELECTED >> 4;
  uint8_t startCol = (REG_SELECTED & 0x0F);
  if (startRow < I2C_REG_ROW && startCol < I2C_REG_COL) {
    Wire.write(&I2C_REG[startRow][startCol], I2C_REG_COL - startCol);
  }
}

void setup() {
  //SlaveでWireライブラリを初期化
  Wire.begin(SLAVE_ADDRESS);
  //イベント設定
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  //内蔵プルアップ無効にする場合
#ifdef DISABLE_INTERNAL_PULLUP
  pinMode(SDA, INPUT);
  pinMode(SCL, INPUT);
#endif
  //HX711用ピン設定
  pinMode(CLK, OUTPUT);
  pinMode(DAT, INPUT);
}

void loop() {
  //HX711データ取得
  uint8_t rawdata[3] = {0, 0, 0};
  while (digitalRead(DAT) == 0);
  while (digitalRead(DAT) != 0);
  for (char k = 0; k < 3; k++) {
    for (char i = 0; i < 8; i++) {
      digitalWrite(CLK, 1);
      delayMicroseconds(1);
      digitalWrite(CLK, 0);
      delayMicroseconds(1);
      rawdata[k] = (rawdata[k] << 1) | (digitalRead(DAT));
    }
  }
  
  I2C_REG[0][0] = 0x12;
  I2C_REG[0][1] = rawdata[2];
  I2C_REG[0][2] = rawdata[1];
  I2C_REG[0][3] = rawdata[0];
}

mills()/micros()を無効にして、Attiny202の67%に収まってる。とりあえずゲインの調整とかもなしで生データをひたすら177msで更新しているので適当なタイミングでi2cから読みに行けばいい感じ。50ms周期とかで読んでもデータ化けはなかったので用途的には大丈夫そうかな。

一応HX711側は5Vで使う場合はI2C側かHX711側でレベル変換すれば良さそう。HX711側でDATだけを抵抗分圧だけで行けないかなとか思っている。テストではFETでI2C側をレベル変換しているけど。

生データでそのまま吐き出しているので(一応ヘッダ的なのはつけておいたけど)、Orange Pi PC側のPythonのライブラリで値を取得するところだけsmbusからデータ取るようにしたらそのまま使えないかなぁとか思ったり。SBC側のソフトは作成中…
i2cdumpではうまく動いている。

せっかくI2Cデバイスにしてマイコン挟むならゲイン調整とかゼロセットとか、重量の換算までできるようにしたいな。I2Cだから複数のHX711を取り扱うのは楽になりそう。

0 件のコメント:

コメントを投稿