Goertek_Mito_Lab / Mbed 2 deprecated ServoTensionerV3

Dependencies:   mbed

main.cpp

Committer:
tkry
Date:
2020-05-10
Revision:
1:b0b5e0e70af8
Parent:
0:cd74549e4be2

File content as of revision 1:b0b5e0e70af8:

/*
基板:4/3モデル,SB16とSB18はカット

参考文献
HX711ロードセルのライブラリ,Read meにインポート方法記載あり
https://github.com/atoy40/mbed-hx711
7セグのアルゴリズム,正負などを変更
https://forum.arduino.cc/index.php?topic=351609.0
https://zhuanlan.zhihu.com/p/73846629
EEPROMのプログラム
https://os.mbed.com/users/bborredon/code/eeprom/docs/925096a4c7f0/classEEPROM.html

変更箇所
ステップ増加とリニア増加の両方をサポート
*/

// ライブラリ
#include "mbed.h"
#include "HX711.h"

// EEPROMのアドレス定義
#define min_address 0
#define stp_address 20
#define t1_address 40
#define t2_address 60
#define f1_address 80
#define f2_address 100

// ピンの初期設定
Serial pc(USBTX,USBRX);             // シリアル通信
HX711 loadcell(D7, D8, 128);        // ロードセル(Data, Sck)
I2C eeprom(D4, D5);                 // EEPROM(sda, scl)
DigitalOut datapin(D2);             // 7セグのDIO
DigitalOut latchpin(D3);            // 7セグのRCLK
DigitalOut clockpin(D0);            // 7セグのSCLK
DigitalOut FET(D1);                 // モーター電源のFET駆動信号
DigitalIn sw1(D9);                  // スイッチ入力1
DigitalIn sw2(D10);                 // スイッチ入力2
DigitalOut Rled(D11);               // 赤LED
DigitalOut Gled(D12);               // 緑LED
AnalogOut aout3(A3);                // アナログ出力
AnalogIn analog_value5(A5);         // オペアンプ非反転入力(+)読み込み
AnalogIn analog_value6(A6);         // オペアンプ反転入力(-)読み込み

// タイマー割り込み
Ticker LED_IRQ;                     // LEDの割り込み(1kHz)
Ticker DAC_IRQ;                     // DACの割り込み(100Hz)

// グローバル変数
unsigned int LedNum = 0;            // LEDの桁の切り替え用変数
int Data = 0;                       // LPF後のロードセルデータ
bool ReadSign1 = 0;                 // 圧力計の代入完了サイン
bool ReadSign2 = 0;                 // LEDディスプレイの出力完了サイン
double Tcnt = 0;                    // DAC用の時間カウント変数
double a = 0;                       // DAC用の傾き変数(1/Ts)
double t = 0;                       // 処理用の立上り時間(Time_start)
double t1;                          // sw1の立上り時間(Time_start)
double t2;                          // sw2の立上り時間(Time_start)
double f1;                          // 張力の減少量
double f2;                          // 張力の減少量
double stp;                         // ステップ分解数
double gain = 0;                    // 張力の減少量を操作するゲイン
double Voltage = 0;                 // DAC用の電圧[V]
double ZeroVoltage = 0.0;           // リセット後の初期位置の電圧
double BiasVoltage = 0.25;          // 線形範囲に収めるためのバイアス電圧
double TargetVoltage = 0.0;         // ポテンショメーター上の目標電圧値
double CommandVoltage = 0.0;        // 目標電圧を出力するためのA3の指令電圧
bool Scnt = 0;                      // DAC用のスイッチカウント変数
bool rewrite = 0;                   // EEPROMの上書きフラグ
char data[100];                     // コマンド入力のバッファ

// 関数のプロトタイプ宣言
void seg(int num, int d);                       // 1桁の出力
void LED4_Display();                            // 4桁の出力
void DAC_switch();                              // DACの切り替え
void DAC_linear();                              // DACのリニア出力
void DAC_step();                                // DACのステップ出力
void pc_rx();                                   // シリアルでのグローバル変数書き換え
void eeprom_update();                           // シリアル入力を判定し,EEPROMのメモリを書き換え
char RomRead(int address);                      // EEPROMの単文字読み取り関数
float RomRead_address(int address);             // EEPROMのデータ読み取り関数
void RomWrite(int address, char c);             // EEPROMの単文字書き込み関数
void RomWrite_address(int address, char c[]);   // EEPROMのデータ読み込む関数
float cmd2num(char c[]);                        // コマンドと数値の変換

