Sunteți pe pagina 1din 173

Cypress

PSoC 5LP Prototyping Kit


Measurement Electronics

hardware and software


By Yury Magda



Copyright © 2015 by Yury Magda. All rights reserved.


The programs, examples, and applications presented in this
book have been included for their instructional value. The
author offer no warranty implied or express, including but not
limited to implied warranties of fitness or merchantability for
any particular purpose and do not accept any liability for any
loss or damage arising from the use of any information in this
book, or any error or omission in such information, or any
incorrect use of these programs, procedures, and applications.

No part of this publication may be reproduced, stored in a
retrieval system, or transmitted in any form or by any means,
electronic, mechanical, photocopying, recording, or otherwise,
without the prior written permission of the author.
Contents
Introduction
Disclaimer
PSoC 5LP Prototyping Kit details
Interfacing PSoC 5LP Prototyping Kit to the PC via an USB-to-UART adapter
Developing a C# control application for the PSoC 5LP Prototyping Kit
Designing the application with PWM controlled by UART
Designing the application with the timer controlled by UART
Processing signals from mechanical switches
Processing several digital inputs at a time
The sine wave synthesizer using a timer and digital-to-analog converter
Measuring analog signals with a SAR analog-to-digital converter
High-precision measurements of analog signals with a Delta-Sigma A/D
converter
Processing multiple analog signals using a Delta-Sigma A/D converter
Designing the Pulse-Width Modulator using digital and analog components
Designing the simple measurement system with an analog temperature sensor
Designing programmable external circuits with digital potentiometers
Designing small signal AC amplifiers
Measuring the frequency of digital signals: project 1
Measuring the frequency of digital signals: project 2
Measuring a pulse width of digital signals
Introduction

Nowadays developers and hobbyists designing various electronic devices and systems
have a lot of opportunities to realize their ideas and thoughts. This book is dedicated to
one of the popular development tools from Cypress Semiconductor, CY8CKIT-059 PSoC
5LP Prototyping Kit, which allows to greatly simplify design of electronic circuitry.
Combined with the free PSoC Creator Integrated Design Environment (IDE), the PSoC
5LP Prototyping Kit enables developers to create unique designs powered by the PSoC
5LP Programmable System-on-Chip. Below is the brief description of a PSoC 5LP
Prototyping Kit taken from the guide on this kit.

The PSoC 5LP prototyping kit is designed as an easy-to-use and inexpensive prototyping
platform. The kit supports the PSoC 5LP device family, delivering a complete system
solution for a wide range of embedded applications at a very low cost. The PSoC 5LP is
the industry’s most integrated SoC with an ARM® Cortex™-M3 CPU. It combines
programmable and reconfigurable high-precision analog and digital blocks with flexible
automatic routing. The unique flexibility of the PSoC 5LP architecture will be helpful for
those who want to rapidly develop products using the PSoC 5LP device family.

The PSoC 5LP Prototyping Kit offers an open footprint breakout board to maximize the
end-utility of the PSoC 5LP device. This kit provides a low-cost alternative to device
samples while providing a platform to easily develop and integrate the PSoC 5LP device
into your end-system. In addition, the board includes the following features:
Micro-USB connector to enable USB application development;
On-board CMOD capacitors to enable CapSense® development;
Bypass capacitors to ensure the high-quality ADC conversions;
An LED to provide feedback;
A push button to provide a simple user input;
Load capacitors to connect 32 kHz external crystal oscillator;
3.3V to 5.5V operation.

The PSoC 5LP prototyping kit also integrates the Cypress KitProg that enables on-board
programming, debugging and bridging functionality, such as USB-UART and USB-I2C.
The KitProg is used to program and debug the target PSoC 5LP device (Fig.1). The
prototyping kit allows you to separate the KitProg board from the PSoC 5LP target board.

Fig.1

The PSoC Creator IDE includes easy-to-use graphical interface that enables designers to
drag and drop pre-characterized, production-ready analog and digital blocks – PSoC
Components – into a single PSoC device to create customized, feature-rich, and highly
differentiated PSoC 5LP applications. All projects described in this book have been
developed using PSoC® Creator™ 3.3 IDE.

This book is thought as a highly practical guide which can help a developer to build
various PSoC 5LP-based mixed electronic circuits, measurement and control systems.
Each project allows to understand various theoretical and practical aspects of design using
PSoC 5LP. The readers will also see how PSoC 5LP can be connected and communicate
with each other. The material of the book assumes that the readers are familiar, at least,
with basics of designing and assembling electronic circuits. For most projects, having
some basic skills in electronics will serve the readers well and allow them to understand
what is going on behind the scenes. Each project is accompanied by a brief description
which helps to make things clear.

Disclaimer

The design techniques described in this book have been tested on the CY8CKIT-059 PSoC
5LP Prototyping Kit board without damage of the equipment. I will not accept any
responsibility for damages of any kind due to actions taken by you after reading this book.

PSoC 5LP Prototyping Kit details

Before using PSoC 5LP Prototyping Kit we should become familiar with several
important points behind this board that are described in the user’s guide.
The PSoC 5LP Prototyping Kit consists of the following blocks:
PSoC 5LP device;
PSoC 5LP header ports J1 and J2;
Micro-USB Connector, J6;
PSoC 5LP Program/Debug JTAG header, J5;
KitProg (PSoC 5LP) device;
KitProg ports J8 and J9 (GPIO);
SWD connection J3 and J7;
PCB USB connector;
One amber LED (Power);
One green LED (Status);
One blue LED (User);
User Push Button and Reset Button;
External reference capacitors (ADC Bypass);
CapSense capacitor (CMOD);
Programming connector, J3;
Perforated ‘snappable’ board design.
All these blocks are shown in Fig.2.

Fig.2

The power supply system on this board is dependent on the source of the power. For most
applications, you can use the 5 V supply from the USB connection to power the system.
You can also connect an external power supply to the board for low-voltage applications.
The kit supports the following connections:
5 V from the KitProg USB;
5 V from the PSoC 5LP Target USB (this will not power the KitProg section of the
board);
3.3V to 5.5V from a regulated supply connected to VDD (this will not power the
KitProg section of the board).

Note: In order to use an external power supply, while KitProg is connected to the PCB
USB, remove diode, D1, from the board. This ensures that VTARG supply from KitProg
is not supplied to the target device. KitProg measures the target voltage and adjusts the
logic levels on the programming pins accordingly.

It is important to understand that this prototyping kit does not have any onboard ESD
protection circuitry. Therefore, the power source for the PSoC 5LP Prototyping Kit must
be of a high quality to ensure that the board is protected from any over-current conditions
and swapped-power connections.

More details concerning this kit you can find out in the user’s guide on the kit.

Interfacing PSoC 5LP Prototyping Kit to the PC via an
USB-to-UART adapter


Many PSoC 5LP applications described in this guide will receive commands and transfer
results via a serial interface (UART), therefore we discuss how to connect a PSoC 5LP
Prototyping Kit to the PC using a serial interface. Nowadays all PCs are equipped with
several USB ports, so you can use one of numerous USB-to-Serial adapters to connect
PSoC 5LP Kit to your computer. While developing the projects, I used a USB-to-Serial
adapter with the FT232RL IC (Fig.3).

Fig.3

Connections between the PSoC 5LP Prototyping Kit and USB-to-UART device are shown
in Fig.4.

Fig.4

In this circuit, the TX and RX pins of the USB-to-UART are wired to the MCU ports
P0[0] and P0[1] of the PSoC 5LP Prototyping Kit.

Note that the signal levels on TX/RX pins of a USB-to-UART adapter should be the
same as those on the corresponding pins (P0.0 and P0.1) of the PSoC 5LP board.
Voltage levels on the digital pins of the PSoC 5LP board are dependent on the power
scheme used in your design.
For example, when the PSoC 5LP Prototyping Kit is powered via the KitProg
interface, the voltage levels on PSOC 5LP digital pins can be as high as +5V,
therefore a USB-to-UART device must provide the same levels on its pins. Usually,
USB-to-UART adapters have an on-board switch to select either 3.3V or 5V logical
levels.
You have to ensure that an external serial interface you want to use will operate with
the logical levels adjusted on the PSOC 5LP board, otherwise you can damage your
equipment!

All projects described in this book use the default power scheme where supply voltage
+5V to PSoC 5LP is fed by the KitProg interface. The KitProg, in turn, is connected to
the USB port of the PC (see Fig.4).
To interact with a PSOC 5LP application we can use some terminal application running on
the PC. In this guide, we will use a popular terminal application puTTY whose
configuration window is shown in Fig.5.

Fig.5

Here you can see the application window with default settings assigned to the
“Serial line” parameters (the serial port COM1 and the baud rate 9600 bps). You should
change these parameters according to you specific configuration.
Alternatively, you can develop your own Windows application to communicate with the
PSoC 5LP board via a serial interface. The following section illustrates how to develop
such application in C# .NET using Microsoft Visual Studio 2013 IDE.

Developing a C# control application for the PSoC 5LP
Prototyping Kit

This project describes a Windows GUI application which can send commands to the PSoC
5LP Prototyping Kit and receive data from this board via a serial interface (USB-to-UART
device, in our case). We will to build such application in Visual Studio 2013 running in
Windows 10.

We will use the Project Wizard which will create the skeleton of a Windows Forms
application in Visual C# .Net as is shown in Fig.6.

Fig.6

After creating an empty project, we should place several components on the main form of
our application (Fig.7).

Fig.7

Our application will use the following components from the Toolbox palette:
button1 instance of Button;
textBox1 instance of TextBox;
listBox1 instance of ListBox;
serialPort1 instance of SerialPort.

When running, the application can receive data stream from a serial interface (UART) and
output data in the listBox1 window. The data typed in the textBox1 field can be
transferred via UART after pressing button1 titled “Send data via UART”.
The serialPort1 component must be configured as is shown in Fig.8.

Fig.8

As you can see, the serialPort1 component has a lot of properties. However, we need to
configure only a few of them. The BaudRate parameter will be assigned the value of
9600 bps (this is a standard baud rate for most applications with UART). The PortName
parameter should be assigned the name of a serial interface being used for communication
with a PSoC 5LP Kit. In Windows 10 I used a USB-to-UART interface with a virtual
serial port COM7, so this value was assigned to the PortName property.

After serialPort1 has been configured, we should write some source code processing the
data flow via UART. Data received via UART can be processed by the event handler
invoked when the DataReceived event occurs (Fig.9).

Fig.9

After double-clicking on the editing area of the DataReceived field we are brought to the
editor window where we should type the source code (Listing 1) for the DataReceived
event handler.

Listing 1.

private void serialPort1_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
{
String s1 = serialPort1.ReadLine();
listBox1.Items.Add(s1);
}

The first statement within the seriaPort1_DataReceived event handler moves the data
from the UART buffer to the string s1. Then this string is displayed in the listBox1
window.
A serial interface should be opened when the application starts running and closed when
the application terminates. The full source code of our application is shown in Listing 2.

Listing 2.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinForm_UART_TX
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
String s1 = textBox1.Text;
serialPort1.WriteLine(s1);
}

private void Form1_Load(object sender, EventArgs e)
{
if (serialPort1.IsOpen == false)
serialPort1.Open();
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort1.IsOpen == true)
serialPort1.Close();
}

private void serialPort1_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
{
String s1 = serialPort1.ReadLine();
listBox1.Items.Add(s1);
}
}
}

Here the button1_Click() event handler is invoked when the button titled “Send data via
UART” has been pressed. The string typed in the textBox1 single-line editor is saved in
the string s1 which is then transferred via the serial interface using the WriteLine()
procedure.
Data received via the serial interface are processed by the serialPort1_DataReceived
event handler. The data received is stored in the string s1, then the contents of s1 is
displayed in the listBox1 window.

Designing the application with PWM controlled by UART

This is our first demo project being created in PSoC Creator 3.3 IDE. The project shows
how to build the application which can adjust the duty cycle of a PWM signal according to
the command received from UART. The project window of the application is shown in
Fig.10.

Fig.10

The following components from the Component Catalog are used in this design:
UART_1 instance of UART [v2.50];
PWM_1 instance of PWM [v3.30];
Clock_1 instance of Clock [v2.20];
Logic Low ‘0’ [v1.0];
P2_7 instance of Digital Output Pin [v2.10]

The design area with the components is shown in Fig.11.
Fig.11

Our application will operate in the following way. The PWM_1 signal source is clocked
by the Clock_1 component operating at 100 KHz. The Logic Low ‘0’ component
connected to the “reset” input of PWM_1 enables the operation. The frequency
FreqPWM of a PWM signal is determined by the “Period” parameter of the PWM_1
component as

FreqPWM = 100000 Hz / Period

