Arduino With C
Arduino With C
in C#
A Practical Guide
By Yury Magda
2
Contents
Introduction
Disclaimer
Using the FT4232H device in measurement systems
Software
Hardware
Example 1
Example 2
Example 3
Example 4
Example 5
Example 6
Example 7
Example 8
Example 9
Example 10
3
Introduction
This book is written for hobbyists and developers who would like to be
able to program Arduino-based measurement and control systems in C#
.NET.
The hardware used in designs from this book consists of the high-
performance USB interface module equipped with the FT4232H device
from FTDI and the Arduino Zero board. 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.
Disclaimer
The design techniques described in this book have been tested using the
Arduino Zero board and FT4232H Breakout module from Numato Lab
(https://numato.com) 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.
4
upon microcontrollers. This is due to the following features:
Single chip USB to quad serial ports with a variety of configurations;
USB 2.0 High Speed (480Mbits/Second) and Full Speed
(12Mbits/Second) compatible;
Two Multi-Protocol Synchronous Serial Engine (MPSSE) on
CHANNEL A and CHANNEL B to simplify synchronous serial
protocol (USB to JTAG, I2C, SPI or bit-bang) design;
Entire USB protocol handled on the chip. No USB specific firmware
programming required;
RS232/RS422/RS485 UART transfers up to 12M baud;
FTDI's royalty-free Virtual Com Port (VCP) and Direct (D2XX)
drivers eliminate the requirement for USB driver development in
most cases.
5
boards with ARM Cortex microcontrollers with corresponding changes in
hardware/software.
Software
For better understanding the subject, you can also view the programming
examples for C# .NET where the D2XX drivers and DLLs are used. That
stuff can be found in the section of the FTDI site:
http://www.ftdichip.com/Support/SoftwareExamples/CodeExamples/CSharp.htm
All our projects will use FTD2XX_NET.dll library so we should put the
files
FTD2XX_NET.dll
FTD2XX_NET.XML
6
into the working directory of each C# .NET project.
Hardware
Fig.1
The pin assignments for the FT4232H Breakout is shown in the table
below.
7
Channel A Channel B Channel C Channel D
Pin Pin Name Pin Pin Name Pin Pin Pin
Name
P1.1 GND P1.13 BDBUS1 P2.13 GND P2.1
P1.2 VCC P1.14 BDBUS0 P2.14 GND P2.2
P1.3 ADBUS1 P1.15 BDBUS3 P2.15 CDBUS7 P2.3
P1.4 ADBUS0 P1.16 BDBUS2 P2.16 CDBUS6 P2.4
P1.5 ADBUS3 P1.17 BDBUS5 P2.17 CDBUS5 P2.5
P1.6 ADBUS2 P1.18 BDBUS4 P2.18 CDBUS4 P2.6
P1.7 ADBUS5 P1.19 BDBUS7 P2.19 CDBUS3 P2.7
P1.8 ADBUS4 P1.20 BDBUS6 P2.20 CDBUS2 P2.8
P1.9 ADBUS7 P1.21 SUSPEND P2.21 CDBUS1 P2.9
P1.10 ADBUS6 P1.22 GND P2.22 CDBUS0 P2.10
P1.11 RESET P1.23 GND P2.23 GND P2.11
P1.12 GND P1.24 VCC P2.24 VCC P2.12
Alternatively, you can also see the user manual on the FT4232H Breakout.
The block diagram that illustrates interfacing the FT4232H board and
Arduino Zero is shown in Fig.2.
8
Fig.2
Example 1
This example illustrates how to implement simple control of the Arduino
Zero through an external interrupt. Triggering the interrupt will be
performed by a Windows application written in C# .NET.
The connections between the FT4232H and Arduino Zero boards are
shown in Fig.3.
9
Fig.3
The design area with components may look like the following (Fig.4).
10
Fig.4
After the project has been created, we should add the directive
using FTD2XX_NET;
at the beginning of the code (file Form1.cs, by default) and add the
reference to FTD2XX_NET to the References section of our project.
Remind that our project will use FTD2XX_NET.dll library so the files
FTD2XX_NET.dll
FTD2XX_NET.XML
should be put into the working directory of the C# .NET project just
created.
Listing 1.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
11
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FTD2XX_NET;
namespace FT4232H_Example_1
{
public partial class Form1 : Form
{
public byte mask = 0xff; // all pins are outputs (0 = input, 1 =
output)
public byte mode = 0x01; // put a device into async. bit-bang
mode
public Form1()
{
InitializeComponent();
}
12
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
13
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
comboBox1.Items.Add(ftdiDeviceList[i].Description.ToString());
}
}
}
// B = 1, // C = 2, D = 3
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
14
ftStatus = gpout.SetBitMode(mask, mode);
// Set up device data parameters
After the application has started, the configuration of the USB FT4232H
device will be displayed in the richTextBox1 editor.
Then we can select one of four channels (A, B, C or D) that will be used
for control of an interrupt line of the Arduino Zero. To select a suitable
channel, we will use the comboBox1 component labeled Select
Channel.
Also we need to type the decimal value between 0 and 255 in the textBox1
text field. Finally, we can write the value just typed to the selected
channel. The binary code corresponding to the value typed in textBox1
will appear on the output pins of the selected channel.
Lets discuss the above code in detail.
To operate with some channel, the program code use two classes:
FTDI.FT_DEVICE_INFO_NODE
FTDI
15
The information concerning FTDI devices of FT4232H chip will be
retrieved when the application is launched (the Form1_Load event
handler). To start operations with some channel, we should first select it
from the list available in the comboBox1 component. Once selected, the
channel will be configured in the comboBox1_SelectedIndexChanged
event handler as described below.
First, we should ensure that the selected channel is closed after possible
previous operations. If OK, we can create a new object gpout. These
operations are executed by the following sequence:
if (gpout.IsOpen) gpout.Close();
gpout = new FTDI();
Then we will open the channel using its serial number That is performed
by the function
ftStatus =
gpout.OpenBySerialNumber(ftdiDeviceList[index].SerialNumber);
The value of the index variable determines the channel to be opened. If,
for example, index = 0, the channel A will be opened; if index = 2, channel
B will be opened, etc. Note that instead of a serial number, we can also use
other parameters to open a channel.
Once a selected channel is open, we can set its parameters. The function
configures mode and I/O direction for the given channel. In this particular
example, the channel will operate in Asynchronous Bit-Bang mode (mode
= 0x01). In this mode, all 8 bits of the channel are read/written in parallel
without clocking. The mask variable (=0xff) configures all 8 bits of the
channel as digital outputs.
We also need to set baud rate for the channel by execution a statement
ftStatus = gpout.SetBaudRate(baudRate);
16
asynchronous bit-bang write operation.
The 8-bit value in the range of 0 255 can be typed in the textBox1 field
labeled Byte to write (0-255). To write the value just entered to the
selected output channel, press button1 (Write a Byte). In this case, the
button1_Click event handler is called to perform the write operation. In
this handler, the statement
bWrite[0] = Convert.ToByte(textBox1.Text);
converts the text string entered in the edit field of textBox1 into a byte.
The result is moved into the first element of the bWrite array.
The statement that follows simply writes the byte held in bWrite[0] to the
digital outputs of the channel:
Listing 2.
int busy = 0;
void setup() {
// put your setup code here, to run once:
pinMode(13, OUTPUT);
void loop() {
if (busy)
17
{
digitalWrite(13, !digitalRead(13));
delay(300);
}
}
void p8ISR()
{
if (digitalRead(8) == 0x1)
busy = 1;
else busy = 0;
}
In this code, the on-board LED (pin 13 of Arduino) is toggled each time
when the external interrupt is triggered on pin 8. The interrupt occurs, the
Interrupt Service routine p8ISR() drives the LED ON/OFF.
Fig.5
Example 2
In this example, we start/stop timer running on the Arduino Zero through
an external interrupt driven by FT4232H digital output. The circuit
diagram of this project is shown in Fig.6.
18
Fig.6
In this circuit, the pulse train of 10.0 KHz on pin 10 of Arduino will be
enabled/disabled by the signal arriving on pin 8 from FT4232H (pin 4).
The C# .NET application for this project is the same as that described in
the previous section.
The source code of the Arduino Zero application is shown in Listing 3.
Listing 3.
#include <Arduino.h>
#include <Adafruit_ASFcore.h>
#include "Adafruit_ZeroTimer.h"
// creating a timer
Adafruit_ZeroTimer zt4 = Adafruit_ZeroTimer(4);
19
void T4Callback0(struct tc_module *const module_inst)
{
digitalWrite(10, !digitalRead(10));
}
void setup() {
pinMode(10, OUTPUT);
pinMode(8, INPUT_PULLUP); // --> to pin 4 of FT4232H
attachInterrupt(digitalPinToInterrupt(8), p8ISR, CHANGE);
zt4.configure(TC_CLOCK_PRESCALER_DIV64, // DIV1
prescaler
TC_COUNTER_SIZE_16BIT, // 16-bit width of
timer/counter
TC_WAVE_GENERATION_MATCH_PWM // match style
);
void loop() {}
void p8ISR()
{
if (digitalRead(8) == 0x1)
zt4.enable(true);
else zt4.enable(false);
}
When designing this application, you should install two libraries from
Adafruit, Adafruit_ASFcore and Adafruit_ZeroTimer. In this code,
20
Timer 4 (zt4) of the ATSAMD21G18 microcontroller produces the pulse
train of 10 KHz on pin 10 of the Arduino Zero. Timer 4 is
enabled/disabled by the C# .NET application through the interrupt
configured on pin 8 of Arduino.
When the signal on pin 8 of Arduino changes from either LOW(log.0) to
HIGH or HIGH to LOW, the interrupt is triggered. The logical level on pin
8 is tested by the Interrupt Service Routine p8ISR() that then
enables/disables Timer 4, depending on the result of test.
Example 3
This example is the advanced version of the previous one. Here the C#
.NET application will enable/disable Timer 3 and Timer 4 of the
ATSAMD21G18 MCU thus affecting two LEDs. Timer 3 drives the on-
board LED (pin 13 of Arduino), while Timer 4 drives the LED network
connected to pin 12.
The circuit diagram of the project is shown in Fig.7.
Fig.7
21
external interrupts (pins 8 and 9 of the Arduino Zero). Channel A of
FT4232H controls Timer 3 by triggering the interrupt on pin 8 of Arduino,
while channel B of FT4232H controls Timer 4 by triggering the interrupt
on pin 9. Both channels are put in the Asynchronous Bit-Bang mode.
The main form of the C# .NET application looks like the following
(Fig.8).
Fig.8
This application allows to write a value in the range 0-255 to the selected
channel (A and/or B) of FT4232H. The values to be written to outputs
should be entered in textBox1 and textBox2. Clicking the button Write
to Ports causes the values entered in textBox1 and textBox2 to be
written to the corresponding channel.
The C# .NET source code (Form1.cs) of the Windows application is
shown in Listing 4.
22
Listing 4.
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;
using FTD2XX_NET;
namespace FT4232H_Example_2
{
public partial class Form1 : Form
{
public byte mask = 0xff; // pins are set as outputs (0 = input, 1
= output)
public byte mode = 0x01; // put a device into async. bit-bang
mode
23
public FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList;
public Form1()
{
InitializeComponent();
}
if (gpoutA.IsOpen) gpoutA.Close();
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(mask, mode);
ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
24
this.Close();
}
ftStatus = gpoutA.Write(bWriteA, 1, ref bytesWrittenA); //
initially, we write 0 into channel A
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to write to port A (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(mask, mode);
25
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.Write(bWriteB, 1, ref bytesWrittenB); // initially
we write 0 into channel B
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to write to the port B (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
textBox1.Text = "0";
textBox2.Text = "0";
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;
26
// Determine the number of FTDI devices connected to the machine
ftStatus = gpout.GetNumberOfDevices(ref ftdiDeviceCount);
// Check status
if (ftStatus == FTDI.FT_STATUS.FT_OK)
richTextBox1.AppendText("Number of FTDI devices: " +
ftdiDeviceCount.ToString() + "\r");
else
{
MessageBox.Show("Failed to get number of devices (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() +
"\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
27
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
}
Listing 5.
#include <Arduino.h>
#include <Adafruit_ASFcore.h>
#include "Adafruit_ZeroTimer.h"
int cnt3 = 0;
int cnt4 = 0;
// Timer 3 callback
28
void T3Callback0(struct tc_module *const module_inst)
{
cnt3++;
if (cnt3 > 100)
{
cnt3 = 0;
digitalWrite(12, !digitalRead(12));
}
}
// Timer 4 callback
void setup() {
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
zt3.configure(TC_CLOCK_PRESCALER_DIV256, // prescaler
TC_COUNTER_SIZE_16BIT, // 16-bit
width of timer/counter
TC_WAVE_GENERATION_MATCH_PWM // match
29
style
);
zt3.setPeriodMatch(250, 1, 0);
zt3.enable(true);
zt4.configure(TC_CLOCK_PRESCALER_DIV256, // prescaler
TC_COUNTER_SIZE_16BIT, // 16-bit
width of timer/counter
TC_WAVE_GENERATION_MATCH_PWM // match
style
);
zt4.setPeriodMatch(250, 1, 0);
void loop() {}
void p8ISR()
{
if (digitalRead(8) == 0x1)
30
zt3.enable(true);
else zt3.enable(false);
}
void p9ISR()
{
if (digitalRead(9) == 0x1)
zt4.enable(true);
else zt4.enable(false);
}
Fig.9
You can easily improve the above C# .NET and Arduino code to control
more peripherals.
Example 4
31
This example illustrates how to configure the duty cycle of a PWM signal
of the Arduino Zero by a C# .NET application. The circuit diagram of the
project is shown in Fig.10.
Fig.10
In this circuit, a data byte from channel A of FT4232H determines the duty
cycle of the PWM signal generated on pin 11 of the Arduino Zero. The C#
.NET program code writes the data byte into channel A connected to pins
0-7 on the Arduino board, then triggers an interrupt on pin 8 of Arduino by
either rising or falling edge. The Interrupt Service Routine associated with
this interrupt enables reading the data from channel A by the Arduino
program code.
Below is a table that details the connections between the Arduino Zero and
FT4232H board for this project.
32
3 5 (channel A bit 3)
4 8 (channel A bit 4)
5 7 (channel A bit 5)
6 10 (channel A bit 6)
7 9 (channel A bit 7)
8 14 (channel B bit 0) -
strobe
The source code of the C# .NET application is the same as that described
in the previous section.
The source code of the Arduino application is shown in Listing 6.
Listing 6.
int i1;
volatile int done = 0;
int Din;
int res[8];
void setup() {
// put your setup code here, to run once:
for (i1 = 0; i1 < 9; i1++)
pinMode(i1, INPUT_PULLUP); // an ext. interrupt is assigned pin 8
void loop() {
// put your main code here, to run repeatedly:
if (done == 1)
{
Din = 0;
for (i1 = 0; i1 < 8; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 8; i1++)
33
Din += (res[i1] & 0x1) << i1;
Din = Din & 0xFF;
analogWrite(11, Din); // configure a PWM output with a duty = Din
done = 0;
}
}
void p8ISR(){
done = 1;
}
In this code, the Interrupt Service Routine p8ISR() when called simply
sets the variable done. This allows the data bits on pins 0-7 of Arduino to
be read and saved in the variable Din. The value of Din is then used for
configuring a duty cycle of the PWM signal on pin 11. The following
sequence performs these operations:
Din = 0;
for (i1 = 0; i1 < 8; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 8; i1++)
Din += (res[i1] & 0x1) << i1;
Din = Din & 0xFF;
analogWrite(11, Din);
To configure the duty cycle of PWM from C# .NET application, enter the
desired value in the text field for channel A. Then, in the text field
corresponding to channel B type the corresponding value and click the
Write button. Note that you should change the value for channel B from
0 to 1, and vice versa each time when you write a data byte to
Arduino.
Example 5
This project allows to set the frequency of the tone generator created by
the Arduino code. The circuit diagram is shown in Fig.11.
34
Fig.11
Below is a table that details the connections between the Arduino Zero and
FT4232H board for this project.
35
7 9 (channel A bit 7)
8 14 (channel B bit 0)
9 13 (channel B bit 1)
10 21 (channel C bit 1)
Fig.12
Listing 7.
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;
36
using System.Threading;
using FTD2XX_NET;
namespace FT4232H_Example_3
{
public partial class Form1 : Form
{
public byte mask = 0xff; // configuring pins as outputs(0 =
input, 1 = output)
public byte mode = 0x01; // put a device into async. bit-bang
mode
37
public Form1()
{
InitializeComponent();
}
// C = 2, D = 3
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(mask, mode);
ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
38
}
}
ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
39
channelC.Checked)
{
byte[] bytes = new byte[2];
Din = Convert.ToUInt16(textBox1.Text);
if (Din >= 10 && Din <= 1000)
{
bytes = BitConverter.GetBytes(Din); // bytes[0] contains a low
byte of Din,
// bytes[1] contains a high
byte
bWriteA[0] = bytes[0]; // a low byte of UInt16 value goes to
channel A
bWriteB[0] = bytes[1]; // a high byte of UInt16 value goes to
channel B
bWriteA[0] = bytes[0];
bWriteB[0] = bytes[1];
40
textBox1.Text = "100"; // an initial value
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
for (UInt32 i = 0; i < ftdiDeviceCount; i++)
{
richTextBox1.AppendText("Device Index: " + i.ToString() +
41
"\r");
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r");
}
}
gpout.Close();
}
42
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutC.SetBitMode(mask, mode);
ftStatus = gpoutC.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
}
}
Listing 8.
int i1;
volatile int done = 0;
int Din, K = 10; // freq.res = 10 Hz
int res[10];
void setup() {
// put your setup code here, to run once:
for (i1 = 0; i1 < 11; i1++)
pinMode(i1, INPUT_PULLUP); // 10 data bits + an ext. interrupt pin 10
are inputs
43
void loop() {
// put your main code here, to run repeatedly:
if (done == 1)
{
Din = 0;
for (i1 = 0; i1 < 10; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 10; i1++)
Din += (res[i1] & 0x1) << i1;
Din = Din & 0x3FF;
tone(12, K * Din); // configure an oscillator output with a freq = K x Din
done = 0;
}
}
void p10ISR(){
done = 1;
}
In this code, the initial frequency (1400 Hz) of the tone generator is
configured in the setup() function:
To configure the output frequency on the fly we will use the following
statement:
tone(12, K * Din);
where K = 10. In this case, the output frequency will be the value held in
the Din variable multiplied by K.
44
Fig.13
In this window, the output frequency will be set to 380 x 10 = 3800 Hz.
Example 6
The Example 5 can be easily modified for configuring a digital-to-analog
converter (DAC) of the Arduino Zero. In this project the 10-bit binary
code from FT4232H will be used for setting the resolution of the DAC.
The circuit diagram of the hardware is shown in Fig.14.
45
Fig.14
In this circuit, the 10-bit binary code from channel A of FT4232H is fed to
pins 0-9 of the Arduino Zero. This code is then read by the Arduino
program code after triggering the interrupt on pin 10 of Arduino by the
rising edge from pin 21 of the FT4232H board.
The main form of the C# .NET windows application is shown in Fig.15.
46
Fig.15
The C# .NET source code (file Form1.cs) that controls Arduino is shown
below (Listing 9).
Listing 9.
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;
using System.Threading;
using FTD2XX_NET;
namespace FT4232H_Example_4
{
public partial class Form1 : Form
{
public byte mask = 0xff; // configuring pins as outputs (0 =
input, 1 = output)
public byte mode = 0x01; // put a device into async. bit-bang
mode
47
public uint bytesWrittenC = 0; // the number of bytes written into
channel C
public Form1()
{
InitializeComponent();
}
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutA.SetBitMode(mask, mode);
48
// Set up device data parameters
ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
49
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
50
else MessageBox.Show("The value is out of range.", "Error",
MessageBoxButtons.OK);
}
}
51
FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];
52
gpoutC = new FTDI();
The source code of the Arduino Zero application is shown in Listing 10.
Listing 10.
int i1;
volatile int done = 0;
int Din; // a value for configuring DAC
int res[10];
void setup() {
53
// put your setup code here, to run once:
for (i1 = 0; i1 < 11; i1++)
pinMode(i1, INPUT_PULLUP); // 10 data bits + an ext. interrupt pin 10
are inputs
void loop() {
if (done == 1)
{
Din = 0;
for (i1 = 0; i1 < 10; i1++)
res[i1] = digitalRead(i1);
for (i1 = 0; i1 < 10; i1++)
Din += (res[i1] & 0x1) << i1;
Din = Din & 0x3FF;
analogWrite(A0, Din); // configure the DAC output voltage
done = 0;
}
}
void p10ISR(){
done = 1;
}
54
Fig.16
Example 7
This example illustrates how to read external signals on FT4232H digital
inputs. The test signals to FT4232H are fed from pins 0-9 of the Arduino
Zero. The circuit diagram of the project is shown in Fig.17.
55
Fig.17
In this circuit, the 10-bit data is read from pins 0-9 of Arduino into
channels A-B of FT4232H after a rising edge from channel C arrives at pin
10 of Arduino.
The main form of the C# .NET application is shown in Fig.18.
56
Fig.18
The source code for the C# .NET windows application is shown in Listing
11.
Listing 11.
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;
using System.Threading;
using FTD2XX_NET;
namespace FT4232H_Example_5
{
public partial class Form1 : Form
{
byte[] bytes = new byte[2]; // array of bytes to store 10-bit value
public byte maskR = 0x00; // mask for configuring pins as inputs
57
public byte maskW = 0xff; // mask for configuring pins as outputs
public byte mode = 0x01; // put a device into async. bit-bang mode
public UInt16 Din; // the variable to hold the 10-bit data obtained
public Form1()
{
InitializeComponent();
}
58
else button1.Enabled = true;
if (gpoutA.IsOpen) gpoutA.Close();
gpoutA = new FTDI();
ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
59
gpoutB.OpenBySerialNumber(ftdiDeviceList[1].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpoutB.SetBitMode(maskR, mode);
ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
Thread.Sleep(10);
60
ftStatus = gpoutB.Read(bReadB, 1, ref bytesReadB); // a high byte
goes to channel B
bytes[0] = bReadA[0];
bytes[1] = bReadB[0];
ftStatus = gpoutA.Purge(FT_PURGE_RX); // purge the receive
buffer of channel A
ftStatus = gpoutB.Purge(FT_PURGE_RX); // purge the receive
buffer of channel B
Thread.Sleep(10);
textBox1.Text = "0";
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;
61
this.Close();
}
62
private void Form1_FormClosing(object sender,
FormClosingEventArgs e)
{
gpoutA.Close();
gpoutB.Close();
gpoutC.Close();
}
63
}
Listing 12.
int i1;
int tmp, Din;
int done = 0;
void setup() {
for (i1 = 0; i1 < 10; i1++)
pinMode(i1, OUTPUT); // 10 data bits are outputs
pinMode(10, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(10), p10ISR, RISING);
Din = 0;
}
void loop() {
if (done == 1)
{
Din += 1;
if (Din > 32) Din = 0;
setOutputs(Din);
done = 0;
}
}
64
}
void p10ISR(){
done = 1;
}
In this code, the 10-bit binary data is output on pins 0-9 of the Arduino
Zero by the setOutputs() function which is called each time when the
interrupt on pin 10 of Arduino is triggered. The function takes a single
parameter Din that changes from 0 to 32 each time the interrupt occurs.
Fig.19
Clicking the button Read Ports causes the value in the text editor to be
incremented by 1 until the input code exceeds 32. Then the input code
starts incrementing from 0.
Example 8
This project shows how to measure analog voltage on pin A2 of the
Arduino Zero and transfer the data obtained to FT4232H. The circuit
65
diagram of the project is shown in Fig.20.
Fig.20
In this circuit, the test voltage to channel A2 of the Arduino Zero analog-
to-digital converter (ADC) is fed from the wiper of potentiometer R1.
ADC is configured to operate with resolution of 10 bit, therefore after
conversion is complete we obtain the 10-bit binary code corresponding to
the analog voltage on pin A2.
This code then appears on digital output pins 0-9 of Arduino followed by
strobe on pin 10 of Arduino. Bringing pin 10 HIGH allows the 10-bit data
to be read via FT4232H by C# .NET application.
66
Fig.21
Listing 13.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;
namespace FT4232H_Example_6
{
public partial class Form1 : Form
{
byte[] bytes = new byte[2]; // byte array to store 10-bit value
67
public byte maskR = 0x00; // all pins are set as inputs (0 = input, 1 =
output)
public byte mode = 0x01; // put a device into async. bit-bang mode
public Form1()
{
68
InitializeComponent();
}
if (gpoutA.IsOpen) gpoutA.Close();
gpoutA = new FTDI();
ftStatus = gpoutA.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
69
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
ftStatus = gpoutB.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
70
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;
71
this.Close();
}
72
}
73
// Set up device data parameters
// Set Baud rate
ftStatus = gpoutC.SetBaudRate(baudRate);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set Baud rate (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
}
74
// converting data to string
The source code of the Arduino Zero application is shown in Listing 14.
Listing 14.
int i1;
int tmp, Din;
void setup() {
for (i1 = 0; i1 < 10; i1++)
pinMode(i1, OUTPUT); // 10 data bits are outputs
pinMode(10, OUTPUT);
75
analogReadResolution(10);
analogWriteResolution(10);
digitalWrite(10, LOW);
}
void loop()
{
vDAC = vDAC + 0.2;
if (vDAC >= 3.2) vDAC = 0.1;
Dout = (int)(vDAC / LSB);
analogWrite(A0, Dout);
delay(5);
Din = analogRead(A2);
setOutput(Din);
digitalWrite(10, HIGH); // the high edge of a strobe arrives at bit 2 of
channel C on the FT4232H board
delay(20);
digitalWrite(10, LOW);
delay(3000);
}
76
DAC output becomes 0.1V and the loop repeats.
Fig.22
Example 9
This example illustrates data transfer between the FT4232H and Arduino
Zero boards using UART. In this project, the data obtained from channel
A2 of the analog-to-digital converter of the Arduino Zero is transferred to
the C# .NET application through channel A of FT4232H configured as
UART. The circuit diagram of the project is shown in Fig.23.
77
Fig.23
In this circuit, the data is transferred from pin 1 (TX) of the Arduino Zero
to pin 3 (RX) of the FT4232H board.
The test signal to channel A2 of the analog-to-digital converter is taken
from the wiper of potentiometer R1.
The main form of the C# .NET application is shown in Fig.24.
78
Fig.24
Listing 15.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;
namespace FT4232H_UART_Example_1
{
public partial class Form1 : Form
{
public uint baudRate = 115200; // the Baud Rate value
public Form1()
{
InitializeComponent();
}
79
private void Form1_Load(object sender, EventArgs e)
{
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;
80
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r\r");
}
}
81
gpio.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,
FTDI.FT_STOP_BITS.FT_STOP_BITS_1,
FTDI.FT_PARITY.FT_PARITY_NONE);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set data characteristics (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = gpio.SetLatency(10);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set a latency timer (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
82
{
if (gpio.IsOpen) gpio.Close();
thread1.Abort();
}
83
}
}
ftStatus = gpio.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
ftStatus = gpio.SetBaudRate(baudRate);
The following statement sets number of data bits, stop bits and parity:
ftStatus =
gpio.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,
FTDI.FT_STOP_BITS.FT_STOP_BITS_1,
FTDI.FT_PARITY.FT_PARITY_NONE);
We should also set timeout for the read operation (in this case, we selected
30 s):
The statement
ftStatus = gpio.SetLatency(10);
is optional. It allows to set the latency timer value that determines the
receive buffer timeout that is used to flush remaining data from the receive
buffer. For FT4232H, this timeout is programmable and can be set at 1 mS
intervals between 2 mS and 255 mS. This allows the device to be better
optimized for protocols requiring faster response times from short data
packets.
84
To operate with UART, the program code creates the separate thread
thread1:
checks if some data bytes are available in the receive buffer. If yes, the
data is stored in the to the string readData whose value is then output in
the richTextBox1. These operations are performed by the following
sequence:
if (numBytesAvailable > 0)
{
ftStatus = gpio.Read(out readData, numBytesAvailable, ref
numBytesRead);
richTextBox1.AppendText(readData);
numBytesAvailable = 0;
}
The source code of the Arduino Zero application is shown in Listing 16.
Listing 16.
void setup() {
// put your setup code here, to run once:
Serial5.begin(115200);
analogReadResolution(10);
analogWriteResolution(10);
vDAC = 0.15; // the initial tes DAC value
85
}
void loop() {
// put your main code here, to run repeatedly:
vDAC = vDAC + 0.1;
if (vDAC >= 3.0) vDAC = 0.15;
Dout = vDAC/LSB;
analogWrite(A0, Dout);
Din = analogRead(A2);
vADC2 = (double)(LSB * Din);
Serial5.print("Voltage on channel A2: ");
Serial5.print(vADC2, 3);
Serial5.println("V");
delay(5000);
}
In this code, the test voltage (variable vDAC) from the digital-to-analog
converter of the Arduino Zero (pin A0) is fed to channel A2 of the analog-
to-digital converter (pin A2). After A/D conversion is complete, the binary
code corresponding to the input voltage on pin A2 is stored in the variable
Din. Then the value of the input voltage in double format (variable
vADC2) is transferred via the Serial5 interface to the FT4232H board.
The loop repeats every 5 s with the test voltage (vDAC) incremented by
0.1 V. When vDAC reaches 3.0V, it starts incrementing from 0.15V.
86
Fig.25
Example 10
This example illustrates how to transfer data from the FT4232H board to
the Arduino Zero via UART. In this project, the Arduino on-board LED
(pin 13) is controlled by a command sent by C# .NET application through
UART (channel A of FT4232H).
The circuit diagram of the project is shown in Fig.26.
87
Fig.26
In this circuit, the data is transferred from pin 4 (TX line of channel A) of
the FT4232H board to pin 0 (RX) of the Arduino Zero.
Fig.27
88
In this application, the CheckBox component is used for toggling the
Arduino on-board LED.
Listing 17.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using FTD2XX_NET;
namespace FT4232H_UART_Example_2
{
public partial class Form1 : Form
{
public uint baudRate = 9600; // the Baud Rate value
public Form1()
{
InitializeComponent();
}
89
private void Form1_Load(object sender, EventArgs e)
{
ftdiDeviceCount = 0;
ftStatus = FTDI.FT_STATUS.FT_OK;
90
richTextBox1.AppendText(" Flags: " + String.Format("{0:x}",
ftdiDeviceList[i].Flags) + " ");
richTextBox1.AppendText("Type: " +
ftdiDeviceList[i].Type.ToString() + " ");
richTextBox1.AppendText("ID: " + String.Format("{0:x}",
ftdiDeviceList[i].ID) + " ");
richTextBox1.AppendText("Location ID: " + String.Format("
{0:x}", ftdiDeviceList[i].LocId) + " ");
richTextBox1.AppendText("Serial Number: " +
ftdiDeviceList[i].SerialNumber.ToString() + " ");
richTextBox1.AppendText("Description: " +
ftdiDeviceList[i].Description.ToString() + "\r\r");
}
}
ftStatus =
uartA.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to open device A (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
91
}
FTDI.FT_STOP_BITS.FT_STOP_BITS_1,
FTDI.FT_PARITY.FT_PARITY_NONE);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set data characteristics (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
ftStatus = uartA.SetLatency(10);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
MessageBox.Show("Failed to set a latency timer (error " +
ftStatus.ToString() + ")",
"Error", MessageBoxButtons.OK);
this.Close();
}
checkBox1.Checked = false;
}
92
{
if (uartA.IsOpen) uartA.Close();
}
ftStatus = gpio.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
...
ftStatus = gpio.SetBaudRate(baudRate);
...
ftStatus =
gpio.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,
FTDI.FT_STOP_BITS.FT_STOP_BITS_1,
93
FTDI.FT_PARITY.FT_PARITY_NONE);
...
ftStatus = gpio.SetTimeouts(5000, 0);
...
ftStatus = gpio.SetLatency(10);
if (checkBox1.Checked)
{
checkBox1.Text = "LED is ON";
dataToWrite = "1";
}
else
{
checkBox1.Text = "LED is OFF";
dataToWrite = "0";
}
ftStatus = uartA.Write(dataToWrite, dataToWrite.Length, ref
numBytesWritten);
...
The source code of the Arduino Zero application is shown in Listing 18.
Listing 18.
void setup() {
// put your setup code here, to run once:
Serial5.begin(9600);
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
}
94
void loop() {
// put your main code here, to run repeatedly:
while (Serial5.available() > 0)
{
// look for the next valid integer in the incoming serial stream:
int bRcv = Serial5.parseInt();
if (bRcv == 1) digitalWrite(13, HIGH);
else if (bRcv == 0) digitalWrite(13, LOW);
}
}
In this code, the command line from the FT4232H board is received
through the Serial5 interface of the Arduino Zero. The program code
parses the command line and drive the on-board LED ON/OFF depending
on the value taken from UART (variable bRcv).
Fig.28
95
Visit my blog for more new books:
www.avxhm.se/blogs/AlenMiler
96
Table des Matires
Introduction 4
Disclaimer 4
Using the FT4232H device in measurement systems 4
Software 6
Hardware 7
Example 1 9
Example 2 18
Example 3 21
Example 4 31
Example 5 34
Example 6 45
Example 7 55
Example 8 65
Example 9 77
Example 10 87
97