// 7セグの対応表
const int state[15][8]= {
    {1, 1, 0, 0, 0, 0, 0, 0}, // 0
    {1, 1, 1, 1, 1, 0, 0, 1}, // 1
    {1, 0, 1, 0, 0, 1, 0, 0}, // 2
    {1, 0, 1, 1, 0, 0, 0, 0}, // 3
    {1, 0, 0, 1, 1, 0, 0, 1}, // 4
    {1, 0, 0, 1, 0, 0, 1, 0}, // 5
    {1, 0, 0, 0, 0, 0, 1, 0}, // 6
    {1, 1, 1, 1, 1, 0, 0, 0}, // 7
    {1, 0, 0, 0, 0, 0, 0, 0}, // 8
    {1, 0, 0, 1, 0, 0, 0, 0}, // 9
    {1, 1, 1, 1, 1, 1, 1, 1}, // NA
    {0, 0, 0, 0, 0, 0, 0, 1}, // 1桁
    {0, 0, 0, 0, 0, 0, 1, 0}, // 2桁
    {0, 0, 0, 0, 0, 1, 0, 0}, // 3桁
    {0, 0, 0, 0, 1, 0, 0, 0}, // 4桁
};

int main()
{
    sw1.mode(PullUp);                           // スイッチのプルアップ
    sw2.mode(PullUp);                           // スイッチのプルアップ

    // EEPROMの読み込み
    ZeroVoltage = RomRead_address(min_address);
    stp = RomRead_address(stp_address);             // stpの代入
    t1 = RomRead_address(t1_address);               // t1の代入
    t2 = RomRead_address(t2_address);               // t2の代入
    f1 = RomRead_address(f1_address);               // f1の代入
    f2 = RomRead_address(f2_address);               // f2の代入

    // 起動後の処理
    Rled = 0;                                   // 動作チェック用の赤LED点灯
    Gled = 1;                                   // 動作チェック用の緑LED点灯,外部電源駆動の場合シリアル割り込み内のgetcで処理が止まる?
    wait(0.8);                                  // モーター電源オンの待ち時間
    FET = 1;                                    // モーター電源オン,EEPROM書き込み時はオフ

    // 割り込みの定義
    pc.attach(pc_rx,Serial::RxIrq);             // シリアルデータ受信時の割り込み
    LED_IRQ.attach(&LED4_Display, 1e-3);        // 5msecごとに桁を更新
    DAC_IRQ.attach(&DAC_switch, 1e-2);           // 10msecごとにDACを変動

    // メイン処理内の変数
    float lpf = 0.8;                            // 値が大きいほどに平滑化
    double raw_data = 0.0;                      // LPF用の一時変数
    double lpf_data = 0.0;                      // LPF用の一時変数
    double pre_data = 0.0;                      // LPF用の一時変数

    int offset = loadcell.read();               // ロードセルの初期値取得

    while (true) {
        Rled = 1;                                   //動作チェック用の赤LED点灯
        Gled = 0;                                   //動作チェック用の緑LED点灯

        if(rewrite == 1) {
            eeprom_update();
            rewrite = 0;
        } else {
            if( ReadSign2 == 0 ) {                  // LEDの出力処理後に実行(LEDの出力処理中(データ送信中)に実行されない)
                raw_data = (float)(loadcell.read()-offset)*0.003525f;      // 実測で調節した方が良さそう?(4.2987/16777216.0/128)/(0.001*8.9/2000.0)
                lpf_data = (1.0f - lpf)*raw_data + lpf*pre_data;
                pre_data = lpf_data;
                ReadSign1 = 1;                      // 読み込む直前で圧力計変数をtrue(読み込み前)にする(データ代入中の値を出力しない)
                Data = (int)lpf_data;
            }
            ReadSign1 = 0;                          // 読み込み完了で圧力計変数をfalse(読み込み後)にする
            //Arduino Serial plotterでのLPFの比較用
            //pc.printf("%f", raw_data);
            //pc.printf(",");
            //pc.printf("%f\n", lpf_data);
        }
    }
}