In our application, “Period” is set to 255, so the frequency of a PWM signal will be equal
to 392 Hz. The duty cycle of a PWM signal will be set through the “CMP Value 1”
parameter which is set to 127 (duty cycle = 50%). The output signal appears on the MCU
port P2 [7] associated with the component P2_7. The UART_1 component allows to
adjust the duty cycle of PWM via a serial interface (UART).
To communicate with external circuitry our application should use several MCU pins.
Physical MCU pins are assigned to components using the Design Wide Resources
(DWR) system of the PSoC Creator. The DWR system provides a single location to
manage all the resources in our design. Such resources include pins, clocks, interrupts,
DMA, etc. Each new design project provides a default design-wide resources file (.cydwr)
file with the same name as the project.

DWR becomes accessible after double-clicking the corresponding “.cydwr” file in
Workspace Explorer.
The resources assigned to our project are shown in Fig.12.

Fig.12

As it is seen, the RX line of the UART component is assigned to the MCU port P0[0] and
the TX line is assigned to the MCU port P0[1]. While wiring your circuit, you must
connect port P0[0] of the PSoC 5LP kit to the TX signal line of an external UART. Port
P0[1] must be connected to the RX signal line of the external UART.
An output pulse train will be taken from the MCU port P2[7] associated with the
component P2_7.

Each component of our design can be configured using its configuration dialog box which
can be opened by double-clicking a component. We will start configuring from the
Clock_1 component whose configuration dialog box is shown in Fig.13.

Fig.13

Here “Source” parameter is set to <Auto> and the “Frequency” parameter is set to 100
KHz.
For better precision we set the “Tolerance” parameter to 2%.
The next to configure is the PWM_1 component. Its configuration dialog box is shown in
Fig.14.

Fig.14
There are several parameters to configure. First, we set the suitable value for the “Period”
parameter which determines the period (frequency) of the PWM signal. This value is
written in the period register of PWM_1. In our case, “Period” is assigned the value of
255 that gives us the signal frequency of about 392 Hz.

The “Resolution” parameter is set to 16 – this determines the maximum possible value
(216) which could be written in the period and compare registers of the PWM_1
component. Our application will produce only a single PWM signal, so the parameter
“PWM Mode” is assigned the string “One Output”.
The duty cycle of a PWM signal is determined by the parameter “CMP Value 1” which
affects the compare register of the PWM_1 component. We select the default value of 127
for this parameter. When an application is running, the “CMP Value 1” parameter can be
assigned a new value received from UART.

The Digital Output Pin component configuration dialog box is shown in Fig.15.

Fig.15

We want to use MCU port P2[7] to produce a pulse train, so it is reasonable to assign the
string “P2_7” to the “Name” parameter. The “Drive mode” parameter should be left
“Strong drive” (by default).
Finally, we need to configure the UART_1 component whose configuration dialog box is
shown in Fig.16.

Fig.16

The dialog box opens the “Configure” tab by default, where we should adjust only the
speed (parameter “Bits per second”) – it must be the same as that used by an external
UART. In our case, we select the common value of 9600 bps. Then we move to the
“Advanced” tab (Fig.17).

Fig.17

We will not use any interrupts, so all controls associated with the “Interrupt sources”
parameter must be cleared.

After configuring the components is complete, we should build our application. If no
errors occurs, the project wizard generates all necessary files and references. To make our
application workable, we have to modify the source code in the main.c file.

The contents of the modified main.c file is shown in Listing 3.

Listing 3.

#include <project.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

uint8 rxState;
char buf[32];
char *pbuf = buf;
int cmp;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
PWM_1_Start();
for(;;)
{
/* Place your application code here. */
pbuf = buf;
UART_1_PutString(“ENTER A DUTY CYCLE (5-95, MAX 2 DIGITS):”);
UART_1_PutCRLF(0xD);
UART_1_ClearRxBuffer();
memset(buf, 0, strlen(buf));
while (1)
{
if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)
{
*pbuf = UART_1_GetChar();
if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))
{
UART_1_ClearRxBuffer();
break;
}
UART_1_PutChar(*pbuf);
UART_1_ClearRxBuffer();
pbuf++;
}
}
UART_1_PutCRLF(0xD);
CyDelay(500);

cmp = atoi(buf);
if ((cmp >= 5) && (cmp <= 95))
PWM_1_WriteCompare((int)(cmp*2.56));
}
}

At the beginning of the source code we place a few “include” directives so that to get an
access to useful C library functions:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

The rxState variable holds the state of the RX FIFO buffer. The array buf[32] of 32 bytes
long holds the characters received via a serial interface. The variable pbuf points to the
first element of the buf array and the cmp variable of int type will hold the value which
will be assigned to the “CMP Value 1” parameter.
First what is needed after an application has been launched is to start the components. The
following fragment of code does that:

UART_1_Start();
Timer_1_Start();
timerISR_1_Start();

The program code waits for the bytes from the serial interface in a while(1) loop. Within
this loop, the statement

if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)

checks whether a new data is put in the FIFO buffer. If yes, this byte is retrieved in the buf
array by the statement

*pbuf = UART_1_GetChar();

The bytes just placed in the buffer are checked by the statement

if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))

If either of two conditions is true, the while(1) loop terminates and the character string
being held in the buf array is converted into an integer value (variable cmp):

cmp = atoi(buf);

Then the value of a new duty cycle is written in the compare register 1 of the PWM_1
component by the statement:

if ((cmp >= 5) && (cmp <= 95))
PWM_1_WriteCompare((int)(cmp*2.56));

If both conditions in the if() statement

if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))

are false, then the following sequence is executed:

UART_1_PutChar(*pbuf);
UART_1_ClearRxBuffer();
pbuf++;

In this sequence, the byte just received is immediately echoed to a terminal application via
UART, a FIFO buffer is cleared and the pointer pbuf is advanced to the next element of
the array buf.
To test our application we should connect PSoC 5LP Prototyping Kit to the PC, then
connect TX and RX lines on the PSoC 5LP board to the external serial interface and
launch some terminal application on the PC, for example, puTTY.
By entering different values for the compare register 1 of PWM_1, we can change the
duty cycle of the output signal on MCU port P2[7]. The terminal window of a puTTY
terminal application is shown in Fig.18.

Fig.18

The C# application described earlier can also be used to control the duty cycle of an
output signal on port P2[7] (Fig.19).

Fig.19

Designing the application with the timer controlled by
UART

This project illustrates how to design the application which can configure the frequency of
a digital pulse train provided by a timer through a serial interface (UART). The project
window in PSoC Creator 3.3 is shown in Fig.20.

Fig.20

The following components from the Component Catalog are used in this design:
UART_1 instance of the component UART [v2.50];
Timer_1 instance of the component Timer [v2.70] with the timer_clock instance of
Clock [v2.20];
timerISR_1 instance of the component Interrupt [v1.70];
P2_7 instance of the component Digital Output Pin [v2.10]

The design area with components is shown in Fig.21.

Fig.21

In this application, the Timer_1 timer is clocked by the timer_clock component which
provides a synchronization signal at 1 MHz. The output frequency on port P2[7] can be
configured through the “Period” parameter of the Timer_1 component. When enabled,
Timer_1 counts up until overflow occurs, then a counter resets and counting continues
from zero. In our application, the event of overflowing will trigger an interrupt associated
with the timerISR_1 component which is connected to the “interrupt” output of the
Timer_1 timer.

Each time when Timer_1 overflows, the interrupt is triggered and the associated Interrupt
Service Routine (ISR) is invoked. In our case, ISR will simply toggle the port P2[7]. Note
that the P2_7 component have no connections to other components – that is reflected in
the P2_7 configuration page.

To communicate with external circuitry our application should use several MCU pins, so
we should enter the Design Wide Resources (DWR) dialog box to make assignments. To
access DWR open the “.cydwr” file and configure pins as is shown in Fig.22.

Fig.22

In this configuration, the RX_1 pin of the UART component is assigned to the MCU port
P0[0] and the TX_1 pin is assigned to port P0[1]. While wiring, the MCU port P0[0] must
be connected to the pin “TX” or “TxD” of an external serial interface. The MCU port
P0[1] must be wired to the pin “RX” or “RxD” of the external serial interface. Note that
signal levels on ports P0[0] and P0[1] of the PSoC 5LP Prototyping Kit must be the same
as those of your external serial interface! An output pulse train is taken from the MCU
port P2[7].

Each component from our design can be configured using a configuration dialog box. The
box is open after double-clicking the component.
The timer_clock configuration dialog box is shown in Fig.23.

Fig.23

The “Source” parameter is assigned <Auto> and the Frequency parameter is assigned the
value of 1 MHz.
To reach a better precision the “Tolerance” parameter is set to 2%.
The next to configure is the Timer_1 component whose configuration dialog box is shown
in Fig.24.

Fig.24

There are several parameters to be set. We need to select the default value of the “Period”
parameter – this will determine the period (frequency) of Timer_1 when an application is
launched. While the application is running, the “Period” parameter can be assigned a new
value received from UART.
The value of “Period” can be calculated as is illustrated in the simple example. Assume
that the Timer_1 frequency should be set to 2 KHz. Since the Timer_1 component is
clocked by a 2 MHz pulse train, the value of Period will be calculated as

Period = 1 MHz / 2 KHz = 500

One more parameter to configure is “Interrupts”. When the Timer_1 timer has
overflowed, the interrupt has to be triggered, therefore we must enable “On TC”.
The “Interrupt” component is left unchanged except the name which has been changed to
timerISR_1 (Fig.25).

Fig.25


We should also configure the Digital Output Pin component whose property page is
shown in Fig.26.


Fig.26

We will use the MCU port P2[7] to produce pulse train, so it is reasonable to assign the
value “P2_7” to the Name parameter. The “Drive mode” parameter should be “Strong
drive” (assigned by default). The parameter “HW Connection” should be cleared because
the pin will not be driven by external signals.
The “Configure” tab of the configuration dialog box of the UART_1 component is shown
in Fig.27.

Fig.27


Only the parameter “Bits per second” should be set to the common value of 9600 bps
(this must be the same as that of the external UART device). The “Advanced” tab of the
dialog box looks like the following (Fig.28).

Fig.28

The UART_1 component will not use interrupts, so all check boxes associated with the
“Interrupt sources” parameter should be unchecked.

After configuring the components is complete, we can build the application. When done, a
number of source files has been created. However, our application will not work properly
until we modify the source code in the main.c file. The modified version of main.c is
shown in Listing 4.

Listing 4.

#include <project.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

uint8 rxState;
char buf[32];
char *pbuf = buf;

int period;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
UART_1_ClearRxBuffer();
Timer_1_Start();
timerISR_1_Start();
for(;;)
{
pbuf = buf;
UART_1_PutString(“ENTER A PERIOD (200-1200, MAX 4 DIGITS):”);
UART_1_PutCRLF(0xD);
UART_1_ClearRxBuffer();
memset(buf, 0, strlen(buf));
while (1)
{
if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)
{
*pbuf = UART_1_GetChar();
if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))
{
UART_1_ClearRxBuffer();
break;
}
UART_1_PutChar(*pbuf);
UART_1_ClearRxBuffer();
pbuf++;
}
}
UART_1_PutCRLF(0xD);
CyDelay(500);

period = atoi(buf);
period = period / 2; // since the frequency on pin P2.7 is half that of the timer
if ((period >= 100) && (period <= 600))
Timer_1_WritePeriod(period);
}
}

At the beginning of the source code should be placed a few include directives allowing
the program code to get access to several library functions:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


The rxState variable holds the state of the RX FIFO buffer. The array buf of 32 bytes long
will hold the characters received via a serial interface and the variable pbuf points to the
first element of the buf array. The new value which will be written to the period register is
held in the period variable of int type.
First what is needed when an application is launched is to start the components. The
following sequence performs that:

UART_1_Start();
Timer_1_Start();
timerISR_1_Start();

The program code waits for the bytes from a serial interface running the while(1) loop.
Within this loop, the statement

if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)

checks whether a new byte is put in the FIFO buffer. If yes, this byte is retrieved in the buf
array by the statement

*pbuf = UART_1_GetChar();

The byte just placed in the buffer is checked by the statement

if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))

If either of two conditions is true, the while(1) loop terminates and the character string
containing the buf array is converted into an integer value (variable period) using an
atoi() function:

period = atoi(buf);

Then the new period value after dividing by 2 is written to the period register of the
Timer_1 timer by the sequence:

period = period / 2;
if ((period >= 100) && (period <= 600))
Timer_1_WritePeriod(period);

If both conditions in the statement

if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))

are false, then the following sequence is executed:

UART_1_PutChar(*pbuf);
UART_1_ClearRxBuffer();
pbuf++;

In this sequence, the byte just received is immediately echoed to a terminal application via
UART, the FIFO buffer is cleared and the pointer pbuf is advanced to the next element of
the buf array.

Besides the main.c file we must modify the ISR procedure responsible for toggling pin P2
[7]. Interrupts generate a number of API functions that are documented in the Interrupt
component datasheet. The API files are located in the Workspace Explorer→
Generated_Source directory under the Source tab. Along with the usual Start/Stop
functions, and those for managing the vector and priority of the ISR, is the handler code.

