Arduino Clone With A Microchip PIC Co-Processor

manny_clone

Some time ago I asked our intern, Manny, to design an Arduino clone.  This was primarily  a learning exercise.  He’s worked with the  Arduino platform one some projects, and created schematic/PCB/firmware for a Microchip PIC based project.  This project was designed to combine those experiences to create a more generic tool.  I felt the results of his effort were pretty cool, so I thought I’d share some of the concepts here.

[Read more…]

Debugging Arduino Sketches with Atmel Studio 7

debug_breakpoint

Atmel’s AtmelStudio version 7 allows you to import Arduino sketches and debug them.  This brings the Arduino into the realm of a professional design engineer tool.

[Read more…]

Creating an Arduino Clone

FB_CLONE

I tasked our intern with creating an Arduino clone.  His job is to create a schematic and printed circuit board with connections similar to our Firstbot product (shown above).  The main reason for the task was to introduce him to the concept of a bootloader and familiarize him with the popular Arduino product line.  It also helps that he gets more experience writing C code for microcontrollers, and creating a printed circuit board.  If you don’t know what a “bootloader” is you can read about it here.

[Read more…]

Hard Iron Calibration of a Magnetometer Using a CSV Data File

image

I recently worked with a client to explore hard-iron calibration of a magnetometer’s output.  I found some really helpful information from Freescale in their application notes AN4246, AN4247, and AN4248.  These all relate to tilt compensation of an eCompass.  These application notes are directed toward a cell phone application.

[Read more…]

VISA COM Example With Oscilloscope

image

Oscilloscopes are an integral part of electronic design.  They can also be powerful data loggers when used with control software.  We have several labs areas populated with Keysight Technologies devices (previously Agilent, and before that Hewlett Packard).  The labs include oscilloscopes, spectrum analyzers, power supplies, network analyzers, function generators, and more.  One thing they all have in common is the ability to control them with VISA COM.  VISA stands for Virtual Instrument Software Architecture.

[Read more…]

Signal Generator in a PIC16F1829–Part 2

PIC16F1829_schematic_waveform

Previously I covered using Excel to create/chart waveform equations and Visual Basic (VB) code to convert the equations into C coded look-up tables (that post is here).  The output of the VB program is copy/pasted into firmware for a Microchip PIC16F1829.    Using the PWM output from the microcontroller, an RC filter, and an op-amp buffer I was able to generate test waveforms for a recent software design.

[Read more…]

Signal Generator in a PIC16F1829–Part 1

signal_generator_sig3

 

I recently completed a software design  to control a Keysight digital storage oscilloscope (DSO).  The end goal of the software was to initialize oscilloscope settings,

set a trigger, and then make timing measurements from a trigger on one scope channel to an undefined point of a signal on a second scope channel.

The second signal I had to measure was not something that would be available to me during development.  This posed a bit of a problem.  I knew the signal would be a rising signal between 0-5V,  but did not know how complex the signal might be.  It could be exponential, a simple ramp, sinusoidal, or some weird combination.

 

[Read more…]

Converting Float Variables to Byte Arrays and Back Using .NET

We regularly create microcontroller designs that use a serial interface for testing or control.  In those designs it’s common to have a variety of variable types.  We might have 16-bit signed integers mixed with unsigned bytes and 32-bit floating point variables.

To send and receive data using a serial interface we need to convert data to a serial array so we can send a stream of bytes.  In the microcontroller this can easily be done using unions.

union LongUnion
    {
    long LW;
    unsigned long uLW;
    int W[2];
    unsigned int uW[2];
    char B[4];
    unsigned char uB[4];
    float FLT;
    };
union LongUnion
MyTemp;

MyTemp union allows you to copy a variety of variable types and easily break them into bytes.  LW (long word) shares the same memory as FLT (floating point values) and uB[4] (an array of 4 unsigned characters).

Say we want to copy a 32-bit unsigned variable to a byte array.  Place the variable in MyTemp.uLW and its byte components can be accessed via MyTemp.uB[0] (LSB), MyTemp.uB[1], MyTemp.uB[2], and MyTemp.uB[3] (MSB).

Example…

if MyTemp.uLW = 0xFF783309

Then

MyTemp.uB[0] = 0x09

MyTemp.uB[1] = 0x33

MyTemp.uB[2] = 0x78

MyTemp.uB[3] = 0xFF
Its pretty simple to break down integers this way.  You can use masks and or bit shifting to convert values.  But let’s also consider floating point variables.

