edge lab LIVE! starts Tuesday in Munich. Grab a workshop seat and take home the board you build on. Code FRIEND-HACKSTER saves 20%. () Learn more!
raspihats
Published © GPL3+

A Configurable Raspberry Pi Hardware Test Bench

A reconfigurable EOL + regression test bench: a Pi, a stack of HATs, and Robot Framework. Change what it tests by changing the HATs.

IntermediateWork in progress16 hours18
A Configurable Raspberry Pi Hardware Test Bench

Things used in this project

Hardware components

Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
×1
DQ5rly-I2C-HAT
Raspberry Pi I/O Module / I2C / 5 Relay Outputs
×1
DQ10rly I2C-HAT
Raspberry Pi I/O Module / I2C / 10 Relay Outputs
×2
DI16ac I2C-HAT
Raspberry Pi I/O Module / I2C / 16 Digital Inputs
×1
DIN Pi Case
Raspberry Pi and HATs enclosure / DIN Rail
×1
DIN Pi Case Extension
HAT case / DIN Rail
×4

Software apps and online services

robotframework

Story

Read more

Schematics

Electrical Diagram PDF

Code

Robotframework Test Suite

Plain text
*** Settings ***
Documentation       Test suite for the DI6acDQ6rly I2C-HAT (6 AC digital inputs + 6 relay outputs).
...
...                 Test bench wiring:
...                 - DI16ac  @ ${RELAY_MONITOR_ADDRESS}: monitors DUT relay outputs (wired to inputs 8..15)
...                 - DQ10rly @ ${INPUT_CONTROL_ADDRESS}: drives DUT digital inputs (ch 0..5) and selects COM pin (ch 6 = COM0, ch 7 = COM1)
...                 - DQ10rly @ ${JUMPER_CONTROL_ADDRESS}: boot jumper (relay 0) and address jumpers 0..3 (relays 4..1)
...                 - DQ5rly  @ ${POLARITY_CONTROL_ADDRESS}: input polarity wiring (0x18 = reverse). The polarity relays
...                   re-wire the source so the expected logical values are identical for normal and reverse polarity.
...
...                 Note: the three IRQ test cases are order-dependent and must run in suite order.

Library             Process
Library             Collections
Library             Dialogs
Library             raspihats.i2c_hats.robot

Suite Setup         Open Test Bench
Suite Teardown      Close Test Bench
Test Setup          Open Test Case


*** Variables ***
# I2C addresses
${DUT_ADDRESS}                  ${0x60}
${BOOTLOADER_ADDRESS}           ${0x3E}
${RELAY_MONITOR_ADDRESS}        ${0x40}
${INPUT_CONTROL_ADDRESS}        ${0x5F}
${JUMPER_CONTROL_ADDRESS}       ${0x5E}
${POLARITY_CONTROL_ADDRESS}     ${0x5D}

# DUT characteristics
${DUT_NAME}                     DI6acDQ6rly I2C-HAT
${CHANNEL_COUNT}                ${6}
${ALL_CHANNELS_MASK}            ${0x3F}
${RELAY_MONITOR_OFFSET}         ${8}
${REVERSE_POLARITY_MASK}        ${0x18}

# Timing
${SETTLE_DELAY}                 0.1s
${BUS_SETTLE_DELAY}             0.5s
${VALUE_SETTLE_DELAY}           0.5s
${IRQ_PULSE_DELAY}              0.015s
${LED_CHASE_DELAY}              0.25s


*** Test Cases ***
Initialize DUT
    [Documentation]    Creates the DUT handle, verifies name and firmware version, and checks idle I/O state.
    ${i2c_hat}=    New DI6acDQ6rly    ${DUT_ADDRESS}
    Set Suite Variable    ${DUT}    ${i2c_hat}
    ${name}=    Get Name    ${DUT}
    Should Be Equal As Strings    ${name}    ${DUT_NAME}
    ${fw_version}=    Get Firmware Version    ${DUT}
    Log To Console    ${fw_version}
    DO Set Value    ${DUT}    ${0}
    ${di_value}=    DI Get Value    ${DUT}
    Should Be Equal As Integers    ${di_value}    ${0}

