CC Blog Projects Research & Design Hub

Designing MCU-Based Lab Instruments

Figure 1 Cypress CY8CKit-059 prototyping board containing the PSoC 5LP MCU. The Debug/Programmer module at left snaps off when development is finished.
Written by Brian Millier

Using the Cypress PSoC 5LP MCU

Brian has lots of experience designing lab instruments from his university days. But that was before modern MCUs were available that have rich sets of analog functions on-chip. Here, Brian steps through how to design lab instruments using the Cypress PSoC 5LP MCU.

  • How to design lab instruments using the Cypress PSoC 5LP MCU

  • What is the PSoC 5LP MCU?

  • How to use the  PSoC 5LP MCU’s dedicated op amps

  • How to avoid traps when using  the dedicated op amps

  • How to use the Sigma-Delta ADC

  • How to do pH measurements

  • How to do conductivity measurements

  • How to design the UI and readout

  • Infineon’s PSoC 5LP MCU

  • Infineon’s CY8CKIT-059 development board

  • Infineon’s PSoC Creator version 4.3

  • Texas Instruments (TI) OPA344 rail-to-rail op amp

  • TI’s TMP61 Linear Thermisto

  •  YSI’s thermilinear sensors

  • Microsoft’s Visual Studio with the Visual Micro Arduino plug-in

A significant amount of my time working in the Chemistry Department at Dalhousie University was spent designing instrumentation for both research and teaching labs. These instruments were almost always mixed-signal designs, containing analog devices for the measurements plus a microcontroller (MCU) to handle display, user interface and often to transfer the data to a PC computer. In general, the analog/mixed-signal peripherals that were a part of the MCU itself generally did not have the required resolution/accuracy needed for these instruments—at least in the early days. Therefore, I used high-quality analog-to-digital converters (ADCs) and digital-to-analog converters (DACs) from Analog Devices, Burr-Brown and other manufacturers for most of my designs.

Times have changed, and currently MCUs are available that contain high-quality mixed-signal peripherals. For example, Analog Devices has introduced Arm-based MCUs with high-resolution analog “front-ends” and DACs specifically targeted at chemical measurements. However, the IAR development IDE needed for those devices is professional software that is too expensive for my situation.

I believe that the PSoC 5LP MCU family from Cypress Semiconductor (now part of Infineon Technologies) is a good alternative. It contains high-resolution ADCs, as well as DACs, op amps, comparators and so forth. Conventional MCUs contain a fixed number of peripheral functions, and possibly a multiplexer or cross-point switch to connect them to the external GPIO pins. The PSoC 5LP MCU also contains some pre-defined mixed-signal functions, but also features a block of generic analog functions and generic digital functions. These can be configured by the user to perform many different tasks. The PSoC 5LP also has a comprehensive set of multiplexers to: 1) interconnect these blocks internally to achieve a specific functionality; and 2) bring out that function block’s inputs and outputs to external IO pins.

Earlier Cypress PSoC 3/4 MCU families were focused upon having many of these generic analog and digital blocks, which could be combined to produce many different analog/digital functions. The trade-off was that high-resolution/quality mixed-signal functions were not available, partially because the versatility of the multiplexer connection options led to signal paths that were not optimized for sensitive analog measurement functions.

In the PSoC 5LP MCU family, the focus is more on including a few high-quality analog/mixed-signal functions with a more direct signal path to the external MCU pins. The generic analog and digital function blocks still exist, but in smaller numbers; they are no longer needed to synthesize these higher-quality mixed signal blocks. Overall, I feel you get plenty of versatility and some high-quality mixed signal functions.


Before getting into the details of the PSoC 5LP MCU, I’d like to mention a few things that drew me to this Cypress MCU family in general. Virtually all Arm 32-bit MCUs come in high-density packages. This means that working with the chips themselves is difficult unless you can handle SMT reflow soldering on a PCB of your own design. The PSoC 5LP MCU can be purchased on a small development module that brings out all its pins to two 26-pin headers (with 0.1” spacing). This is easy to mount on the prototype boards that I routinely use.

Virtually all the I/O pins are uncommitted—that is, no other peripheral ICs are included on the board that would reserve IO pins. However, to support the PSoC 5LP’s three ADCs, bypass capacitors are included on several IO pins, since these capacitors should be placed close to the chip to be most effective. The Cypress CY8CKIT-059 development board [1] costs only $15 and comes with a snap-off programmer/debugger. Figure 1 shows the board with its programmer still attached.

Figure 1 Cypress CY8CKit-059 prototyping board containing the PSoC 5LP MCU. The Debug/Programmer module at left snaps off when development is finished.
Figure 1
Cypress CY8CKit-059 prototyping board containing the PSoC 5LP MCU. The Debug/Programmer module at left snaps off when development is finished.

Because some of my previous projects used PSoC 3, 4 and 5LP devices, I am familiar with Cypress’ free PSoC Creator development IDE. This is a Windows-only application. I credit that as the reason why it loads and runs so quickly. It’s not a Java application like other multi-platform development IDEs. I find it to be quite user-friendly.

