SCPI-over-Ethernet Foot Controller

A microcontroller-based solution to foot-controlling test equipment.

Similar projects worth following
SCPI-over-Ethernet Foot Controller

I've started this project because I've heard a lot of complaints from the Hardware Engineers I support. They want to be able to use some of the front panel buttons on the oscilloscopes we use (Tektronix DPO/MSO4000B family instruments) while their hands are preoccupied with holding probes. Besides USB and GPIB/HPIB control, the 3000 and 4000 series Tektronix oscilloscopes offer control over Ethernet by sending TCP packets containing the SCPI commands detailed in the Digital Phosphor Oscilloscope Programmer Manual.

I've chosen the PIC32 Ethernet Starter Kit for its built-in Ethernet 10/100 MAC, hand-solderable TQFP-100 package, and the abundance of Microchip ICD3 programmers available to me in the hardware development lab at work. The PIC32MX offers me sufficient CPU resources, RAM, and program memory, for future expansion, while still being easy to develop for without necessitating the time-consuming process of booting into a full-blown OS. The Starter Kit also has an on-board programmer/debugger.

For software development, I'm using Microchip's MPLAB X v3.30 and their Harmony Framework. I've found the MPLAB X IDE pretty easy to work in and debug with. Harmony is a software framework which provides APIs and drivers which simplify interaction with the PIC32s peripherals. In this project, I'll be utilizing the Harmony TCP/IP stack (which also provides the DHCP client for IP auto-configuration), the USART and Timer drivers (for user configuration and switch debouncing, respectively,) and the MAC and PHY drivers for configuring the board's Ethernet interface.

  • 1 × PIC32 Ethernet Starter Kit
  • 1 × PIC32MX795F512L Microprocessors, Microcontrollers, DSPs / ARM, RISC-Based Microcontrollers
  • 1 × DP83848C Interface and IO ICs / Ethernet, T1, E1
  • 1 × MAX232D Interface and IO ICs / RS-232, RS-422, RS-423 and RS-485

  • Boards (mostly) assembled!

    John W.07/06/2016 at 23:18 0 comments

    Here they are! I still need to get my MAX232DRs and populate the indicator LED resistors and headers, but once I do so, they should be good to go. I also whipped up an RJ13->6 pin adapter for the ICD 3 so I could program (and debug, if necessary) the PICs. That part went off without a hitch. The PICs power up, they run the code, they program through the ICD3, it's all great.

    I had to fix a bunch of layout oopsies, like not seeing grounds I had to connect, or a footprint error I made, or forgetting entirely to lay out one of the RMII signals. If I lay these out again at some point, I'm definitely going 4-layer next time. The routing pain-in-the-ass of dealing with only having 2 layers isn't really worth the cost reduction on such a small board. If I was just using the USB host or device and GPIO functionality on the chip, it might not have been so bad. RMII, however, is pretty spread out, with the signals on 3 sides of the IC. Bleh!

    Oh well! Lessons learned, right?

  • PCBs from OSH Park

    John W.06/30/2016 at 20:41 0 comments

    I received my set of 3 boards from OSH Park today! I did the schematic capture and board layout in KiCAD (also known as everyone's favorite board design program to POOP ON.)

    The drills are probably a mil off, but they're within my via pads, so I'm okay with that. The soldermask, however... Yikes. For most purposes (0603 passives, things with big chunky pads) the soldermask is completely serviceable. For my 100TQFP microcontroller footprint, it's pretty significantly off.

    My chosen PIC32 part, the 12x12mm 100-TQFP PIC32MX795F512L, should have a layout with pads nominally 0.23mm wide. If that's how wide these pads are, then that means we're looking at probably 0.1mm (slightly less than 4 mils) alignment error in both x and y axes. Luckily the soldermask is also fairly thin, which makes it a trivial task to scrape off the offending misaligned soldermask with a well-applied hobby knife.

    I'd say that's review of OSH Park PCBs is still on-point. If you read and understand the tolerances outlined on the OSH Park website, you should be in fine shape. Through-holes and vias appear well-plated, holes designated as non-plated aren't plated, and silkscreen text is crisp and legible.

    So far as solderability is concerned, I attached the 1A 3.3V LDO I selected to see how solder flows on the pad.

    Verdict: It's good! The ENIG finish solders excellently. I've soldered on some (presumably?) tin-plated PCB Express boards which just did not want to flow, even with copious liquid flux.

    Overall, I'm pretty happy with the quality. I wouldn't dare expect to take these boards as-is and send them to a CM to have them machine-populated due to the alignment issues, but for quick prototyping or low-volume hand-built boards, they're a great value proposition. Hopefully in the next week I can get my parts ordered so I can begin building these up and programming them! I'm pretty excited! :D

  • Microchip MPLAB Harmony, or How I Learned To Love State Machines In C

    John W.06/14/2016 at 22:46 0 comments

    As Microchip explains it in their brochure for the MPLAB Harmony Integrated Software Framework, "MPLAB Harmony is a flexible, abstracted, fully integrated firmware development environment for PIC32 microcontrollers." What does this mean to people who aren't in the marketing department and majored in buzzwords?

    Harmony is a software package that attaches itself to MPLAB. There's a large range of drivers for interfacing with internal and external devices connected to the PIC32 (ADC, Camera, CAN, Comparator, ethernet MAC and PHY, timers, USARTs, SD cards, SPI, Wi-Fi, it's a pretty extensive list) and libraries/stacks (TCP/IP, USB, audio and video decoders, cryptography, bluetooth, OS abstraction layer). You select which drivers, libraries, or stacks, you'd like to use with a check box-heavy Harmony Configurator utility, then build the skeleton of your application code with it. It takes care of piecing together the basic initialization code for the components you chose and dumps out a number of source and header files for you to put your application code into. There's even a pretty Clock Diagram tab for setting up the internal PLLs for CPU core and peripheral clock rates.

    Where do the above mentioned state machines come into play? That's how Harmony multitasks! You are expected to build your application in the form of a state machine. This way, the larger Harmony state machine can run through its housekeeping tasks, such as managing DMA, peripherals, and stacks, and then take care of your application code in a timely fashion.

    If you're not too familiar with writing a state machine in C, it's pretty simple! Here's a quick example:

    typedef enum {
    APP_STATES state;
    state = APP_STATE_INIT;
            case APP_STATE_INIT:
                //Do init state stuff here.
                state = APP_STATE_TASK1;
            case APP_STATE_TASK1:
                //Do the first part of your task here.
                state = APP_STATE_TASK2;
            case APP_STATE_IDLE:
                //Now we're gonna chill out here until we want to act again.
                if( taskStimulus )
                    state = APP_STATE_TASK1;
    In the enumerated type at the top (which Harmony puts in the application header file), you define names for your states for readablity. You initialize a variable to keep track of your current state, then run a loop that transitions between states as it needs to. The main challenge here is figuring out how to break up your work into chunks for each state.

    There's a pitfall here though! I learned about it in the course of trying to figure out why my application was sitting in the state where it's supposed to collect data from the serial port for configuration where other stimulus should make it move into a packet sending state. Be very careful about whether the functions you use from the Harmony API are blocking or non-blocking.

    A Blocking Function won't return to the caller until it's done whatever it's supposed to do. In my case, I was calling DRV_USART_Read() function, which was waiting until it read the 1 byte I specified as a parameter. Until that byte came in through the UART, it was just sitting on the function doing nothing else.

    A Non-Blocking Function returns immediately and requires some way to check progress on the operation you started. In the Harmony USART driver API, you can set a buffer handler that will call a callback function that you write when a buffer transaction is completed. For instance, my project has a command line interface, so when the buffer detects that it has received one byte it calls my callback function and puts the application state machine into a state which takes care of echoing the byte, adding a newline character after a return character is echoed, backspacing the array where I store the characters after receiving them, and going back into the idle state.

    Another easy mistake to make in a switch-case state machine is forgetting the "break;" from the...

    Read more »

  • Software Switch Debouncing

    John W.06/05/2016 at 21:42 0 comments

    There are countless code examples for debouncing a switch solely with software, but there's really no reason to use an example, it only takes a few lines of C to do this, even for a relative beginner like me. Here's what I came up with:

    void ISR_Debounce ( uintptr_t context, uint32_t alarmCount )
        if((BSP_SwitchStateGet(BSP_SWITCH_1) == BSP_SWITCH_STATE_PRESSED) && debounceData.sw1counter < 5)
        if(BSP_SwitchStateGet(BSP_SWITCH_1) == BSP_SWITCH_STATE_RELEASED) debounceData.sw1counter = 0;
        if((BSP_SwitchStateGet(BSP_SWITCH_2) == BSP_SWITCH_STATE_PRESSED) && debounceData.sw2counter < 5)
        if(BSP_SwitchStateGet(BSP_SWITCH_2) == BSP_SWITCH_STATE_RELEASED) debounceData.sw2counter = 0;
        appData.dbSwitch1 = (debounceData.sw1counter == 5) ? 1 : 0; //Ternary for setting debounce output.
        appData.dbSwitch2 = (debounceData.sw2counter == 5) ? 1 : 0;

    sw1counter and sw2counter are defined in my debounceData structure as uint8's, and dbSwitch1 and dbSwitch2 in the appData struct are bools.

    The function parameters (uintptr_t context and uint32_t alarmCount) are there because the callback parameter in the dynamic timer driver is defined like this:

    typedef void (* DRV_TMR_CALLBACK)(uintptr_t context, uint32_t alarmCount);

    So you give it what it wants, right? :) I did a little copying from Microchip's typo-ridden Harmony examples.

    Here's the business:

    1. Every X number of timer ticks (as defined when you register the timer alarm) the debounce ISR function runs.
    2. A switch is first checked if it's pressed and the counter variable is less than 5. If both of these conditions are met, the counter is incremented.
    3. If the switch is in its released state, the counter is reset back to 0.
    4. A ternary operation is used to set the debounced output. If the counter is 5, the output is true.

    This just implements a really simple kind of low-pass button-press filtering. In Verilog, I've seen the same technique used, but with a several-bit-wide shift register. All the bits in the register are ANDed together, so when the register fills with 1's from a button press, the output of the AND gate is your debounced button bit.

View all 4 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates