أدوات عتاديةتدوينات مصغرة

اختبار الدارات الإلكترونية باستخدام إطار Behave بلغة البايثون ودارة Analog Discovery 2

إن عملية الاختبار هي خطوة أساسية في أثناء تطوير أو إنتاج أي منتج، وبالقدر الذي تتوفر فيه خطط اختبار واضحة وشاملة لكل المزايا بقدر ما يتم اكتشاف الأخطاء بشكل مبكّر.

يستخدم البعض الاختبار كمنهجية تطوير وذلك خلال تطوير البرمجيات في منهجية agile software development وتسمى هذه المنهجية بـBehavior-driven development أو اختصاراً BDD حيث تساعد هذه المنهجية في التعاون بين الأطراف التقنية وغير التقنية في عمليّة التطوير وهذا وبمنظور أوسع يمكن إعادة استخدامه في تطوير الدارات والبرامج المضمّنة Embedded Code.

إحدى طرق توصيف خطط الاختبار هي باستخدام لغة  Gherkin language وهي لغة توصيفية غير تقنية ومقروءة بشرياً لتوصيف مثل هذه الخطط.

إن العديد من فرق تطوير النظم المضمّنة Embedded Systems تقوم بتصميم منصّة الاختبار الخاصة بها وهي ومن وجهة نظر تقنيّة مشروع بحد ذاته. هذه المنصات تكون عادة Test Jig وبداخلها دارة الاختبار وتصل الإشارات بين دارة الاختبار والدارة المُختبرة عبر أرجل راصوريّة تسمى Pogo Pins وهي أرجل مزودة براصور/نابض لتحقق الاتصال مع نقاط الاختبار في الدارة المُختبرة Test Points.

هذا يتضمّن دارة قادرة على إصدار إشارات تماثلية DAC وقراءة الإشارات التماثلية ADC وقراءة الإشارات الرقمية ، وبالتأكيد يجب إظهار نتيجة الاختبار بأبسط أشكالها، ليد LED أخضر في حالة النجاح وليد LED أحمر في حالة الفشل، وفي بعض الأحيان قد يكون من المطلوب إظهار معلومات تفصيليّة عن الاختبار وبهذه الحالة يجب توفير اتصال مع حاسب وغالباً عبر اليو إس بي USB وهذا بطبيعة الحال يوجب تصميم بروتوكول وطريقة تواصل خاصة مع دارة الاختبار وكتابة برنامج قيادة صغير Driver Code من جهة الحاسب المضيف.

عندما قمت بمراجعة لمزايا الجهاز متعدد المهام Analog Discovery 2 ووجدت أن جميع مزاياه المتقدّمة يمكن الوصول لها من أي برنامج آخر عبر الحزمة التطويريّة SDK الموفَّرة، كان أحد أول الأشياء التي فكرت بها كتطبيق هي استخدام Analog Discovery 2 مع إطار Behave بلغة البايثون. إن Behave تستخدم الاختبارات المكتوبة بلغة بشرية لتنفيذها وتقوم بتوصيفها بلغة البايثون.

  Scenario Outline: Static I/O
     Given Analog Discovery 2 is connected
      When Output #<output_number> set to <output_state>
      Then Input #<input_number> is <input_state>
      Then Close connection with Analog Discovery 2
    Examples:
        |  output_number  |  input_number | output_state | input_state | 
        |  0              |  1            |  1           | 1           |
        |  0              |  1            |  0           | 0           |

هذا مثال عن اختبار مكتوب بلغة Gherkin language وسوف نرى كيف سيتم تنفيذه باستخدام Behave وجهاز Analog Discovery 2.

عوضاً عن هدر الوقت بتصميم دارة اختبار من الصفر، فإنه يمكن إعادة استخدام مزايا Analog Discovery 2 المتضمّنة على وليس فقط، راسم إشارة بتردد 30 ميغا هرتز ومولد إشارة بتردد 12 ميغا هرتز، ومحلل منطقي بـ16 قناة و16 دخل/خرج عام الإستخدام. شخصياً، وكمطور، لا أستطيع أن أجد أكثر سهولة وبساطة من تنفيذ الاختبار بالدمج بين Analog Discovery 2 وإطار Behave.