Somewhere between the introduction of the PSoC 5LP and the latest PSoC 6 MCUs, Cypress decided to introduce a radically different development toolchain called the Modus Toolbox. This new package follows the general philosophy that Microchip uses in the current version of MPLAB, along with the STM32Cube IDE provided by STMicro. That philosophy is: Simplify the C programming of the device as much as possible by including various configuration “wizards” and “middleware” packages to minimize the complexity of programming. I admit that the complexity of the drivers needed to handle Arm MCU peripherals is quite daunting, but I have found the latter two development IDEs hard to learn and painful to use. Although I have downloaded and poked around the Modus Toolbox, I continue to use the PSoC Creator version 4.3, which is probably the final Creator version.

It’s important to note that unlike most Arm-based 32-bit MCUs, the PSoC 5LP is capable of running with a power supply of up to 5V. When so powered, it has 5V logic levels. The CY8CKIT-059 prototyping board has no on-board regulator, and therefore operates the PSoC 5LP at 5V when it is powered by either the attached programmer board or the Micro-USB port on the target board, itself. You must be cognizant of this if you plan to use newer 3.3V peripheral devices.


The PSoC 5LP MCU contains four dedicated op amps, each of which has 25mA drive capability. They are rail-to-rail on both input and output. To achieve the 25mA output capability, you have to configure the op amp for the High-Power mode. Since these op amps are contained within the MCU, they operate on whatever voltage you are using for its VDDA power supply. For the CY8CKIT-059 module, the VDDA pin is connected to the VDD power supply, which is 5V. Note that you could remove R15, a 0Ω resistor on the board, if you wanted to set VDDA and VDD to different voltages.

Using these op amps gave me the most trouble of any of the blocks that I used, for several reasons. First, each op amp has three external IO pins dedicated to its output and inverting, non-inverting inputs. If you anticipate needing high current capability on any of these op amps, it is important that the output of the op amp is connected directly to its dedicated IO pin, not routed through the PSoC’s internal analog multiplexer arrays. These multiplexers and their signal paths have too high a resistance to be able to supply anywhere near 25mA.

If you plan to use an op amp as a unity gain follower, it is tempting to configure the op amp in the Follower mode, using Creator’s Configuration menu for that component. That means you don’t have to place a physical wire between the op amp’s Output pin and its inverting Input pin. You thereby free up an IO pin.

If you need any significant amount of output drive capacity, this doesn’t work! In reality, the op amps are NOT directly connected to these dedicated pins, but rather through an electronic switch. This gets activated only when you have enabled the op amp using the Opamp_x_start() command (where “x” indicates the specific op amp). This switch has some resistance. The voltage follower doesn’t work properly, because the effective op amp configuration looks like Figure 2. Any voltage drop due to the output switch resistance is not sent as negative feedback to the inverting input pin, so the output voltage drops in proportion to the current draw.

Figure 2 The internal routing presents a significant source impedance to the PSoC 5LP’s internal op amps when you configure them as Voltage Followers. I explain in the narrative how to overcome this.
Figure 2
The internal routing presents a significant source impedance to the PSoC 5LP’s internal op amps when you configure them as Voltage Followers. I explain in the narrative how to overcome this.

Early in my design process, I had given up on using these op amps in places where I needed up to 10mA, since the output voltage dropped significantly as the current draw increased. Once I switched to using the op amp in the Amplifier mode and ran an external wire between the output and inverting input pin, everything worked fine. Note that the op amp’s output enable switch resistance is still present, so you would not be able to get rail-to-rail output under higher loads (≥10mA). In my case, it worked fine sending a sine wave signal through the op amp, with a 1V peak-to-peak (pp) voltage amplitude and riding on an offset of 2.048V.


The second trap in using these op amps is that four of the so-called “dedicated” op amp pins are connected to 1µF capacitors (going to ground) on the CY8CKIT-059 board. These are used as bypass capacitors for the voltage references needed by the Sigma-Delta ADC and the two SAR ADCs. If you are not using any or all the ADCs (or under some ADC reference configuration conditions), you can remove one or more of these capacitors. They are clearly marked on the board, but are about the smallest SMD capacitors you can get. They are too close to the PSoC 5LP MCU to use a hot-air gun, but if you place your soldering tip across the top of them, once the solder melts, they will usually stick to the iron’s tip, and can be removed easily.

The four capacitors are as follows:

C9 on P0_4 SAR0 reference

C13 on P0_3 ExtRef0

C12 on P0_2 SAR1 reference

C7 on P3_2 ExtRef1

I was using only the Sigma-Delta ADC, which used ExtRef0 and ExtRef1. Those pins (P0_3 and P3_2) are the “dedicated” pins for Opamp_0 (inverting) and Opamp_3 (inverting). That prevented me from using those two op amps with their “dedicated” inverting input pins. But you can connect the inverting input to another IO pin and designate that as an Analog Input pin. It’s not really clear from the op amp’s datasheet that when you enable Opamp_x in your program, it only connects Opamp_x’s input pins to the “dedicated” external IO pin if you do NOT manually “wire” it up to another IO pin in your TopDesign schematic. “TopDesign” is what the PSoC Creator tool calls your top-level schematic.

