Documente Academic
Documente Profesional
Documente Cultură
Windows on the
Raspberry Pi (3)
SPI and I2C
By Tam Hanna (Germany)
Microsoft has been involved with embedded systems for lon- OLED display
ger than you might think: many years in fact. But their offer- A particularly noteworthy feature of the Raspberry Pi is its ability
ings have all had one thing in common: there is no low-cost to output video over its HDMI port. Sadly HDMI-equipped dis-
embedded system based on Microsoft software that is suitable plays tend to be large and power-hungry. However, the market is
for use in hard real-time applications. awash with Chinese-made 128-by-64 pixel OLED displays, which,
being monochrome, do not require much bandwidth to drive. The
The situation with Windows 10 running on the Raspberry Pi example in Figure 1 was bought by the author from AliExpress,
looks equally grim, as applications must be written in the and identical components can be had from eBay and from many
form of managed code. This means that hardware can only electronics suppliers. The controller built into these displays is
be accessed by a cumbersome roundabout method involving known for its flexibility. It can talk not only I2C but also two vari-
the operating system. ants of SPI, the communication format being selected according
to which resistors are fitted on the back of the printed circuit
Fortunately when Broadcom designed the main processor for board. Figure 2 shows the components fitted when the module
the Raspberry Pi they included engines to handle the I2C and is delivered, which put the module into a proprietary communi-
SPI buses, handling communications entirely in hardware. All cation mode highly reminiscent of SPI. Figure 3 shows how the
the operating system has to do is set up a sequence of com- module can be connected to the Raspberry Pi. The project can
mands and gather their results, and in neither case are these easily be assembled on a breadboard (see Figure 4).
operations particularly time-critical. Eagle-eyed readers will observe at this point that there is no
means for information to flow back from the display to the pro-
cessor. This is not a bug in the circuit: the display itself does
not have provision for sending information back to its host.
The controller in the display module can receive two types of
information: data and control commands. Which is being sent
is indicated by the level on the DC pin. The connection of the
reset input to a GPIO pin is needed so that we can provide a
reset pulse to the display controller at start-up.
We now have the means to make a start on another universal
app. The complete program can as always be downloaded as
a Visual Studio project from the Elektor magazine website [3].
Figure 1. The OLED display: small Figure 2. The interface mode of the Display commands
but perfectly formed. display is selected by fitting SMD re- First of all we declare some variables which will be needed for
sistors (here SPI mode is selected). communication.
Raspberry Pi 2
J8
1 2 3V3
12 GPIO18
Display
CS
CS
DC
DC
19 MOSI RES
RES
CE0 D1
D1
23 24 CLK D0
D0
VCC
VCC
GND
GND
D0 ... SPI_CLK
38 GPIO16 D1 ... SPI_MOSI
39 40 GND
Figure 3. Just five signal wires are required to drive the display. Figure 4. The display project can easily be built on a breadboard.
GpioController myGCIc;
Listing 1. Initializing the display.
GpioPin myDataPin;
SpiDevice mySPIDevice; public MainPage()
{
GpioPin myRstPin; this.InitializeComponent();
myGCIc = GpioController.GetDefault();
byte[] gfxBuf = new byte[128 * 64 / 8]; Random myRND = new Random();
myRND.NextBytes(gfxBuf);
There are two important points here. First, the instances of all
the pins and the SPI device that we declare are global, which
avoids the possibility of the garbage collector (see text box) bringLCDUp();
interfering with them; and second, we create a byte array }
which will act as a buffer to store the pixel data to be shifted
out to the display. sync void bringLCDUp()
{
Next come a few more global variables that contain the strings
myDataPin = myGCIc.OpenPin(16);
of command bytes that we will be using. Here it is helpful
refer to the Arduino drivers for the display controller, which myDataPin.SetDriveMode(GpioPinDriveMode.Output);
are available from Adafruit and other manufacturers and dis- myDataPin.Write(GpioPinValue.Low); //Write commands
tributors. In this case we are fortunate to have example code
from Microsoft as well. //Resetsequenz abarbeiten
myRstPin = myGCIc.OpenPin(18);
private static readonly byte[] CMD_DISPLAY_OFF = myRstPin.SetDriveMode(GpioPinDriveMode.Output);
{ 0xAE };
myRstPin.Write(GpioPinValue.High);
private static readonly byte[] CMD_DISPLAY_ON =
await Task.Delay(TimeSpan.FromMilliseconds(1));
{ 0xAF };
myRstPin.Write(GpioPinValue.Low);
private static readonly byte[] CMD_CHARGEPUMP_ON =
await Task.Delay(TimeSpan.FromMilliseconds(100));
{ 0x8D, 0x14 };
myRstPin.Write(GpioPinValue.High);
private static readonly byte[] CMD_MEMADDRMODE =
{ 0x20, 0x00 };
10k
10k
10k
3.3V
~ 10
5V
5V
~9
FindAllAsync(spiAqs);
GND
POWER
8
GND
GND mySPIDevice = await SpiDevice.
DIGITAL (PWM ~ )
VIN 7
29
~6 FromIdAsync(devicesInfo[0].Id, settings);
AN0 ~5
AN1 4
2X BSS138 ~3
ANALOG IN
AN2
AN3 2 The final step to make the display show us something is to
A4 SCL
AN4 TX 1
39 40 GND A5 SDA
AN5 RX 0 send the initialization commands and write out the contents of
the display buffer. This is done in the last part of the function
bringLCDUp(), which is as follows.
SendCommand(CMD_CHARGEPUMP_ON);
Figure 5. This level shifting circuit ensures that the Raspberry Pi 2 is not
damaged by the 5 V logic levels from the Arduino Uno. SendCommand(CMD_MEMADDRMODE);
SendCommand(CMD_SEGREMAP);
SendCommand(CMD_COMSCANDIR);
In the constructor of MainPage (see part 1 of this series [1])
SendCommand(CMD_DISPLAY_ON);
we call the function bringLCDUp() and fill the display buffer
array with random data (see Listing 1). The random data are
SendCommand(CMD_RESETCOLADDR);
important, as a completely black OLED emits no signs of life
SendCommand(CMD_RESETPAGEADDR);
whatsoever: there is more than one programmer who, encoun-
tering a completely black display when testing a game on an SendData(gfxBuf);
OLED smartphone, has rebooted the device in the belief that
it had crashed! The last lines inform the display controller that the data we are
The function bringLCDUp() begins with the reset sequence about to send should be displayed starting at the top left cor-
as specified in the data sheet. Old hands at .NET coding will ner. Then the entire bitmap is sent in one go to the controller,
wonder at this point why we do not use the delay function which copies it to its memory and displays it.
thread.Sleep(). The reason is that this old-style method The functions SendCommand() and SendData() differ from one
is no longer implemented: Windows RT applications are not another only in the level output to to the DC pin on the display
allowed to hog control of the processor, so that they can be while the data bytes are being sent.
as responsive as possible.
private void SendCommand(byte[] Command)
SPI {
In the next step we initialize the SPI bus. Controlling external myDataPin.Write(GpioPinValue.Low);
hardware buses under Windows 10 always happens via an mySPIDevice.Write(Command);
object declared according to the following pattern.
}
Standalone Raspberry Pi
Our Raspberry Pi is at the moment a rather poor embedded The next step is to add your application using the
controller. For example, when power is lost it restarts in the IotStartup add command: for further details see
control app (see part 1 of this series [1]), and will only run https://ms-iot.github.io/content/en-US/win10/tools/
our code on receipt of a suitable command from Visual Studio. CommandLineUtils.htm. Microsoft changes the syntax of this
To overcome this problem you first have to make a connection command fairly frequently, and so it is a good idea to take a
to the Raspberry Pi using PowerShell. Open PowerShell on the look at the documentation before using it.
desktop computer and enter the following command.
And finally, for the sake of completeness, we should add
IotStartup list that the Raspberry Pi running Windows 10, like any full-
scale Windows computer, should be shut down in an
Windows 10 responds by displaying a list of applications orderly fashion. This can be done using the command
installed on the Raspberry Pi. If the names are not shown you shutdown/s/t0: when the start screen appears the
can locate your program using its UUID, found in the project Raspberry Pi can be unplugged.
properties.
Data can also be read back over an SPI port: for more on this
see [4].
namespace ElektorI2C
{
public MainPage()
{
this.InitializeComponent();
initI2c();
}
//Prozessrechner abfragen
myI2CArduino.Write(new byte[] { (byte)128, (byte)0, (byte)128, (byte)200 });
byte[] i2cReadField = new byte[16];
var result = myI2CArduino.ReadPartial(i2cReadField);
if (result.Status == I2cTransferStatus.PartialTransfer || result.Status == I2cTransferStatus.FullTransfer)
{
if (result.BytesTransferred >= 4)
{
System.Diagnostics.Debug.WriteLine("Arduino works!");
}
}
}
}
}
We can work around these weaknesses with the help of an Web Links
Arduino, and if we implement the real-time section from indi- [1] www.elektormagazine.com/150465
vidual components (the AVR microcontroller and a printed cir-
[2] www.elektormagazine.com/150519
cuit board) the extra cost need not be too great. The question
is then one of division of labor: what should the Raspberry Pi [3] www.elektormagazine.com/150520
do, and what should the Arduino do? Answering that question [4] https://msdn.microsoft.com/en-us/library/windows.devices.
would fill a textbook in itself! spi.aspx
(150520)