Version 180

    Peter, Jon and Jan are building a programmable electronic load. This document is the common design sheet.

    It's obviously work in progres. And fun.

     

     

     

     

     

     

     

    Hardware

     

     

    MSP432 LaunchPad pin assignments

     

    PinNameUse
    J1.1+3V33v3 for the i2c pull-ups
    J1.3P3.2UART1 RX
    J1.4P3.3UART1 TX
    J1.9P6.5i2c SCL
    J1.10P6.4i2c SDA
    J3.22GNDground
    J1.2P6.0LCD Power Control
    J1.5P4.13LCD SPI CS
    J1.6P1.5LCD SPI CLK
    J2.2P2.5LCD External COM Inversion (not used)
    J2.6P1.6LCD SPI SIMO
    J3.29 reservedP5.4ADC_INPUT_A1 (not used)
    J3.30 reservedP5.5ADC_INPUT_A0 (not used)
    J4.39 reservedP2.6Board_PWM0 (not used)
    J4.38 reservedP2.4Board_PWM1 (not used)
    need to add SPI1 reserved
    need to add SPI2 reserved
    need to add SPI3 reserved
    need to add SPI4 reserved

    We can ignore the reserved pins. They aren't used. I'd like to avoid that we do use them unless we understand what flex we give up though.

    The PCB should have a white triangular mark next to J1.1 if possible. It's the indicator for the mounting direction (because the connectors are symmetrical, it's easy to mount the wrong way around).

     

    DAC-ADC BoosterPack

     

    eload_1_2.jpg

     

     

    20170809_110138a.jpg

     

     

    Output pin assignments

    All pins isolated from LaunchPad if P1 and P2 open.

    P1 and P2 are for BoosterPack testing only, to deliver power to the DAC, ADC and REF ship when no power board is connected.

    In the final design, P1 and P2 need to be open and the power board has to provide GND and V+ for these 3 ICs.

    It should be at least 3V (ISO min. VCC2 requirement) and max 5V5 (max rating for ADC). Let's use 3V3 to 5V.

     

     

    PinDirNameDescription
    P3.1analog outVREFfuture
    P3.2analog outDAC Dfuture
    P3.3analog outDAC Cfuture
    P3.4analog outDAC Bfuture
    P3.5analog outDAC Acurrent control voltage
    P3.6analog inADC Aactually set current readback
    P3.7analog inADC Bsense voltage at device under test or terminals
    P3.8analog inADC Ctemperature
    P3.9analog inADC Dfuture
    P3.10depends on P2GNDshould come from power board GND if we run isolated (P2 open)
    P4.1depends on P2GNDshould come from power board GND if we run isolated (P2 open)
    P4.2depends on P13V3should come from power board if we run isolated (P1 open). 3V3 - 5V
    P4.3digital outi²C SCLfuture
    P4.4digital in/outI²C SDAfuture

     

     

     

     

    BOM for pcb version 1_2:

    Changes between versions of the ADC/DAC pcb:

     

    protopackageV1_1packageV1_2package
    oshparkseeedseeed
    ISO1541DRISO1541DR8-SOICISO1541DRISO1541DR8-SOICISO1541DRISO1541DR8-SOIC
    REF5020IDREF5020ID8-SOICREF5020AIDGKTREF5020AIDGKT8-VSSOPREF5020AIDGKTREF5020AIDGKT8-VSSOP
    DAC8571IDGKRDAC8571IDGKR8-VSSOPDAC8571IDGKRDAC8571IDGKR8-VSSOPDAC8574IPWDAC8574IPW16-TSSOP
    ADS1115IDGST10-MSOPADS1115IDGST10-MSOPADS1115IDGST10-MSOP
    bodge

      U3 Vsense to VOUT

    U3 Vsense to VOUT

    ADCs labeled reverse on the PCB.

    D=A, C=B, B=C and A=D

     

     

    Solder advice: in hindsight I should have used larger IC packages. The board isn't easy to solder at home. But it's doable with proper care, hand stability of a snooker player and eyesight of an eagle.

    It's best to leave capacitor C5 off and solder it separately when the other components (in particular U2) are mounted. Even then it may be good to move it as far as you can from U2's pins. I didn't provide enough clearance in my PCB design.

    The VSSOP footprint of U2 is narrow on the V1_2 board. If you are brave, you can use pliers to bend the pins a bit down. I cut of the pins 5, 7 and 8 with a hobby knife.

     

    For measurements on this board, check Programmable Electronic Load - ADC and DAC BoosterPack test.

     

    Load Board

    CAD version:

     

    eload driver board, power section

     

    optional BoosterPack breakout edge connectors to add user interface elements. These aren't isolated from the LaunchPad.

    eload_power_schema_BP_2ndtry.png

    optional use as a PSU

    eload_power_schema_psu_2ndtry.png

    PCB

     

    boardtop.pngboardbottom.png

     

     

    BOM:

    RefValueFootprintoptionalloadpsumanf. Nrsourcedescriptionurl
    C1330nCapacitors_SMD:C_1206_HandSolderingxx
    C210nCapacitors_SMD:C_1206_HandSolderingxx
    C347uCapacitors_SMD:C_1210_HandSolderingoptionalxxGRM32ER61C476KE15Lelement14MURATA - SMD Multilayer Ceramic Capacitor, 1210 [3225 Metric], 47 µF, 16 V, ± 10%, X5R, GRM Serieshttp://be.farnell.com/murata/grm32er61c476ke15l/cap-mlcc-x5r-47uf-16v-1210/dp/1828819
    C4100pCapacitors_SMD:C_1206_HandSolderingxx
    C539pCapacitors_SMD:C_1206_HandSolderingxx
    C6100nCapacitors_SMD:C_1206_HandSolderingxx
    C7100nCapacitors_SMD:C_1206_HandSolderingxx
    C84n7Capacitors_SMD:C_1206_HandSolderingxx
    C9100pCapacitors_SMD:C_1206_HandSolderingxx
    C10100nCapacitors_SMD:C_1206_HandSolderingxx
    C1139pCapacitors_SMD:C_1206_HandSolderingx
    C12100nCapacitors_SMD:C_1206_HandSolderingx
    C13100nCapacitors_SMD:C_1206_HandSolderingx
    C1439pCapacitors_SMD:C_1206_HandSolderingx
    C15100nCapacitors_SMD:C_1206_HandSolderingx
    C1639pCapacitors_SMD:C_1206_HandSolderingx
    C1747uCapacitors_SMD:CP_Elec_5x5.3xx16SVPG47M16SVPG47Melement1416SVPG47M16SVPG47MCapacitor 47 F 16 V OS-CON SVPG Series Radial Can SMD 0.025 ohm 5000 hours 105°C http://be.farnell.com/panasonic-electronic-components/16svpg47m/cap-alu-polymer-47uf-16v-rad-can/dp/2491822
    C1847uCapacitors_SMD:CP_Elec_5x5.3xx16SVPG47M16SVPG47Melement1416SVPG47M16SVPG47MCapacitor 47 F 16 V OS-CON SVPG Series Radial Can SMD 0.025 ohm 5000 hours 105°C http://be.farnell.com/panasonic-electronic-components/16svpg47m/cap-alu-polymer-47uf-16v-rad-can/dp/2491822
    C1947uCapacitors_SMD:C_1210_HandSolderingoptionalxxGRM32ER61C476KE15Lelement14MURATA - SMD Multilayer Ceramic Capacitor, 1210 [3225 Metric], 47 µF, 16 V, ± 10%, X5R, GRM Serieshttp://be.farnell.com/murata/grm32er61c476ke15l/cap-mlcc-x5r-47uf-16v-1210/dp/1828819
    C2047uCapacitors_SMD:CP_Elec_5x5.3xx16SVPG47M16SVPG47Melement1416SVPG47M16SVPG47MCapacitor 47 F 16 V OS-CON SVPG Series Radial Can SMD 0.025 ohm 5000 hours 105°C http://be.farnell.com/panasonic-electronic-components/16svpg47m/cap-alu-polymer-47uf-16v-rad-can/dp/2491822
    C2147uCapacitors_SMD:C_1210_HandSolderingoptionalxxGRM32ER61C476KE15Lelement14MURATA - SMD Multilayer Ceramic Capacitor, 1210 [3225 Metric], 47 µF, 16 V, ± 10%, X5R, GRM Serieshttp://be.farnell.com/murata/grm32er61c476ke15l/cap-mlcc-x5r-47uf-16v-1210/dp/1828819
    C2215pCapacitors_SMD:C_1210_HandSolderingoptionalxx
    D1MMSZ5233BMMSZ5233B6V Diodes_SMD:D_SOD-123xxMMSZ5233BMMSZ5233Belement14MMSZ5233BMMSZ5233BZener Single Diode MMSZ52 Series 6 V 500 mW SOD-123 5 2 Pins 150°C http://be.farnell.com/diodes-inc/mmsz5233b/diode-zener-6v-0-5w-sod123/dp/2061470
    D21N4148Diodes_SMD:D_SOD-123xx
    D31N4148Diodes_SMD:D_SOD-123xx
    J1Ti_Booster_40_J1Pin_Headers:Pin_Header_Straight_1x10_Pitch2.54mmxxHDFL10Paliexpresspin headers.
    I use ones with extended pins, female side up, e.g. can be combined using multiple Stackable Header 10-pin 2.54mm for Ar duino Shields Pin Lenght 11mm
    One Lot is enough for the whole build
    https://www.aliexpress.com/item/2-Pieces-lot-Stackable-Header-10-pin-2-54mm-for-Arduino-Shields-Pin-Lenght-11mm/1022022366.html?spm=a2g0s.9042311.0.0.JwkKAe
    J2Ti_Booster_40_J2Pin_Headers:Pin_Header_Straight_1x10_Pitch2.54mmoptionalxxHDFL10Paliexpresspin headers.
    I use ones with extended pins, female side up, e.g. can be combined using multiple Stackable Header 10-pin 2.54mm for Ar duino Shields Pin Lenght 11mm
    One Lot is enough for the whole build
    https://www.aliexpress.com/item/2-Pieces-lot-Stackable-Header-10-pin-2-54mm-for-Arduino-Shields-Pin-Lenght-11mm/1022022366.html?spm=a2g0s.9042311.0.0.JwkKAe
    J3Ti_Booster_40_J3Pin_Headers:Pin_Header_Straight_1x10_Pitch2.54mmxxHDFL10Paliexpresspin headers.
    I use ones with extended pins, female side up, e.g. can be combined using multiple Stackable Header 10-pin 2.54mm for Ar duino Shields Pin Lenght 11mm
    One Lot is enough for the whole build
    https://www.aliexpress.com/item/2-Pieces-lot-Stackable-Header-10-pin-2-54mm-for-Arduino-Shields-Pin-Lenght-11mm/1022022366.html?spm=a2g0s.9042311.0.0.JwkKAe
    J4Ti_Booster_40_J4Pin_Headers:Pin_Header_Straight_1x10_Pitch2.54mmoptionalxxHDFL10Paliexpresspin headers.
    I use ones with extended pins, female side up, e.g. can be combined using multiple Stackable Header 10-pin 2.54mm for Ar duino Shields Pin Lenght 11mm
    One Lot is enough for the whole build
    https://www.aliexpress.com/item/2-Pieces-lot-Stackable-Header-10-pin-2-54mm-for-Arduino-Shields-Pin-Lenght-11mm/1022022366.html?spm=a2g0s.9042311.0.0.JwkKAe
    P1CONN_01X02Pin_Headers:Pin_Header_Straight_1x02_Pitch2.54mmxx282834-2282834-2element14282834-2282834-2Wire-To-Board Terminal Block 2.54 mm 2 Ways 30 AWG 16 AWG 1.4 mm Screw http://be.farnell.com/buchanan-te-connectivity/282834-2/terminal-block-wire-to-brd-2pos/dp/2112482
    P2CONN_01X08Pin_Headers:Pin_Header_Straight_1x08_Pitch2.54mmxx282834-8282834-8element14282834-8282834-8Wire-To-Board Terminal Block 2.54 mm 8 Ways 30 AWG 16 AWG 1.4 mm Screw http://be.farnell.com/te-connectivity-buchanan/282834-8/terminal-block-wire-to-brd-8pos/dp/2396256
    P3CONN_01X10Pin_Headers:Pin_Header_Straight_1x10_Pitch2.54mmxxpin headers. I use ones with extended pins, female side up
    P4CONN_01X04Pin_Headers:Pin_Header_Straight_1x04_Pitch2.54mmxxpin headers. I use ones with extended pins, female side up
    P5CONN_01X20Pin_Headers:Pin_Header_Straight_1x20_Pitch2.54mmoptionalxx
    P6CONN_01X20Pin_Headers:Pin_Header_Straight_1x20_Pitch2.54mmoptionalxx
    P7CONN_01X02Pin_Headers:Pin_Header_Straight_1x02_Pitch2.54mmoptionalxx
    Q1BSS138TO_SOT_Packages_SMD:SOT-23xxBSS138-7-Felement14BSS138-7-F - MOSFET Transistor, N Channel, 200 mA, 50 V, 1.4 ohm, 10 V, 1.2 Vhttp://be.farnell.com/diodes-inc/bss138-7-f/mosfet-n-ch-50v-0-2a-sot23-3/dp/1843693
    R1100KResistors_SMD:R_1206_HandSolderingxx
    R2100KResistors_SMD:R_1206_HandSolderingxx
    R3100KResistors_SMD:R_1206_HandSolderingxx
    R4100KResistors_SMD:R_1206_HandSolderingxx
    R5680KResistors_SMD:R_1206_HandSolderingx
    R618KResistors_SMD:R_1206_HandSolderingx
    R7100KResistors_SMD:R_1206_HandSolderingxx
    R8100KResistors_SMD:R_1206_HandSolderingxx
    R910KResistors_SMD:R_1206_HandSolderingxx
    R100RResistors_SMD:R_1206_HandSolderingoptionalxx
    R115KResistors_SMD:R_1206_HandSolderingxx
    R12100KResistors_SMD:R_1206_HandSolderingxx
    R13100KResistors_SMD:R_1206_HandSolderingxx
    R14100KResistors_SMD:R_1206_HandSolderingxx
    R15100KResistors_SMD:R_1206_HandSolderingxx
    R16100KResistors_SMD:R_1206_HandSolderingxx
    R17100KResistors_SMD:R_1206_HandSolderingxx
    R18100KResistors_SMD:R_1206_HandSolderingxx
    R193K3Resistors_SMD:R_1206_HandSolderingxx
    R203K3Resistors_SMD:R_1206_HandSolderingxx
    R21100KResistors_SMD:R_1206_HandSolderingxx
    R2210KResistors_SMD:R_1206_HandSolderingxx
    R23100KResistors_SMD:R_1206_HandSolderingxx
    R244K7Resistors_SMD:R_1206_HandSolderingxx
    R254K7Resistors_SMD:R_1206_HandSolderingxx
    R2647KResistors_SMD:R_1206_HandSolderingx
    R2747KResistors_SMD:R_1206_HandSolderingx
    R2847KResistors_SMD:R_1206_HandSolderingx
    R2947KResistors_SMD:R_1206_HandSolderingx
    R303K3Resistors_SMD:R_1206_HandSolderingx
    R313K3Resistors_SMD:R_1206_HandSolderingx
    R32680KResistors_SMD:R_1206_HandSolderingoptionalxx
    R33470RResistors_SMD:R_1206_HandSolderingxx
    R344K7Resistors_SMD:R_1206_HandSolderingx
    R352KResistors_SMD:R_1206_HandSolderingxx
    R360RResistors_SMD:R_1206_HandSolderingoptionalxx
    U1LM78L05ACMLM78L05ACMHousings_SOIC:SO-8_5.3x6.2mm_Pitch1.27mmxxLM78L05ACMLM78L05ACMelement14LM78L05ACMLM78L05ACMLinear Voltage Regulator 7805 Fixed Positive 6.7V To 35V In 5V And 0.1A Out SOIC-8 http://be.farnell.com/texas-instruments/lm78l05acm/ic-v-reg-5-0v-smd-78l05-soic8/dp/9489436
    U2LM2662Housings_SOIC:SO-8_5.3x6.2mm_Pitch1.27mmxxLM2662MLM2662Melement14LM2662MLM2662MDC/DC Adjustable Charge Pump Voltage Converter 1.5V to 5.5V in-5.5V to-1.5V/200 mA out SOIC-8 http://be.farnell.com/texas-instruments/lm2662m/volt-converter-smd-2662-soic8/dp/9306803
    U3TLE2144SMD_Packages:SO-16-WxxTLE2144CDWG4TLE2144CDWG4element14TLE2144CDWG4TLE2144CDWG4Operational Amplifier Quad 4 Amplifier 5.9 MHz 45 V/s 2V to 22V WSOIC 16 Pins http://be.farnell.com/texas-instruments/tle2144cdwg4/op-amp-quad-low-noise-smd-2144/dp/1234864
    U3LT1058

     

    SMD_Packages:SO-16-W

    xxLINEAR TECHNOLOGY LT1058 Quad, JFET Input Precision High Speed Op Amp
    U4PCA9557DPCA9557DHousings_SOIC:SOIC-16_3.9x9.9mm_Pitch1.27mmxxPCA9557DPCA9557D
    PCA9557DPCA9557D
    element14
    rs online
    PCA9557DPCA9557DI/O Expander 8bit 400 kHz I2C SMBus 2.3 V 5.5 V SOIC
    PCA9557DPCA9557D8-channel I/O Expander 400kHz I2C SMBus 16-Pin SOIC
    http://be.farnell.com/nxp/pca9557d/ic-io-port-i2c-smbus-8bit-16soic/dp/2212102
    http://uk.rs-online.com/web/p/i-o-expanders/8253274/

     

    The schematic explains what is optional and why. If you build the DC load, you dont' have to order components without an x in the load column.

    I haven't looked up sources for jellybean components, only for those that require some thought.

    U4  PCA9557DPCA9557D can be sourced from NXP or TI Both versions are ok

     

     

     

    OFF-BOARD Components

     

    For progress on the design of the power stage, check Programmable Electronic Load - Power Stage.

     

    Firmware

     

    Prerequisites:

    • CCS 8 with MSP432 compiler TI v 16.12.0.STS or >
    • TI-RTOS for MSP43X, any SimpleLink version
    • MSP432 LaunchPad (not the black pre-prod because it's out of support)

     

    The firmware is for the MSP432 microcontroller. But the harware can be driven from any controller, processor, PSoC or computer that can deliver i2c and power between 3V3 and 5V.

    That includes Arduino, Raspberry Pi, BeagleBone, Warp7, IOT20xx, the ST Neo family, ... As long as they match that voltage range and have I2C master, it 'll work.

    The ADC/DAC board takes care that the logic side is galvanicaly isolated from the power side (that is, if you leave the P1 and P2 jumpers open).

     

    The only requirement is that you have a positive supply between 3V3 and 5V, a ground, and I2C signals (the board has I2C pull-ups that connect to the positive supply that you provide. If you don't want that (maybe your dev board already has 'm), leave R1 and R2 off.

    ADC, DAC and Enable are all I2C driven.

     

    GITHUB location: https://github.com/jancumps/msp432/tree/master/MSP432_SCPI_ElectronicLoad

    download latest source files as a zip : https://github.com/jancumps/msp432/archive/master.zip

    GIT artifacts don't contain CCS project files, only sources - get a zip with CCS project from the attachments at the end of this document.

    eload003.jpg

    SCPI Interface

     

    SCPI CommandstatusSCPI standardCommentExample
    *IDN?worksStandard SCPI, implemented by librarycallsignTHEBREADBOARD,ELECTRONICLOAD,0,01.00
    *CLScheckStandard SCPI, implemented by library
    SYST:ERR?worksStandard SCPI, implemented by library
    SYST:ERR:COUN?worksStandard SCPI, implemented by library
    SYST:VERS?worksStandard SCPI, implemented by librarySCPI Standard version1999.0
    *RSTworksStandard SCPI, custom implementation neededreset to a  known status.
    Switch input off and set to constant current mode. Does not alter stored or temporary calibration settings.
    *ESEcheck
    *ESE?check
    *ESR?check
    *OPCcheck
    *OPC?check
    *SREcheck
    *SRE?check
    *STB?check
    *TST?todoStandard SCPI, custom implementation neededTest instrument. Currently does nothing
    *WAIcheck

    <read voltage>

    MEASure:VOLTage:DC?

    worksQuery the voltage on the voltage sense inputs.

    <read current>

    todo

    <set mode constant current, constant resistance, what have you, ...>

    [:SOURce]:FUNCtion

     

    values:

    CURRent

    RESistance

    VOLTage

    POWer

    wipset the constant regulation mode

    On reset, always constant current

    At this time supports constant current

    FUNC CURRent

    FUNC RESistance

    FUNC VOLTage

    FUNC POWer

    [:SOURce]:FUNCtion?wipget the constant regulation modeAt this time supports constant currentFUNC?

    [:SOURce]:INPut[:STATe] <b>

    worksSet the input state to ON or OFF

    INP ON

    INP OFF

    [:SOURce]:INPut[:STATe]?works

    Query the input state

    INP?
    <limits>todo
    DEVElop:DAC# <16 bit value>works for DAC1Development low levelSend the passed value to the DAC. Currently only for the first DAC

    DEVE:DAC1 2000

    DEVE:DAC1 #H4000

    DEVElop:ADC#?

    DEVElop:ADC#:RAW?

    DEVElop:ADC#:VOLTage?

    works for ADC1Development low levelRetrieve the last buffered ADC value. Currently only for ADC 1 - 3

    DEVE:ADC1?

    DEVE:ADC2:RAW?

    DEVE:ADC3:VOLT?

    [:SOURce]:CURRent[:LEVel][:IMMediate]worksConstant Current ModeCurrently accepts RAW DAC value instead of a voltage

    CURR 5

    [:SOURce]:CURRent[:LEVel][:IMMediate]?works

    Retriev@e the set current level

    CURR?
    [:SOURce]:VOLTage[:LEVel][:IMMediate]todoConstant Voltage ModeWIP. Mode not implemented yetVOLT 2
    CALibratewipCalibration and Configurationdocumented here: Programmable Electronic Load - Calibration Steps

     

    For ref: this is what typical instrument (DMM) commands look like

    We may want to check the commands of a few existing loads, to see if there's a pattern or standard

     

        /* DMM */
        {.pattern = "MEASure:VOLTage:DC?", .callback = DMM_MeasureVoltageDcQ,},
        {.pattern = "CONFigure:VOLTage:DC", .callback = DMM_ConfigureVoltageDc,},
        {.pattern = "MEASure:VOLTage:DC:RATio?", .callback = SCPI_StubQ,},
        {.pattern = "MEASure:VOLTage:AC?", .callback = DMM_MeasureVoltageAcQ,},
        {.pattern = "MEASure:CURRent:DC?", .callback = SCPI_StubQ,},
        {.pattern = "MEASure:CURRent:AC?", .callback = SCPI_StubQ,},
        {.pattern = "MEASure:RESistance?", .callback = SCPI_StubQ,},
        {.pattern = "MEASure:FRESistance?", .callback = SCPI_StubQ,},
        {.pattern = "MEASure:FREQuency?", .callback = SCPI_StubQ,},
        {.pattern = "MEASure:PERiod?", .callback = SCPI_StubQ,},

     

    For ref: SCPI command help

    http://www.the-control-freak.com/SCPI/User%20Manual.pdf

    Where Can I Find Documentation about the SCPI Specification? - National Instruments

    http://www.ivifoundation.org/docs/scpi-99.pdf

    SCPI Commands Common

    Common Commands SCPI

    from http://www.the-control-freak.com/SCPI/User%20Manual.pdf

    Suggested commands - this is how BK Precision structures its SCPI (may want to check other loads to see if there is some sort of standard):

    • INPut ON|OFF
    • VOLTage
    • CURRent
    • CURRent:PROTection:STATe ON | OFF
    • CURRent:PROTection:DELay
    • RESistance
    • POWer
    • :FETch or :MEASure VOLTage CURRent POWer (Fetch last sampled, Measure do a sample, I don't think we need to distinguish)
    • SYSTem:LOCal
    • SYSTem:REMote
    • SYSTem:RWLock

     

    Or the ones from Rigol's DL3000 DC range. They seem to be very similar to the BK ones.

     

     

    POSIX threads scheduling and priority overview

     

    This part of the documentation is changed to SimpleLink and POSIX API.

     

     

    RTOS Tasks overview

     

    TaskPriorityVitalStackArgument0 (schedule)Comments
    threadHeartBeat1

    no

    1024

    1000pulse led as visual clue of RTOS health
    threadUART10no1024

    0

    Managed by Semaphore SEM_uart_rx

    react on incoming traffic on UART. Send to SCPI lib.
    threadDisplay10no20481000updates LCD display according to schedule
    threadADC10no10241000samples according to schedule
    threadDAC10no1024

    0

    Managed by QUEUE_NAME_DAC

    reacts on a mail message to set the DAC output value
    threadControl15no51210000

    experimental: if a control strategy (e.g.: constant voltage) needs adjustment at regular times, this task will call that function at scheduled time.

    As not needed by constant current strategy (feedback handled by the hardware), this is now scheduled conservative. It's up to later control strategies to adapt the schedule to the own needs...

    threadInputEnable10no1024

    0

    Managed by QUEUE_NAME_INPUTENABLE

    reacts on a mail message to enable or disable the input
    threadTemperatureOverProtection10no5121000over-temperature watchdog

     

     

    RTOS Semaphore overview

     

    SemaphoreScopeTypeCommentFunctions
    SEM_uart_rxlibrary: uart_implbinary

    halts the UART read task until char receive interrupt.

    This avoids polling. The UART read functionality takes no processor time unless it receives data.

    wait: threadUART()

    release: UART00_IRQHandler()

     

     

    RTOS Event and MailBox overview

     

    QueueMessage  countCommentFunctions
    QUEUE_NAME_DAC1

    halts the DAC read task until an mbDAC message arrives.

    This avoids polling. The DAC functionality takes no processor time unless it receives a message.

    wait: threadDAC()

    send: eloadRawSetDac()

    QUEUE_NAME_INPUTENABLE1
    halt the Input Enable task until an mbInputEnable message arrives.

    wait: threadInputEnable()

    send: eloadInputEnable()

     

     

     

    LCD Display

     

    check here for a detailed explanation: Programmable Electronic Load - LCD Display

     

    The firmware supports SHARP LCD display,

     

     

     

     

     

    Error Handling

     

    SCPI

    good example: https://github.com/eez-open/psu-firmware/tree/master/eez_psu_sketch, in particular https://github.com/eez-open/psu-firmware/blob/master/eez_psu_sketch/scpi_user_config.h

    Complete list: SCPI Errors

    e.g.: this can be used to report an invalid function call (set constant voltage value when in constant current mode, ...):

     

    -221

    std_settingsConflict

    Settings conflict - Indicates that a legal program data element was parsed but could not be executed due to the current device state.

     

     

     

    catch in the SCPI interface:

    missing parameter

    parameter out of bounds

    index out of bounds

     

    - a non-implemented ADC channel:

    DEVE:ADC5?
    SYST:ERR?
    -131,"Invalid suffix"

     

    - a missing DAC value:

    DEVE:DAC2
    SYST:ERR?
    -109,"Missing parameter"

     

    - more than 16 bits to the DAC:

    DEVE:DAC1 66666
    SYST:ERR?
    -224,"Illegal parameter value"

     

     

    push SCPI errors from deeper levels, without making code dependent on SCPI:

    todo

     

    Display?

     

    UART

     

    UART module

    9600/8/1/N

    uses TI-RTOS UART driver.

    Buffered output for transmit

    Interrupt driven for receive, wake up RTOS task (with semaphore) whenever a character arrives on the Rx line.

     

     

    void *threadUART(void *arg0)
    {
        char        input;
        UART_Params uartParams;
        int iError;
    
    
        /* Call driver init functions */
    //    GPIO_init();
        UART_init();
    
    
        /* Configure the LED pin */
        GPIO_setConfig(Board_GPIO_LED0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    
    
        /* Create a UART with data processing off. */
        UART_Params_init(&uartParams);
        uartParams.writeDataMode = UART_DATA_BINARY;
        uartParams.readDataMode = UART_DATA_BINARY;
        uartParams.readReturnMode = UART_RETURN_FULL;
        uartParams.readEcho = UART_ECHO_OFF;
        uartParams.baudRate = 9600;
        uartParams.readMode = UART_MODE_CALLBACK; // the uart uses a read interrupt
        uartParams.readCallback = &UART00_IRQHandler; // function called when the uart interrupt fires
    
    
    #ifdef SCPI_UART_0
        uart = UART_open(Board_UART0, &uartParams);
    #else
    #ifdef SCPI_UART_1
        uart = UART_open(Board_UART1, &uartParams);
    #else
    #error "define a valid UART"
    #endif
    #endif
    
    
        if (uart == NULL) {
            /* UART_open() failed */
            while (1);
        }
    
    
        iError = sem_init(&SEM_uart_rx, 0, 0);
        /* Configure the LED pin */
        GPIO_setConfig(Board_GPIO_LED1, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    
    
    
    
        /* Loop forever echoing */
        while (1) {
            UART_read(uart, &input, 1);
            iError = sem_wait(&SEM_uart_rx); // when a character is received via UART, the interrupt handler will release the binary semaphore
            while (iError) {
                // POSIX reported error with semaphore. Can't recover.
            }
            // in my case: I get an interrupt for a single character, no need to loop.
            GPIO_toggle(Board_GPIO_LED1); // LED B - visual clue that we've received a request over USB
            scpi_instrument_input((const char *)&input, 1);
            GPIO_toggle(Board_GPIO_LED1); // LED B - visual clue off
        }
    }

     

    The interrupt is very simple. It posts a semaphore to flag our UART task that we have a character on the receive line.

     

    void UART00_IRQHandler(UART_Handle handle, void *buffer, size_t num)
    {
        sem_post(&SEM_uart_rx);
    }

     

    There are 2 touchpoints with the SCPI interpreter. UART module knows about SCPI lib. SCPI is UART-unaware.

    • initialises the SCPI lib (is that smart? *) with scpi_instrument_init() edit: moved to main().
    • each character received directly sent to SCPI lib with scpi_instrument_input().

     

        while (1) {
            UART_read(uart, &input, 1);
            iError = sem_wait(&SEM_uart_rx); // when a character is received via UART, the interrupt handler will release the binary semaphore
            while (iError) {
                // POSIX reported error with semaphore. Can't recover.
            }
            // in my case: I get an interrupt for a single character, no need to loop.
            GPIO_toggle(Board_GPIO_LED1); // LED B - visual clue that we've received a request over USB
            scpi_instrument_input((const char *)&input, 1);
            GPIO_toggle(Board_GPIO_LED1); // LED B - visual clue off
        }

     

    • SCPI lib uses SCPI_Write() to send info to UART (although the SCPI lib isn't aware of that. The linker resolves this dependency. The only thing that the SCPI lib requires is that "there is a function named SCPI_Write()" somewhere).

     

    size_t SCPI_Write(scpi_t * context, const char * data, size_t len) {
        (void) context;
        GPIO_toggle(Board_GPIO_LED1); // LED B - visual clue that we send a reply over USB
        UART_write(uart, data, len);
        GPIO_toggle(Board_GPIO_LED1); // LED B - visual clue offB
        return len;
    }

     

    *  I did this because the UART module is the only one that talks to SCPI lib and didn't want to introduce additional dependencies.

    Open for discussion. Maybe in the main() function, where most of the modules are initialised? edit:done

     

     

    compile option to choose the UART:

    SCPI_UART_0

    SCPI_UART_1

     

    Undefining SCPI_UART_1 and defining SCPI_UART_0 enables USB

    Undefining SCPI_UART_0 and defining SCPI_UART_1 enables UART1 (TTL 3V3)

    It allows to select the port without code changes.

     

    #ifdef SCPI_UART_0
        uart = UART_open(Board_UART0, &uartParams);
    #else
    #ifdef SCPI_UART_1
        uart = UART_open(Board_UART1, &uartParams);
    #else
    #error "define a valid UART"
    #endif
    #endif

     

     

     

    ADC

     

    I've broken out the ADC description to a separate post, because I'm working on improvements:

    Programmable Electronic Load - ADC Firmware

     

     

    DAC

     

    DAC output is set on command, using RTOS MailBox and messaging.

    The payload for a DAC message contains the DAC channel that needs to be set, and the value.

     

    typedef struct MsgDAC {
        uint8_t module;
        uint16_t value;
    } MsgDAC;

     

    The DAC task is started by RTOS. It inialises the DAC settings, then waits until it receives a message.

     

    void *threadDAC(void *arg0) {
    
    
        MsgDAC d_msg;
        d_daci2cTransaction.writeBuf = d_dactxBuffer;
        d_daci2cTransaction.readBuf = d_dacrxBuffer;
        d_daci2cTransaction.slaveAddress = DAC_I2C_ADDR;
        d_daci2cTransaction.writeCount = 3;
        d_daci2cTransaction.readCount = 0;
    
    
        mqd_t mq;
        struct mq_attr attr;
    
    
        attr.mq_flags = 0;
        attr.mq_maxmsg = 1;
        attr.mq_msgsize = MSGDAC_SIZE;
        attr.mq_curmsgs = 0;
        mq = mq_open(QUEUE_NAME_DAC, O_CREAT | O_RDONLY, 0644, &attr);
    
    
        while (1) {
            ssize_t bytes_read;
            bytes_read = mq_receive(mq, (char *)&d_msg, MSGDAC_SIZE, NULL);
    
    
            /* wait for mailbox to be posted by writer() */
            if (bytes_read) {
    // ...

     

    The waiting doesn't take processor time. RTOS takes care that it will only get a slice of clock cycles when there is a message.

    Two things in the RTOS configuration make this happen:

    The mailbox with room for exactly one message, and a receive event.


    Because there is usually no message in the mailbox, the execution of the DAC logic becomes inactive at this line:

     

            bytes_read = mq_receive(mq, (char *)&d_msg, MSGDAC_SIZE, NULL);

     

    When we send a DAC message somewhere else in the code (e.g.: when the user requests a new setting of the electronic load), RTOS reactivates the process and hands over the payload message.

    The DAC task sets the output of the requested channel and returns to the point where it waits for a new message.

     

       while (1) {
            ssize_t bytes_read;
            bytes_read = mq_receive(mq, (char *)&d_msg, MSGDAC_SIZE, NULL);
    
    
            /* wait for mailbox to be posted by writer() */
            if (bytes_read) {
                d_dactxBuffer[0] = getAddressFromModule(d_msg.module); // set value direct
                d_dactxBuffer[1] = d_msg.value >> 8; // MSB
                d_dactxBuffer[2] = d_msg.value; // LSB
                if (! I2C_transfer(i2c_implGetHandle(), &d_daci2cTransaction)) {
    //                System_printf("I2C Bus fault\n");
    //                System_flush();
                }
            }
        }

     

    You select the DAC channel by setting bit 2 and 1 in the control byte (tx_buffer[0]). There's a helper function that gives a correct control record.

     

    // address 7 - 6: 0, load mode 5 - 4: 01 direct from i2c, 3: reserved 0, 2 - 1: channel select, 0: pwr down 0
    #define DAC857X_CFG_H0 0b00010000
    #define DAC857X_CFG_H1 0b00010010
    #define DAC857X_CFG_H2 0b00010100
    #define DAC857X_CFG_H3 0b00010110

     

     

    static const uint8_t array_DAC857X_CFG_H[4] = {DAC857X_CFG_H0, DAC857X_CFG_H1, DAC857X_CFG_H2, DAC857X_CFG_H3};
    /**
     * get the hex address for the requested DAC module
     */
    uint8_t getAddressFromModule(uint8_t module) {
        return array_DAC857X_CFG_H[module];
    }

     

    Bits 7 - 3 and 0 are all fixed.

     

     

    Input Enable

     

    The functionality to activate and deactivate the load is documented here: Programmable Electronic Load - Input Enable Functionality .

     

    Control Strategies

     

    The firmware uses strategies to handle the different types of load operation (e.g. constant current, constant, voltage, ...).

     

    A strategy is a set of functions that you can plug in, and that together run that particular operation type (this is a c version of the Gang of Four's Strategy Design Pattern).

    The goal is to avoid that the firmware is riddled with if or switch statements whenever different behaviour is needed depending on the instrument's mode.

    Each operation strategy will have the same set of functions that run the instrument in that mode.

    The price to pay is not expensive - speed and memory burden is low. It is a little more complex to understand than the c++ version. Once you step trough it with a debugger, things become clear.

    The code also tries to hide that we're using strategies

     

    Currently starting to implement constant current strategy. The more difficult operation types can be added later, one by one, with not too much impact on the existing code across the firmware.

    . Only the eload API (see below) knows about it. All code goes via that eload API.

    API for the stategies is minimal now:

     

        controlFunction;
        getMode;
        getChar;

     

      

    eload API

     

    The eload API offers an abstraction layer for the strategies, so that the rest of the firmware doesn't have to know about it. It simplifies switching operation mode and driving the instrument.

     

    Example: The eload API offers a simple function eloadGetMode() to check what the instrument's current mode is.

     

    eload_mode eloadGetMode() {
    
        return getControlStrategy()->getMode();
    
    }

     

    In the background, it uses the strategy mechanism to fetch the mode from the currently active strategy and call the implementation function.

     

    return getControlStrategy()->getMode();

     

    The eload API also has the common eload functions that don't need a strategy because they are always valid, regardless of mode.

     

    Calibration and Configuration Data

     

    The functionality to store calibration and configuration data in Flash is described in a separate article: Programmable Electronic Load - Calibration Data in Flash.

    The calibration and configuration procedures are documented in Programmable Electronic Load - Calibration Steps

     

    Changes To Standard MSP_EXP432P401R.c

     

    The standard file generated by the TI-RTOS application wizard has defaults for peripherals.

    The following has changed: none - I removed this section because with the external ADC/DAC board and the SimpleLink RTOS version, everything is kept standard.

     

    Remote Software

     

    Teminal

     

    Serial communication to the USB UART port.

    9600/8/1/None

    SCPI is a non-echoing protocol. To make the characters you type show in the Terminal, set Local echo to Force on.

    If you also set Local line editing to Force on, you have the opportunity to correct characters in the current line before they are sent to the serial port.

     

    Test:

    *IDN?;SYST:ERR:COUN?

    Should return

    THEBREADBOARD,ELECTRONICLOAD,0,01.00;0

     

    Windows GUI

     

    There are two . A .NET made by Peter, a Java version made by Jan.

     

    The .net GUI supports reading the 4 ADCs, setting one of the DACs abd shoot any SCPI command.

    There's a window for SCPI output and a status window that shows SCPI errors.

    Source and binary available from https://github.com/thebreadboard/SCPI_DC_LOAD_WIN

     

     

     

    The Java GUI is made as a POC that cross-platform instrument GUIs are possible.

    It allows for switching the load on and off, set constant current and replicate the LCD values.

    Additionally, you can requesst error status and run arbitrary SCPI commands.

    Programmable Electronic Load - Java GUI Part 1: Basic Functionality

     

     

     

    LabVIEW

     

    The instrument comes with a LabVIEW library: Programmable Electronic Load - Write a LabVIEW Library part 1: Initialise Block

     

     

     

     

    Related blog

    Programmable Electronic Load - Input Enable Functionality
    Programmable Electronic Load - Power Stage
    Programmable Electronic Load - Temperature Protection
    Programmable Electronic Load - Calibration Data in Flash
    Programmable Electronic Load - Calibration Steps
    Programmable Electronic Load - LCD Display
    Programmable Electronic Load - ADC Firmware
    Programmable Electronic Load - Current Sense Circuit
    Programmable Electronic Load - ToDo and Done
    peteroakes articles:
    Electronic DC Load - Design and Build to test PSU Project
    youtube: Electronic DC Load Design and Testing
    youtube: Full Build Analogue DC Load
    youtube: Electronic DC Load - Performance Improvements
    Raspberry PI 2, Fun with I2C DACs and ADC's
    jc2048 articles:
    Programmable Electronic Load: Dynamic Behaviour: Part 1 Overview
    Programmable Electronic Load: Dynamic Behaviour: Part 2 The Servo Loop
    Programmable Electronic Load: Dynamic Behaviour: Part 3 Effect of Output Inductance
    Programmable Electronic Load: Dynamic Behaviour: Part 4 Effect of Output Voltage Change
    Programmable Electronic Load: Dynamic Behaviour: Part 5 Stability
    Programmable Load Build
    jancumps articles:
    Programmable Electronic Load - ADC and DAC BoosterPack test
    Programmable Electronic Load - Analyse the Summing Node Zero Point
    Programmable Electronic Load - Measurements Part 1: Control Circuit
    Java GUI
    Programmable Electronic Load - Java GUI Part 1: Basic Functionality
    Programmable Electronic Load - Java GUI Part 2: Support for Current Mode, Input On/Off, Error Log
    LabView
    Programmable Electronic Load - Write a LabVIEW Library part 1: Initialise Block
    Programmable Electronic Load - Write a LabVIEW Library part 2: Read Output Block
    Programmable Electronic Load - Write a LabVIEW Library part 3: Close Block
    Programmable Electronic Load - Write a LabVIEW Library part 4: Function Set Block
    Programmable Electronic Load - Write a LabVIEW Library part 5: Input Control Block
    Programmable Electronic Load - Write a LabVIEW Library part 6: Raw DAC Block
    Programmable Electronic Load - Write a LabVIEW Library part 7: Raw ADC Block
    Programmable Electronic Load - Automating a DC Switcher Efficiency Test with LabVIEW
    Programmable Electronic Load - LabVIEW Test Automation: Characterise the Instrument
    Programmable Electronic Load - LabVIEW Test Automation: Characterise the Instrument Pt 2: Oscilloscope Measurements
    Example LabVIEW Scenario: test a design with a Rigol PSU and a Keithley DMM
    TI-RTOS: Switching to the SimpleLink Distribution
    Switch from TI-RTOS to SimpleLink POSIX: Threads and Semaphores
    Switch from TI-RTOS to SimpleLink POSIX: EEPROM API
    Switch from TI-RTOS to SimpleLink POSIX: From MailBox to Message Queue
    Switch from TI-RTOS to SimpleLink POSIX: LCD Display Driver
    Switch from TI-RTOS to SimpleLink POSIX: Sleep when Idle