A third confusing thing is the way the op amps are numbered. When you first place an op amp on your TopDesign schematic, it will be labeled as Opamp_1. However, the four op amps are labeled Opamp_0 to Opamp_3 in the datasheet. So, what physical op amp does this newly-placed Opamp_1 represent? If you hover over the Vout_1 pin, it is described as a high impedance analog pin, but it’s not assigned to any particular IO pin. You must click the “pins” configuration icon and select the IO pin that corresponds to the “dedicated” output pin for the particular op amp you want to use for this function. That is what determines which physical op amp is assigned to the Opamp_1 instance.

You must assign external IO pins manually whenever you use other analog and digital components, but generally those components are “synthesized” from the array of generic analog/digital blocks contained in the PSoC 5LP device. Since there are four discrete op amps in the PSoC 5LP, and they have “dedicated” external IO pins, I expected the Opamp_1 instance to correspond to the physical Opamp_0 (or maybe Opamp_1). That is not automatically the case, however, as explained above.

In this design, I used physical Opamp_2 for the excitation amplifier and physical Opamp_0 for the virtual ground-rail generator. I configured the excitation amplifier as an amplifier and made it a voltage follower by wiring it up externally (connecting IO pins P0_0 and P0_5 together). This wiring connection is actually done in the DB9 plug, because I have another use case where the connection to the inverting input is connected to something else in an electrochemical cell, rather than directly to Opamp_2’s output.


The single Sigma-Delta ADC contained in the PSoC 5LP is very versatile. Its resolution can vary from 8 bits to 20 bits, and its speed can go up to 384,000 samples per second (SPS) at 8 bits or down to 10SPS at 20 bits. The ADC full-scale voltage ranges from ±2.048V down to ± 0.064V in five ranges. In addition to those ADC ranges, the Sigma-Delta ADC also contains an optional input buffer with gains from 1 to 8. The ADC has a differential input, even when the optional buffer is used.

All Sigma-Delta ADCs have inherent nonlinearity at both the extreme top and bottom of their ranges. To combat this, the PSoC 5LP’s Sigma-Delta ADC block has a few tricks built in. For a given sensitivity range, the circuit is designed to scale that full-scale voltage to somewhat less than that chosen ADC full-scale. As a result, the ADC never operates in the small sections at the extremes of its real range, where the nonlinearity exists. Software in the API automatically scales the ADC result back into the full range represented by the number of bits for which the ADC is configured. You will see this, for example, if you exceed the F.S. spec. for the ADC. Instead of getting readings of +32,767 (or -32,768 for negative), expected from a 16-bit ADC, you will get numbers slightly higher. You have to consider this if you are expecting the normal 16-bit full-scale values to represent an over-range condition.

Finally, the Sigma-Delta ADC has an optional Modulation input. This can be connected to other circuit blocks and used to invert the polarity of the incoming signal. In this project, that feature is used to implement synchronous detection of a sine wave signal used for sensor excitation.


pH (“potential of hydrogen”) is a measure of the degree of acidity or alkalinity of a chemical solution. It requires a special pH electrode for measurement, and is essentially a reading of the voltage developed by the electrode. The relationship between the pH value and the voltage measured is near-linear with a negative slope, which depends on temperature. Figure 3 shows this relationship.

Figure 3 Relationship between pH value and the voltage output of a pH electrode at different temperatures.
Figure 3
Relationship between pH value and the voltage output of a pH electrode at different temperatures.

There are two issues involved in pH measurements.

  1. The source impedance of the pH electrode is extremely high. To get an accurate reading, the measuring device should have a minimum input impedance of 1012Ω, and even higher than this is better.
  2. pH electrodes are not perfectly calibrated “out of the box” (and they age over time). To get accurate readings, the operator generally must adjust a “Slope” pot to compensate for probe age/temperature. There is also a “Standardize” pot, which is what we would refer to as an offset adjustment in electronics. This adjustment is made by placing the pH electrode in a “buffer” solution of known pH and adjusting the Standardize pot to get the expected reading.

The first issue is the one area in which the PSoC 5LP’s analog blocks are not up to the task. I doubt that the individual op amps in this MCU would have a high enough input impedance, but the fact that they also must be routed internally through multiplexers and so forth made me skeptical that they were a viable choice.

With that in mind, I decided to add the only external active device in the project—a Texas Instruments (TI) OPA344 rail-to-rail op amp [2]. This device has an input impedance of 1013Ω in parallel with 3pF. Thirty years ago, when I was building a lot of pH meters, the only readily available and reasonably priced op amp with input impedance >1012Ω was RCA’s CA3140 op amp. Today, many op amps are suitable for this use. I happened to have the OPA344 on hand.

