Make Your Move

Welcome to Make Your Move written by engineers at Solutions Cubed, LLC. Here we highlight projects we’re working on and discuss interesting technology in the news.Our company creates and sells electronic modules for machine and robotic systems. We also design custom electronic systemsfor a variety of markets.

Please feel free to comment, we’d love to hear from you.

Retriggerable Analog Measurements with National Instruments’ DAQMX

usb6366 retriggerable scope

We’ve been using a National Instruments (NI) system for the last year on a pretty complicated system for one of our clients.  That system converts laser energy to voltage and takes 16 simultaneous 16-bit analog samples.  Each of the 16 channels takes more than a thousand samples per millisecond, and reports the data back to a PC running a Microsoft Visual Studio application.

In our earliest endeavors we ran into problems with timing that stemmed from the National Instruments hardware drivers.  It was no problem to take a single trigger initiated sample of all of our channels.  But when we went to “re-trigger” the analog measurements 5-10 milliseconds would elapse before the sampling occurred.  This would not work for our system.  We needed analog data each and every millisecond, without fail.

The trick to getting this functionality relied on creating a retriggerable digital task, and coupling that with an analog callback event in Visual Studio.  In short you create an analog task in NI’s DAQMX driver software that collects X number of samples and reports them back with the analog callback to your software.  You also create a digital trigger event that generates X number of pulses whenever a rising edge is seen.  The pulses generated by the digital task become the sample clock for the analog task.  Since the digital task is retriggerable, and the analog sampling is also continuous (it just reports when it has a specific number of samples accumulated) this method allows you to collect data continuously.

In the scope capture above you can see the trigger signal (yellow) and the sample clocks (green) generated by the digital task.  The analog signal (purple) is sampled with each rising edge transition of the sample clock.  In this case the scope capture shows 1000 samples, each happening every 0.5uS.

For the test above we used NI’s USB-6366, a pretty powerful tool.  It allows up to 8 channels analog of data to be read simultaneously, as well as providing a number of counters and digital i/o.  Better yet, it can read 2 million samples per second per channel, and gets the data back to you over the USB port.  With a little diligence you could create a nice 8 channel instrument under PC control using the USB-6366.

Below is a screen capture of a simple Visual Studio 2010 program that captures and displays a single channel.  This is the same signal that’s shown in the screen capture of the oscilloscope.

usb6366 vs2010 form1

The source code for this Visual Studio program is here.  The main tasks associated with retriggerable analog measurements are located in the DAQ_Module.vb file.  The task definitions are also shown below for reference.

You’ll need to download and install the DAQMX drivers and include the appropriate namespace in your Visual Studio program.  This example measures voltages in the range of +/-10V and returns a signed INT16 value for each sample.