Boot Pin
    [Documentation]    With the boot jumper active, the DUT must enumerate at the bootloader address after reset.
    Activate Boot Jumper
    Reset DUT
    ${i2c_devices}=    Scan I2C Bus
    Should Contain    ${i2c_devices}    ${BOOTLOADER_ADDRESS}
    Deactivate Boot Jumper
    # The device stays in bootloader mode (at ${BOOTLOADER_ADDRESS}) after the jumper is released. The bus scan 
    # below is what resets the bootloader and returns control to the application.
    Scan I2C Bus
    Sleep    ${BUS_SETTLE_DELAY}
    New DI6acDQ6rly    ${DUT_ADDRESS}

Address Pins
    [Documentation]    Each address jumper must shift the I2C address by its bit weight.
    [Template]    Verify Address Jumper
    # jumper index    expected address
    ${0}               ${0x61}
    ${1}               ${0x62}
    ${2}               ${0x64}
    ${3}               ${0x68}

Digital Input Channels On COM Pin 0
    [Template]    Verify Digital Input Channel
    # COM pin    channel
    ${0}          ${0}
    ${0}          ${1}
    ${0}          ${2}
    ${0}          ${3}
    ${0}          ${4}
    ${0}          ${5}

Digital Input Channels On COM Pin 1
    [Template]    Verify Digital Input Channel
    # COM pin    channel
    ${1}          ${0}
    ${1}          ${1}
    ${1}          ${2}
    ${1}          ${3}
    ${1}          ${4}
    ${1}          ${5}

All Digital Input Channels
    [Template]    Verify All Digital Input Channels
    # COM pin    polarity
    ${0}          normal
    ${0}          reverse
    ${1}          normal
    ${1}          reverse

IRQ Disabled - Input Pulse Does Not Assert IRQ
    Use Digital Inputs COM Pin    ${0}
    Disable And Clear IRQ
    IRQ Pin State Should Be    ${1}
    Pulse Digital Input    ${0}
    IRQ Pin State Should Be    ${1}

IRQ Rising Edge Enabled - Input Pulse Asserts IRQ
    DI Set IRQ Reg Rising Edge Control    ${DUT}    ${ALL_CHANNELS_MASK}
    Pulse Digital Input    ${0}
    IRQ Pin State Should Be    ${0}

IRQ Cleared By Capture Register
    DI Set IRQ Reg Capture    ${DUT}    ${0}
    IRQ Pin State Should Be    ${1}
    [Teardown]    Disable And Clear IRQ

Relay Channels
    [Template]    Verify Relay Channel
    ${0}
    ${1}
    ${2}
    ${3}
    ${4}
    ${5}

All Relay Channels
    Verify All Relay Channels

LEDs Visual Inspection
    Pause Execution    Press OK when you are ready to start the LEDs visual inspection!
    Use Digital Inputs COM Pin    ${0}
    Chase Channel LEDs    ${INPUT_CONTROL}
    Chase Channel LEDs    ${DUT}
    Execute Manual Step    Your evaluation after observing the LEDs?    LEDs visual inspection failed.


*** Keywords ***
# ----------------------------------------------------------------------------
# Test bench lifecycle
# ----------------------------------------------------------------------------
Open Test Bench
    Init Irq Pin
    ${i2c_hat}=    New DI16ac    ${RELAY_MONITOR_ADDRESS}
    Set Suite Variable    ${RELAY_MONITOR}    ${i2c_hat}
    ${i2c_hat}=    New DQ10rly    ${INPUT_CONTROL_ADDRESS}
    Set Suite Variable    ${INPUT_CONTROL}    ${i2c_hat}
    ${i2c_hat}=    New DQ10rly    ${JUMPER_CONTROL_ADDRESS}
    Set Suite Variable    ${JUMPER_CONTROL}    ${i2c_hat}
    ${i2c_hat}=    New DQ5rly    ${POLARITY_CONTROL_ADDRESS}
    Set Suite Variable    ${POLARITY_CONTROL}    ${i2c_hat}
    Reset Test Bench Outputs

Close Test Bench
    Reset Test Bench Outputs