The OPA344 is also used as a transimpedance amplifier for other measurements. Since this is a different configuration from the voltage follower (buffer) configuration needed for pH measurements, the configuration of the input pins is handled by using a 9-pin “D” connector, and handling the specific input configuration using jumper(s) in the individual DB9 plugs for each measurement probe.

Figure 4 shows how the pH circuitry is implemented (neglecting the specific way in which the OPA344 is wired through the DB9 socket/plug). Note that pH measurements require the ability to measure voltages in the ±600mV range, but the PSoC 5LP and OPA344 operate on a single +5V power supply. To accomplish this bipolar voltage measurement, a virtual ground of 2.048V is generated, and all measurements are taken with respect to this virtual ground. That is, the shield (common) wire of the pH electrode is connected to the virtual ground, not the PSoC 5LP’s ground bus. The PSoC 5LP’s Sigma-Delta ADC is configured for differential operation, and its negative input is also connected to the 2.048V virtual ground.

Figure 4 Configuration of the analog “front-end” for pH measurements. The OPA344 has an input impedance of 1013Ω, which is needed for pH measurements.
Figure 4
Configuration of the analog “front-end” for pH measurements. The OPA344 has an input impedance of 1013Ω, which is needed for pH measurements.

Regarding the second issue, the easiest way to implement the functions of the slope and standardize pots found in strictly analog pH meters was to use two rotary encoders. They are read in software, and those encoder values are used to adjust the pH reading accordingly. The slope adjustment could be made automatically if you knew the temperature of the solution, and indeed some pH electrodes have built-in temperature sensors.

This project has a separate temperature measuring mode, designed for a specific, solid-state temperature sensor, so automatic temperature compensation (ATC) was not implemented in the pH measurement.


The conductivity of a solution is another common measurement in chemistry labs. It’s measured with a conductivity probe, one form of which is shown in Figure 5. Conductivity probes are somewhat expensive, and I have not taken any of them apart to see why this is the case, except that they do contain platinum.

Figure 5 One type of Conductivity probe
Figure 5
One type of Conductivity probe

Conductance is measured in siemens (S), and is the reciprocal of resistance, which is measured in ohms. Conductivity is measured in siemens per cm. Conductivity probes have a calibration factor associated with them that allows you to convert a conductivity value in siemens per cm into a conductance value in siemens. In this project, I apply a known voltage across the conductivity probe, and measure the resulting current.

Using Ohms law, I then calculate the resistance, which is displayed on the LCD. By inverting that number, and applying the probe’s calibration factor, one can derive the conductivity, in siemens per cm. You cannot impose a DC voltage across the conductivity probe or the solution will ionize and possibly damage the probe. Therefore, I use an AC voltage source, which is how it is done in commercial instruments.

I used the PSoC 5LP’s WaveDAC8 component to generate the sine wave. I configured the WaveDAC8 to generate an 8-bit, 254-sample sine wave at a frequency of 787Hz (the highest frequency achievable with the clock and sample size parameters chosen). The amplitude of the sine wave is either 0.512Vpp or 1.024Vpp, depending on the resistance range being measured. In both cases, the AC waveform rides on a 2.048V offset, which matches the 2.048V virtual ground used as a reference for the bipolar measurement. As mentioned in the section on pH measurement, this allows bipolar voltages (AC) to be measured by an instrument powered solely by a 5V source.

A transimpedance amplifier (TIA) is used to measure the current. The PSoC 5LP contains a TIA component, but since the OPA344 was already needed for the pH measurement, it was used instead. To measure a wide range of conductivity, an auto-ranging scheme is used. Three parameters can be manipulated to give the best measurement conditions:

  1. For the lowest resistance (i.e. highest conductance) measurements, the lower of the two sine wave amplitudes is used (0.512Vpp), otherwise the 1.024Vpp setting is used.
  2. The TIA has feedback resistors for three decade-related ranges: 1kΩ, 10kΩ and 100kΩ.
  3. The Sigma-Delta ADC has 3 configurations defined: 2.048V, 1.024V and 0.512V full scale.

These parameters are manipulated to provide 10 different auto-range sensitivities. A block diagram of the conductivity measurement circuit is shown in Figure 6. (The specific details of how the wiring is accomplished with the DB9 socket jumpers is not shown).

Figure 6 Configuration of the analog “front-end” for conductance measurements. The probe uses a sine-wave AC excitation voltage, and the current through the probe is converted to voltage by the OPA344 in a transimpedance amplifier configuration (TIA).
Figure 6
Configuration of the analog “front-end” for conductance measurements. The probe uses a sine-wave AC excitation voltage, and the current through the probe is converted to voltage by the OPA344 in a transimpedance amplifier configuration (TIA).