Waveform هو اسم البرنامج الحاسوبي الذي يقوم بالتواصل والتحكم بتجهيزة Analog Discovery 2 ويتوفر معه SDK يحوي على التوابع APIs اللازمة للوصول إلى جميع ميزات الجهاز، ويتوفر ملفاته ضمن مجلد التنصيب للبرنامج ويوجد معه ملف samples يحوي على أمثلة بلغة السي والبايثون حول تقريباً كل المزيات، وكما يتوفر كتيّب مرجعي لكل التوابع المتوفّرة في الـSDK.


اطّلع على محتوى متعلّق بـ Analog Discovery 2:

مراجعة جهاز Analog Discovery 2.
– اختبار الدارات الإلكترونية باستخدام إطار Behave بلغة البايثون ودارة Analog Discovery 2.
فحص مخارج شريحة FTDI باستخدام Analog Discovery 2.

لنبدأ التنفيذ العملي وذلك بإنشاء اختبار يسمى Feature بمصطلحات إطار Behave ويتألف من سينارويهين اثنين، الأول هو لاختبار تغير القراءة في دخل عند تغير الخرج وذلك عبر وصل رجلتين مع بعضهما البعض من Analog Discovery 2 والسيناريو الثاني هو وصل المحلل المنطقي مع مصدر UART والتحقق من وصول محارف معيّنة.

لنبدأ بإنشاء مجلّد ولنقم بتسمية ملف داخله اسمه basics.feature والذي يحوي على التالي:

Feature: Check Baiscs

  Scenario Outline: Static I/O
     Given Analog Discovery 2 is connected
      When Output #<output_number> set to <output_state>
      Then Input #<input_number> is <input_state>
      Then Close connection with Analog Discovery 2
    Examples:
        |  output_number  |  input_number | output_state | input_state | 
        |  0              |  1            |  1           | 1           |
        |  0              |  1            |  0           | 0           |
        
  Scenario Outline: UART
     Given Analog Discovery 2 UART is configured
      Then Wait to send 0x<rx_char> via UART
      Then Close connection with Analog Discovery 2
    Examples:
        |  rx_char  |  
        |  AA       | 
        |  55       |  

بداخل نفس المجلد، قم بإنشاء مجلد فرعي باسم steps وقم بإنشاء ملف مصدري باسم basics.py والذي سيحوي التوصيف البرمجي لما ورد في basics.feature.

وكمثال هذا ما سيبدو عليه توصيف الجملة Given Analog Discovery 2 is connected:

@given('Analog Discovery 2 is connected')
def step_impl(context):
    dwf.FDwfDeviceOpen(c_int(-1), byref(hdwf))
    assert hdwf.value != hdwfNone.value,"Check USB connection with AD2"

ويبدو When Output #<output_number> set to <output_state> كالتالي:

@when('Output #{output_number} set to {output_state}')
def step_impl(context,output_number,output_state):
    # enable output/mask on 8 LSB IO pins, from DIO 0 to 7
    IO_enable_mask = 0x0000 | (1 << int(output_number))
    IO_state_mask = 0x0000 | (int(output_state)<<int(output_number)) 
    dwf.FDwfDigitalIOOutputEnableSet(hdwf, c_int(IO_enable_mask)) 
    # set value on enabled IO pins
    dwf.FDwfDigitalIOOutputSet(hdwf, c_int(IO_state_mask)) 

وللمرجعية هذا هو الكود الكامل للملف المصدري:

from behave import *
from ctypes import *
from dwfconstants import *
import time
import sys

if sys.platform.startswith("win"):
    dwf = cdll.dwf
elif sys.platform.startswith("darwin"):
    dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf")
else:
    dwf = cdll.LoadLibrary("libdwf.so")
    
    
hdwf = c_int()

cRX = c_int(0)
fParity = c_int(0)
rgRX = create_string_buffer(8193)

version = create_string_buffer(16)
dwf.FDwfGetVersion(version)
print("DWF Version: "+str(version.value))

@given('Analog Discovery 2 is connected')
def step_impl(context):
    dwf.FDwfDeviceOpen(c_int(-1), byref(hdwf))
    assert hdwf.value != hdwfNone.value,"Check USB connection with AD2"