Open Test Case
    DO Set Value    ${JUMPER_CONTROL}    ${0}
    Set Normal Polarity For Digital Inputs

Reset Test Bench Outputs
    DO Set Value    ${JUMPER_CONTROL}    ${0}
    DO Set Value    ${INPUT_CONTROL}    ${0}
    DO Set Value    ${POLARITY_CONTROL}    ${0}

# ----------------------------------------------------------------------------
# Generic helpers
# ----------------------------------------------------------------------------
Scan I2C Bus
    ${result}=    Run Process    i2cdetect    -y    1
    ${bus_devices}=    Evaluate
    ...    [int(x, 16) for x in """${result.stdout}""".replace('--', '').split() if len(x) == 2 and int(x, 16) > 0x0F]
    RETURN    ${bus_devices}

Reset DUT
    Reset    ${DUT}
    Sleep    ${BUS_SETTLE_DELAY}

# ----------------------------------------------------------------------------
# Boot and address jumpers (JUMPER_CONTROL hat)
# ----------------------------------------------------------------------------
Activate Boot Jumper
    [Arguments]    ${delay}=${SETTLE_DELAY}
    DO Set Channel    ${JUMPER_CONTROL}    ${0}    ${1}
    Sleep    ${delay}

Deactivate Boot Jumper
    [Arguments]    ${delay}=${SETTLE_DELAY}
    DO Set Channel    ${JUMPER_CONTROL}    ${0}    ${0}
    Sleep    ${delay}

Set Address Jumper
    [Documentation]    Address jumpers 0..3 are wired to relays 4..1 of the jumper control hat.
    [Arguments]    ${index}    ${state}    ${delay}=${SETTLE_DELAY}
    ${relay_index}=    Evaluate    4 - ${index}
    DO Set Channel    ${JUMPER_CONTROL}    ${relay_index}    ${state}
    Sleep    ${delay}

Verify Address Jumper
    [Arguments]    ${index}    ${expected_address}
    Set Address Jumper    ${index}    ${1}
    Reset DUT
    ${i2c_hat}=    New DI6acDQ6rly    ${expected_address}
    Set Address Jumper    ${index}    ${0}
    Reset    ${i2c_hat}
    Sleep    ${BUS_SETTLE_DELAY}
    ${i2c_hat}=    New DI6acDQ6rly    ${DUT_ADDRESS}
    Set Suite Variable    ${DUT}    ${i2c_hat}

# ----------------------------------------------------------------------------
# Digital input stimulation (INPUT_CONTROL and POLARITY_CONTROL hats)
# ----------------------------------------------------------------------------
Set Normal Polarity For Digital Inputs
    DO Set Value    ${POLARITY_CONTROL}    ${0x00}

Set Reverse Polarity For Digital Inputs
    DO Set Value    ${POLARITY_CONTROL}    ${REVERSE_POLARITY_MASK}

Use Digital Inputs COM Pin
    [Documentation]    Selects COM pin 0 or 1. Channel 6 connects COM0, channel 7 connects COM1; only one may be active.
    [Arguments]    ${com_pin}
    DO Set Channel    ${INPUT_CONTROL}    ${{6 + $com_pin}}    ${1}
    DO Set Channel    ${INPUT_CONTROL}    ${{7 - $com_pin}}    ${0}
    Sleep    ${BUS_SETTLE_DELAY}

Drive Digital Input
    [Arguments]    ${index}    ${state}    ${delay}=${SETTLE_DELAY}
    DO Set Channel    ${INPUT_CONTROL}    ${index}    ${state}
    Sleep    ${delay}

Pulse Digital Input
    [Arguments]    ${index}    ${pulse}=${IRQ_PULSE_DELAY}
    Drive Digital Input    ${index}    ${1}    ${pulse}
    Drive Digital Input    ${index}    ${0}    ${pulse}

Digital Input State Should Be
    [Arguments]    ${index}    ${expected}
    ${state}=    DI Get Channel    ${DUT}    ${index}
    Should Be Equal As Integers    ${state}    ${expected}