For conductance measurement, the AC current through the conductivity probe is converted to an AC voltage by the TIA. The transfer function depends on which of the three feedback resistors is being used. This AC signal must be rectified. The PSoC 5LP’s Sigma-Delta ADC has an optional modulation input. When the ADC is configured to use this pin, the input signal is inverted when the “mi” pin is high. Using an 8-bit counter fed by the same clock as the WaveDAC8, the counter’s output compare pin (set for 127 counts) provides a 50% duty cycle signal to provide this modulation control signal. With this configuration, the Sigma-Delta ADC synchronously demodulates the AC current signal. The way that I have configured the Counter compare, the ADC would normally produce negative output values during that part of the sine wave, which is positive, but the TIA also inverts the phase of the signal, so the two cancel out.


When I designed this circuit, I assumed that the AMux switches would have some resistance, but not enough to change the effective values of the 3 feedback resistors materially. I’m used to discrete multiplexer chips that have Ron values in the range of 10Ω to 50Ω. The AMux datasheet doesn’t specify this, but I found that it approached 1,000Ω. So, the TIA transfer function isn’t as straightforward as I intended. In the program, I have determined calibration factors for each of the 10 ranges—derived by measuring known resistance values and calculating these calibration factors from that.


Advertise Here

To simplify how the auto-ranging works, the LCD display prompts you to press the start button when you want to measure conductivity. It then steps through the 10 ranges (starting at the least sensitive), until it finds one in which the ADC output value is neither too low for good resolution, nor so high that the peaks of the sine wave signal are greater than the full-scale value of the ADC (between 0.512V and 2.048V, depending upon the specific range).

The actual conductance measurement is performed by taking ADC readings over 787 sine wave cycles (1 second). The ADC is configured for 15,000 samples/second, so about 15,000 readings are averaged. The average signal is 63.4% of the peak value, so that must be factored into the calculations. To perform another measurement (and to correctly auto-range), the start switch must be pressed again, to repeat this process.

While this AC excitation and synchronous demodulation scheme was necessary for measuring solution conductivity, as a bonus, I found that the readings obtained were extremely stable—much more than you get when measuring a DC voltage with the Sigma-Delta ADC (without the modulation input, of course). Any DC offset in the TIA gets canceled out. Also, any minor mismatches between the offset voltage of the WaveDAC8 and the virtual ground generator are also ignored. For the that reason, I decided to also use AC excitation and synchronous detection for the temperature measurement, which was effectively just measuring the resistance of the temperature sensor.

I have mentioned the 2.048V virtual ground used in the project. This is implemented by using the same 1.024V voltage reference contained in the PSoC 5LP that is used by both the WaveDAC8 and the Sigma-Delta ADC. The 1.024V reference is fed into the PSoC 5LP’s Opamp_0, which is configured as a non-inverting amplifier with a gain of 2.0. Due to the high drive capacity of the PSoC 5LP’s op amps, this provides a “stiff” virtual ground.


Some of the temperature-measuring instruments that I built in the past were calorimeters. This involved measuring solution temperatures over a fairly limited range, but with very high resolution (generally to 0.01°C). I had discovered YSI’s thermilinear sensors, which were a composite of two highly accurate thermistors and a matching resistor network. With dual-slope integrating ADCs (common at the time), I was easily able to achieve resolutions of 0.01°C or better. These YSI sensors were meant for aerospace applications and were expensive compared to other temperature sensors. YSI has moved on to other products now, though the thermilinear thermistors are still available from other sources.

Technology moves on, and I came across the TMP61 Linear Thermistor [3] made by TI. These are PTC thermistors with a reasonably linear response—in contrast to NTC thermistors commonly used for temperature measurement (generally over a restricted temperature range). Also, they are consistent enough that their nonlinearity can be corrected by using the coefficients for one of two equations that TI supplies in an Excel spreadsheet. The spreadsheet is available for download from Circuit Cellar’s article code and files webpage. At less than $1 each, they are 1/40 the price of the YSI thermilinear devices that I used in the past. Figure 7 is a graph of TMP61 resistance at various ambient temperatures. You might say that calling the TMP61 a “linear” thermistor is a stretch, but it is far more linear than conventional PTC thermocouples!

Figure 7 Temperature versus resistance relationship of the Texas Instruments TMP61 “linear” thermistor device. Note that it is not really linear, but rather is a gentle enough curve that it can be represented by either the Steinbeck-Hart equation or a fourth order polynomial.
Figure 7
Temperature versus resistance relationship of the Texas Instruments TMP61 “linear” thermistor device. Note that it is not really linear, but rather is a gentle enough curve that it can be represented by either the Steinbeck-Hart equation or a fourth order polynomial.

The TMP61 is available in several tiny SMT packages. They have just two terminals, and can be embedded in the small stainless-steel probe bodies that I use. They also come in the common TO-92 transistor package, for easier handling, if the additional thermal mass is not a problem in your application.

The TI Excel spreadsheet has tables containing the resistance values for every 1˚C, and the expected voltage across the device given a user-selected VBIAS (what I call the “excitation voltage”). This spreadsheet is designed for a voltage divider configuration with a 10,000Ω series resistor (RBIAS). The spreadsheet is protected (with a password) with respect to adjusting that 10,000Ω value, so that is what I used in my circuit.