In the example below we  copy a float variable, an unsigned word, a signed word, and two unsigned characters over our byte array to send to our serial port…

unsigned char TXArray[10];

float FloatValue;
unsigned int UnsignedIntValue;
int SignedIntValue;
unsigned char CharacterValue1;
unsigned char CharacterValue2;


MyTemp.FLT = FloatValue;
TXArray[0] = MyTemp.uB[0];
TXArray[1] = MyTemp.uB[1];
TXArray[2] = MyTemp.uB[2];
TXArray[3] = MyTemp.uB[3];
MyTemp.uW[0] = UnsignedIntValue;
TXArray[4] = MyTemp.uB[0];
TXArray[5] = MyTemp.uB[1];
MyTemp.W[0] = SignedIntValue;
TXArray[6] = MyTemp.uB[0];
TXArray[7] = MyTemp.uB[1];
TXArray[8] = CharacterValue1;
TXArray[9] = = CharacterValue1;

Send_Serial(TXArray);

This places our variables into an array of bytes that we can send through our serial port hardware.

NOTE:  We do a lot of Microchip designs, and use their XC8 compiler with their 8-bit chips.  In the case of XC8 floating point variables default to a 24-bit truncated version.  To interface with .NET using Visual Basic you really want your floating point variable to be in the IEEE 754 32-bit format.  In XC8 this is done by changing the compiler options.  The option is under project properties –> XC8 Linker / Memory Model options.

 

image

 

Let’s say we’ve sent the serial data in TXArray out the microcontrollers serial port, and it has been received by a computer running a Visual Studio application (in this case Visual Studio 2010 with .NET 4.0, although this technique is also available in newer versions of .NET).

The sent data is now in a variable byte array called RXArray…

Using “BitConverter” we can pull the information out of the received serial data array in the correct format.  Since our float variable is located  in RXArray locations 0-3, we just call BitConverter and tell it to convert to a “Single” variable type the data in RXArray starting at an offset of 0.  BitConverter will convert the bytes in RXArray locations 0 through 3 to a Single (32-bit floating point single precision  variable).

To convert the unsigned and signed 16-bit values in the received byte array you do the same thing but change “convert to value” and the RXArray offset.   The unsigned integer is located in RXArray bytes 4 andf 5.  The signed  integer is located in RXArray btyes 6 and 7.    Bytes 8 and 9 contain character values, which can also be converted.

 

    Private Sub ConvertSerialData(RXArray As Byte())


        Dim FloatVariable As Single
        Dim UnsignedIntVariable As UInt16
        Dim SignedIntVariable As Int16
        Dim Char1 As Char
        Dim Char2 As Char

        FloatVariable = BitConverter.ToSingle(RXArray, 0)
        UnsignedIntVariable = BitConverter.ToUInt16(RXArray, 4)
        SignedIntVariable = BitConverter.ToInt16(RXArray, 6)
        Char1 = BitConverter.ToChar(RXArray, 8)
        Char2 = BitConverter.ToChar(RXArray, 9)


    End Sub

 

Using the BitConverter class in .NET makes converting between data types pretty simple.  The only thing you really need to keep an eye on is the order of the data you’ve broken into bytes and sent via a serial link.  Sending your data LSB first works with BitConverter and the MyTemp variable as shown in this example.

The NetBurner MOD54415 Experience: Part 1

net_burner_MOD54415

I’ve decided to experiment with networked electronics.  We’ve worked with some of Microchip’s development boards running their TCP/IP stack in the past.  But I wanted to check out some other products to see if they might make it easier to develop low volume production electronics with Internet connectivity.  I’ve started with NetBurner’s MOD54415.

[Read more…]

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()

        Try

            ' 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

                myTask_Trigger.Start()
            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
                myTask_Analog.Control(TaskAction.Verify)

                ' Commit the Task
                myTask_Analog.Control(TaskAction.Commit)

                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
            UnloadTask_Analog()

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

        End Try

    End Sub

    ' cancel the analog task
    Public Sub UnloadTask_Analog()

        Try

            If Not runningTask_Analog Is Nothing Then
                myTask_Analog.Dispose()
                runningTask_Analog = Nothing
                myAsyncCallback = Nothing
            End If

            If Not runningTask_Trigger Is Nothing Then
                myTask_Trigger.Dispose()
                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)

        Try

            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
            UnloadTask_Analog()

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

        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