#Region "DAQ Tasks"

    Public Sub CreateTask_Analog()


            ' myTask_Trigger uses PFIO as an input and starts the analog sample clock for the my_Task analog.  The clock is output on PFI12 which is the counter 0 default output. 
            If runningTask_Trigger Is Nothing Then

                'Create New Task
                myTask_Trigger = New NationalInstruments.DAQmx.Task()

                'Pulse task outputs on PFI12
                myTask_Trigger.COChannels.CreatePulseChannelTime("Dev1/ctr0", "", COPulseTimeUnits.Seconds, COPulseIdleState.Low, AnalogTriggerDelay, (AnalogSampleClock / 2), (AnalogSampleClock / 2))

                'Digital edge and retriggerable
                myTask_Trigger.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger("PFI0", DigitalEdgeStartTriggerEdge.Rising)
                myTask_Trigger.Triggers.StartTrigger.Retriggerable = True

                'Finite pulse train of predetermined length 
                myTask_Trigger.Timing.ConfigureImplicit(SampleQuantityMode.FiniteSamples, AnalogSamplesPerChannel)

                'Configure Delay Property
                myTask_Trigger.COChannels.All.EnableInitialDelayOnRetrigger = True

                ''Set to false to allow on non-GUI thread
                'myTask_Trigger.SynchronizeCallbacks = SynchronizeTasks

                runningTask_Trigger = myTask_Trigger

            End If

            ' myTask_Analog sets up the analog read a single channel channel which is returmed to the asynchronous callback at AnalogInCallback.
            If runningTask_Analog Is Nothing Then

                myTask_Analog = New NationalInstruments.DAQmx.Task()

                myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai0", "", CType(-1, AITerminalConfiguration), -10.0, 10.0, AIVoltageUnits.Volts)

                '' You can add more channels but you'll then need to redimension the first element of copyArray and analogsamplesArray
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai1", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai2", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai3", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai4", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai5", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai6", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)
                'myTask_Analog.AIChannels.CreateVoltageChannel("Dev1/ai7", "", CType(-1, AITerminalConfiguration), 0.0, 10.0, AIVoltageUnits.Volts)

                'Use counter:  Continuous samples - Configure Timing Specs generates pulses as sample clock at PFI12 triggered by signal at PFI0. 
                myTask_Analog.Timing.ConfigureSampleClock("PFI12", 2000000, SampleClockActiveEdge.Rising, SampleQuantityMode.ContinuousSamples, 720000)

                ' Verify the Task

                ' Commit the Task

                runningTask_Analog = myTask_Analog

                'Access properties
                myAsyncCallback = New AsyncCallback(AddressOf AnalogInCallback)

                analogRawReader = New AnalogUnscaledReader(myTask_Analog.Stream)
                ' set to true to have the callback on the GUI thread
                analogRawReader.SynchronizeCallbacks = False
                analogRawReader.BeginReadInt16(AnalogSamplesPerChannel, myAsyncCallback, myTask_Analog)

            End If

        Catch exception As DaqException
            Exceptions += "CreateTask_Analog():" + exception.ToString + vbCrLf + vbCrLf

        Catch ex As Exception
            Exceptions = Exceptions + "CreateTask_Analog():" + ex.ToString + vbCrLf + vbCrLf

        End Try

    End Sub

    ' cancel the analog task
    Public Sub UnloadTask_Analog()


            If Not runningTask_Analog Is Nothing Then
                runningTask_Analog = Nothing
                myAsyncCallback = Nothing
            End If

            If Not runningTask_Trigger Is Nothing Then
                runningTask_Trigger = Nothing
            End If

        Catch exception As DaqException
            Exceptions = Exceptions + "UnloadTask():" + exception.ToString + vbCrLf + vbCrLf
        End Try

    End Sub

    ' this callback copies the analog data to an array we can use elsewhere
    Public Sub AnalogInCallback(ByVal ar As IAsyncResult)


            copyArray = analogRawReader.EndReadInt16(ar)
            If analogsampleReady = False Then
                analogsamplesArray = copyArray
                analogsampleReady = True
            End If

            ThreadTracker.AnalogCallBack = Thread.CurrentThread.ManagedThreadId.ToString

            analogRawReader.BeginReadInt16(AnalogSamplesPerChannel, myAsyncCallback, myTask_Analog)

        Catch exception As DaqException
            Exceptions += "AnalogInCallback():" + exception.ToString + vbCrLf + vbCrLf

        Catch ex As Exception
            Exceptions = Exceptions + "AnalogInCallback():" + ex.ToString + vbCrLf + vbCrLf

        End Try

    End Sub

#End Region

The physical connections to the USB-6366 terminal blocks are also pretty straightforward.  PFI12 is shown, and may be probed to verify the sample clock generation, but it is routed internally to act as the DAQ’s sample clock, and no external connections are necessary.


usb6366 connections

Thermistor and Thermocouple Temperature Logger Part 2



This week in the epic adventure I like to call Thermistor and Thermocouple Temperature Logger Part 2, I took a look at the thermistor portion of my circuit.

[Read more…]

Undocumented Microchip Errata Strike Again

Two years ago, I found an issue with the open drain performance of one of the I/O lines of a PIC24FJ32GB002 microcontroller.  I got it confirmed from Microchip, but they never actually put out an errata on that behavior.  This time, I have found an even larger problem, that I am sure will rise to the level of an official errata.