To calculate the TMP61’s resistance, you must first calculate the current through the voltage divider. Having measured the voltage across the TMP61, you can then calculate the current using the following equation:

You can use either the Steinhart-Hart equation or a fourth-order polynomial to determine the temperature for the measured TMP61’s resistance. Both sets of coefficients are provided in the spreadsheet, and I chose the fourth-order polynomial equation.

The only complication here is that I wasn’t using a DC excitation, but rather the same 787Hz sine wave excitation signal for the voltage divider as the one I used for the conductivity measurement. I did this because of the highly stable readings I had obtained using AC excitation and synchronous demodulation.

I used a 1.024Vpp sine-wave signal, which corresponds to 0.512V peak. Since the Sigma-Delta ADC measures the average voltage over the full sine wave, the effective VBIAS is:

0.512V × 63.4% or 0.32461V

This excitation voltage, along with the fourth-order coefficients from the spreadsheet, are included at the top of the main.c code, using #define directives.


Another type of instrument that I designed and built at Dalhousie was a Potentiostat or Cyclic Voltammetry instrument. Briefly, it applies a voltage ramp (in the ±1.5V range) to an electrochemical cell, and measures the current that is drawn at each part of the ramp. This is performed for many cycles, and the results are displayed graphically on a PC and stored to a file for analysis. The endpoint voltages, voltage step, number of cycles and current full-scale range are all specified by the operator. When a run is started, it may proceed for minutes or days, depending on the experiment.

This PSoC 5LP project has all the elements needed to implement such an instrument, except for a voltage ramp generator. In Figure 8, the TopDesign schematic for the unit, you can see the DVDAC_1 block. This can be programmed to provide a voltage ramp. The PSoC 5LP DACs are only 8 bits, but by using hardware dithering, they can be extended to 12-bit resolution.

Figure 8 Complete schematic of the unit, as drawn on PSoC Creator’s TopDesign.cysch page.
Figure 8
Complete schematic of the unit, as drawn on PSoC Creator’s TopDesign.cysch page.

This dithering is done at a high frequency (250kHz) and is filtered by the 10nF capacitor connected to the DVDAC_1 output (on P3_5). Analog multiplexer AMux_2 is used to switch between this ramp generator and the sine-wave excitation signal provided by WaveDAC8_1, depending on measurement mode. The PSoC 5LP firmware and the supporting PC application for voltammetry are quite involved, and would have to be the subject of a separate article.


Color TFT touchscreen displays weren’t necessary to use in this project. Early on, I wired up a common 2.8″ TFT touchscreen (with an ILI9341 controller) to try it out. I don’t have an optimized ILI9341 graphics library for the PSoC 5LP, like those that I use with the Teensy or ESP32 boards in many of my projects. I did find the psoc_ugfx library, which is the “universal” ugfx library ported to the PSoC 5LP. Since this is a generic library meant to be readily ported to different MCUs, it isn’t optimized, and I found it to be too slow for my liking.

Instead, I settled on the common 16×2-character LCD display, for which there is a “component” available in the PSoC 5LP’s library. This works fine, though it is rather limited in what port pins it will work with. Only Port 2 can be used without interfering with the pins needed for other functions in this project.

The pH function requires two adjustment “pots,” which I implemented using rotary encoders. Generally, rotary encoders are interfaced using two GPIO pins and an interrupt service routine in software to read them. The PSoC 5LP has a QuadDec component that can be configured as an 8- to 32-bit up-down counter. This makes it easy to handle the rotary encoder. All you must do is read the QuadDec counter at any time, and it will give you the current encoder position. Similarly, you can just write to the counter, if you want to have the encoder report a given value from its current position. The Mode and Measure switches are monitored by a 5-bit Status_Reg component, connected to five GPIO lines.

During development, you can send values to the PC by using the UART component, with its Rx pin defined as P12.6 and Tx as P12.7. These two lines are pre-wired to a similar UART on the KitProg board, and the signals are then forwarded to the PC via the KitProg’s USB port. The KitProg will show up as KitProg USB-UART (COMxx) in Device Manager.


Advertise Here

It’s important to note that if you break away the KitProg board from the target board, you can still use the programming/debugging features of KitProg by connecting the two boards with a 5-conductor header cable connected to the 5-pin headers on each board. However, you will lose the connection between the UARTs on each board when you do this. Tiny traces beyond those used for the 5-pin header get broken when the board is snapped off, so the serial debugging mentioned above no longer works. To reinstate it, you need to install jumpers between the following two sets of pins:


Tx P12.7

Rx P12.6


Rx P12.6

Tx P12.7

All four of these are available on standard 0.1” header connections on their respective boards. Once you have removed the KitProg board, you can replace its KitProg USB-UART connectivity with the USBUART component on the target PSoC 5LP, itself. This USB port is wired to the micro-USB socket on the target board. Power for the target board (5V) can also be supplied using a cable from this micro-USB socket to a host PC’s USB port.