This handler is defined in the interrupt source file and it contains tags for protecting your
code between builds. Source code surrounded by the `#START name` and `#END` text
will not be over-written by the build process. Below is the template for writing your
Interrupt Service Routine:

CY_ISR(myInterrupt)
{
/* Place your Interrupt code here. */
/* `#START tick_100ms_Interrupt` */
/* `#END` */
}

Note that the CY_ISR() macro is used to define the function name and arguments. This
macro handles the compiler-specific C language extensions for interrupt functions and
ensures that the code remains portable to different PSoC architectures.
Let’s go to practice. Once we need to develop our own ISR procedure for toggling the
P2[7] port, we should find out the file timerISR_1.c first. This file contains the template
source code of the ISR procedure of the timerISR_1 interrupt. We must insert a few lines
at the beginning of timerISR_1.c as is shown in Listing 5.

Listing 5.

/*************************************************************************
* Place your includes, defines and code here
**************************************************************************/
/* `#START timerISR_1_intc` */
#include “P2_7.h”
#include “Timer_1.h”
uint8 p27Read;
uint8 tState;
/* `#END` */

Our ISR procedure must have access to pin P2[7] (component P2_7) and timer Timer_1,
so the corresponding header files, P2_7.h and Timer_1.h, should be added in the section
of definitions of the file timerISR_1.c. The variable p27Read is used for toggling port
P2[7]. The variable tState is used for clearing interrupt flag by reading the Timer_1 status
register.

Then we must find the section where the template source code of the ISR procedure
resides and insert a few lines of the source code (Listing 6).

Listing 6.

/***************************************************************************
* Function Name: timerISR_1_Interrupt
****************************************************************************
*
* Summary:
* The default Interrupt Service Routine for timerISR_1.
*
* Add custom code between the coments to keep the next version of this file
* from over writting your code.
*
* Parameters:
*
* Return:
* None
*
***************************************************************************/
CY_ISR(timerISR_1_Interrupt)
{
#ifdef timerISR_1_INTERRUPT_INTERRUPT_CALLBACK
timerISR_1_Interrupt_InterruptCallback();
#endif /* timerISR_1_INTERRUPT_INTERRUPT_CALLBACK */

/* Place your Interrupt code here. */
/* `#START timerISR_1_Interrupt` */
tState = Timer_1_ReadStatusRegister();
p27Read = P2_7_Read();
p27Read =~ p27Read;
P2_7_Write(p27Read);
/* `#END` */
}

In this section, the statement

tState = Timer_1_ReadStatusRegister();

simply reads the Timer_1 status register thus clearing the TC bit – this allows to avoid
triggering the same interrupt one more.
The next sequence allows to toggle the MCU port P2[7]:

p27Read = P2_7_Read();
p27Read =~ p27Read;
P2_7_Write(p27Read);

To test our application we should connect the PSoC 5LP Prototyping Kit to the PC, then
connect “TX” and “RX” lines of PSoC 5LP to the external serial interface and launch
some terminal application, for example, puTTY.
By entering the different values for the Timer_1 period, we can adjust the output
frequency on port P2 [7]. The terminal window of puTTY is shown in Fig.29.

Fig.29

The C# application which controls the output frequency on port P2[7] produces the
following output (Fig.30).

Fig.30

Processing signals from mechanical switches

Reading digital sensors with PSoC 5LP is implemented using a Digital Input Pin
components. Often an application needs to process input signals provided by mechanical
switches which tend to make and break connections for a finite time before a stable state is
reached. Within this settling time, the digital circuit can see multiple transitions as the
switch contacts bounce between make or break conditions.

To resolve this problem a developer can use a Debouncer component that takes an input
signal from a bouncing contact and generates a clean output for digital circuits. The
component will not pass the signal to the output until the predetermined period of time
when the switch bouncing settles down. In this way, the circuit will respond to only one
pulse generation performed by the pressing or releasing of the switch and not several state
transitions caused by contact bouncing.
The following demo project illustrates processing a signal from a mechanical switch. The
application will count the number of falling edges of the PSoC 5LP on-board switch wired
to the MCU port P2[2]. After the switch on P2[2] has been pressed 3 times, the on-board
LED wired to port P2 [1] is toggled.
The project window in PSoC Creator 3.3 will look like the following (Fig.31).

Fig.31

The design area with components is shown in Fig.32.

Fig.32

The following components from the Component Catalog are used in this design:
SW_P22 instance of Digital Input Pin [v2.10];
LED_P21 instance of Digital Output Pin [v2.10];
Counter_1 instance of Counter [v3.0];
cntINT instance of Interrupt [v1.70];
Debouncer_1 instance of Debouncer [v1.0];
Clock_1 and Clock_2 instances of Clock [v2.20].

The DWR page is shown in Fig.33

Fig.33

This application operates in the following way. First, the signal from pin SW_P22 is
processed by the Debouncer_1 component. Clock_1 is used to sample the “d” input. The
frequency depends on anticipated transition time of the signal. This must be high enough
that the system is responsive, but low enough to sample only once during an input
transition. In our case, the Clock_1 frequency is set to 100 Hz that is between 10 and 200
Hz as recommended. The output signal appears on the “neg” output that goes high one
clock cycle after a negative edge is detected and stays high for one clock cycle.
The configuration dialog box of the Debouncer_1 component is shown in Fig.34.

Fig.34

The “Edge type” parameter must be assigned “Negative edge” only. The “Signal width
(bits)” parameter should be left 1 (by default).

The Counter_1 configuration dialog box includes 3 tabs. We deal only with the
“Configure” and “Advanced” tabs. The “Configure” tab looks like the following
(Fig.35).

Fig.35

Here we select the UDB implementation with the resolution of 8. Counter_1 picks up 3
events triggered by the SW_P22 falling edges, therefore the “Period” and “Compare
Value” parameters are set to 3. The “Compare Mode” parameter is assigned “Equal To”.
Counter_1 will count up, so the “Clock Mode” parameter is assigned the value “Up
Counter”.
The “Advanced” tab of the Counter_1 dialog box is shown in Fig.36.

Fig.36

Here the “Run Mode” property must be left “Continuous” (assigned by default). We need
to reload Counter_1 after 3 ticks have passed, therefore the “Reload Counter” parameter
is assigned both “On Compare” and “On Reset”. Toggling the LED_P21 pin is
performed after an interrupt has been triggered. The interrupt is activated when the value
in the counter register of Counter_1 becomes equal to the value written in the period
register (3, in our case). For this reason, the “Interrupt” parameter is assigned “On
Compare”.
The SW_P22 component is configured as the “Resistive pull up” stage (parameter
“Drive mode”, Fig.37).


Fig.37

LED_P21 and cntINT components use the parameters assigned by default.
After configuring components is complete, we should build an application. To make our
application workable we have to modify several source files generated during compilation.
The contents of the main.c file will look like the following (Listing 7).

Listing 7.

#include <project.h>

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
LED_P21_Write(0x0);
cntINT_Start();
Counter_1_Start();
for(;;)
{
/* Place your application code here. */
}
}

Only 3 statements added to the source code generated by the compiler. The statement

LED_P21_Write(0x0);

allows to drive LED on the MCU port P2 [1] OFF before pressing the switch SW_P22.
The statement that follows enables the interrupt associated with Counter_1:

cntINT_Start();

When done, the application forces the counter Counter_1 to run by the statement:

Counter_1_Start();

Besides editing main.c, we also need to configure the Interrupt Service Routine (ISR)
associated with the interrupt cntINT. We must put the source code of ISR in the file
cntINT.c already created for us by the compiler. There are two sections in cntINT that
should be changed.
The first section is located at the beginning of the cntINT.c. After adding a few definitions
the contents of this fragment appears as follows (Listing 8).

Listing 8.

/******************************************************************************
* Place your includes, defines and code here
******************************************************************************
/* `#START cntINT_intc` */
#include “Counter_1.h”
#include “LED_P21.h”

uint8 p21State;
uint8 cntState;
/* `#END` */

Our ISR procedure will deal with Counter_1 and LED_P21 components, so we should
make the references to the corresponding header files (.h) by using the include directives.
Two variables, p21State and cntState, are used in the ISR procedure while reading the
states of the MCU registers.

The second section in the cntINT.c file to be altered comprises the template source code
of the ISR procedure. After modifications has been made to this section, the source code
of the Interrupt Service Routine will look like the following (Listing 9).

Listing 9.

/******************************************************************************
* Function Name: cntINT_Interrupt
******************************************************************************
*
* Summary:
* The default Interrupt Service Routine for cntINT.
*
* Add custom code between the coments to keep the next version of this file
* from over writting your code.
*
* Parameters:
*
* Return:
* None
*
******************************************************************************
CY_ISR(cntINT_Interrupt)
{
#ifdef cntINT_INTERRUPT_INTERRUPT_CALLBACK
cntINT_Interrupt_InterruptCallback();
#endif /* cntINT_INTERRUPT_INTERRUPT_CALLBACK */

/* Place your Interrupt code here. */
/* `#START cntINT_Interrupt` */
cntState = Counter_1_ReadStatusRegister();
p21State = LED_P21_Read();
p21State = ~p21State;
LED_P21_Write(p21State);
/* `#END` */
}

Four statements have been added to the template source code of ISR. The first statement
clears the interrupt flag by reading the status register:

cntState = Counter_1_ReadStatusRegister();

This statement is needed because the interrupt source Counter_1 is clear-on-read and
requires the status register to be read (cleared), otherwise the ISR will continue to remain
in pending state.
The following sequence allows to toggle LED on port P2[1]:

p21State = LED_P21_Read();
p21State = ~p21State;
LED_P21_Write(p21State);

Processing several digital inputs at a time

This project illustrates the design of the application that can process several digital inputs
at a time. This could be useful when an application should check several digital sensors
simultaneously and perform different tasks depending on the state of the group of sensors.
The application will read three MCU ports, P2[3] – P2[5], configured as inputs and
transfer the binary code obtained via a serial interface. When the binary code reaches 7,
the program code drives the LED connected to port P2[1] ON. The binary code different
from 7 causes the LED to be driven OFF.
The project window in PSoC Creator 3.3 is shown in Fig.38.

Fig.38

The design area with the components is shown in Fig.39.

Fig.39

The following components from the Component Catalog are used in this design:
SReg_1 instance of Status Register [v1.90];
Timer_1 instance of Timer [v2.70];
P2_3, P2_4 and P2_5 instances of Digital Input Pin [v2.10];
P21_LED instance of Digital Output Pin [v2.10];
UART_1 instance of UART [v2.50];
timerINT instance of Interrupt [v1.70].

Our application will read the state of ports P2[3] – P2[5] via the SReg_1 register every 5
seconds (this interval is determined by the Timer_1 timer). Timer_1 is configured so that
it activates an interrupt after the 5 s interval has passed. The Interrupt Service Routine
(ISR) associated with the Timer_1 interrupt then toggles the LED on port P2[1]. The state
of the digital inputs P2[3] – P2[5] will be transferred to some terminal application via a
serial interface associated with the UART_1 component.
The configuration dialog box of the SReg_1 component is shown in Fig.40.

Fig.40

We will process only 3 inputs, therefore the “Inputs” parameter is set to 3. The “Mode”
parameter must be assigned “Transparent”.

Next to configure is Timer_1. Its configuration dialog box is shown in Fig.41

Fig.41

We select the “UDB” implementation with the resolution of 32 bit. The “Period”
parameter is set to 120000000 that corresponds to 5 s. The “Run Mode” parameter is
assigned “Continuous”. When Timer_1 overflows, it should trigger an interrupt, so the
“Interrupts” parameter is assigned “On TC”. Other parameters are left unchanged.

The component UART_1 must be configured as is shown in Fig.42.

Fig.42

Our application will only transfer data so the “Mode” parameter is assigned “TX only”.
One more parameter to configure is “Bits per second” – we select the common value of
9600 bps.
Our project also includes three digital input pins. All pins have the same settings such as
those shown in the dialog box of the P2_3 component (Fig.43).

Fig.43
The only parameter to configure is “Drive mode” that is assigned “Resistive pull up”. In
this project, the “Name” parameter is assigned “P2_3”.
The timerINT component hasn’t been configured, except the “Name” parameter which is
assigned the “timerINT” value.
The last to consider is the P21_LED component whose configuration dialog box is shown
in Fig.44.

Fig.44

Here the parameter “HW connection” should be cleared and “Drive mode” should be
assigned “Strong drive”.
The DWR page of the project is shown in Fig.45.

Fig.45

Here the components P2_3 – P2_5 are assigned the MCU ports P2[3] – P2[5],
respectively; the P21_LED component is assigned the MCU port P2[1] and the Tx_1
component relating to UART_1 is assigned the MCU port P0[1].
After configuring all components we can build our application. When done, there will be a
number of the source files generated by the compiler. To make our application workable,
we must modify the source code in the main.c file as is shown in Listing 10.

Listing 10.

#include <project.h>
#include <stdio.h>

char buf[128];

int bRead;
uint32 cnt;

uint8 pinsState, done = 0;
uint8 tState;