Verify Digital Input Channel
    [Arguments]    ${com_pin}    ${index}
    Use Digital Inputs COM Pin    ${com_pin}
    Drive Digital Input    ${index}    ${1}
    Digital Input State Should Be    ${index}    ${1}
    Drive Digital Input    ${index}    ${0}
    Digital Input State Should Be    ${index}    ${0}

Verify All Digital Input Channels
    [Arguments]    ${com_pin}    ${polarity}=normal    ${delay}=${VALUE_SETTLE_DELAY}
    IF    '${polarity}' == 'reverse'
        Set Reverse Polarity For Digital Inputs
    ELSE
        Set Normal Polarity For Digital Inputs
    END
    Use Digital Inputs COM Pin    ${com_pin}
    ${do_value}=    DO Get Value    ${INPUT_CONTROL}
    DO Set Value    ${INPUT_CONTROL}    ${{$do_value | $ALL_CHANNELS_MASK}}
    Sleep    ${delay}
    ${state}=    DI Get Value    ${DUT}
    Should Be Equal As Integers    ${state}    ${ALL_CHANNELS_MASK}
    DO Set Value    ${INPUT_CONTROL}    ${{$do_value & ~$ALL_CHANNELS_MASK}}
    Sleep    ${delay}
    ${state}=    DI Get Value    ${DUT}
    Should Be Equal As Integers    ${state}    ${0}

# ----------------------------------------------------------------------------
# Relay verification (RELAY_MONITOR hat)
# ----------------------------------------------------------------------------
Relay State Should Be
    [Documentation]    DUT relay outputs are wired to relay monitor inputs 8..15.
    [Arguments]    ${index}    ${expected}    ${delay}=${SETTLE_DELAY}
    Sleep    ${delay}
    ${state}=    DI Get Channel    ${RELAY_MONITOR}    ${{$index + $RELAY_MONITOR_OFFSET}}
    Should Be Equal As Integers    ${state}    ${expected}

Verify Relay Channel
    [Arguments]    ${index}
    DO Set Channel    ${DUT}    ${index}    ${1}
    Relay State Should Be    ${index}    ${1}
    DO Set Channel    ${DUT}    ${index}    ${0}
    Relay State Should Be    ${index}    ${0}

Verify All Relay Channels
    [Arguments]    ${delay}=${VALUE_SETTLE_DELAY}
    DO Set Value    ${DUT}    ${ALL_CHANNELS_MASK}
    Sleep    ${delay}
    ${state}=    DI Get Value    ${RELAY_MONITOR}
    Should Be Equal As Integers    ${state}    ${{$ALL_CHANNELS_MASK << $RELAY_MONITOR_OFFSET}}
    DO Set Value    ${DUT}    ${0}
    Sleep    ${delay}
    ${state}=    DI Get Value    ${RELAY_MONITOR}
    Should Be Equal As Integers    ${state}    ${0}

# ----------------------------------------------------------------------------
# IRQ helpers
# ----------------------------------------------------------------------------
Disable And Clear IRQ
    DI Set IRQ Reg Rising Edge Control    ${DUT}    ${0}
    DI Set IRQ Reg Falling Edge Control    ${DUT}    ${0}
    DI Set IRQ Reg Capture    ${DUT}    ${0}

IRQ Pin State Should Be
    [Documentation]    IRQ pin is active low: 1 = idle, 0 = asserted.
    [Arguments]    ${expected}
    ${irq_state}=    Get IRQ Pin State
    Should Be Equal As Integers    ${irq_state}    ${expected}

# ----------------------------------------------------------------------------
# LED inspection
# ----------------------------------------------------------------------------
Chase Channel LEDs
    [Arguments]    ${i2c_hat}    ${on_time}=${LED_CHASE_DELAY}
    FOR    ${index}    IN RANGE    ${CHANNEL_COUNT}
        DO Set Channel    ${i2c_hat}    ${index}    ${1}
        Sleep    ${on_time}
        DO Set Channel    ${i2c_hat}    ${index}    ${0}
        Sleep    ${on_time}
    END

Python glue code

raspihats python package interface to Robot Framework

Credits

raspihats
1 project • 0 followers
Software, firmware & hardware engineer. From idea to market, creator of raspihats Raspberry Pi I/O boards!

Comments