Using the USBUART component is fairly simple. You just add the USBUART component to your schematic on the TopDesign.cysch tab. If you double click on it to view the Configuration wizard, you will find many different tabs, of which the CDC tab is the only relevant one, considering that the USBUART is a USB CDC device.

Nothing in any of these tabs must be changed, apart from maybe the String Descriptor tab. Here you could change strings such as “Cypress Semiconductor” or “USBUART,” if you need to identify your project more definitively from within a PC host application.

Beyond this USBUART Configuration menu (where everything is pre-configured), one thing must be done manually. You must select the “Clocks” icon in the Workshop Explorer window, and select a USB Clock. That will bring up a screen shown in Figure 9. You must select an IMO clock as “Osc” at 24MHz. Then you must set the USB clock to “IMOx2- 48MHz”. Last, the ILO clock must be set to 100kHz. Until you do this, the compiler will pester you to correct the USB clock settings.

Figure 9 To use the PSoC 5LP’s on-board USB port, you must set the USB clock properly. This is the “Clocks” GUI screen, where this is easily accomplished.
Figure 9
To use the PSoC 5LP’s on-board USB port, you must set the USB clock properly. This is the “Clocks” GUI screen, where this is easily accomplished.

As with any component that you add to your TopDesign.cysch tab, adding the USBUART will generate the API needed to use that component. This API can be found in the USBUART_1_cdc.h and .c files in the WorkPlace Explorer window. There you will find routines including CDCIsReady, PutData, PutString, GetData and DataIs Ready.

Here I found a few things were different from what I expected. With PSoC 5LP’s standard UART component (and MCU UARTs in general), when you are transmitting data, the transmit routine will check to see if the UART transmitter has completed any previous transmissions. It may block until the UART transmitter is free to accept the byte(s) you are now sending it. Maybe there is a FIFO associated with the UART, and if so, that will accept incoming byte(s) until it is full. There are two important ideas here:

  1. The transmit routine may block momentarily until the UART is free to accept the incoming byte(s).
  2. No consideration is given as to whether the UART transmitter is actually connected to any receiver. That is, the routine won’t block if nothing is “listening” to the UART transmitter.

With the PSoC 5LP’s USBUART, however, things are different—at least regarding transmission. To begin, USBUART transmissions are block-oriented with up to 64 characters sent in a block. Blocks are only sent every 1ms. The USBUART transmitter driver contains a 64-byte FIFO buffer, which matches the above protocol.


Before using the USBUART component for reception/transmission, you must configure it early in your program. That’s only a few lines of code and I won’t elaborate. Beyond that, however, if you want to transmit bytes through the USBUART, you must first call the USBUART_1_CDCIsReady routine in a loop until it returns a ready status.


Advertise Here

This is where things become cloudy. To illustrate, here are some observations that I made:

  1. If I powered the PSoC 5LP board from the PC via the USBUART port, early in the boot process (long before Windows was loaded), the CDCIsReady status would become true. This remained the case after Windows was loaded. Therefore, my program would not block—it would send out data packets as if everything were fine, even though there was no Terminal Emulator program running to accept this data.
  2. If I powered the PSoC 5LP from some other source and booted up the PC, the results were the same as described above.
  3. If I rebooted the PSoC 5LP board (using the reset switch) when Windows was already loaded, the CDCIsReady status would remain false until a Terminal Emulator program was opened—that is, when something was “listening” to the USBUART data coming from the PSoC 5LP.

I found this behavior puzzling. I couldn’t use the CDCIsReady status in the initialization part of my program, because it might or might not block my program’s execution, depending on the order in which the PSoC 5LP was powered up or plugged into the host PC.

However, the CDCisReady status is still needed to prevent your program from sending bytes to the USBUART port before it is ready to accept them (for example, when busy with the last message). I found that if you called the USBUART_1_PutString API twice in a row, with no delay between the calls, data would be dropped from the first packet. This occurred even when the sum of the number of bytes being sent was much less than the 64 bytes available in the FIFO buffer.

When I examined the API code, it made a check for USBUART_1_IN_BUFFER_FULL. However, that did not seem to be sufficient to stop this dropping of characters. In the comment section ahead of this API, they mention that the USBUART_1_CDCIsReady function should be checked first, before calling this API. I decided, instead, to define my own USBUART_1_SendString function. Due to the different behaviors, I observed with the CDCIsReady status, I took the easy way out by ignoring the CDCIsReady status, and instead, insert a 1ms delay after every data transmission in my USBUART_1_SendString routine. That ensures that the data packet has had enough time to be sent out, even though there may be nothing “listening” on the PC side. This is not an efficient general-purpose solution to the problem, but is fine for this use case, in which data is being sent out the UART port regardless of whether or not a PC application is actually logging that data.

One other issue that is somewhat related to the USBUART function concerns the standard C sprintf function. This function accepts variables of different numeric types, converts them to the String format, and optionally concatenates them with some “labeling” string (such as “value =”). It’s needed for displaying numbers on the LCD screen or sending them out via UART. With all other compilers that I have used, floating-point variables are handled properly with sprintf. With PSoC 5LP/Creator 4.3 software, only integer numbers are handled by default. Floats are ignored, and no compiler error is raised. This has been a feature (or bug) of Creator for as long as I’ve used it. What you have to do to make sprintf work with floats is not obvious, so here is the solution.

