{"id":383,"date":"2016-12-26T22:47:13","date_gmt":"2016-12-27T06:47:13","guid":{"rendered":"http:\/\/www.ferzkopp.net\/wordpress\/?p=383"},"modified":"2016-12-26T23:15:18","modified_gmt":"2016-12-27T07:15:18","slug":"pitft-on-windows-iot-core","status":"publish","type":"post","link":"https:\/\/www.ferzkopp.net\/wordpress\/2016\/12\/26\/pitft-on-windows-iot-core\/","title":{"rendered":"PiTFT on Windows IoT Core"},"content":{"rendered":"<p>Interfacing a Raspberry PI 2 with an Adafruit PiTFT 2.2 HAT mini TFT display &#8211; the Windows IoT way!<\/p>\n<p><!--more--><\/p>\n<p>As a Christmas project, I tried to bring up Windows IoT core on my RP2 and then implement a &#8220;driver&#8221; that can interface with Adafruit&#8217;s inexpensive <a href=\"https:\/\/www.adafruit.com\/product\/2315\">PiTFT 2.2<\/a> display:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-386 size-medium\" src=\"http:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/W10IoTRP2-128x75.png\" width=\"128\" height=\"75\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/W10IoTRP2-128x75.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/W10IoTRP2.png 573w\" sizes=\"auto, (max-width: 128px) 100vw, 128px\" \/>&nbsp; +&nbsp; <img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-384\" src=\"http:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/PiTFT22-128x89.png\" alt=\"\" width=\"128\" height=\"89\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/PiTFT22-128x89.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/PiTFT22.png 704w\" sizes=\"auto, (max-width: 128px) 100vw, 128px\" \/>&nbsp; =&nbsp; <img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-388\" src=\"http:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/ChristmasTree-77x128.png\" alt=\"\" width=\"77\" height=\"128\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/ChristmasTree-77x128.png 77w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/ChristmasTree.png 433w\" sizes=\"auto, (max-width: 77px) 100vw, 77px\" \/><\/p>\n<p><strong>Prerequisites<\/strong> needed for this project:<\/p>\n<ul>\n<li>Raspberry PI 2<\/li>\n<li>PiTFT 2.2 board with GPIO header soldered on<\/li>\n<li>16GB SD card and SD card reader<\/li>\n<li>Windows 10 PC with VS 2015 SP3<\/li>\n<li>RP2 wired to network (tip: fix IP of RP2 via MAC address in router), HDMI screen, and a keyboard\/mouse<\/li>\n<\/ul>\n<p>First, I connected the PiTFT display board to my PI2 using 2 extension ribbon-cables (14 wires each) which connect pins 1-28 of the PI2 to the corresponding ones on the PiTFT. This keeps the display flexible in my cluster-stack of ARM boards which contained the PI2. I then followed the published <a href=\"https:\/\/learn.adafruit.com\/adafruit-2-2-pitft-hat-320-240-primary-display-for-raspberry-pi\/easy-install\">Easy Install<\/a> steps to make sure the display works, i.e. boot up Adafruit&#8217;s custom Raspbian Linux distribution.<\/p>\n<p>While Linux is a fine platform, the real goal was to run this on <strong>Windows 10 IoT<\/strong> core. So, next, one needs to get the <a href=\"https:\/\/developer.microsoft.com\/en-us\/windows\/iot\">Windows IoT<\/a> install going on the RP2 which requires a few steps:<\/p>\n<ul>\n<li>Clear SD card using <a href=\"https:\/\/www.sdcard.org\/downloads\/formatter_4\/\">SDFormatter<\/a> with Option &#8216;Format Size Adjustment = ON&#8221;<\/li>\n<li>Download, unpack and copy all <a href=\"https:\/\/www.raspberrypi.org\/downloads\/noobs\/\">NOOBS<\/a> files to SD card<\/li>\n<li>Boot RP2 \/w screen + keyboard\/mouse + ethernet or wifi and select Windows IoT<\/li>\n<li>Pick Core RTM release<\/li>\n<li>Click OK to reboot and boot Windows IoT<\/li>\n<\/ul>\n<p>After this initial setup, the Windows 10 installation on the PI2 can be accessed using a browser.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-389\" src=\"http:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/AndysWindowsIoT.png\" alt=\"\" width=\"1024\" height=\"480\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/AndysWindowsIoT.png 1024w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/AndysWindowsIoT-128x60.png 128w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/AndysWindowsIoT-642x300.png 642w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>The interface provides some basic controls, iformation and can also be used to update the RTM release that NOOBS loads to the latest official version of Windows 10.&nbsp; The web portal also had some bugs: remote client access via the Windows IoT Remote Client (Preview) app didn&#8217;t initially work since the server portion wasn&#8217;t running and couldn&#8217;t be enabled with the web portal either. Thankfully, one can SSH into the PI2 and then run the following commands to schedule it for startup:<br \/>\n<code>schtasks.exe \/Create \/SC ONSTART \/TN \\Microsoft\\Windows\\NanoRDP\\Start \/TR %SystemRoot%\\\\System32\\\\NanoRDPServer.exe \/RU DefaultAccount \/F<\/code><br \/>\n<code>schtasks.exe \/Run \/TN \\Microsoft\\Windows\\NanoRDP\\Start<\/code><\/p>\n<p>Windows IoT comes with a ton of samples code on github which can be opened in VS2015 and deployed to the PI2 for testing the overall workflow and verifying the new Windows IoT setup.<\/p>\n<p>On to the real problem: getting the PiTFT 2.2 board to display some pixels.<\/p>\n<p>The display is driven by an <a href=\"http:\/\/www.adafruit.com\/datasheets\/ILI9340.pdf\">ILI9340<\/a> chip that uses 2 GPIO pins for basic data type and reset controls, and the SPI lines for command and data transfers. Bringing up a GIOP interface with the C# interface is dead simple. Similarly, the IoT documentation comes with some nice <a href=\"https:\/\/developer.microsoft.com\/en-us\/windows\/iot\/samples\/spidisplay\">SPI coding samples in C#<\/a> which make using that slightly more complicated interface easy as well. So I had quickly a skeleton <strong>IO-connection<\/strong> setup going, using code like this:<\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Raspberry Pi 2<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; private const string SPI_CONTROLLER_NAME = \"SPI0\";<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private const int SPI_CHIP_SELECT_LINE = 0;<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ PITFT_2_2<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; private const int PITFT22_DATA_COMMAND_PIN = 25;<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private const int PITFT22_RESET_PIN = 23;<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Get default controller<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gpio = GpioController.GetDefault();<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ GPIO pin number for the D\/C pin<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dcPin = gpio.OpenPin(PITFT22_DATA_COMMAND_PIN);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dcPin.Write(GpioPinValue.High);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dcPin.SetDriveMode(GpioPinDriveMode.Output);<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ GPIO pin number for the RST pin<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rstPin = gpio.OpenPin(PITFT22_RESET_PIN);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rstPin.SetDriveMode(GpioPinDriveMode.Output);<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var spiSettings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spiSettings.ClockFrequency = 32000000; \/\/\/\/ 64000000 was not reliable<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spiSettings.Mode = SpiMode.Mode0;<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string spiDeviceSelector = SpiDevice.GetDeviceSelector(SPI_CONTROLLER_NAME);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IReadOnlyList&lt;DeviceInformation&gt; devices =<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await DeviceInformation.FindAllAsync(spiDeviceSelector);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spi = await SpiDevice.FromIdAsync(devices[0].Id, spiSettings);<\/code><\/p>\n<p>Some of the various settings and port numbers were scavenged from a <a href=\"https:\/\/github.com\/Funkrusha\/Adafruit_Python_ILI9340\">python library<\/a> for this chip. The tricky part is to create the basic initialization sequence. Most of the sequence was taken from the <a href=\"https:\/\/github.com\/notro\/fbtft\/blob\/master\/fb_ili9340.c\">fb_ili9340.c<\/a> Linux driver code, but my version also comes with some additional modifications and tweaks necessary for correct operation which were pulled from the chip spec.&nbsp; (The full sequence is&nbsp; too long to copy in here &#8211; see the source code ZIP at the end of the article.)<\/p>\n<p>After some mucking around with the RGB ordering and display rotation setup &#8211; I was running the board in &#8220;landscape&#8221; mode, but the default pixel transfer is for &#8220;portrait&#8221; ordering &#8211; I finally got it to work and show correct colors. The final step is to wire-up the XAML display to the TFT, by rendering the XAML into a bitmap, then copying the bitmap around a couple of times for color-space transformations from BGRA8 to RGB565, and finally sending it off to the display via the SPI interface. Code looks like this:<\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tft != null &amp;&amp; tft.Initialized)<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Render parent to bitmap<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var renderBitmap = new RenderTargetBitmap();<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await renderBitmap.RenderAsync(this.parentGrid, tft.Width, tft.Height);<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Get the pixels<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IBuffer pixelBuffer = await renderBitmap.GetPixelsAsync();<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] pixelsBGRA8 = pixelBuffer.ToArray();<\/code><\/p>\n<p><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Transfer to display<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tft.Clear(0);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tft.SetBitmap(pixelsBGRA8, renderBitmap.PixelWidth);<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tft.Display();<\/code><br \/>\n<code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/code><\/p>\n<p>The <strong>test program<\/strong> wraps this routine up into the event handler of a DispatchTimer() that fires a few times a seconds, et voila &#8230;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-391\" src=\"http:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/XAMLImageTransfer-1.png\" alt=\"\" width=\"650\" height=\"403\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/XAMLImageTransfer-1.png 650w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/XAMLImageTransfer-1-128x79.png 128w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p><strong>Limitations<\/strong>? yes, it is not real-time fast and you won&#8217;t be playing games with this; but it is good enough for informational displays, clocks, charts, or image slideshows. Unfortunately, the constant re-rendering of the XAML grid into memory will put some pressure on the GC which has to clean up the constantly created temporary bitmaps and pixel buffers.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-392\" src=\"http:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/VisualStudioDebugging.png\" alt=\"\" width=\"800\" height=\"512\" srcset=\"https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/VisualStudioDebugging.png 800w, https:\/\/www.ferzkopp.net\/wordpress\/wp-content\/uploads\/2016\/12\/VisualStudioDebugging-128x82.png 128w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>The MIT licensed <strong>code<\/strong> for this project &#8211; a C# driver for the Adafruit PiTFT 2.2 &#8211; can be downloaded <a href=\"http:\/\/www.ferzkopp.net\/Projects\/PiTFT.zip\">here<\/a>.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Interfacing a Raspberry PI 2 with an Adafruit PiTFT 2.2 HAT mini TFT display &#8211; the Windows IoT way!<\/p>\n","protected":false},"author":1,"featured_media":395,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[22],"tags":[74,81],"class_list":["post-383","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software","tag-rp2","tag-windows-iot"],"_links":{"self":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/383","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/comments?post=383"}],"version-history":[{"count":9,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/383\/revisions"}],"predecessor-version":[{"id":401,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/383\/revisions\/401"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/media\/395"}],"wp:attachment":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/media?parent=383"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/categories?post=383"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/tags?post=383"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}