CY_ISR(timerISR)
{
tState = Timer_1_ReadStatusRegister();
pinsState = SReg_1_Read() & 0x7;
if (pinsState == 0x7)
P21_LED_Write(0x1);
else
P21_LED_Write(0x0);
done = 1;
}

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */

UART_1_Start();
timerINT_StartEx(timerISR);
Timer_1_Start();
for(;;)
{
/* Place your application code here. */
if (done == 1)
{
memset(buf, 0, sizeof(buf));
sprintf(buf, “P2.3 - P2.5 input: 0x%X”, pinsState);
UART_1_PutString(buf);
UART_1_PutCRLF(0xD);
done = 0;
}
}
}

This source code uses another programming technique to process the Timer_1 interrupt.
Here we use the template function which looks like ISR_StartEx(cyisraddress address).
In this application, this function appears as timerINT_StartEx(timerISR). The
timerINT_StartEx(timerISR) function allow to set up the Timer_1 interrupt and enable
it.
More detail, this function disables the interrupt, sets the interrupt vector based on the
address passed, sets the priority from the value in the DWR Interrupt Editor, then
enables the interrupt in the interrupt controller.
The CY_ISR(timerISR) Interrupt Service Routine moves the binary code read from the
MCU ports P2[3] – P2[5] to the pinsState variable by the statement

pinsState = SReg_1_Read() & 0x7;

and toggles port P2[7] when pinsState holds 7:

if (pinsState == 0x7)
P21_LED_Write(0x1);
else
P21_LED_Write(0x0);

The done variable is used for synchronizing the interrupt and the program code being
executed by the main() procedure.
The library function

sprintf(buf, “P2.3 - P2.5 input: 0x%X”, pinsState);

forms the character string to be sent via a serial interface. The data is transferred via
UART using the sequence:

UART_1_PutString(buf);
UART_1_PutCRLF(0xD);

The terminal window of puTTY produces the following output (Fig.46).

Fig.46
The sine wave synthesizer using a timer and digital-to-
analog converter

This project allows to build the application producing a sine wave signal on the MCU port
P2[7]. The project window in PSoC Creator 3.3 is shown in Fig.47.

Fig.47

The following components from the Component Catalog are used in this design:
VDAC8_1 instance of Voltage DAC (8-bit) [v1.90];
Timer_1 instance of Timer [v2.70];
BUS_CLK instance of Clock [v2.20];
P2_7 instance of Analog Pin [v2.10];
timerISR_1 instance of Interrupt [v1.70];
Logic Low ‘0’ [v1.0].

The design area itself with the components is shown in Fig.48.

Fig.48

The application will operate in the following way. The Timer_1 timer operating at a few
tens KHz triggers an interrupt each time when a counter reaches zero. The interrupt
associated with the timerISR_1 component passes the control to an Interrupt Service
Routine (ISR) which writes a data byte from a Look-Up Table (LUT) to the digital-to-
analog converter (DAC) that uses the VDAC8_1 component.
The LUT contains 28 = 256 values representing a single period of a sine wave signal. The
LUT can easily be build using either the sin() or cos() library function contained in the
math library.
Let’s see on how components are configured.
The configuration dialog box of the VDAC8_1 component is shown in Fig.49.

Fig.49

Two parameters are configured here. The “Range” parameter determines the voltage
swing (amplitude) of the signal on the VDAC8_1 output. In this project, the amplitude is
selected to be in the range of 0 – 1.020V. Also we select the “High Speed” value for the
“Speed” parameter. Other parameters may be left unchanged.
The Timer_1 parameters are shown in Fig.50.

Fig.50

Here we set the resolution (parameter “Resolution”) to 16 bit although 8 bit is also
suitable. The “Period” parameter is set to 80 – this gives the frequency of Timer_1 equal
to 24 MHz / 80 = 300 KHz. In this application, this is a maximum value which allows to
obtain a quality sine wave signal. We will use the Timer_1 interrupt, so the “On TC” flag
of the “Interrupts” parameter should be checked. The Clock component (BUS_CLK)
that provides 24 MHz clocking for Timer_1 is left unchanged.
The timerISR_1 interrupt dialog box is shown in Fig.51.

Fig.51

The only parameter to be changed (if needed) is “Name” which, in our case, is assigned
“timerISR_1”.
The last to configure is the P2_7 component whose dialog box is shown in Fig.52.

Fig.52

Here the “Name” parameters is assigned “P2_7”. The “Drive mode” parameter should be
left “High impedance analog” (assigned by default).
The DWR page for our application looks like the following (Fig.53).

Fig.53

As it is seen, our application will use a single MCU port P2[7] associated with the P2_7
component.
After configuring components is complete, we should build the application. When it is
done, we need to modify the source code in the main.c file. The contents of main.c is
shown in Listing 11.

Listing 11.

#include <project.h>
#include <math.h>

int NUM_SMPL = 256;
unsigned char SINE_TABLE[256];
uint8 tFlag;
int i,cnt = 0;

CY_ISR(timerISR_1_Routine)
{
tFlag = Timer_1_ReadStatusRegister();
if(cnt > 255)
cnt = 0;
VDAC8_1_SetValue(SINE_TABLE[cnt]);
cnt++;
}


void FillBuf()
{
//sine period is 2*PI
float step = (2*M_PI)/(float)NUM_SMPL;
float s;

// calculationa are in radians
for(i = 0;i < NUM_SMPL;i++)
{
s = sinf(i*step);
SINE_TABLE[i] = (unsigned char) (128.0 + (s*127.0));
}
}

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */
FillBuf();
timerISR_1_StartEx(timerISR_1_Routine);
/* Place your initialization/startup code here (e.g. MyInst_Start()) */
VDAC8_1_Start();
Timer_1_Start();

for(;;)
{
/* Place your application code here. */
}
}

In this source code, the function FillBuf() generates the Look-Up Table for a sine wave.
The LUT values are held in the SINE_TABLE array of 256 bytes long. The Timer_1
interrupt is configured using the timerISR_1_StartEx() function which takes the address
of the Interrupt Service Routine timerISR_1_Routine as the parameter.
The ISR procedure timerISR_1_Routine when invoked writes a single byte from the LUT
to the DAC VDAC8_1 by the statement:

VDAC8_1_SetValue(SINE_TABLE[cnt]);

Then the index cnt is incremented thus preparing the next element of the SINE_TABLE
array to be processed the next time when the interrupt is activated. When cnt reaches 256,
it is assigned 0 and the new period of a sine wave signal begins.

Once we want to use functions from the math library, we need to properly configure a
linker (Fig.54). Select “Build Settings…” from the “Project” menu, then select a
“General” option below a “Linker” and simply type ‘m’ in the “Additional Libraries”
field. This forces the linker to seek additional libraries beginning with ‘m’ (including
“math”).

Fig.54


Measuring analog signals with a SAR analog-to-digital
converter

This project allows to build the application capable of measuring analog signals using a
SAR analog-to-digital converter of the PSoC 5LP Prototyping Kit. The analog voltage is
applied to the MCU port P2[7] configured as analog input. The result is then transferred to
the terminal application via a serial interface (UART).
The project window in PSoC Creator 3.3 is shown in Fig.55.

Fig.55

The design area with components is shown below (Fig.56).

Fig.56

The following components from the Component Catalog are used in this design:
ADC_SAR_1 instance of SAR ADC [v3.0];
UART_1 instance of UART [v2.50];
P2_7 instance of Analog Pin [v2.10].

The DWR page for this project is shown in Fig.57.

Fig.57

As it is seen, the pins Rx_1 and Tx_1 of the UART_1 component are assigned to MCU
ports P0[0] and P0[1], respectively. The analog pin component P2_7 is assigned the MCU
port P2[7]. There is also pin \ADC_SAR_1:Bypass\ which is internally connected to the
reference voltage assigned automatically to the MCU port P0[2].

Let’s look at the configuration dialog box of the ADC_SAR_1 component (Fig.58).

Fig.58

Only a couple of parameters needs to be configured. First, we select the “Internal Vref,
bypassed” value for the “Reference” parameter. With this reference (1.024V), we can
operate with input signals ranging from 0 through 2.048 V. This reference voltage will be
taken from port P0[2] (see DWR, Fig.57). The second parameter to be configured is
“Sample mode”. We select the “Software trigger” value for this parameter – this will
allow us to start A/D conversion programmatically. The “Resolution” parameter is set to
12.

The UART_1 configuration dialog box is shown in Fig.59.

Fig.59

Our UART_1 component will only send data to the terminal application via a serial
interface, so we can use TX line only. Therefore, the “Mode” parameter is set “TX only”.
The parameter “Bits per second” is assigned the value 9600 bps.
The P2_7 configuration dialog box is shown in Fig.60.

Fig.60

All parameters are left unchanged, except “Name” which is assigned “P2_7”. After
building the application we should change the source code in the main.c file as is shown in
Listing 12.

Listing 12.

#include <project.h>
#include <stdlib.h>
#include <stdio.h>

uint8 adcState;
int16 adcRes;
int16 resmV;
char buf[64];
int len;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
ADC_SAR_1_Start();
UART_1_Start();
CyDelay(100);
for(;;)
{
/* Place your application code here. */
memset(buf, 0, sizeof(buf));
ADC_SAR_1_StartConvert();
while((adcState =
ADC_SAR_1_IsEndConversion(ADC_SAR_1_WAIT_FOR_RESULT)) == 0);
adcRes = ADC_SAR_1_GetResult16();
resmV = ADC_SAR_1_CountsTo_mVolts(adcRes);
ADC_SAR_1_StopConvert();

len = sprintf(buf,“The voltage level on pin P2.7, mV: %d”, resmV);
UART_1_PutString(buf);
UART_1_PutCRLF(0xD);
CyDelay(7000);
}
}

The source code is simple and straightforward. We start ADC with the

ADC_SAR_1_StartConvert();

statement. A/D conversion takes some time, so the program must wait until the conversion
is complete. This is accomplished by the following statement:

while((adcState =
ADC_SAR_1_IsEndConversion(ADC_SAR_1_WAIT_FOR_RESULT)) == 0);

Then the result in the binary format is saved in the adcRes variable. We need to convert
the binary code into the suitable format, for example, in millivolts. This is done by the
following statement:

resmV = ADC_SAR_1_CountsTo_mVolts(adcRes);

Then we can stop the conversion by invoking

ADC_SAR_1_StopConvert();

The result is then converted into the string and is sent to a terminal application via a serial
interface.

For example, the puTTY terminal application produces the following output (Fig.61).

Fig.61

The C# application produces the following output (Fig.62).

Fig.62


High-precision measurements of analog signals with a Delta-
Sigma A/D converter

This project illustrates the design of the simple measurement system that uses a high-
precision Delta-Sigma analog-to-digital converter for measuring analog signals on the
MCU port P2[7]. The result is then transferred to a terminal application via a serial
interface.
The project window in PSoC Creator 3.3 is shown in Fig.63.

Fig.63

The design area with components is shown in Fig.64.

Fig.64

The following components from the Component Catalog are used in this design:
ADC_DelSig_1 instance of Delta Sigma ADC [v3.20];
UART_1 instance of UART [v2.50];
P2_7 instance of Analog Pin [v2.10].

The DWR windows of the project is shown in Fig.65.

Fig.65

Here the P2_7 Analog Pin component is assigned the MCU pin P2[7]. UART_1 pins
Rx_1 and Tx_1 are assigned MCU pins P0[0] and P0[1], respectively.
To configure the ADC_DelSig_1 component we should open its configuration dialog box
and move to the “Common” tab first (Fig.66).

Fig.66

We want to use the Delta-sigma A/D converter in single ended mode, therefore the
parameter “Input mode” is set to “Single ended”. Other parameters should be left
unchanged. Then we move to the “Config1” tab of the dialog box (Fig.67).

Fig.67

We need to periodically perform a single conversion, so we select “0 – Single Sample” for
the “Conversion mode” parameter. We also need the 16-bit resolution, so the “Resolution
(bits)” parameter is set to 16. One more parameter to set is an “Input range” – it is
assigned “Vssa to 2.048V (0.0 to 2*Vref)”. This parameter depends on the “Reference”
parameter which is assigned “Internal 1.024 Volts”. Other parameters on this tab can be
left unchanged.
Note that the picture in the rightmost side of the dialog box shows the actual range of the
analog input signal that can be processed by the Delta-Sigma A/D converter.
The UART_1 component should transfer data to a terminal application, so only the TX
line will be used. This is reflected in the UART_1 configuration as is shown in Fig.68.
Fig.68

The baud rate for this component (parameter “Bits per second”) is set to the common
value 9600 bps.
The last to consider is pin P2_7 configured as analog input (Fig.69).

Fig.69

We only changed the “Name” parameter to “P2_7”. Note that the “Drive mode”
parameter should be left “High impedance analog”.
After configuring all parameters we should build the application. When it is done, we
should modify the source code of the file main.c so that our application can work
properly.
The contents of the modified file main.c is shown in Listing 13.