// 1桁出力関数
void seg(int num, int d)
{
    // 数値データの送信
    for(int i=0; i<=7; i++) {
        datapin=state[num][i];
        clockpin=1;
        clockpin=0;
    }

    // 桁データの送信
    for(int i=0; i<=7; i++) {
        datapin=state[d+10][i];
        clockpin=1;
        clockpin=0;
    }
    latchpin=0;
    latchpin=1;
}

// 4桁出力関数
void LED4_Display()
{
    if(ReadSign1 == 0) {            // 圧力計の読み込み処理後に実行(圧力計の読み込み処理中に実行されない)
        ReadSign2 = 1;              // 出力直前でLED計変数をtrue(読み込み前)にする
        if( Data < 0) {             // 0以下の場合に値を0にする
            Data = 0;
        }

        LedNum++;                   // 桁変え
        if(LedNum>4) {
            LedNum=1;
        }
        switch(LedNum) {            // 桁ごとに出力
            case 1:
                seg((int)Data%10/1,1);
                break;
            case 2:
                if(Data < 10) {
                    seg(10,2);
                } else {
                    seg((int)Data%100/10,2);
                }
                break;
            case 3:
                if(Data < 100) {
                    seg(10,3);
                } else {
                    seg((int)Data%1000/100,3);
                }
                break;
            case 4:
                if(Data < 1000) {
                    seg(10,4);
                } else {
                    seg((int)Data%10000/1000,4);
                }
                break;
        }
        // 出力完了でLED変数をfalse(読み込み後)にする
        ReadSign2 = 0;
    }
}

// DACの切り替え
void DAC_switch()
{
    if(stp==1) {
        DAC_linear();
    } else {
        DAC_step();
    }
}

void DAC_linear()
{
    if(sw1==false) {
        t = t1;                     // sw1の変動時間
        gain = f1;                  // sw1の変動割合
    }
    if(sw2==false) {
        t = t2;                     // sw2の変動時間
        gain = f2;                  // sw2の変動割合
    }
    // SWの動作
    if((sw1==false||sw2==false) && Tcnt<t*1e2 ) {
        // 立ち上がり状態,ボタンは押されている,他の状態でない
        Tcnt += 1;
        a = +1/t;
    } else if(sw1==false||sw2==false) {
        // 継続状態,ボタンは押されている
        Tcnt += 1;
        a = 0;
    } else {
        Voltage = 0;
        Tcnt = 0;
        a = 0;
    }
    // 電圧範囲の設定
    TargetVoltage = gain*(ZeroVoltage-1.692) + 1.692;         // 50%の電圧
    CommandVoltage = 1.539*TargetVoltage - 2.360 - BiasVoltage;
    Voltage = Voltage + CommandVoltage*a*1e-2;
    aout3.write((Voltage+BiasVoltage)/3.3);
}

void DAC_step()
{
    // SWの動作
    if(sw2==false) {
        Voltage = 0;
        Tcnt = 0;
        a = 0;
        Scnt = false;
    } else if(Scnt == false && sw1==false && Tcnt<stp ) {
        // 立ち上がり状態,ボタンは押されている,他の状態でない
        Tcnt += 1;
        a = +1/stp;
        Scnt = true;
    } else {
        // 継続状態,ボタンは押されている
        a = 0;
        if(sw1==false) {
            Scnt = true;
        } else {
            Scnt = false;
        }
    }
    // 電圧範囲の設定(範囲1.0で設定)
    TargetVoltage = 1.0*(ZeroVoltage-1.692) + 1.692;                // 全範囲の電圧
    CommandVoltage = (1.539*TargetVoltage - 2.360) - BiasVoltage;   // 最大値の電圧
    Voltage = Voltage + CommandVoltage*a;
    aout3.write((Voltage+BiasVoltage)/3.3);
}

