LoginSignup
2
2

More than 3 years have passed since last update.

Lチカで始めるテスト自動化(3)オシロスコープの組込み

Posted at

1. はじめに

Lチカで始めるテスト自動化シリーズ第三弾です。

昨今の省エネ事情を考慮しテスト対象のLEDの点灯方式をスタティック点灯からダイナミック点灯に仕様変更します。そこで、LEDの点灯電圧、点灯周波数をオシロスコープで測定してGo/No-Go判定を自動でできるようテストベンチを改修します。

IMG_0603s.jpg

これまでの記事はこちらをご覧ください。

  1. Lチカで始めるテスト自動化
  2. Lチカで始めるテスト自動化(2)テストスクリプトの保守性向上

2. テスト対象の改修

スタティック点灯から1000Hz、デューティ比50%のダイナミック点灯に変更します。tone()noTone()で点灯/消灯を制御します。

ArduinoLedLight.ino(変更部分)
void Led::on(void)
{
    //digitalWrite(m_led_pin, HIGH);
    tone(m_led_pin, 1000);
}

void Led::off(void)
{
    //digitalWrite(m_led_pin, LOW);
    noTone(m_led_pin);
}

3. テストランナーの改修

  • 下記のコマンドを追加します。
コマンド 引数 機能
open_dso なし ・PyVISAをimportしオシロスコープをopenする
dso VISAコマンド ・オシロスコープとコマンドを送受信する
・受信データに含まれる","は"-"に置換する
eval_dbl_eq 期待値(double型1) ・変数valが期待値の値(double型)と等しいか評価する
eval_dbl_gt 基準値(double型) ・変数valが基準値(double型)より大きいか評価する
eval_dbl_lt 基準値(double型) ・変数valが基準値(double型)より小さいか評価する
  • PyVISAをインストールしてください2
  • USBバス上のVISA機器は1台だけと仮定し、機器一覧の中から最初にopenできた機器を使用します。
test-runner.py
#!/usr/bin/python3

#
# This software includes the work that is distributed in the Apache License 2.0
#

from time import sleep
import serial
import codecs
import csv
import sys
import visa

UNINITIALIZED = 0xdeadbeef

def serial_write(h, string):
    if h == UNINITIALIZED:
        print("UART Not Initialized.")
        return False
    else:
        string = string + '\n'
        string = str.encode(string)
        h.write(string)
        return True


def open_dso():
    rm = visa.ResourceManager()
    resources = rm.list_resources()
    #print(resources)
    for resource in resources:
        #print(resource)
        try:
            dso = rm.open_resource(resource)
        except:
            print(resource, "Not Found.")
        else:
            print(resource, "Detected.")
            return dso

    #Throw an error to caller if none succeed.
    return dso