I was trying to use a PIC24FJ64GC010 microcontroller in a design for a client.  The design has a capacitive sensing keypad that has worked well in the past with a slightly different Microchip microcontroller.  I moved to the PIC24FJ64GC010 because it had some other features that were necessary for the design.  Unfortunately, migrating to the new micro would prove to be problematical.  The capacitive keypad was implemented as shown in the schematic below.  In order to get the best performance, each button was implemented on its own.  In addition, the CTMU output current was routed through the internal op-amp as a voltage follower so that the noise immunity of the keypad would be greater.  Little did I know that dragons lurked in this approach.



Problems arose when I started shaking out the PCB.  For the life of me I could not get some I/O lines to output a current pulse.  The scope capture below shows the problem.  The yellow trace shows the output of the op-amp.  Every time the current from the I/O goes up, the current on the op-amp should ramp up.  The current is turned off and then a different I/O is selected.  Every time a button is sampled, the micro would toggle an I/O as shown with the green trace of the scope capture.  As can be seen from the scope capture, only 8 current ramps are shown.  However, there are 12 toggles on showing that the micro was trying to output current with the CTMU.



At this point, I put on my troubleshooting cap and went to work, because when problem like this arise it is almost always the engineers fault.  I individually addressed the I/Os shown in the schematic in digital mode – I could read inputs and set outputs as desired on ALL I/O lines.  I could also read analog inputs on all of the I/O lines (which due to the new A/D architecture is a feat in itself and will be detailed in an upcoming blog).  I could individually turn on and off the internal pull up resistors.  However, I could not make 4 of the I/O lines output CTMU pulses.  I even lifted individual I/O pins off of the PCB to ensure that the problem was not somehow related to the PCB layout.

I spent some time trying to cajole the Microchip application engineers that there was a problem and could they help me.  After fending off their initial dismissive replies (ie – “try using our sample code”, “it works here”, “have you used our demo board”, “you are overdriving the outputs, they should not clip”, etc), after 2 weeks, I was finally able to convince them to look at the problem for real.  I had to send them my hardware. and from there it was easy for them to confirm the problem.  Another engineer here was able to initially figure out the pattern that allowed the diagnosis, which is fairly odd:

“The CTMU will NOT output on any A/D that is even and less than 15”.  For example, RB4(AN4) will not output CTMU pulses while RB1(AN1) will.  Unfortunately for me, I had randomly selected four of the offending analog I/O lines.  This was a setback to the schedule and my stress level.  However, at least I know I am not crazy and I did everything I could to get the part to work.  In order to get the design to work, I needed to reroute the board and leave off these bad I/Os.

Unfortunately, I do not know if this is a family-wide problem, or a problem with any of the new advanced analog micros, or just related to the PIC24FJ64GC010.  It will be interesting to see what the errata says.  Microchip put out a new errata sheet on 4-20-14 (about 3 weeks after they confirmed the problem), but this issue is not listed.

Thermistor and Thermocouple Temperature Logger


(Image: scope capture of three T type thermocouples being read with SPI and the MAX31855 ICs)

We’ve been developing a barbecue controller as part of an R&D project.  My portion of the project is interfacing the temperature sensors to the overall system.  I’ve decided to include both thermistors and thermocouple sensors, as well as battery backup and data logging functions.  In the end the project won’t need all of that but  I wanted to come up with something “stand-alone” I could develop.

Thus far I’ve designed the schematic and first revision circuit board.  I’ve been able to test the temperature sensors, but not much else.  I did want to share the MAX31855 interface, which I found pretty cool and a definite time saver.  The Maxim Integrated Circuits’ MAX31855 (datasheet) is a cold-junction compensated thermocouple interface.  It provides a 14-bit signed digital value that is temperature in 0.25 degree C increments with +/-2 degree C accuracy.  You buy a chip specific to the thermocouple you’re using.  In our case I placed 4 of these ICs on the board and selected “T” type thermocouples.  The device uses an SPI interface, although it only outputs data.  You can see a partial schematic below (click to enlarge).



