STM32C011でI2Cスレーブで受信した値をアナログサーボに出力(I2C(PA9,PA10))


x 過去ログを見よ

x 逆起電圧がわかる人、限定　数百ボルトが発生します。
x 起動電流がわかる人、限定　起動電流は、論理値無限代です。
x 安全対策を適切にしないと一発でパソコンを壊します。
x 動かす時は、最低でもモバイルバッテリに繋いで動かしてね!!
x サーボの電源は、共通アースすで別電源の単三電池3本定格4.5Vがおすすめ5,6アンペア流せるのでモーターが起動できます。
x SG90には、1チップの専用アナログ、アプリケーションICが使われているらしい。よって、適当なアナログ機器です。

====完成 202403172103====


目的
例　0x20(8ビット表記0x40)で値を受信して
アナログサーボ信号を出力
AI、小学生、ノーコード、ロードコード用にいろいろ自動化する
入力は、　角度　または、　0から255 
角度は、時計周り-90から0、90とプラスマイナスちょっと
ちょっとは、個体差で実測値を採用
(ちょつとは、)ロックに当たてサーボがぶっ壊れるけどエラーなしで入力を受け付ける
適当な値も受け付ける360で割ったあまりで物理範囲以外は、ギャーが削れてぶっ壊れる
もしくは、ケースが変形する
サーボが焼けようがMCUが焼けようがPCが焼けようが自己責任
(超ハイパワーサーボ(軸出力100kgオーバー)は、端に当たって壊れるですけどどうすればいいんですか?先生)(何の事?)(笑い、笑い)(なにやってるんだこのアホーーー)
数学的に合わせるのでサーボを真横にして0度とする

例

```

Servo_S0(-90);
delay(1000);

Servo_S0(0);
delay(1000);

Servo_S0(90);
delay(1000);

Servo_S0(0);
delay(1000);



```

いろいろ
何に言っているかわからないて?
大学の初等か高校で習うサイン、コサインの話
大学では、解析学と行列の話
最終話は、フーリエ変換の話
虚数もでるぜ
ラジアンを使うとさらに分からなくなるぜ
きっいようだが甘えるな
前にも言ったけど別のアクセスログを見ていると
半分は、中国で半分は、欧米(チェコ、ハンガリー、ローマ、ロシア)で半分、
1パーセント以下が東工大(名称変更で東京なんちゃらかんちゃら)、各県高専
基本、「くれよんーーー~~　ーーーしんちゃん」(くれよ!!)でコピペだけ。
ひとり、だけでも、役に立てばいいか?「あなたひとりだけょん!!」加藤茶ふう
以上


![o_cop993.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/f9432399-1145-0d1f-9e4b-db1fccd8f5d9.jpeg)


![o_cop994.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b6925f0b-02f3-8c4c-c3d5-eef96c073433.jpeg)

(何のこと?)

![o_cop996.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/3014f7f7-14cb-0e7d-dea0-469824b35db6.jpeg)

物理的なホームポジションを決める
ストッパーが当たる真ん中
ケースに対して水平を
物理ポジションとする。
パルスの信号的のポジションの求め方は、
電気的にボリュームで物理ポジションに持っていき
データを取って平均した値が電気的なポジション

電気的なポジション値を与えてあげればちょつとなんとなく物理ポジションに行く。

-90度の電気的なポジションを求める
-90度の物理値の時の電気的な平均した値を求める。

反対側の90度の値は、計算から自動的に求める


1度当たりのステップは、

```

A 0度のステップ
B -90度のステップ

S = (A-B) / 90

S 角度あたりのステップ

角度

P = HH + ( S * (角度 - 90) )

P ポジションのステップ

HH 開始位置

```

れんちゅうのレポートを読むと問題になるのが
計算値と機械的な精度の差が問題になるらしい。
歯車で回るものは、そのままサイン波が出てくる
時計で言うところのチクタクて言う事、わざとだけど
計算値>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>機械的な精度