// シリアルデータ受信時の処理,グローバル変数の書き換え
void pc_rx ()
{
    // 文字列受信用の配列
    int data_index = 0;
    // 配列に受信したASCIIを代入
    while(1) {
        if (!pc.readable()) continue;
        char c = pc.getc();
        data[data_index] = c;
        if (c == '\n') {
            data[data_index+1] = '\0';
            pc.printf(">");
            pc.puts(data);                  //エコーしないと割り込みが終了しない?
            break;
        }
        data_index++;
    }
    rewrite = 1;
}

void eeprom_update()
{
    // minの変更
    if (data[0]=='z' && data[1]=='e' && data[2]=='r' && data[3]=='o') {
        pc.puts("Calibration done\n");
        int AnalogVal = 100*3.3*analog_value6.read();
        ZeroVoltage = AnalogVal/100.0;
        int Num1 = AnalogVal/100;
        int Num2 = (AnalogVal%100)/10;
        int Num3 = (AnalogVal%10)/1;
        // EEPROMへの書き込み
        RomWrite(min_address+0, 'z');
        RomWrite(min_address+1, 'e');
        RomWrite(min_address+2, 'r');
        RomWrite(min_address+3, 'o');
        RomWrite(min_address+4, '=');
        RomWrite(min_address+5, (char)Num1+'0');
        RomWrite(min_address+6, '.');
        RomWrite(min_address+7, (char)Num2+'0');
        RomWrite(min_address+8, (char)Num3+'0');
        RomWrite(min_address+9, '\n');
        RomWrite(min_address+10, '\0');
    }

    // stpの変更
    if (data[0]=='s' && data[1]=='t' && data[2]=='p' && data[3]=='=') {
        float cmd = cmd2num(data);
        stp=cmd;
        if(cmd<=0) {
            pc.puts("Error 0 < stp\n");
        } else {
            RomWrite_address(stp_address, data);
            pc.puts("change stp=");
            pc.printf("%f\n",RomRead_address(stp_address));
        }
        Voltage = 0;
        Tcnt = 0;
        a = 0;
    }

    // t1の変更
    if (data[0]=='t' && data[1]=='1' && data[2]=='=') {
        float cmd = cmd2num(data);
        t1=cmd;
        if(cmd<=0.01f) {
            pc.puts("Error 0.01 <= t1\n");
        } else {
            RomWrite_address(t1_address, data);
            pc.puts("change t1=");
            pc.printf("%f\n",RomRead_address(t1_address));
        }
    }

    // t2の変更
    if (data[0]=='t' && data[1]=='2' && data[2]=='=') {
        float cmd = cmd2num(data);
        t2=cmd;
        if(cmd<=0.01f) {
            pc.puts("Error 0.01 <= t2\n");
        } else {
            RomWrite_address(t2_address, data);
            pc.puts("change t2=");
            pc.printf("%f\n",RomRead_address(t2_address));
        }
    }

    // f1の変更
    if (data[0]=='f' && data[1]=='1' && data[2]=='=') {
        float cmd = cmd2num(data);
        f1=cmd;
        if(cmd<=0.01f || cmd>=0.99f) {
            pc.puts("Error 0.01 <= f1 <= 0.99\n");
        } else {
            RomWrite_address(f1_address, data);
            pc.puts("change f1=");
            pc.printf("%f\n",RomRead_address(f1_address));
        }
    }

    // f2の変更
    if (data[0]=='f' && data[1]=='2' && data[2]=='=') {
        float cmd = cmd2num(data);
        f2=cmd;
        if(cmd<=0.01f || cmd>=0.99f) {
            pc.puts("Error 0.01 <= f2 <= 0.99\n");
        } else {
            RomWrite_address(f2_address, data);
            pc.puts("change f2=");
            pc.printf("%f\n",RomRead_address(f2_address));
        }
    }

    // 変数の確認
    if (data[0]=='p' && data[1]=='a' && data[2]=='r' && data[3]=='a' && data[4]=='m') {
        pc.puts("zero=");
        pc.printf("%.2lf \n",RomRead_address(min_address));
        pc.puts("stp=");
        pc.printf("%.2lf \n",RomRead_address(stp_address));
        pc.puts("t1=");
        pc.printf("%.2lf \n",RomRead_address(t1_address));
        pc.puts("t2=");
        pc.printf("%.2lf \n",RomRead_address(t2_address));
        pc.puts("f1=");
        pc.printf("%.2lf \n",RomRead_address(f1_address));
        pc.puts("f2=");
        pc.printf("%.2lf \n",RomRead_address(f2_address));
    }
}

