Up polling vs interrupt 作成: 2026-10-19
更新: 2026-10-19

4足歩行ロボット > 自律型> コンピュータ > プログラミング > モータストア > polling

    モータストアのプログラムは,main() がつぎの形になる:

    main()
    {
      // ハードウェア定義・初期設定
      INIT()

      // 着信監視を設定
      HAL_I2C_EnableListen_IT(
        &hi2c1
      );

      // motor_store_buffer を
      // 着信データを受けるバッファに設定
      HAL_I2C_Slave_Receive_IT(
        &hi2c1,
        motor_store_buffer,
        24
      );

      // PI 制御ループ
      PI_Loop();
    }

    プログラムは,main() から PI_Loop() に入って,無限ループする。


    PI_Loop() は,目標電流値 I_cmd が固定なら,つぎのようなる:

    PI_Loop()
    {
      while(1)
      {  
        // ADC 処理 (実電流値 I-meas の取得)
        I_MEAS_Get();

        // PI 処理
        PI_Calc();

        // PWM, DIR 処理
        // (モータドライバIC のモータ電圧生成へ)
        PWM_DIR_Send();
      }
    }

    しかし I_cmd は更新されるので,つぎの処理が要る:
      1. 更新値を取得
      2. PI 処理での目標電流値を更新


    polling 方式だと,PI_Loop() をつぎのようにつくることになる:

    PI_Loop()
    {
      while(1)
      {  
        // 着信処理
        I_CMD_Get();
        
        I_MEAS_Get();
        PI_Calc();
        PWM_DIR_Send();
      }
    }

    そして I_CMD_Get() は,例えばつぎのよう:

    // 受信バッファ
    // (2byte電流値が 12モータ分)
    uint8_t motor_store_buffer[24];

    // 新着チェック用
    uint8_t last_value[24] = 0;

    I_cmd_Get()
    {
      // 更新されていない
      if(memcmp(
        last_value,
        motor_store_buffer,
        24
      ) == 0)
      {
        return;
      }
      // last_value に motor_store_buffer をコピー
      memcpy(
        last_value,
        motor_store_buffer,
        24
      );

       <目標電流値 I_cmd 復元処理>
    }

    ここで <目標電流値 I_cmd 復元処理>は:

      for(i = 0 ; i < 12 ; i++)
      {
        //符号なし8bit整数型
        uint8_t high;
        uint8_t low;

        high = motor_store_buffer[i * 2];
        low = motor_store_buffer[i * 2 + 1];

        i_cmd[i] = (int16_t)( (high << 8) | low );
      }


    ところで,HAL にはつぎの関数がある:

    I2C_HandleTypeDef hi2c1;
    void HAL_I2C_SlaveRxCpltCallback(
      I2C_HandleTypeDef *hi2c)
    {
      (着信があったときの処理)
    }

    polling 方式はそのままにしてこれの利用しようとすると,つぎのようになる:

    // 新着フラグ
    volatile bool cmd_arrived = false;

    void HAL_I2C_SlaveRxCpltCallback(
      I2C_HandleTypeDef *hi2c)
    {
      cmd_arrived = true;

      // HAL_I2C_Slave_Receive_IT() は
      //  1回受信したら終了
      //  →再登録が必要

      HAL_I2C_Slave_Receive_IT(
        &hi2c1,
        motor_store_buffer,
        24
      );
    }

    I_cmd_Get()
    {
      if( memcmp( last_value, motor_store_buffer ) == 0 )
      {
        return;
      }
      last_value = motor_store_buffer;

       <目標電流値 I_cmd 復元処理>
    }

    着信チェック処理が,memcmp( last_value, motor_store_buffer を使うやり方と比べて,軽くなった。


    しかしこれは,HAL_I2C_SlaveRxCpltCallback() の<割り込み>機能の使い方が中途半端である。
    そして中途半端なのは,polling 方式にとらわれているため。

    実際,つぎのようにすれば,PI_Loop() の while(1) ループから,I_CMD_Get() をまるごと無くせる:

    void HAL_I2C_SlaveRxCpltCallback(
      I2C_HandleTypeDef *hi2c)
    {
       <目標電流値 I_cmd 復元処理>

      HAL_I2C_Slave_Receive_IT(
        &hi2c1,
        motor_store_buffer,
        24
      );
    }

    ──────────────────────────
    以上まとめて,つぎのようになる:


    I2C_HandleTypeDef hi2c1;
    uint8_t motor_store_buffer[24];
    volatile int16_t i_cmd[12];


    void HAL_I2C_SlaveRxCpltCallback(
      I2C_HandleTypeDef *hi2c)
    {
      int i;

      for(i = 0 ; i < 12 ; i++)
      {
        uint8_t high;
        uint8_t low;

        high = motor_store_buffer[i * 2];
        low = motor_store_buffer[i * 2 + 1];

        i_cmd[i] = (int16_t)( (high << 8) | low );
      }

      HAL_I2C_Slave_Receive_IT(
        &hi2c1,
        motor_store_buffer,
        24
      );
    }


    PI_Loop()
    {
      while(1)
      {  
        I_MEAS_Get();
        PI_Calc();
        PWM_DIR_Send();
      }
    }


    main()
    {
      INIT()

      HAL_I2C_EnableListen_IT(
        &hi2c1
      );

      HAL_I2C_Slave_Receive_IT(
        &hi2c1,
        motor_store_buffer,
        24
      );

      PI_Loop();
    }