Listing 13.

#include <project.h>
#include <stdio.h>

char buf[128];

int32 codeADC;
int16 mV;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
ADC_DelSig_1_Start();
for(;;)
{
/* Place your application code here. */
codeADC = ADC_DelSig_1_Read32();
mV = ADC_DelSig_1_CountsTo_mVolts(codeADC);
memset(buf, 0, sizeof(buf));
sprintf(buf, “The voltage on pin P2.7 of the Delta-Sigma ADC in milliVolts: %d”, mV);
UART_1_PutString(buf);
UART_1_PutCRLF(0xD);
CyDelay(5000);
}
}

In this source code, both components, UART_1 and ADC_DelSig_1, are started by the
sequence:

UART_1_Start();
ADC_DelSig_1_Start();

The function

codeADC = ADC_DelSig_1_Read32();

simplifies getting results from the ADC when only a single reading is required. When
called, it will start ADC conversions, wait for the conversion to be complete, stop ADC
conversion and return the result. This is a blocking function and will not return until the
result is ready. The 32-bit result of a conversion is stored in the codeADC variable.
To convert the binary code held in codeADC into millivolts (mV) we use the function

mV = ADC_DelSig_1_CountsTo_mVolts(codeADC);

The result is saved in the mV variable. Then the sprintf() library function forms the text
string held in the character array buf:

sprintf(buf, “The voltage on pin P2.7 of the Delta-Sigma ADC in milliVolts: %d”, mV);

The

UART_1_PutString(buf);

transfers the string held in buf via UART.
The puTTY terminal application produces the following results (Fig.70).

Fig.70

Processing multiple analog signals using a Delta-Sigma A/D
converter

This project illustrates the design of the data acquisition system with a Delta-Sigma
analog-to-digital converter capable of processing multiple analog input channels. The
results of measurements are transferred to a terminal application via a serial interface
(UART). Our application will process 3 analog channels as is shown in the project
window (Fig.71).

Fig.71

The design area with components placed here is shown in Fig.72.

Fig.72

The following components from the Component Catalog are used in this design:
ADC_DelSig_1 instance of Delta Sigma ADC [v3.20];
UART_1 instance of UART [v2.50];
AMux_1 instance of Analog Mux [v1.80];
P2_3 – P2_5 instances of Analog Pin [v2.10].

Two tabs of the configuration dialog box of the ADC_DelSig_1 component are shown in
Fig.73 – Fig.74.

Fig.73

The “Common” tab is shown in Fig.73. Here we should select “Single ended” for the
“Input mode” parameter. The configuration settings for the “Config1” tab (Fig.74) are
the same as those for the previous project.

Fig.74

The UART_1 configuration dialog box is shown in Fig.75.

Fig.75

Here we select “TX only” for the “Mode” parameter and 9600 bps for “Bits per second”.
The configuration dialog box of the AMux_1 component is shown in Fig.76.

Fig.76

We are going to process 3 analog channels (0 through 2), so the “Channels” parameter is
set to 3. Other parameters can be left unchanged.
All Analog Pin components are identical, so we will show the configuration dialog box
only for P2_3 (Fig.77).

Fig.77

Here the “Drive mode” parameter should be set to “High impedance analog” (assigned
by default).
The DWR configuration for the project shown in Fig.78.

Fig.78

Here the analog pins P2_3 – P2_5 are assigned the MCU ports P2[3] – P2[5],
respectively. The Tx_1 pin of UART_1 is assigned the MCU pin P0[1].

After building the application we should modify the source code in main.c as is shown in
Listing 14.

Listing 14.

#include <project.h>
#include <stdio.h>

char buf[128];

int32 codeADC;
int16 mV;
int ch;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
AMux_1_Start();
ADC_DelSig_1_Start();
while (1)
{
for(ch = 0; ch < 3; ch++)
{
/* Place your application code here. */
AMux_1_Select(ch);
codeADC = ADC_DelSig_1_Read32();
mV = ADC_DelSig_1_CountsTo_mVolts(codeADC);
memset(buf, 0, sizeof(buf));
sprintf(buf, “The voltage on channel %d in milliVolts: %d”, ch, mV);
UART_1_PutString(buf);
UART_1_PutCRLF(0xD);
CyDelay(3000);
}
UART_1_PutCRLF(0xD);
}
}

In this source code, the following sequence

UART_1_Start();
AMux_1_Start();
ADC_DelSig_1_Start();

allows the components UART_1, AMux_1 and ADC_DelSig_1 to start operating.
Reading analog inputs (0 through 2) is performed in the for(ch = 0; ch < 3; ch++) loop.
Only a single channel is selected to read in the current iteration by the statement

AMux_1_Select(ch);

Other source code is the same as that of the previous project.
The puTTY terminal application produces the following output (Fig.79).

Fig.79


Designing the Pulse-Width Modulator using digital and
analog components

This project illustrates how to design the Pulse-Width Modulator using digital and analog
components of PSoC 5LP. Such PWM will be controlled via UART by some terminal
application running on the PC. A project window in PSoC Creator 3.3 IDE is shown in
Fig.80.

Fig.80

The design area with components placed there is shown in Fig.81.

Fig.81

The following components from the Component Catalog are used in this design:
WaveDAC8_1 instance of Waveform DAC (8-bit) [v2.0];
VDAC8_1 instance of Voltage DAC (8-bit) [v1.90];
Comp_1 instance of Comparator [v2.0];
UART_1 instance of UART [v2.50];
Two Not [v1.0] components;
P2_6 – P2_7 instances of Digital Output Pin [v2.10];
Logic Low ‘0’ [v1.0].

The basic idea behind this design is illustrated in Fig.82.

Fig.82

As it is seen from the above figure, a PWM signal can be obtained when a sawtooth or
triangle signal is clipped at some voltage level Vref. The pulse width of a resulting signal
is dependent on Vref so that we can adjust the pulse width in a wide range by altering
Vref. The period T of such signal will not change. This approach is realized in the design
illustrated in Fig.81 – Fig.82.
In this design, the sawtooth signal from the WaveDAC8_1 component and the reference
voltage from the VDAC8_1 component are compared by the comparator Comp_1. The
output signal of Comp_1 will be a pure PWM signal.
Two Not inverters at the Comp_1 output allow to obtain TTL-compatible non-inverted
and inverted signals on the digital output pins P2_7 and P2_6, respectively.

The sawtooth signal provided by the WaveDAC8_1 component should be configured via
the configuration dialog box shown in Fig.83.

Fig.83

Since the “ws” input of the component is wired to log.”0”, WaveDAC8_1 will generate
the Waveform 1 signal on its DAC output. We must set the “Wave type” parameter to
“Sawtooth” for the Waveform 1 leaving other parameter unchanged. The output sawtooth
signal will have the frequency of 1000 Hz (by default).

The reference voltage to the comparator Comp_1 is formed by the VDAC8_1 component
whose configuration dialog box is shown in Fig.84.

Fig.84

Here we set the output voltage of VDAC8_1 to 600 mV – this will be an initial reference
voltage that adjusts the duty cycle of a PWM signal equal to 40%. The “Speed” parameter
is assigned “High Speed”, other parameters can be left unchanged.
The configuration dialog box of the Comp_1 component is shown in Fig.85.

Fig.85

Here we should change a few parameters assigned by default. First, we must enable the
hysteresis by setting the “Hysteresis” parameter to “Enable” – this allows to add
approximately 10 mV of hysteresis to the comparator, so the slowly moving voltages or
slightly noisy voltages will not cause the output of the comparator to oscillate when the
two input voltages are near equal. The “Speed” parameter provides a way for optimizing
speed vs power consumption. In our case, this parameter is set to “Fast”. Our comparator
will not be synchronized by some clock, so the “Sync” parameter is set to “Bypass”.

The UART_1 component configuration dialog box is shown in Fig.86.

Fig.86

Here we need to set only the “Bits per second” parameter to 9600 bps. Then we move to
the “Advanced” tab where we uncheck all interrupt sources.
Both digital output pins P2_6 and P2_7 should have the same parameters except names.
You should only ensure that the “Drive mode” parameter is set to “Strong drive” as is
shown in the configuration dialog box for P2_7 (Fig.87).


Fig.87

The DWR page for this project is shown in Fig.88.

Fig.88

We assign Rx_1 and Tx_1 pins of the UART_1 component to the MCU ports P0[0] and
P0[1], respectively. Digital output pins P2_6 and P2_7 are assigned the MCU ports P2[6]
and P2[7], respectively.

After building the application we should modify the main.c file as is shown in Listing 15.

Listing 15.

#include <project.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

uint8 rxState;
char buf[32];
char *pbuf = buf;
uint8 vdac, duty;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
WaveDAC8_1_Start();
VDAC8_1_Start();
Comp_1_Start();
UART_1_Start();
for(;;)
{
/* Place your application code here. */
pbuf = buf;
UART_1_PutString(“ENTER A DUTY CYCLE (5-95, MAX 2 DIGITS):”);
UART_1_PutCRLF(0xD);
UART_1_ClearRxBuffer();
memset(buf, 0, strlen(buf));
while (1)
{
if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)
{
*pbuf = UART_1_GetChar();
if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))
{
UART_1_ClearRxBuffer();
break;
}
UART_1_PutChar(*pbuf);
UART_1_ClearRxBuffer();
pbuf++;
}
}
UART_1_PutCRLF(0xD);
CyDelay(500);

duty = atoi(buf);
if ((duty >= 5) && (duty <= 90))
{
vdac = (uint8)(255 - (duty * 2.55)); // 4mV/bit in the range from 0 through 1.020V
VDAC8_1_SetValue(vdac);
}
}
}

In this source code, the sequence

WaveDAC8_1_Start();
VDAC8_1_Start();
Comp_1_Start();
UART_1_Start();

allows the components to start operating.

The value of a duty cycle is passed to the application via a serial interface. The byte
stream processed by the UART_1 component is saved in the array buf, then the value of a
duty cycle will be calculated using the atoi() library function and saved in the duty
variable:

duty = atoi(buf);

The if() control statement that follows checks the range of a duty cycle that should be
between 5 and 90. In order to set the new duty cycle we should change an output voltage
provided by the VDAC8_1 component. The statement

vdac = (uint8)(255 - (duty * 2.55));

calculates the binary code corresponding to the new duty cycle. The contents of the
variable vdac can range from 0 to 255. The variable vdac will be the parameter for the
function

VDAC8_1_SetValue(vdac);

which writes the new output voltage to VDAC8_1.

Designing the simple measurement system with an analog
temperature sensor

This project illustrates the design of the simple temperature measurement system which
can process a signal from an analog temperature sensor. The project window in PSoC
Creator 3.3 IDE is shown in Fig.89.

Fig.89

The following components from the Component Catalog are used in this design:
VDAC8_1 instance of Voltage DAC (8-bit) [v1.90];
Comp_1 instance of Comparator [v2.0];
Opamp_1 instance of Opamp [v1.90];
UART_1 instance of UART [v2.50];
P2_6 instance of Digital Input Pin [v2.10];
LED2_1 instance of Digital Output Pin [v2.10].

This application uses a temperature sensor LM35 wired to the MCU port P2[6] associated
with the component P2_6 as is shown in Fig.90.

Fig.90

The LM35 series are precision integrated-circuit temperature sensors, whose output
voltage is linearly proportional to the Celsius temperature with a + 10.0 mV/⁰C scale
factor. For example, at the temperature of 25⁰C the sensor provides the output voltage
Vout as

Vout = 10.0 mV/⁰C x 25⁰C = 0.25V

The design area with all components is shown in Fig.91.

Fig.91

The system operates in the following way. The signal from the temperature sensor LM35
connected to the analog input P2_6 is fed to the non-inverting input of the voltage
comparator (component Comp_1) through the voltage follower (component Opamp_1).
The reference voltage (threshold) is fed to the inverting input of Comp_1. When the
voltage level on the non-inverting input of the comparator Comp_1 exceeds the threshold
on the inverting input, the Comp_1 output goes HIGH and the LED connected to the
digital output pin LED2_1 is driven ON.
If the non-inverting input of Comp_1 becomes less than the threshold on the inverting
input, the LED is driven OFF. The voltage on the non-inverting input of Comp_1 is
linearly proportional to the temperature of environment where a sensor resides, so we get
the system capable of checking temperature. If we need to control temperature using some
cooler driven by a power switch, we can use one more digital output pin that will drive a
power switch built around a bipolar transistor or MOSFET. Such power switch could drive
the DC motor of a cooler or other load.
The threshold can be adjusted via a serial interface (UART) by a terminal application.
Let’s see on configuration dialog box of the VDAC8_1 component (Fig.92).

Fig.92

Three parameters are configured here. The “Range” parameter is assigned the range of 0 –
1.020 V. The initial DAC output is set to 100 mV (parameter “Value”) and the “Speed”
parameter is assigned “High Speed”.

The configuration dialog box of the comparator Comp_1 is shown in Fig.93.

Fig.93