float cmd2num(char c[])
{
    char t_data[16];
    int t_equal_index=0;
    int t_point_index=0;
    int t_end_index=0;

    // データとインデックスの代入
    for(int i=0; i<16; i++) {
        t_data[i] = c[i];
        if(t_data[i]=='=') {
            t_equal_index=i;
        }
        if (t_data[i]=='.') {
            t_point_index = i;
        }
        if((t_data[i]=='\r')||(t_data[i]=='\n')) {
            t_end_index = i;
            break;
        }
    }

    // Asciiと数値の変換
    double t_command_num = 0;
    if(t_equal_index!=0) {
        // 小数の有無で分岐
        if(t_point_index==0) {
            // 整数部の処理
            for(int i=t_equal_index+1; i<t_end_index; i++) {
                t_command_num += (t_data[i] - '0')*pow((double)10,(double)t_end_index-i-1);
            }
        } else {
            // 整数部の処理
            for(int i=t_equal_index+1; i<t_point_index; i++) {
                t_command_num += (t_data[i] - '0')*pow((double)10,(double)t_point_index-i-1);
            }
            // 小数部の処理
            for(int i=t_point_index+1; i<t_end_index; i++) {
                t_command_num += (t_data[i] - '0')*pow((double)10,(double)t_point_index-i);
            }
        }
    }
    return t_command_num;
}

// 指定のアドレスにデータを書き込む
void RomWrite_address(int address, char c[])
{
    for(int i=0; i<15; i++) {
        RomWrite(i+address,c[i]);
    }
    RomWrite(15+address,'\0');
}

// 指定のアドレスからデータを読み込む
float RomRead_address(int address)
{
    char r_data[16];
    int r_equal_index=0;
    int r_point_index=0;
    int r_end_index=0;

    // データとインデックスの代入
    for(int i=0; i<16; i++) {
        r_data[i] = RomRead(address+i);
        if(r_data[i]=='=') {
            r_equal_index=i;
        }
        if (r_data[i]=='.') {
            r_point_index = i;
        }
        if((r_data[i]=='\r')||(r_data[i]=='\n')) {
            r_end_index = i;
            break;
        }
    }

    // Asciiと数値の変換
    double r_command_num = 0;
    if(r_equal_index!=0) {
        // 小数の有無で分岐
        if(r_point_index==0) {
            // 整数部の処理
            for(int i=r_equal_index+1; i<r_end_index; i++) {
                r_command_num += (r_data[i] - '0')*pow((double)10,(double)r_end_index-i-1);
            }
        } else {
            // 整数部の処理
            for(int i=r_equal_index+1; i<r_point_index; i++) {
                r_command_num += (r_data[i] - '0')*pow((double)10,(double)r_point_index-i-1);
            }
            // 小数部の処理
            for(int i=r_point_index+1; i<r_end_index; i++) {
                r_command_num += (r_data[i] - '0')*pow((float)10,(float)r_point_index-i);
            }
        }
    }
    return r_command_num;
}

// 指定のアドレスからchar型で一文字を返す
char RomRead(int address)
{
    char data[3];
    data[0] = 0;                       // MSB address
    data[1] = address;                 // LSB address
    eeprom.write(0xA0, data, 2);
    char response[1];
    eeprom.read(0xA0, response, 1);
    return response[0];
}

// 指定のアドレスにint型で一文字書き込む
void RomWrite(int address, char c)
{
    char data[3];
    data[0] = 0;            // MSB address
    data[1] = address;      // LSB address
    data[2] = c;            // data
    eeprom.write(0xA0, data, 3);
    while(eeprom.write(0xA0, NULL, 0)); // wait to complete
}