For the microcontroller interface I used the PIC16F1789, a Microchip product.  This part has 12-bit analog to digital converters and operates between 2.5V-5.5V, which works well with the battery powered system I’ve envisioned,

Configuring the SPI interface is pretty straightforward, and here is the code I used to do this.

/* Configure_SPIPort:
   Configures registers used to send and receive using the SPI hardware       */
void Configure_SPIPort(void)
    SSPSTATbits.SMP = 0;  // Input data sampled at middle of data output time
    SSPSTATbits.CKE = 0;   // Transmit occurs on transition from Idle to active clock state

    SSPCONbits.CKP = 0; //  Idle state for clock is a low level
    SSPCONbits.SSPM = 2; //SPI Master mode, clock = FOSC/64
    SSPCONbits.SSPEN = 1; // Enables serial port and configures SCK, SDO, SDI and SS as the source of the serial port pins

    SSPCON2 = 0;
    SSPCON3 = 0;

I’m running the SPI at 500KHz, which is a little fast for some SPI ICs, but the MAX31855 is spec’ed at 5MHz max speed.  The actual communication with a MAX31855 is shown in the code below.  The code is repeated for each thermocouple (with different variables).…

/* Read_Thermocouples:
 Reads temperature signals from MAX31855 using the SPI interface.  
void Read_Thermocouples(void)
    _CS_TEMP1 = 0;  // select the first MAX31855
    SSPBUF = 0xAA;          // doesn't matter what we write but H'AA' easy to see on scope
    while(SSPSTATbits.BF == 0); // wait for 8 bits to clock in
    MyTemp.B[1] = SSPBUF; // save data
    SSPBUF = 0xF0; // doesn't matter what we write but H'F0' easy to see on scope differs / from H'AA'
    while(SSPSTATbits.BF == 0); // wait for 8 bits to clock in
    MyTemp.B[0] = SSPBUF; // save data
    NV.Ints.Thermocouple1 = MyTemp.W[0]; // use union to combine bytes in 16 bit value, stick in reg
    _CS_TEMP1 = 1;   // de-select the MAX31855

    INDICATOR.Bits.THERMO1 = 0; // clear my fault flag
    if (NV.Ints.Thermocouple1 & 0x1 == 1) // check to see if an fault flag was set by MAX31855
        INDICATOR.Bits.THERMO1 = 1; // if so, then set flag in my structure
    NV.Ints.Thermocouple1 = NV.Ints.Thermocouple1>>2; // shift temperature data two to the right
    NV.Ints.Thermocouple1 = NV.Ints.Thermocouple1 & 0x3fff; // make sure upper 2 bits are clear
    if ((NV.Ints.Thermocouple1 & 0x2000) != 0 ) // check sign bit
        NV.Ints.Thermocouple1 += 0xc000; // if sign bit is set then extend sign to all 16 bits
    if (NV.Ints.Thermocouple1 == 0x1fff) // check to see if thermocouple is attached
        INDICATOR.Bits.THERMO1 = 1; // if not then set flag

The code deserves some decryption.  Here are some descriptions of the variables used above.
_CS_TEMP1: A constant that refers to the chip select pin.
INDICATOR.Bits.THERMO1: A variable structure that maintains fault conditions and other flags in the firmware.
NV.Ints.Thermocouple1: A variable structure of integers that can be stored in EEPROM and accessed via a serial UART, this is where the temperature data is stored.
MyTemp: This variable can be a little confusing in this context. It is not related to temperature.  MyTemp is a variable union that I often use as a temporary register to quickly break apart or assemble other variables.  For example, I can read two bytes from the SPI port and place them in byte sized members of the union and then use an integer sized member of the union to copy the bytes as a single 16-bit value.

union LongUnion
    long LW;
    unsigned long uLW;
    int W[2];
    unsigned int uW[2];
    char B[4];
    unsigned char uB[4];
union LongUnion

In my firmware I select the MAX31855 by asserting the chip select pin and then sending hexadecimal 0xAA and 0xF0.  The data I send doesn’t matter, but the PIC16F1789 needs to have the SSBUF register written to in order to begin clocking data out and reading data in.  The SSPSTATbits.BF variable is defined by Microchip in this part’s header file, and will be set when the SPI receive buffer is full.  After 16 bits of data are read the chip is de-selected.  The image at the head of this blog entry shows a scope capture of this process.

Once I’ve read 16 bits I need to convert the data to a temperature value.  Not all of the bits relate to temperature so I need to adjust the values I read somewhat.  Bit 0 is a fault flag (set if there is a fault) and bit 1 is reserved (read as 0).  Additionally, bit 15 is the sign bit (using two’s compliment format this bit is set if the value is negative).  In the firmware I check the fault flag (bit 0) and then shift the 16-bits I read from the MAX31855 two places to the right.  This leaves the sign flag in the 13th bit location.  I clear the upper two bits of the register which is kind of an old school operation.  In assembly code when you shift data the carry bit can be shifted into a register.  In C I’ve never seen a “1” get shifted in, but I’ve been doing this for so many years I just can’t stop.   I then check the sign bit and if it is set I set the two highest bits of the temperature data to maintain the two’s compliment formatting.

This diagram shows the process more visually.


There are some additional points related to the MAX31855 that deserve mention.

First, if the thermocouple is disconnected the datasheet states that the MAX31855 will respond with a cleared sign bit and all other temperature data bits set (0x1FFF in hexadecimal).  I didn’t see that, but also didn’t delve into why it didn’t happen.

There are an additional 16-bits of data that can be read from the MAX31855.  You would read this data by clocking in 16 more bits before setting the chip select pin.   This additional data includes the 12-bit internal temperature of the IC and more details on the nature of any fault that occurs.

Lastly, the MAX31855 calculates temperature when the chip is not selected for SPI communication.  It also requires a maximum of 100mS to take the measurements.  Therefore, the fastest you can sample a thermocouple using this IC is 10Hz.

Dual Axis Solar Panel Tracker / Controller Part 5


The saga of the dual axis solar panel controller continues.  As an R&D project I couldn’t put a lot of time into this design this week.  Here is what I did get accomplished.

[Read more…]

Dual Axis Solar Panel Tracker / Controller Part 4


I finally received the PCB for this design.  And in general it turned out okay.  There were a couple of shortcoming in my design that will force me to to re-spin the PCB, so it’ll be a while before I can finish everything up.  But having the PCB gives me the ability to debug my firmware and test the overall concept for this solar panel tracker.

[Read more…]

Simple and Low Cost Temperature and Humidity Sensor: Part 2


Above you can see the primary circuitry for the simple temperature and humidity sensor I described last week.  The board shown above is part of a client’s design, so there is quite a bit more circuitry than is required for the sensor I described last week.

[Read more…]

Simple and Low Cost Temperature and Humidity Sensor


Here’s a simple and low cost temperature and humidity sensor.  I’ve been developing a control system for an industrial application for one of our clients.  As part of that system we have a microcontroller located in a box with some other equipment, and thought it would be good to include some environmental monitoring.  Mostly we want to know if the things get too hot, or if they start to get wet due to maintenance people spraying down the outside of the equipment boxes (which are supposed to be water proof).

[Read more…]

Dual Axis Solar Panel Tracker / Controller Part 3


I’ve been working on a solar panel tracker/controller as an R&D project (previous blog entries part 1 and part 2).  I was able to finish the circuit board design a couple of weeks ago.  I want to panelize it with some other projects so I haven’t shipped it off yet.

[Read more…]

Voice Recording and Playback with an Arduino (BM023)

Back in the late ‘90s we created some small robots for a Microchip technical conference.  They rolled around and used a servo with an IR sensor mounted on it to detect objects.  They had a bit of a Wall-e look, long before that look was made cool by the movie.  Whenever they detected an object they would play a random message that we recorded onto an application.  I was thinking of those robots when I came up with the single message record/play breakout module.

[Read more…]