異常にセンター(センターホームポジション(乃木坂ならカッキーさんとさくらさん))にこだわるのは、360サーボを個人的に買ったから
サーボのセンター(中心、中央)なんかぶっちゃけどうでもいい!!
プログラムも書かずにダラダラしているかと言うと仕様がかたまらないから
原則しては、ドライバー側で個別の特性の差を取ってあげるのがいい。
理想としては、サーボ側にEEPROM等に個別の情報を記憶させるのがいい。
ソフト的には、サーボ側は、ざっくりした精度でアプリ側で計算値と機械的な精度を確保してほしい。
今回の仕様としては、アプリ側(エンドユーザ側)で調整してほしい。

例　センターが右に2度ずれていたら-2してほしい

```

Servo_S0(0-2);

```

![o_cop997.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/6264f6d6-3902-8759-9edf-8c2ad238b6d0.jpeg)


SG-90に関しては、0.5ms～2.4msをざっくり256に分ける
256÷180で1どあたり、約1.4ステップ

![o_cop998.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/fa2512fe-c80f-c3df-a226-3501c07ec3f7.jpeg)

SNはusの10進の固定小数点3桁

```


//#define SN(n6)  (n6*((65536/20000)*1000))
//↓式変形と要約↓6万を2万で割ると3ぐらい、3を1000倍するとだいたい3000
#define SN(n6)    (n6*3277)

  ii = 1450;
  analogWrite(DD, DIV1000(SN(ii))  );   // analogRead values go from 0

```


2400-500 = 1900
1900÷256 = 7.421875

B 0から255の入力
H サーボバルス長 us

精度の為　10の固定小数点

```

//H = ((500*10) + (B * (7.4*10)))÷10

式変形

H = ((500*10) + (B * (74)))÷10

```

だいたい、仕様が固まったのでプログラムが書けるようになる!!

結果

![o_cop999.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/71738296-fa85-27b0-7ebc-53454d7364b0.jpeg)



プログラム

受信(スレーブ)

```c++


//I2C_Slave_Servo_C011_1


//インクルド
#include <Arduino.h>
#include <Wire.h>


//      0 1 2 3 4 5
//定義1 a b c d e f
#define ADD_Servo_S0 ((0x40)>>1)
#define ADD_Servo_S1 ((0x42)>>1)
#define ADD_Servo_S2 ((0x44)>>1)
#define ADD_Servo_S3 ((0x46)>>1)
#define ADD_Servo_S4 ((0x48)>>1)
#define ADD_Servo_S5 ((0x4A)>>1)
#define ADD_Servo_S6 ((0x4C)>>1)
#define ADD_Servo_S7 ((0x4E)>>1)

#define Sensor_Sx ADD_Servo_S0

//定義2
//1000の割り算 だいたい正しい。
#define DIV1000(n) ((n+((n*25)>>10))>>10)

//data SG90
// 2400us 2.4ms
#define s_max  2400
// 500us 0.5ms
#define s_min  500
#define s_r    (2400-500)
#define s_c    (65536/20000)*1000

//#define SN(n6)  (n6*((65536/20000)*1000))
//↓式変形と要約↓6万を2万で割ると3ぐらい、3を1000倍するとだいたい3000
#define SN(n6)    (n6*3277)

#define DD PB7    // potentiometer connected to analog pin x


//初期化
void setup() {

  delay(3000); //not delete

  //i2cの初期化
  Wire.setSDA(19);   //PA10
  Wire.setSCL(18);   //PA9
  Wire.begin(Sensor_Sx);  //I2Cスレーブアドレスの設定
  Wire.onReceive(receiveEvent); //データが来ると呼ばれる関数

  //GPIOの初期化
  pinMode(DD, OUTPUT);   // sets the LED pin as output
  analogWriteFrequency(50);   //周波数
  analogWriteResolution(16);  //分解能

} //setup


//メインループ
void loop() {
  delay(1); //ダミー
} //loop


//レシーブイベント
void receiveEvent(int howMany) {

  int x = Wire.read(); //I2C受信データの読み込み

  if ( x < 0 ) {x = 256 + x;}
  //x = 255 - x;//反転
  int ii;
  ii = (s_min * 10) + (x * ((s_r * 10) >> 8)); // 0から255 を　500から2400
  ii = (ii * 205) >> 11; //雑に10分の1にする

  //ii = 1450;//degug
  analogWrite(DD, DIV1000(SN(ii))  );   // analogRead values go from 0
  // to 4095, pwmWrite values
  // from 0 to 65535, so scale rough

}//receiveEvent

```