Here we should change a few parameters assigned by default. We must enable the
hysteresis by setting the “Hysteresis” parameter to “Enable” – this allows to add
approximately 10 mV of hysteresis to the comparator, so the slowly moving voltages or
slightly noisy voltages will not cause the output of the comparator to oscillate when the
two input voltages are near equal. The “Speed” parameter provides a way for optimizing
speed vs power consumption. In our case, this parameter is set to “Fast”. Our comparator
will not be synchronized by some clock, so the “Sync” parameter is set to “Bypass”.

The Opamp_1 configuration is shown in Fig.94.

Fig.94

Two parameters should be configured for Opamp_1. The “Mode” parameter should be
assigned “Follower” and the “Power” parameter should be assigned “Low Power”.

The configuration settings of the UART_1 component are shown in Fig.95 – Fig.96.

Fig.95

Fig.96

We need to set the “Bits per second” parameter to 9600 bps on the “Configure” tab and
uncheck all interrupt sources on the “Advanced” tab.

The configuration dialog box of the P2_6 component is shown in Fig.97.

Fig.97

Here the “Drive mode” parameter should be assigned “High impedance analog”.
The LED2_1 component has the following configuration (Fig.98).

Fig.98

Here we should select “Strong drive” for the “Drive mode” parameter.

The DWR page for our application is shown in Fig.99.

Fig.99

Here the LED2_1 component is assigned the MCU port P2[1], P2_6 is assigned the MCU
port P2[6]. Rx_1 and Tx_1 pins of the UART_1 component are assigned MCU ports
P0[0] and P0[1], respectively.
After building an application we get a bunch of files. Off them, we must only modify the
main.c whose source code is shown in Listing 16.

Listing 16.

#include <project.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

uint8 rxState;
char buf[32];
char *pbuf = buf;

int threshold; // threshold in mV

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
UART_1_ClearRxBuffer();
VDAC8_1_Start();
VDAC8_1_SetValue(25); // threshold = 100 mV
Opamp_1_Start();
Comp_1_Start();
for(;;)
{
/* Place your application code here. */
pbuf = buf;
UART_1_PutString(“Enter a threshold in mV (100-800, MAX 3 DIGITS):”);
UART_1_PutCRLF(0xD);
UART_1_ClearRxBuffer();
memset(buf, 0, strlen(buf));
while (1)
{
if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)
{
*pbuf = UART_1_GetChar();
if((isdigit((uint8)*pbuf) == 0) || (*pbuf == ‘\n’))
{
UART_1_ClearRxBuffer();
break;
}
UART_1_PutChar(*pbuf);
UART_1_ClearRxBuffer();
pbuf++;
}
}
UART_1_PutCRLF(0xD);
CyDelay(500);

threshold = atoi(buf);
threshold = threshold / 4; // for the range of 0-1.020V (LSB = 4mV)
if ((threshold >= 25) && (threshold <= 200))
VDAC8_1_SetValue(threshold);
}
}

In this source code, the sequence

UART_1_Start();
UART_1_ClearRxBuffer();
VDAC8_1_Start();
VDAC8_1_SetValue(25);
Opamp_1_Start();
Comp_1_Start();

enables all components and sets the initial value of the threshold to 100 mV that
corresponds to the temperature of 10⁰C.
The new value of the threshold received by PSoC 5LP application via UART is parsing
within the if() statement

if((rxState = UART_1_ReadRxStatus()) == UART_1_RX_STS_FIFO_NOTEMPTY)

Then the string representation of data is converted into the integer value (variable
threshold) suitable for writing into VDAC8_1:

threshold = atoi(buf);

Since the LSB is 4 mV we should divide threshold by 4:

threshold = threshold / 4;

The new value of the DAC output, within the selected range of 25 – 200, will be set by the
statement:

if ((threshold >= 25) && (threshold <= 200))
VDAC8_1_SetValue(threshold);

You can easily modify this project for operating with other types of analog sensors (LDR,
pressure, etc.).
The C# terminal application controlling the PSoC 5LP application produces the following
output (Fig.100).

Fig.100

Designing programmable external circuits with digital
potentiometers

Digital potentiometers may significantly expand capabilities of electronic circuits, as they
can simplify building digitally controlled DC voltage sources and replace variable
resistors. In this section we will discuss how to apply popular MCP41XXX devices
produced by Microchip Corp. Some excerpts from the relevant datasheets will clarify the
matter.

According to the datasheet, the MCP41XXX/42XXX devices are 256 position single and
dual digital potentiometers that can be used in place of standard mechanical
potentiometers. Digital potentiometers have resistance values of 10K, 50K and 100K. In
the following projects we apply a MCP41010 device which has the resistance of 10K.
Each potentiometer is made up of a variable resistor and an 8-bit (28 = 256 position) data
register that determines the wiper position. The nominal wiper resistance equals 52 Ohm
for the 10K version and 125 Ohm for the 50K and 100K versions. The resistance between
the wiper and either of the resistor endpoints varies linearly according to the value stored
in the data register.

The MCP41XXX series provides 256 taps and accept a power supply from 2.7 through
5.5V. In our projects we feed the MCP41010 device with +5V. This also means that LSB
will be equal to 5 / 28 = 0.0195 V. If, for example, application writes the binary code of
100 to the device, the digital potentiometer yields 0.0195 × 100 = 1.95 V on its output.

Digital potentiometer applications usually fall into two categories: those that use a
″rheostat″ mode and applications where a ″potentiometer″ mode is employed. The
″potentiometer″ mode is frequently called a ″voltage divider″ mode. In the ″rheostat″
mode, the potentiometer is used as a two-terminal resistive element. The unused terminal
should be tied to the wiper as shown in Fig.101.
Note that reversing the polarity of the A and B terminals will not affect operation.

Fig.101

Using the device in this mode allows control of the total resistance between the two nodes.
The total measured resistance would be the least at code 0x00, where the wiper is tied to
the B terminal. The resistance at this code is equal to the wiper resistance, typically 52Ω
for the 10 kΩ MCP4X010 devices, 125Ω for the 50 kΩ (MCP4X050), and 100 kΩ
(MCP4X100) devices. For the 10 kΩ device, the LSB size would be 39.0625Ω (assuming
10 kΩ total resistance). The resistance would then increase with this LSB size until the
total measured resistance at code 0xFFh would be 9985.94Ω. The wiper will never be
directly connected to the A terminal of the resistor stack.

In the 0x00 state, the total resistance is the wiper resistance. To avoid damage of the
internal wiper circuitry in this configuration, care should be taken to ensure the current
flow never exceeds 1 mA.

In the ″potentiometer″ mode, all three terminals of the device are tied to different nodes in
the circuit. This allows the potentiometer to output a voltage proportional to the input
voltage (Fig.102). The potentiometer is used to provide a variable voltage V2 by adjusting
the wiper position between two endpoints A and B. The A endpoint is wired to the voltage
source V1 and the B point is tied to the common wire of a circuit (″ground″).Note that
reversing the polarity of the A and B terminals will not affect operation.


Fig.102

For the MCP41XXX devices the output analog voltage V2 appears on the PW line. In the
case of MCP41010, this will be pin 6 (“wiper”). Note that the digital potentiometers
provide the relatively high output impedance, so an additional buffer should be inserted
between the wiper and external circuitry. The buffer is also needed if an external load
draws large currents. The simplest buffer may be arranged as a voltage follower with an
op-amp.

As you can see, the voltage divider is composed of the RWA and RWB resistors. The total
resistance between A and B endpoints is determined by the parameters of a particular
device. MCP41010, for instance, has the full resistance equal to 10K. The resistance of
each “resistor” of this voltage divider may be calculated as follows:

RWA(Dn) = (RAB) × (256 – Dn) / 256 + RW
RWB(Dn) = (RAB) × (Dn) / 256 +RW,

where RWA is a resistance between the terminal PA and the wiper (see Fig.102), RWB is a
resistance between the terminal PB and the wiper, RW is a wiper resistance, RAB is the
overall resistance of the particular device and Dn is a 8–bit binary code that determines the
output voltage of the potentiometer. In our projects we will ignore the wiper resistance
because it is very low, though for very precision circuits this value should be considered.
When the device is powered on, the data registers will be set to mid-scale (0x80).

Let’s look at how to program the MCP41XXX chips in PSoC Creator 3.3. To configure
RWA and RWB resistors we should write the predetermined 16-bit value in the data register
of the digital potentiometer. The timing diagram (Fig.103) illustrates the write operation.

Fig.103

Writing the bit stream into the digital potentiometer is accomplished via SPI-compatible
interface. As you can see, the 16-bit binary code consists of the command and data bytes.
We will assign the value of 0x11 to the command byte – this value will be used in the
following project. The data byte (Dn) will determine of the output voltage of the digital
potentiometer. More details about interfacing MCP41XXX can be found in the datasheet
on the devices.

The write operation begins when the CS signal is pulled high then low. Each data bit of
the bit stream must be brought onto the SI line while the clock signal SCK is kept low.
The data bit is written to the data register on the rising edge of SCK. After all 16 bits have
been passed into the device data register, the CS signal is pulled high thus completing the
operation.
The following project shows the simple digitally programmed DC voltage source with the
MCP41010 device. The project window in PSoC Creator 3.3 IDE is shown in Fig.104.

Fig.104

The design area with components is shown in Fig.105.

Fig.105

The following components from the Component Catalog are used in this design:
SPIM_1 instance of SPI Master Bidirectional Mode macro [v2.50];
Opamp_1 instance of Opamp [v1.90];
SI_P25 instance of Digital Bidirectional Pin [v2.10];
SCK_P26 and CS_P27 instances of Digital Output Pin [v2.10];
DigPot_P24 and Vout_P23 instances of Analog Pin [v2.10].

The DWR page of our project is shown in Fig.106.

Fig.106

The MCU resources used in this project are detailed in the table below.

Component MCU port

Vout_P23 P2[3]

DigPot_P24 P2[4]

SI_P25 P2[5]

SCK_P26 P2[6]

CS_P27 P2[7]


To make our application workable we need to assemble the circuit shown in Fig.105
together with a digital potentiometer MCP41010 (10k). The full circuit diagram of our
application looks like the following (Fig.107).

Fig.107

In this circuit, the digital potentiometer MCP41010 is controlled by the component
SPIM_1 via an SPI-compatible interface. The connections between SPIM_1 and
MCP41010 are detailed in the table below.

PSOC 5LP pin MCP41010 pin

SCK_P26 2 (SCK)

SI_P25 3 (SI)

CS_P27 1 (CS)



The digital potentiometer MCP41010 operates in ″potentiometer″ mode. The output
voltage of MCP41010 (pin 6) goes to the non-inverting input of the Opamp_1 component
configured as a voltage follower through the MCU port P2[4] (analog pin DigPot_P24).
The op-amp Opamp_1 acts as a buffer and provides the very low output impedance for
external circuitry.
The output voltage is taken from the MCU port P2[3] (analog pin Vout_P23). The power
to the digital potentiometer (pin 8) is fed by the PSoC 5LP Prototyping Kit (either of pins
labeled “VDD” on the board). The capacitor C1 should be placed as close as possible to
the MCP41010 part.

The configuration dialog box of the component SPIM_1 include two tabs shown in
Fig.108 – Fig.109.

Fig.108

All parameters to be set are on the “Configure” tab. Here we select the “CPHA = 0,
CPOL = 0” for the “Mode” parameter which is accompanied by the timing diagram for
this mode. Note that the timing diagram on the property page is the same as that provided
in the datasheet for MCP41010.

The data being written to MCP41010 are 16-bit long, so the “Data Bits” parameter is
assigned the value of 16. The data being shifted on the SI line begins with the MSB bit,
therefore the “Shift Direction” parameter should be assigned “MSB First” (assigned by
default).
The “Data Lines” parameter should be assigned “Bidirectional” (a default value). The
last parameter to be configured on this tab is “Bit Rate”. Note that this parameter depends
on the bit rate suitable for the MCP41010 device indicated in its datasheet. In our case, we
select a bit rate equal to 200 kbps.

The “Advanced” tab of the SPIM_1 component is shown in Fig.109.

Fig.109

We can leave all parameters on this page unchanged.
The Opamp_1 configuration dialog box is shown in Fig.110.

Fig.110
Here we put the Opamp_1 component in the “Follower” mode. The “Power” parameter
is set “Low Power”. When put in low-power mode, the op-amp provides a small output
current that ensures more precision. The voltage reference circuits usually operates in low-
power high-precision circuits, so low-power mode suits the best.
Input and output signals to Opamp_1 are analog, so components DigPot_P24 and
Vout_P23 are configured as is shown in the configuration dialog box for DigPot_P24
(Fig.111).

Fig.111

Note that the “Drive mode” parameter should be assigned the value “High impedance
analog” (a default value).
The component SI_P25 is configured as a bidirectional digital port (Fig.112).

Fig.112

Note that the “Drive mode” parameter should be assigned the value “Strong drive” (a
default value). The components SCK_P26 and CS_P27. Configuration settings for both
components are identical as is shown in the dialog box for SCK_P26 (Fig.113).