Referring to Figure 10 (Build Settings), change “use newlib-nano Float Formatting” from “false” to “true.” In “additional Libraries” make sure an “m” is shown. In Figure 11 (Build Settings) place “-u _printf_float” in the command line section. None of this will work until you click on the “System” icon (in the Workspace Explorer window) and increase the Heap Size from its default (0x80 I think) to 0x1000. Obviously, when you start a new project, the sprintf function requires a lot more heap space than the Creator software allocates by default.

Figure 10 This is the first screen you must access to enable the sprintf function to work with floating-point numbers.
Figure 10
This is the first screen you must access to enable the sprintf function to work with floating-point numbers.
Figure 11 This is the second screen you must access to enable the sprintf function to work with floating-point numbers.
Figure 11
This is the second screen you must access to enable the sprintf function to work with floating-point numbers.

The circuitry used for each function is discussed in previous sections of the article. There is no power supply as such. The project is powered entirely by 5V from the host PC, through the USB port on the PSoC 5LP target board, or with a phone charger with a micro-USB cable, if a PC is not used. Figure 8 is the TopDesign schematic of the project, taken directly from the Creator software. Figure 12 is a photo of the unit. The empty headers were placed there for TFT screens that I tried and then decided against using.

The system I use to configure the OPA344 op amp for the different operating modes is as follows. Basically, I use a DB9 plug as the sensor connector. Each sensor is connected to different pins, as needed, and jumpers are used to place the OPA344 in either a TIA configuration or a voltage-follower configuration. A small reed relay, RE1, connects the OPA344’s inverting input to the DB9. This is only needed for Voltammetry mode, and is always activated in the modes described in this article. Figure 13 is a diagram of the three sensor interfaces.

Figure 12 Photo of the circuit. Apart from the PSoC 5LP and the OPA344 op amp, there isn’t much else on the board other than headers that connect to the panel-mount components.
Figure 12
Photo of the circuit. Apart from the PSoC 5LP and the OPA344 op amp, there isn’t much else on the board other than headers that connect to the panel-mount components.
Figure 13 DB9 wiring for the three different sensors. Jumpers are used to re-wire the OPA344 for the various input configurations needed.
Figure 13
DB9 wiring for the three different sensors. Jumpers are used to re-wire the OPA344 for the various input configurations needed.

I wish I could think of more projects in which to use PSoC devices. The Creator 4.3 application is a pleasure to work with. Drawing a schematic from within the Creator application, and having the PSoC 5LP internally “wired up” to match that drawing is quite addictive. Clicking on a component and being presented with a configuration wizard tailored to the specific functionality of that component is also very nice.

Once you have included a component on your TopDesign schematic, the necessary API to use it is automatically generated and displayed in the Workspace Explorer window. So, you don’t have to scout around for the necessary library and #include it in your program. Also, the Creator program doesn’t add a huge BSP (board support package) library to your program, which contains a lot of libraries you may not need, and which makes finding things harder.

When writing your own code, Creator features syntax hints and auto-completion. All the APIs have the component’s name as a prefix, and once you type that in, you will be presented with a list of the available function calls. Once you’ve specified a function, the parameter list and variable types are presented. At the right of the screen is the Code Explorer, which has a hierarchical list of the Global variables and all the Function calls, making it easy to jump to the correct part of your code.

Creator also includes several helpful dedicated screens. The Clock screen is a graphical wizard that allows you to configure all the PSoC’s different clock sources, without writing any code or wading through the MCU technical reference manual. The Analog screen is a detailed schematic drawing showing you how all the analog/mixed signal blocks are internally interconnected, and/or fed out to specific IO pins.

Some of these features are also available in the development IDE of other MCU families, but they are absent from the Arduino IDE. I use the Arduino IDE with most of the MCU families that I work with. However, I am accustomed to most of the above features because I don’t use Arduino IDE directly, but instead use the Visual Studio with the Visual Micro Arduino plug-in, which also has those features.

Working with the PSoC 5LP and the Creator software gives you a taste of what designing with FPGAs must feel like. I admit that the complexity of working with FPGAs has discouraged me from trying them so far. 


[1] Infineon (Cypress) CY8CKIT-059 Prototyping board for PSoC
[2] Texas Instruments OPA344:
[3] Texas Instruments TMP61 10kΩ linear thermistor:
The TI Excel worksheet for this device is available from Circuit Cellar’s article code and files webpage.

Arduino |
Cypress Semiconductor |
Infineon Technologies |
Texas Instruments |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Brian Millier runs Computer Interface Consultants. He was an instrumentation engineer in the Department of Chemistry at Dalhousie University (Halifax, NS, Canada) for 29 years.

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Designing MCU-Based Lab Instruments

by Brian Millier time to read: 31 min