------------------------------------
別マイコン


送信(ヘッダー)

```c++


//Servo_I2C.h


//インクルド
#include <Arduino.h>
#include <Wire.h>


//     0 1 2 3 4 5
//定義 a b c d e f
#define ADD_Servo_S0 ((0x40)>>1)
#define ADD_Servo_S1 ((0x42)>>1)
#define ADD_Servo_S2 ((0x44)>>1)
#define ADD_Servo_S3 ((0x46)>>1)
#define ADD_Servo_S4 ((0x48)>>1)
#define ADD_Servo_S5 ((0x4A)>>1)
#define ADD_Servo_S6 ((0x4C)>>1)
#define ADD_Servo_S7 ((0x4E)>>1)

#define Servo_S0(angle_NUM_O) Servo_Sx(ADD_Servo_S0,angle_NUM_O)
#define Servo_S1(angle_NUM_O) Servo_Sx(ADD_Servo_S1,angle_NUM_O)
#define Servo_S2(angle_NUM_O) Servo_Sx(ADD_Servo_S2,angle_NUM_O)
#define Servo_S3(angle_NUM_O) Servo_Sx(ADD_Servo_S3,angle_NUM_O)
#define Servo_S4(angle_NUM_O) Servo_Sx(ADD_Servo_S4,angle_NUM_O)
#define Servo_S5(angle_NUM_O) Servo_Sx(ADD_Servo_S5,angle_NUM_O)
#define Servo_S6(angle_NUM_O) Servo_Sx(ADD_Servo_S6,angle_NUM_O)
#define Servo_S7(angle_NUM_O) Servo_Sx(ADD_Servo_S7,angle_NUM_O)

//左端 -90のステップ
#define V_M_90   5
//中心の0のステップ
#define V_0    127
//右端 90　計算値
//#define V_90 250

//-90から0までのステップ
#define STEP_Q (( (( V_0 - V_M_90 )*1000)  / 90 )+7)

//角度から0から255の数値に変換(マクロ)
//Vは、角度
#define KI(V) ( (V_M_90*1000) + ( (STEP_Q) *  (V + 90) )         )

//1000の割り算 だいたい正しい。
#define DIV1000(n) ((n+((n*25)>>10))>>10)

//ざっくり1000で割る
#define KJ(V)   DIV1000( (KI(V)) )

//マクロを実関数に割り当てる
#define Servo_Sx(ADD_Servo,angle_NUM)   Servo_F_Sx(ADD_Servo,KJ(angle_NUM))


int SET_Servo = 0;//状態

//I2Cの初期化
void INT_Servo_I2C() {

  //I2Cの初期化
  Wire.setSDA(19);   //PA10
  Wire.setSCL(18);   //PA9
  Wire.begin();  //I2Cの初期化

}//INT_Servo_I2C


//サーボ関数(関数としての実態)
//入力は、0から255 サーボが仕様の端から端まで動く値
void Servo_F_Sx(int add_1 , int b ){

  if(SET_Servo == 0){INT_Servo_I2C();SET_Servo = 1;}

  //0から255の「b」の転送
  Wire.beginTransmission( add_1 );
  Wire.write( b );
  Wire.endTransmission();

} //Servo_F_Sx


```

送信(メイン)

```c++


//I2C_Master_Servo_C011_1


//インクルド
#include "Servo_I2C.h"


//初期化
void setup() {

  delay(3000);//マッポの手先、消したら、いかんぜょ(斎藤由紀風)

} //setup


//メインループ
void loop() {

  Servo_S0(-90); delay(2000); //2秒待つ

  Servo_S0(0);   delay(2000); //2秒待つ

  Servo_S0(90);  delay(2000); //2秒待つ

  Servo_S0(0);   delay(2000); //2秒待つ

} //loop


```