@when('Output #{output_number} set to {output_state}')
def step_impl(context,output_number,output_state):
    # enable output/mask on 8 LSB IO pins, from DIO 0 to 7
    IO_enable_mask = 0x0000 | (1 << int(output_number))
    IO_state_mask = 0x0000 | (int(output_state)<<int(output_number)) 
    dwf.FDwfDigitalIOOutputEnableSet(hdwf, c_int(IO_enable_mask)) 
    # set value on enabled IO pins
    dwf.FDwfDigitalIOOutputSet(hdwf, c_int(IO_state_mask)) 

@then('Input #{input_number} is {input_state}')
def step_impl(context,input_number,input_state): 
    dwRead = c_uint32()
    # fetch digital IO information from the device 
    dwf.FDwfDigitalIOStatus (hdwf) 
    # read state of all pins, regardless of output enable
    dwf.FDwfDigitalIOInputStatus(hdwf, byref(dwRead)) 
    assert  bin(dwRead.value)[2:].zfill(16)[15-int(input_number)] == input_state,[dwf.FDwfDeviceCloseAll(),"Input is not matching output"][1]
    
    
    
@given('Analog Discovery 2 UART is configured') 
def step_impl(context):   
    dwf.FDwfDeviceOpen(c_int(-1), byref(hdwf))
    assert hdwf.value != hdwfNone.value,"Check USB connection with AD2"
    dwf.FDwfDigitalUartRateSet(hdwf, c_double(9600)) # 9.6kHz
    dwf.FDwfDigitalUartRxSet(hdwf, c_int(2)) # RX = DIO-2
    dwf.FDwfDigitalUartBitsSet(hdwf, c_int(8)) # 8 bits
    dwf.FDwfDigitalUartParitySet(hdwf, c_int(0)) # 0 none, 1 odd, 2 even
    dwf.FDwfDigitalUartStopSet(hdwf, c_double(1)) # 1 bit stop length

@then('Wait to send 0x{rx_char} via UART')
def step_impl(context,rx_char):
    dwf.FDwfDigitalUartRx(hdwf, None, c_int(0), byref(cRX), 0)# initialize RX reception
    time.sleep(1)
    print("Receiving on RX, press Ctrl+C to stop...")
    try:
        while True:
            time.sleep(0.001)
            dwf.FDwfDigitalUartRx(hdwf, rgRX, c_int(sizeof(rgRX)-1), byref(cRX), 0) # read up to 8k chars at once
            if cRX.value > 0:
                rgRX[cRX.value] = 0 # add zero ending
                #sz = rgRX.value.decode()
                #print(sz, end = '', flush=True) # works with CR+LF or LF
                if(int.from_bytes(rgRX.value, "little") == int(rx_char,16)):
                    break
    except KeyboardInterrupt: # Ctrl+C
        pass

@then('Close connection with Analog Discovery 2') 
def step_impl(context):
    dwf.FDwfDeviceCloseAll()    
يوجد ملف اسمه dwfconstants.py يوجد ضمن الأمثلة samples الموجودة في ملفات الـSDK يجب نسخه إلى المجلد steps.

والآن لتنفيذ الاختبار، سنقوم بتنفيذ التعليمة behave داخل المجلّد، وهذه هي النتيجة التي ستظهر عند تجاوز كل الاختبارات بنجاح.

وبالقيام بخطأ مقصود، عبر قطع الاتصال بين رجل الدخل ورجل الخرج، ستظهر نتيجة تدل على وجود الخطأ في أحد الاختبارات.

هذه كانت لمحة سريعة عن كيفية استخدام Analog Discovery 2 وإطار Behave من أجل القيام باختبار عتادي. هل استخدمت أو تستخدم طريقة أسرع وأحسن، شاركنا إياها مشكوراً في التعليقات.

Yahya Tawil

يحيى هو مدير التحرير في عتاديات ويؤمن بأهمية المحتوى المكتوب المجاني والنوعي والعملي. خبرته في مجال النظم المضمّنة تتركز في كتابة البرامج المضمنة وتصميم الدارات المطبوعة والنظرية وإنشاء المحتوى.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

هذا الموقع يستخدم Akismet للحدّ من التعليقات المزعجة والغير مرغوبة. تعرّف على كيفية معالجة بيانات تعليقك.

زر الذهاب إلى الأعلى