Fig.113

Note that the “Drive mode” parameter should be assigned the value “Strong drive”
(assigned by default).
After configuring the components is complete, we should build an application, then
modify the main.c file to provide the functionality to our application.
The modified source code of the main.c file is shown in Listing 17.

Listing 17.

#include <project.h>
uint16 cmd = 0x1100;
uint16 binData, binCode;

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
binData = 111; // this gives us Vout = 1.975V at Vref = Vdd = 4.55V
binCode = cmd + binData;
Opamp_1_Start();
SPIM_1_Start();
SPIM_1_TxEnable();
SPIM_1_WriteTxData(binCode);
for(;;)
{
/* Place your application code here. */
}
}

As you can see, the source code for this application is simple and straightforward. The
sequence

binData = 111;
binCode = cmd + binData;

forms the 16-bit word to be written to MCP41010.
That is followed by

Opamp_1_Start();
SPIM_1_Start();

statements that initialize and enable Opamp_1 and SPIM_1 components. The sequence

SPIM_1_TxEnable();
SPIM_1_WriteTxData(binCode);

writes the 16-bit data word to the MCP41010 device register.
Designing small signal AC amplifiers

This project shows how to develop a PSoC 5LP application providing amplification of
small AC signals. Such amplifiers especially come in handy when we need to measures a
low-level signals by analog-to-digital converters. One possible arrangement uses an op-
amp connected as an inverting amplifier. The basic idea behind this application is shown
in the following circuit diagram (Fig.114).

Fig.114

The signal Vout at the output of the inverting amplifier A1 is a superposition of two
amplified signals, Vin and Vref. The signal Vin is AC coupled to the inverting input of the
op-amp A1 via the capacitor C1. Note that the PSoC 5LP Prototyping Kit uses a single
power supply scheme, so that our op-amp A1 is fed by the positive DC voltage source.
Using the suitable voltage reference Vref ensures that the output signal Vout will not be
clipped during the negative input swings of Vin.
The circuit shown in Fig.114 can easily be analyzed using the superposition theorem.
According to this theorem, we can analyze the behavior of our amplifier separately for AC
and DC signals. The amplifier circuit for AC signals will look like that shown in Fig.115.

Fig.115

Since the impedance of the capacitor C1 (1/2πfC) for the frequency of 1000 Hz will be
very low (about 60 Ohm), C1 acts as a short circuit for AC signals. Therefore, the output
signal Vout1 can be expressed as

Vout1 = Vin x (-R2/R1)

For the AC signals the DC voltage source Vref has a very low impedance, so the non-
inverting input of A1 is put short.
For DC signals, the capacitor C1 acts as a broken circuit, so that the circuit arrangement
appears as a voltage follower for the Vref signal applied to the non-inverting input of the
op-amp A1 (Fig.116).

Fig.116

As you can see, the output voltage Vout2 will repeat Vref. The signal Vout at the output
of A1 will therefore be a superposition of signals Vout1 and Vout2:

Vout = Vout1 + Vout2

Let’s see how to realize the above amplifier circuit with PSoC 5LP Prototyping Kit and
PSoC Creator 3.3. The project window is shown in Fig.117.

Fig.117

The design area with components is shown in Fig.118.

Fig.118

The following components from the Component Catalog are used in this design:
WaveDAC8_1 instance of Waveform DAC (8-bit) [v2.0];
DVDAC_1 instance of Dithered VDAC (9 to 12 bits) [v2.10];
PGA_Inv_1 instance of Inverting PGA [v2.0];
Opamp_1 instance of Opamp [v1.90];
Logic Low ‘0’ [v1.0];
P1_5, P1_6, P1_7 and P2_3 instances of Analog Pin [v2.10];

Besides these components, two capacitors should be added to our circuit. The complete
circuit diagram for our application with external components (capacitors C1 and C2) is
shown in Fig.119.

Fig.119

In this circuit, the small AC signal (sine wave) from the WaveDAC8_1 component is
passed through the P1_6 pin, coupling capacitor C1 and P1_7 pin to the inverting input
Vin of the PGA_Inv_1 component. The voltage reference formed by the DVDAC_1
component goes to the non-inverting input Vref of PGA_Inv_1 through the P1_5 pin and
voltage follower Opamp_1. The Opamp_1 op-amp acts as a buffer with the low output
impedance thus providing the high stability for the voltage reference source. The capacitor
C2 is connected to the DVDAC_1 output as is required in the specification on this
component.

Let’s configure components. WaveDAC8_1 provides a sine wave signal that is fed to the
inverting input of the PGA_Inv_1 component. The configuration dialog box of
WaveDAC8_1 is shown in Fig.120.


Fig.120

We will pick up an output signal “Waveform 1” (input ws = 0). The “Wave type”
parameter is assigned “Sine”; the amplitude of a sine wave signal is determined by the
“Amplitude (Vpp)” parameter that is set to 0.15V. We also set the “Offset (V)” parameter
to 0.51V – its value is not critical for our design because the application will operate with
AC signals. The “Range selection” parameter is assigned “VDAC 0 – 1.020 V
(Buffered)”. Other parameters may be left unchanged.
The DVDAC_1 component provides the voltage reference to the non-inverting input of
the PGA_Inv_1 component. The DVDAC_1 configuration is shown in Fig.121.

Fig.121

Here the “Voltage range” parameter is set to “0 – 1.020 V (0.25mV/bit)”, “Resolution” is
assigned 12-bit and “Initial value” of the DAC output is set 0.5V. Notice the right side of
the dialog box where it is recommended to wire the filter capacitor of 10 nF to the
DVDAC_1 output.
The Opamp_1 component acts as a buffer for the DVDAC_1 output. Opamp_1 is
arranged as a voltage follower as it is reflected in the configuration dialog box (Fig.122).

Fig.122

The PGA_Inv_1 components is configured as is shown in Fig.123.

Fig.123

In this configuration, we select the “Inverting Gain” parameter to be 3. The “Power”
parameter is assigned “Medium Power”.
Our design contains 4 analog pins (P1_5, P1_6, P1_7 and P2_3). Except the “Name”
parameter which will be different for each analog pin, other parameters are left as they
were assigned by default. Fig.124 shows the property for the P1_5 component.

Fig.124

A DWR window for our project is shown in Fig.125.

Fig.125

Here the P1_5 – P1_7 components are assigned the MCU ports P1[5] – P1[7],
respectively. Pin P2_3 is assigned the MCU port P2[3].

After configuring the components is complete, we can build the application. When done,
we must modify the source code of the main.c file as is shown in Listing 18.

Listing 18.

#include <project.h>

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
WaveDAC8_1_Start();
Opamp_1_Start();
DVDAC_1_Start();
PGA_Inv_1_Start();
for(;;)
{
/* Place your application code here. */
}
}

Since our application preferably uses hardware, the source code is extremely simply. We
need to start the components involved by invoking their Start() functions.
Measuring the frequency of digital signals: project 1

With the PSoC 5LP Prototyping Kit we can easily build application measuring parameters
(frequency/period and pulse width) of digital signals. This project illustrates how to design
a simple frequency meter. The results of measurements are transferred via a serial
interface to some terminal application running on the PC.
The project window of our application is shown in Fig.126.

Fig.126

The design area with all components placed there is shown in Fig.127.

Fig.127

The following components from the Component Catalog are used in this design:
Counter_1 instance of Counter [v3.0];
Timer_1 instance of Timer [v2.70];
FreqDiv_1 instance of Frequency Divider [v1.0];
timerIntr instance of Interrupt [v1.70];
Logic Low ‘0’ and Logic High ‘1’ instances;
Input_P23 instance of Digital Input Pin [v2.10];
UART_1 instance of UART [v2.50];
BUS_CLK instance of Clock [v2.20].

The DWR page for our project is shown in Fig.128.

Fig.128

Our application uses only two MCU pins. Pin Input_P23 is assigned the MCU port P2[3],
the Tx_1 pin is assigned the MCU port P0[1].
The idea behind the frequency meter is simple. To measure the frequency of a digital pulse
train the application simply counts the number of rising edges arriving at input Input_P23
during 1 s. The calculated value will be a frequency of a pulse train in Hz. This approach
is illustrated in Fig.129.

Fig.129

In our application, a digital pulse train to be measured arrives at pin Input_P23. To
provide the measurement of high-frequency signals the FreqDiv_1 component dividing
the frequency by 10 is used. Its configuration dialog box is shown in Fig.130.

Fig.130

Here we configure only the “Divider” parameter which is set to 10. Note that the value of
the frequency measured should then be multiplied by 10 to obtain the final result.

The framing interval of 1 s is provided by the Timer_1 component whose configuration
dialog box is shown in Fig.131.

Fig.131

Here we select the 32-bit UDB implementation with the “Period” parameter set to
24000000. This value provides the Timer_1 period equal to 24 MHz / 24000000 = 1 s.
Our Timer_1 timer must activate the interrupt after 1 s interval expires, so we select “On
TC” for the “Interrupts” parameter. The “Run Mode” parameter is assigned “One Shot
(Halt on Interrupt)” – this means that after 1s passes the Timer_1 will be stopped.

Counting the input pulses is accomplished by the Counter_1 component configured as is
shown in Fig.132.

Fig.132

Here we select the 32-bit UDB implementation for Counter_1. The “Period” parameter is
assigned the maximum value, the “Compare Value” parameter is assigned the same value
– 1. The “Compare Mode” parameter is assigned “Less Than”. The last to configure is
the “Clock Mode” parameter which is assigned “Up Counter”.

Both Timer_1 and Counter_1 components are clocked by the Clock component
(BUS_CLK) providing 24 MHz output.
The UART_1 component allows to transfer the result of measurement via a serial
interface. The configuration dialog box of the component is shown in Fig.133.

Fig.133

With this configuration, the “Mode” parameter is assigned “TX only”. One more
parameter to configure is “Bits per second” which is set to 9600.

After configuring components we should build our application. Then we should modify
the main.c file to make our application workable. The source code of the modified main.c
is shown in Listing 19.

Listing 19.

#include <project.h>
#include <stdio.h>

char buf[128];

int bRead;
uint32 cnt;

uint8 tState;

CY_ISR(timerISR)
{
tState = Timer_1_ReadStatusRegister();
Counter_1_Stop();
}

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
timerIntr_StartEx(timerISR);

Timer_1_Start();
Counter_1_Start();

CyDelay(1500);
cnt = Counter_1_ReadCounter() * 10;

memset(buf, 0, sizeof(buf));
sprintf(buf, “The frequency of a digital signal on pin P2.3, Hz: %ld”, (long)cnt);
UART_1_PutString(buf);
UART_1_PutCRLF(0xD);

for(;;)
{
}
}

In this source code, the components are initialized and started by the following sequence:

UART_1_Start();
. . .
Timer_1_Start();
Counter_1_Start();

The Timer_1 interrupt is configured by invoking the function

timerIntr_StartEx(timerISR);

that takes the address of the timerISR() Interrupt Service Routine as the parameter.
The timerISR() procedure is executed once. Its purpose is to stop counting being
performed by the Counter_1 component. The following statement from timerISR() stops
counting:

Counter_1_Stop();

After measuring is complete, the result multiplied by 10 is stored in the cnt variable:

cnt = Counter_1_ReadCounter() * 10;

Then the value in cnt is converted into its character representation and a complete string is
saved in the buf array by the sprintf() library function:

sprintf(buf, “The frequency of a digital signal on pin P2.3, Hz: %ld”, (long)cnt);

The string in buf is then transferred via a serial interface by the statement:

UART_1_PutString(buf);

After rebuilding our application and programming the PSoC 5LP device we can
accomplish the frequency measurement by pressing the RST switch (SW3) on the PSoC
5LP board.
The results of measurements may look like those shown in the window of the terminal
application puTTY (Fig.134).

Fig.134

Measuring the frequency of digital signals: project 2

This project illustrates one more method for measuring the frequency (period) of digital
signals. The project window of the application in PSoC Creator 3.3 is shown in Fig.135.

Fig.135

The design area with components is shown in Fig.136.

Fig.136

The following components from the Component Catalog are used in this design:
Timer_1 instance of Timer [v2.70];
BUS_CLK instance of Clock [v2.20];
UART_1 instance of UART [v2.50];
timerINT instance of Interrupt [v1.70];
P23 instance of Digital Input Pin [v2.10].

The DWR window for this project is shown in Fig.137.

Fig.137

This project uses the MCU port P2[3] associated with the component P23. The UART_1
component will only transfer data to a terminal application, so pin Tx_1 is assigned the
MCU port P0[1].
The idea behind this project is illustrated by Fig.138.

Fig.138

