Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,4 @@ paket-files/
appsettings.Development.json
.vscode/settings.json
/devices/Sim7080/samples/secret
devices/XPT2046/XPT2046-EN.pdf
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Most of the bindings have been migrated from [.NET IoT repository](https://githu
* [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Iot.Device.Vl6180X.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Iot.Device.Vl6180X/) [Vl6180X - distance sensor](devices/Vl6180X)
* [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Iot.Device.Ws28xx.Esp32.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Iot.Device.Ws28xx.Esp32/) [Ws28xx/WS2812B/WS2815B/WS2808/SK6812/Neo pixel for ESP32 using RMT - LED drivers](devices/Ws28xx.Esp32)
* [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Iot.Device.Ws28xx.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Iot.Device.Ws28xx/) [Ws28xx/WS2812B/WS2815B/WS2808/SK6812/Neo pixel using SPI - LED drivers](devices/Ws28xx)
* [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Iot.Device.XPT2046.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Iot.Device.XPT2046/) [XPT2046 - Resistive Touch Screen Controller](devices/XPT2046)
* [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Iot.Device.Yx5300.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Iot.Device.Yx5300/) [YX5200/YX5300 - MP3 Player](devices/Yx5300)
</devices>

Expand Down
27 changes: 27 additions & 0 deletions devices/XPT2046/Point.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation and Contributors
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
// See LICENSE file in the project root for full license information.

namespace Iot.Device.XPT2046
{
/// <summary>
/// A touch point.
/// </summary>
public struct Point
{
/// <summary>
/// Gets or sets X.
/// </summary>
public int X { get; set; }

/// <summary>
/// Gets or sets Y.
/// </summary>
public int Y { get; set; }

/// <summary>
/// Gets or sets the amount of pressure of the touch.
/// </summary>
public int Weight { get; set; }
}
}
26 changes: 26 additions & 0 deletions devices/XPT2046/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation and Contributors
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
// See LICENSE file in the project root for full license information.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Iot.Device.XPT2046")]
[assembly: AssemblyDescription("XPT2046 Touch Screen Controller driver for .NET nanoFramework")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("nanoFramework Contributors")]
[assembly: AssemblyProduct("Iot.Device.XPT2046")]
[assembly: AssemblyCopyright("Copyright © .NET Foundation and Contributors")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// Version information managed by Nerdbank.GitVersioning package.
101 changes: 101 additions & 0 deletions devices/XPT2046/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# XPT2046 - Touch screen controller

The XPT2046 from XPTEK is a 4-wire resistive touch screen controller that incorporates a 12-bit 125 kHz successive approximation register type A/D converter. The XPT2046 can detect the pressed screen location by performing two A/D conversions. As well as the location, the XPT2046 also measures touch screen pressure. A multiplexer allows it to measure chip temperature and battery voltage.

The XPT2046 is common in a number of touch screen modules from suppliers like WaveShare and an number of ESP32 based display boards.

Communication is via the SPI bus which can communicate at maximum speed of 2Mhz and uses Mode 0 for the clock polarity/phase setting. The host sends an 8 bit command and the chip responds with 16 bits of data althought the first bit and last three bits are not part of the conversion. XPT2046 needs 16 clock cycles to read a value, so this driver sends 8 bits and a padding byte between each command, and the device can be reading one result whilst sending the command for the next measurement. When reading we need to skip the first 8 bits.

An additional interupt pin which is usually in a high state and goes low when a touch is detected. Note that communicating with the chip will interfere with the interupt.

The chip can use an internal voltage reference or be configured to use an external reference if more accuracy is required.

The XPT2046 will enter power down mode and stop communicating when the chip select pin is taken high. This is why TransferFullDuplex is used as the framework does not expose the raw SPI transaction.

The driver does a best of 3 average, thanks Paul Stoffregen for the suggestion via your Arduino library.

## Documentation

Datasheet - https://www.waveshare.com/wiki/File:XPT2046-EN.pdf

## Usage

See TouchDemo for a complete example of usage based on the ESP32 and a cheap yellow display module.

The driver also supports a maximum X and Y value so you can map the output to your screen size.

The driver scales the output based on a maximum X and Y value so you can map it to pixels on your screen.

**Important**: For the ESP32 the default pins can be remapped, do so before creating the `SPIDevice`.

```csharp
using nanoFramework.Hardware.ESP32;

// Pin Definitions for the Cheap Yellow Display
const int XPT2046_CS = Gpio.IO33; // Chip Select
const int XPT2046_PenIRQ = Gpio.IO36; // Touch detected interupt
const int XPT2046_COPI = Gpio.IO32;
const int XPT2046_CIPO = Gpio.IO39;
const int XPT2046_CLK = Gpio.IO25;

// If you're using an ESP32, use nanoFramework.Hardware.Esp32 to remap the SPI pins
Configuration.SetPinFunction(XPT2046_COPI, DeviceFunction.SPI1_MOSI);
Configuration.SetPinFunction(XPT2046_CIPO, DeviceFunction.SPI1_CLOCK);
Configuration.SetPinFunction(XPT2046_CLK, DeviceFunction.SPI1_MISO);
```

```csharp
using System.Device.Spi;

SpiDevice spiDevice;
SpiConnectionSettings connectionSettings;
connectionSettings = new SpiConnectionSettings(1, XPT2046_CS);
connectionSettings.ClockFrequency = 2_000_000;
connectionSettings.DataBitLength = 8;
connectionSettings.DataFlow = DataFlow.MsbFirst;
connectionSettings.Mode = SpiMode.Mode0;

// Then you create your SPI device by passing your settings
spiDevice = SpiDevice.Create(connectionSettings);

using GpioController gpio = new();
using Xpt2046 sensor = new(spiDevice);
var ver = sensor.GetVersion();

Debug.WriteLine($"version: {ver}");

var point = sensor.GetPoint();

Debug.WriteLine($"ID: {point.TouchId}, X: {point.X}, Y: {point.Y}, Weight: {point.Weigth}, Misc: {point.Miscelaneous}");

```

Use with an interupt pin. Note that interupts that happen during conversion are not reliable. Hence setting touchDetected after calling GetPoint();

```csharp
bool touchDetected = false;

gpio.OpenPin(XPT2046_PenIRQ, PinMode.Input);
// This will enable an event on GPIO36 on falling edge when the screen if touched
gpio.RegisterCallbackForPinValueChangedEvent(XPT2046_PenIRQ, PinEventTypes.Falling, TouchInterrupCallback);

while (true)
{
if (touchDetected) {
var point = sensor.GetPoint();
touchDetected = false;

Debug.WriteLine($"ID: {point.TouchId}, X: {point.X}, Y: {point.Y}, Weight: {point.Weigth}, Misc: {point.Miscelaneous}");

Thread.Sleep(500);

}

Thread.Sleep(20);
}

void TouchInterrupCallback(object sender, PinValueChangedEventArgs pinValueChangedEventArgs)
{
touchDetected = true;
}
```
112 changes: 112 additions & 0 deletions devices/XPT2046/Settings.StyleCop
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<StyleCopSettings Version="105">
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<Rules>
<Rule Name="FileHeaderMustContainFileName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveValidCompanyText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertyDocumentationMustHaveValueText">
<RuleSettings>
<BooleanProperty Name="Enabled">True</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustBeginWithACapitalLetter">
<RuleSettings>
<BooleanProperty Name="Enabled">True</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustEndWithAPeriod">
<RuleSettings>
<BooleanProperty Name="Enabled">True</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustShowCopyright">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveCopyrightText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustBeSpelledCorrectly">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustContainWhitespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings>
<BooleanProperty Name="IgnorePrivates">True</BooleanProperty>
<BooleanProperty Name="IgnoreInternals">True</BooleanProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
<Rules>
<Rule Name="PrefixLocalCallsWithThis">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PrefixCallsCorrectly">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
<Rules>
<Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustAppearInTheCorrectOrder">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeOrderedByAccess">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
<Rules>
<Rule Name="FieldNamesMustNotBeginWithUnderscore">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FieldNamesMustNotUseHungarianNotation">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
</Analyzers>
</StyleCopSettings>
87 changes: 87 additions & 0 deletions devices/XPT2046/TouchDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Iot.Device.XPT2046;
using nanoFramework.Hardware.Esp32;
using System;
using System.Device.Gpio;
using System.Device.Spi;
using System.Diagnostics;
using System.Threading;

namespace TouchDemo
{

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please look at the how to add the linter in the project, it will point you on couple of things to adjust in the project. See: https://github.com/nanoframework/nanoFramework.IoT.Device/blob/develop/StyleCop/README.md

Copy link
Author

@Workshopshed Workshopshed Aug 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't seem to work as expected. But manually copied the tags into the project and the style file into the folder. And that got me all the usual recommendations. Full stops, spaces etc


public class Program
{
static bool touchDetected = false;

public static void Main()
{
Debug.WriteLine("Hello from nanoFramework!");

const int XPT2046_CS = Gpio.IO33; // Chip Select
const int XPT2046_PenIRQ = Gpio.IO36; // Touch detected interupt
const int XPT2046_COPI = Gpio.IO32;
const int XPT2046_CIPO = Gpio.IO39;
const int XPT2046_CLK = Gpio.IO25;

try
{
//Move Display pins to correct SPI bus as the default overlaps with the XPT2046 pins
Configuration.SetPinFunction(Gpio.IO13, DeviceFunction.SPI1_MOSI);
Configuration.SetPinFunction(Gpio.IO14, DeviceFunction.SPI1_CLOCK);
Configuration.SetPinFunction(Gpio.IO12, DeviceFunction.SPI1_MISO);

// For the XPT2046 Touch controller
Configuration.SetPinFunction(XPT2046_COPI, DeviceFunction.SPI2_MOSI);
Configuration.SetPinFunction(XPT2046_CIPO, DeviceFunction.SPI2_MISO);
Configuration.SetPinFunction(XPT2046_CLK, DeviceFunction.SPI2_CLOCK);

SpiDevice spiDevice;
SpiConnectionSettings connectionSettings;

connectionSettings = new SpiConnectionSettings(2, XPT2046_CS);
connectionSettings.ClockFrequency = 1_000_000; //Set clock speed to slowest device on bus
connectionSettings.DataBitLength = 8;
connectionSettings.DataFlow = DataFlow.MsbFirst;
connectionSettings.Mode = SpiMode.Mode0;

spiDevice = SpiDevice.Create(connectionSettings); // For XPT2046 Touch controller

using GpioController gpio = new();
using Xpt2046 sensor = new(spiDevice);
var ver = sensor.GetVersion();
Debug.WriteLine($"version: {ver}");

gpio.OpenPin(XPT2046_PenIRQ, PinMode.InputPullUp);
// This will enable an event on GPIO36 on falling edge when the screen if touched
gpio.RegisterCallbackForPinValueChangedEvent(XPT2046_PenIRQ, PinEventTypes.Falling, TouchInteruptCallback);


while (true)
{
if (touchDetected)
{
var point = sensor.GetPoint();
Debug.WriteLine($"point: {point.X},{point.Y},{point.Weight} ");
Thread.Sleep(30);
touchDetected = false;
}

Thread.Sleep(300);
}

}
catch (Exception ex)
{
Debug.WriteLine($"Critical Exception, Terminating: {ex.Message}");

Thread.Sleep(Timeout.Infinite);
}

}
static void TouchInteruptCallback(object sender, PinValueChangedEventArgs pinValueChangedEventArgs)
{
touchDetected = true;
}
}
}
Loading