def main():
    is_passed = True
    val = str(UNINITIALIZED)
    dso = UNINITIALIZED
    uart = UNINITIALIZED
    arg = [""]

    with codecs.open('script.csv', 'r', 'utf-8') as file:
        script = csv.reader(file, delimiter=',', lineterminator='\r\n', quotechar='"')

        with codecs.open('result.csv', 'w', 'utf-8') as file:
            result = csv.writer(file, delimiter=',', lineterminator='\r\n', quotechar='"')

            for cmd in script:
                print(cmd)

                if "#" in cmd[0]:
                    pass

                elif cmd[0]=="sleep":
                    sleep(float(cmd[1]))
                    cmd.append("OK")

                elif cmd[0]=="open_dso":
                    try:
                        dso = open_dso()
                    except:
                        cmd.append("NG")
                        is_passed = False
                    else:
                        cmd.append("OK")

                elif cmd[0]=="dso":
                    try:
                        if "?" in cmd[1]:
                            val = dso.query(cmd[1]).rstrip().replace(",", "-")
                            cmd.append(val)
                        else:
                            dso.write(cmd[1])
                            cmd.append("OK")
                    except:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="open_uart":
                    try:
                        uart = serial.Serial(cmd[1], 115200, timeout=1.0, dsrdtr=1)
                        cmd.append("OK")
                    except:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="send":
                    ret = serial_write(uart, cmd[1])
                    if ret == True:
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="rcvd":
                    try:
                        val = uart.readline().strip().decode('utf-8')
                        cmd.append(val)
                        cmd.append("OK")
                    except:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_str_eq":
                    if str(val) == str(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_int_eq":
                    if int(val) == int(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_int_gt":
                    if int(val) > int(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_int_lt":
                    if int(val) < int(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_dbl_eq":
                    if float(val) == float(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_dbl_gt":
                    if float(val) > float(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                elif cmd[0]=="eval_dbl_lt":
                    if float(val) < float(cmd[1]):
                        cmd.append("OK")
                    else:
                        cmd.append("NG")
                        is_passed = False

                else:
                    cmd.append("#")

                print(cmd)
                result.writerow(cmd)

                if is_passed == False:
                    print("FAIL")
                    sys.exit(1)

    if is_passed == True:
        print("PASS")
        sys.exit(0)


main()

4. テストスクリプトおよびスクリプトジェネレータ

4.1 (単機能)テストスクリプト

以下のテストスクリプトをscript-partsフォルダへ追加します。

  • VISAコマンドはRIGOL製オシロスコープの例です。
  • setup_dso.csvの機種の文字列はお使いの機種に合わせてください。
setup_dso.csv
#
# Setup DSO
open_dso,
dso,*IDN?
eval_str_eq,RIGOL TECHNOLOGIES-DS1104Z-XXXXXXXXXXXXXX-00.04.04.SP3
dso_write_timescale_200us.csv
#
# Timebase Scale 200us
dso,:TIMebase:SCALe 0.0002
dso_query_vmax_gt1700-lt1900.csv
#
# 1.80V +/- 0.10V
dso,:MEASure:VMAX? CHANnel1
eval_dbl_gt,1.70
eval_dbl_lt,1.90
dso_query_freq_gt995-lt1005.csv
#
# 1000Hz +/- 5Hz
dso,:MEASure:FREQuency? CHANnel1
eval_dbl_gt,995
eval_dbl_lt,1005

4.2 スクリプトジェネレータ

改修は下記2点です。

  • オシロスコープのセットアップを追加
  • LED点灯時のチェックをオシロスコープに変更
script-generator.bat
REM script generator for LED Light Test Bench

set script_parts_dir=script-parts
set script_file_name=script.csv

REM テストベンチのセットアップ
copy /b %script_parts_dir%\setup.csv %script_file_name%

REM デジタルオシロスコープのセットアップ
copy /b %script_file_name% + %script_parts_dir%\setup_dso.csv
copy /b %script_file_name% + %script_parts_dir%\dso_write_timescale_200us.csv

REM LEDが点灯していないこと(LEDの電圧が200mVより小さいこと)
copy /b %script_file_name% + %script_parts_dir%\eval_int_lt0200.csv

REM リレーをONし、
copy /b %script_file_name% + %script_parts_dir%\operation_relay_on.csv
REM 1. LEDの点灯電圧が1700mVより大きく1900mVより小さいこと
copy /b %script_file_name% + %script_parts_dir%\dso_query_vmax_gt1700-lt1900.csv
REM 2. LEDの点灯周波数が995Hzより大きく1005Hzより小さいこと
copy /b %script_file_name% + %script_parts_dir%\dso_query_freq_gt995-lt1005.csv

REM リレーをOFFし、LEDの電圧が200mVより小さいこと
copy /b %script_file_name% + %script_parts_dir%\operation_relay_off.csv
copy /b %script_file_name% + %script_parts_dir%\eval_int_lt0200.csv

5. テスト実行結果

result.csv
# setup
# wait for if restart Arduino Uno
sleep,2,OK
#
# Open UART
open_uart,COM3,OK
#
# model and version check
send,i,OK
rcvd,Arduino Test Bench Ver.100,OK
eval_str_eq,Arduino Test Bench Ver.100,OK
#
# relay assigned Pin number check
send,p,OK
rcvd,12,OK
eval_int_eq,12,OK
#
# Setup DSO
open_dso,,OK
dso,*IDN?,RIGOL TECHNOLOGIES-DS1104Z-XXXXXXXXXXXXXX-00.04.04.SP3
eval_str_eq,RIGOL TECHNOLOGIES-DS1104Z-XXXXXXXXXXXXXX-00.04.04.SP3,OK
#
# Timebase Scale 200us
dso,:TIMebase:SCALe 0.0002,OK
#
# eval_int_lt0200
send,v,OK
rcvd,0,OK
eval_int_lt,200,OK
#
# relay ON
send,n,OK
sleep,1,OK
#
# 1.80V +/- 0.10V
dso,:MEASure:VMAX? CHANnel1,1.840000e+00
eval_dbl_gt,1.70,OK
eval_dbl_lt,1.90,OK
#
# 1000Hz +/- 5Hz
dso,:MEASure:FREQuency? CHANnel1,9.999999e+02
eval_dbl_gt,995,OK
eval_dbl_lt,1005,OK
#
# relay OFF
send,f,OK
sleep,1,OK
#
# eval_int_lt0200
send,v,OK
rcvd,0,OK
eval_int_lt,200,OK

6. おわりに

オシロスコープの測定結果にもとづいたGo/No-Go判定をPythonで自動化できました\(^o^)/


  1. Pythonのfloatは倍精度浮動小数点数ですが一般的にはfloatは単精度、doubleは倍精度のためここではdouble型と表記しています。 

  2. 公式サイトではpipコマンドによるインストール手順が提示されています。また、筆者がMiniconda環境にcondaコマンドでインストールした手順はこちらです。 

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2