To measure the period (frequency) of a digital pulse train with PSoC 5LP we use the
Timer_1 component. We can measure the time interval between any two successive rising
edges of a pulse train which gives us the period T of an input signal (or a frequency which
is 1/T). To get the value of T we can multiply the number N of the timer ticks by the value
of the interval ttick between two successive ticks. The reciprocal value, 1/T, gives us the
frequency of an input signal. From a practical point of view, Timer_1 should start
counting when the first rising edge arrives and stop counting when the second rising edge
is found.
The precision of our measurements mainly depends on the value of the time interval ttick.
It would be reasonable to take the value of ttick as low as possible. This can be achieved
when Timer_1 is clocked by the highest frequency of 24 MHz. In this case, the interval
ttick will be equal to 0.04167 uS (0.04167 x 10-6 s).
Let’s consider how the components are configured.
The configuration dialog box of the Timer_1 component is shown in Fig.139.

Fig.139

To provide the maximum functionality, we select the UDB implementation with a
resolution of 32-bit. Our timer will operate in “One-Shot” mode (the “Run” parameter).
The “Period” parameter should be assigned the value that is much greater than that of the
period T of an input signal being measured. In our case, this parameter is simply set to its
maximum value of 178.957 s.
We also should determine the point at which a measuring process begins. The Timer_1
timer should start counting down only when some rising edge is detected on the “trigger”
input of Timer_1, therefore the “Trigger Mode” parameter is assigned “Rising Edge”.
We also need to detect the second rising edge on the “capture” input of Timer_1, so the
“Capture Mode” parameter is assigned “Rising Edge” as well. When the second rising
edge arrives, the Timer_1 interrupt must be activated. When this happens, Timer_1 is
stopped and the contents of the counter register is captured. We should assign “One Shot
(Halt On Interrupt)” to the “Run Mode” parameter. The “Interrupts” parameter should
be assigned “On Capture[1-4]”.
The configuration dialog box of the UART_1 component is shown in Fig.140.

Fig.140

Since our application only transmits data via a serial interface, the “Mode” parameter is
assigned “TX only”. The “Bits per second” parameter is assigned the common value of
9600 bps.
The component P23 has the following configuration (Fig.141).

Fig.141

Here the “Driver mode” parameter is assigned “Resistive pull up”, other parameters are
left unchanged, except name which changes to “P23”.
In this project, we use functions from the math library, so we need to configure the
Linker parameters as is shown in Fig.142.

Fig.142

Select “Project” → “Build Settings…”→ “Linker”→ “General” and type ‘m’ in the
“Additional Libraries” field – this forces compiler to search library functions in libraries
beginning with ‘m’.

After building the application we need to modify the source code in the main.c file as is
shown in Listing 20.

Listing 20.

#include <project.h>
#include <stdio.h>
#include <math.h>

char buf[128];
char* pbuf = buf;

float inpPeriod; // the period of an input signal
long inpFreq; // the frequency of an input signal

int i, ticks;

float oneTick = 0.04167; // length of each tick (in uS) of a timer at Fclk = 24MHz

uint8 tState, p23State;
uint32 capVal;

CY_ISR(timerISR)
{
tState = Timer_1_ReadStatusRegister();
capVal = Timer_1_ReadCapture();
}

int main()
{

CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */
UART_1_Start();
timerINT_StartEx(timerISR);

while((p23State = P23_Read()) != 0);
Timer_1_Start();
while((p23State = P23_Read()) == 0);
CyDelay(3000);

ticks = 0xFFFFFFFF - capVal;
inpPeriod = ticks * oneTick * 1.01; // a period in uS
inpFreq = lroundf(1000000.0 / inpPeriod);

memset(buf, 0, sizeof(buf));
sprintf(buf, “The frequency of a digital signal on pin P2.3: %ld”, inpFreq);
UART_1_PutString(buf);
UART_1_PutString(” Hz”);
UART_1_PutCRLF(0xD);

for(;;)
{
/* Place your application code here. */
}
}

In this source code, the variable ticks holds the number of ticks counted for one period of
an input signal. the variable capVal holds the value read from the counter when the second
rising edge arrives. The inpPeriod variable holds the value of a period in microseconds
(uS) and the frequency of a signal is held in the inpFreq variable.
Timer_1 will be started by invoking the statement

Timer_1_Start();

However, Timer_1 doesn’t begin counting until the first rising edge arrives at the
“trigger” input. This occurs after executing the statement

while((p23State = P23_Read()) == 0);

The Timer_1 timer then begins counting down from the maximum 32-bit value
0xFFFFFFFF. The program code should stop counting when the second rising edge arrives
at the “capture” input of the Timer_1 timer. The second rising edge also activates the
interrupt associated with the timerINT component. The Interrupt Service Routine
(timerISR) associated with the timerINT interrupt is then invoked and executed. The
statement

tState = Timer_1_ReadStatusRegister();

within the timerISR() procedure clears the interrupt flag in the status register so that the
same interrupt can’t trigger again. This is followed by the statement

capVal = Timer_1_ReadCapture();

which reads the value of the capture register in the capVal variable.
After the timerISR() procedure terminates, Timer_1 is stopped.
The total number of ticks passed between two rising edges is stored in the ticks variable
by the statement:

ticks = 0xFFFFFFFF - capVal;

The period of the pulse train is stored in inpPeriod:

inpPeriod = ticks * oneTick * 1.01;

Here the empirical coefficient 1.01 reflects the fact that several ticks could be missed out
while executing statements.
The frequency of an input signal is calculated as

inpFreq = lroundf(1000000.0 / inpPeriod);

The data obtained is then converted into a string by the sprintf() function and transferred
via UART.
After rebuilding our application and programming the PSoC 5LP device the measurement
process can be accomplished after the “RST” switch (SW3) on the PSoC 5LP board has
been pressed.
Measuring a pulse width of digital signals

This project describes the design of the system which can measure the pulse width of
digital input signals. The basic principle behind this project is almost the same as that
shown in Fig.138. The only difference is that capturing is performed on the falling edge of
the same pulse (Fig.143).

Fig.143

As it is seen from the above diagram, the width of the input pulse (tw) can be calculated
when we know the number of clock pulses (N) passed between the rising and falling edges
of the input pulse (tw in Fig.143). The formula for calculating a pulse width appears as
follows:

tw = N x ttick

In the above example, N = 14. Note that the precision of the result will depend on the
interval ttick between pulses of a timer (counter). The less the value of ttick, the more
precision result (tw) will be obtained. In our project, we selected the maximum allowable
frequency (24 MHz) of the clock source feeding Timer_1 thus minimizing the value of
ttick.

The project window in PSoC Creator 3.3 is shown in Fig.144.

Fig.144

The design area with the components is shown in Fig.145.

Fig.145

The following components from the Component Catalog are used in this design:
Timer_1 instance of Timer [v2.70];
BUS_CLK instance of Clock [v2.20];
UART_1 instance of UART [v2.50];
timerINT instance of Interrupt [v1.70];
P23 instance of Digital Input Pin [v2.10].


The DWR window is shown in Fig.146.

Fig.146

As it is seen, this project uses the MCU port P2[3] associated with the component P23.
The UART_1 component will only transfer data to a terminal application via a TX line, so
pin Tx_1 is assigned the MCU port P0[1].

The configuration dialog box of Timer_1 is shown in Fig.147.


Fig.147

To provide the maximum functionality, we must select the UDB implementation with a
32-bit resolution. Our timer will operate in “One-Shot (Halt on Interrupt)” mode (the
“Run” parameter). The “Period” parameter is assigned the value that is much greater than
that of the period T of an input signal. In our case, I simply set a maximum value (178.957
s) to “Period”.
We also should determine when the measurement process begins. The Timer_1
component has to start counting down only when some rising edge arrives at the “trigger”
input. To enable triggering we assign “Rising Edge” to the “Trigger Mode” parameter.

The falling edge of a pulse that follows will be captured on the “capture” input of the
Timer_1 timer. To enable this we assign “Falling Edge” to the “Capture Mode”
parameter. When the falling edge has been detected, the timerINT interrupt is activated
and Timer_1 is stopped. The control is then passed to the Interrupt Service Routine
procedure which reads the counter register and stores the value obtained in memory.
The “Run Mode” parameter is assigned “One Shot (Halt On Interrupt)”. The
“Interrupts” parameter is assigned “On Capture[1-4]”.

The UART_1 configuration is shown in Fig.148.

Fig.148

Once the application will only transmit data via a serial interface, the “Mode” parameter is
assigned “TX only”. The “Bits per second” parameter is assigned the common value of
9600 bps.
The component P23 is configured as is shown in Fig.149.

Fig.149

Here the “Driver mode” parameter is assigned “Resistive pull up”, other parameters are
left unchanged, except “Name” which is changed to “P23”. In this project we use
functions from the math library, so we need to indicate this for Linker as is shown in
Fig.150.

Fig.150

Select “Project” → “Build Settings…”→ “Linker”→ “General” and type ‘m’ in the
“Additional Libraries” field – this forces linker to search library functions in the libraries
beginning with ‘m’.

After building the application we need to modify the source code of the main.c file as is
shown in Listing 21.

Listing 21.

#include <project.h>
#include <math.h>

char ascBuf[128];
char* pascBuf = ascBuf;

#define PREC 3 // the length of a fraction part

float pWidth; // pulse width in uS
int i, ticks;
float oneTick = 0.041667; // length of each tick (in uS) of a timer at Fclk = 24MHz

uint8 tState, p23State;
uint32 capVal;


char* ftostr(float fnum)
{
int intPart = fnum;
float fractionPart;
int digit = 0, reminder = 0;
int logVal = log10(fnum);
int index = logVal;
long base = 0;
float tmp, tmp1;

char buf[128];
char* pbuf = buf;

memset(buf, 0, sizeof(buf));

//Extract the integer part from the float fnum
for(i = 1 ; i < logVal + 2 ; i++)
{
base = pow(10.0,i);
reminder = intPart % base;
digit = (reminder - digit) / (base/10);

//Save a digit in a string
buf[index—] = digit + 0x30; // the ASCII value of a digit
if (index == -1)
break;
}
index = logVal + 1;
buf[index] = ‘.’;

fractionPart = fnum - intPart;
tmp = 0;
tmp1 = fractionPart;

//Extract the fraction part from fnum
base = 10;
for (i= 1; i < PREC; i++)
{

tmp = tmp1 * base;
digit = tmp;

//place the digit in the string
buf[++index] = digit + 0x30; // an ASCII value of a digit
tmp1 = tmp - digit;
}
return pbuf;
}

CY_ISR(timerISR)
{
tState = Timer_1_ReadStatusRegister();
capVal = Timer_1_ReadCapture();
}

int main()
{
CyGlobalIntEnable; /* Enable global interrupts. */

/* Place your initialization/startup code here (e.g. MyInst_Start()) */

UART_1_Start();
timerINT_StartEx(timerISR);
while ((p23State = P23_Read()) != 0);
Timer_1_Start();
while((p23State = P23_Read()) == 0);
while ((p23State = P23_Read()) != 0);

CyDelay(3000);

ticks = 0xFFFFFFFF - capVal;
pWidth = ticks * oneTick * 1.01; // a period in uS
pascBuf = ftostr(pWidth);

memset(ascBuf, 0, sizeof(ascBuf));
UART_1_PutString(“The Pulse Width on pin P2.3: “);
UART_1_PutString(pascBuf);
UART_1_PutString(” uS”);
UART_1_PutCRLF(0xD);

for(;;)
{
/* Place your application code here. */
}
}


In this source code, the measurement task is performed by the following section:

timerINT_StartEx(timerISR);
while ((p23State = P23_Read()) != 0);
Timer_1_Start();
while ((p23State = P23_Read()) == 0);
while ((p23State = P23_Read()) != 0);

Here the timerINT_StartEx(timerISR) function configures the interrupt for the Timer_1
timer. The Interrupt Service Routine timerISR() is called once after a falling edge has
arrived at the “capture” input.
The first while() loop

while((p23State = P23_Read()) != 0);

repeats until the pulse changes its level from HIGH to LOW. When the while() loop exits,
the Timer_1 timer starts by invoking the statement

Timer_1_Start();

Although the above function enables Timer_1, the counter doesn’t changes its value until
the first rising edge is detected on the “trigger” input.
After counting is complete, the number of ticks per one period is calculated and stored in
the ticks variable:

ticks = 0xFFFFFFFF - capVal;

Then the pulse width of a digital pulses is calculated and stored in the pWidth variable by
the statement:

pWidth = ticks * oneTick * 1.01; // a period in uS

Here the coefficient 1.01 is empirical – it reflects the fact that several ticks could be
missed while MCU instructions are executed.
The value in pWidth is then converted into ASCII string associated with the ascBuf array
(pointer pascBuf):

pascBuf = ftostr(pWidth);

The following sequence transfers the result as a series of ASCII strings via a serial
interface:

UART_1_PutString(“The Pulse Width on pin P2.3: “);
UART_1_PutString(pascBuf);
UART_1_PutString(” uS”);

After rebuilding our application and programming the PSoC 5LP device the pulse width
measurement can be accomplished after the “RST” switch (SW3) on the PSoC 5LP board
has been pressed.

S-ar putea să vă placă și