Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
OK, so continuing the series on PI 2 with windows 10 for IoT, I have now added to the previous app and created a demo app for a pair of Sharp 96*96 LCD displays
Again, there where no libraries for the boards. The LS013B4DN04 SPI display is the actual Sharp part and I wrote a library to drive as many of these as you have GPIOpins free on the PI, I then proceeded to put a cool UI ontop of it so I can play with the board under Windows 10.
Here is a video running through the demo app, I will follow up with a software overview and a technical look at the SPI buss as there are some interesting things I did to get things running fast
This is a view of the boards I used, you could just as easily use the Adafruit version without changing the software
This is the strip board i made up for the demo, it has minimal wiring so no need to bother with a schematic
there are a total of 6 wires to the PI as shown below, Power control and LCD Enable both are tied to 3.3V, one display SPI CS goes to GPIO5, one to GPIO6
probably the easies setup for the hardware yet
So this is the UI I created in full technicolour
The video explains it all but in summary
The two displays have a textbox overlay so you can input your own text and send it to the displays, the long slider will allow you to change the font size. While the display library will support multiple fonts sizes on the screen at once along with graphics elements, the demo does not show this (Yet). clicking wither of the "Update" buttons will send the display content to the respective SPI SHARP96 Display, the left one is connected to CS GPIO5, the right to GPIO6 (The code could be changed easily to use and available GPIO pin).
The fill 1's and 0's will fill a 2D array in memory for each display with all 1's or all 0's depending on the button. All 1's is actually the light grey colour, the 0's gives you a mirror basically as the reflective index of these displays is so high.
The demo button runs through a set of patterns and showcases some of the libraries capabilities
- Write Pixel
- Line vertical
- Line Horizontal
- Line any direction
- Arcs
- Circles (Optimised to only calc 45deg of the circle and still plot all of it. also only integer math involved)
- Filled Rectangle
- Write a string or Character supporting the \n \r
all the text can be started at any x,y co-ordinate, not just on a fixed boundary
UPDATE: 13th August 2015:- Minor update to the VCOM routine to prevent screen BIAS effect, I noticed it was not working so I fixed it. the problem was it flipflops the bit but as I was doing it twice for each tick due to two displays, each display never got the alterate bit so it did not work. I moved the bit to the Sharp96Display class to it is unique to each display and adapted the routing. Now working well.
Here is what happens without it, the right display (On the first image) is supposed to be an even gray, as you can see, the old image remains
the other image was the result of being left on all night but with the bug i found unfixed and the third image shows what you see when you put another image on the display, as you can see, there is still text from the previous text
Now with the working code, the images will over a few minutes or more, fix themselves to no real harm done but somthing to pay attention to.
The attached code now contains the fix.
At this point I want to thank TI and Adafruit for their great graphics linraries that where the inspiration for this code. The above code is heavily modified and adapted from anything I saw at either site but their code helped immensly in figuring out the algorithms and base set of needed functions. The above code will also drive both thier products so win win all arround.
The TI PDF can be found here: http://www.ti.com/lit/ug/slau553/slau553.pdf
Adafruits link here: https://www.adafruit.com/products/1393
Sharps Datasheet here http://www.sharpmemorylcd.com/resources/ls013b4dn04_application_info.pdf
and a really useful APP note here: http://www.sharpmemorylcd.com/resources/programming_memory_lcd_app_note.pdf
The one thing not mentioned clearly anywhere is the need to swap the bits of the addressing around before sending the commands to the display, all other bytes go as normal but the address does not... wierd but its sorted in the code.
using System;
using Windows.Security.Cryptography;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using SHARP96Driver;
using System.Threading.Tasks;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace SHARP96Test
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
sharp96Display display1 ;
sharp96Display display2 ;
private static DispatcherTimer timer; // for VCOM refresh of displays
bool fontsizecaptured = false;
double fontsizeX1 = 0;
public MainPage()
{
this.InitializeComponent();
/* Register for the unloaded event so we can clean up upon exit */
Unloaded += MainPage_Unloaded;
/* Initialize GPIO, SPI, and the display */
InitAll();
}
/* Initialize GPIO, SPI, and the display */
private async void InitAll()
{
try
{
await Sharp96.InitSPI();
//Sharp96.InitGPIO();
display1 = new sharp96Display("\n Display 1\n\n brought to you\n by\n\nthebreadboard.ca\n and\n\n Element14", sharp96Display.SHARP_BLACK, 5); // using GPIO 5 for CS as the CS0 and 1 work the wrong way
display2 = new sharp96Display("\n Display 2\n\n brought to you\n by\n\nthebreadboard.ca\n and\n\n Element14", sharp96Display.SHARP_WHITE, 6); // using GPIO 6 for CS as the CS0 and 1 work the wrong way
InitSharp96(); // do this last so we dont kick non existant displays
}
/* If initialization fails, display the exception and stop running */
catch (Exception ex)
{
Text_Status.Text = "Exception: " + ex.Message;
if (ex.InnerException != null)
{
Text_Status.Text += "\nInner Exception: " + ex.InnerException.Message;
}
return;
}
}
public void InitSharp96()
{
// Start the VCOM toggle
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(100);
timer.Tick += Timer_Tick;
timer.Start();
}
// Send out the alternating VCOM to prevent screen bias build up
private void Timer_Tick(object sender, object e)
{
// Somewhere to do stuff in the background
// like the screen VCOM to prevent static buildup messing the display
Sharp96.Sharp96x96_SendToggleVCOMCommand(display1);
Sharp96.Sharp96x96_SendToggleVCOMCommand(display2);
}
private void MainPage_Unloaded(object sender, object args)
{
/* Cleanup */
}
// clear both screens to silver
private void button1_Click(object sender, RoutedEventArgs e)
{
Sharp96.Sharp96x96_ClearScreen(display1, 0x00);
Sharp96.Sharp96x96_ClearScreen(display2, 0x00);
}
// clear both screens to white
private void button2_Click(object sender, RoutedEventArgs e)
{
Sharp96.Sharp96x96_ClearScreen(display1, 0xFF);
Sharp96.Sharp96x96_ClearScreen(display2, 0xFF);
}
// Run a DEMO loop, adjust speed with onscreen slider and forever loop with checkbox
private async void button4_Click(object sender, RoutedEventArgs e)
{
bool doneOneloop = false;
sharp96Display nextDisplay;
try
{
while (LoopDemo.IsChecked == true || doneOneloop == false)
{
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display2 : display1;
doneOneloop = true;
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0xFF);
Sharp96.Graphics_Rectangle rect = new Sharp96.Graphics_Rectangle();
rect.XMin = 0;
rect.XMax = 95;
rect.YMin = 0;
rect.YMax = 95;
Sharp96.Sharp96x96_RectFill(nextDisplay, rect, 0x0);
rect.XMin = 20;
rect.XMax = 76;
rect.YMin = 20;
rect.YMax = 76;
Sharp96.Sharp96x96_RectFill(nextDisplay, rect, 0x1);
rect.XMin = 40;
rect.XMax = 56;
rect.YMin = 40;
rect.YMax = 56;
Sharp96.Sharp96x96_RectFill(nextDisplay, rect, 0x0);
Sharp96.Sharp96x96_Flush(nextDisplay);
await Task.Delay((int)LoopDelay.Value);
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display1 : display2;
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0xFF);
for (int x = 0; x < sharp96Display.LCD_HORIZONTAL_MAX / 2 - 1; x += 2)
{
int x2 = sharp96Display.LCD_VERTICAL_MAX - 1 - x;
int y = x;
int y2 = sharp96Display.LCD_VERTICAL_MAX - 1 - y;
Sharp96.Sharp96x96_LineDrawH(nextDisplay, x, x2, y, 0);
Sharp96.Sharp96x96_LineDrawH(nextDisplay, x, x2, y2, 0);
Sharp96.Sharp96x96_LineDrawV(nextDisplay, x, y, y2, 0);
Sharp96.Sharp96x96_LineDrawV(nextDisplay, x2, y, y2, 0);
}
Sharp96.Sharp96x96_Flush(nextDisplay);
Task.Delay((int)LoopDelay.Value);
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display1 : display2;
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0xFF);
for (int x = 0; x < 50; x++)
{
Sharp96.Sharp96x96_drawLine(nextDisplay, GenerateRndNumber(), GenerateRndNumber(), GenerateRndNumber(), GenerateRndNumber(), 0);
}
Sharp96.Sharp96x96_Flush(nextDisplay);
await Task.Delay((int)LoopDelay.Value);
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display1 : display2;
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0xFF);
Sharp96.Arc(nextDisplay, 6, 6, 83, 90, 180, 0);
Sharp96.Arc(nextDisplay, 89, 89, 83, 270, 360, 0);
Sharp96.Arc(nextDisplay, 6, 89, 83, 0, 90, 0);
Sharp96.Arc(nextDisplay, 89, 6, 83, 180, 270, 0);
Sharp96.DrawCircle(nextDisplay, 48, 48, 41, 0);
Sharp96.Sharp96x96_Flush(nextDisplay);
await Task.Delay((int)LoopDelay.Value);
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display1 : display2;
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0x00);
Sharp96.setCursor(nextDisplay, 15, 30);
Sharp96.write(nextDisplay, "Hello".ToCharArray(), 2, 1);
Sharp96.setCursor(nextDisplay, 32, 47);
Sharp96.write(nextDisplay, "Hi There".ToCharArray(), 1, 1);
Sharp96.Sharp96x96_Flush(nextDisplay);
await Task.Delay((int)LoopDelay.Value);
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display1 : display2;
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0xFF);
Sharp96.setCursor(nextDisplay, 0, 0);
Sharp96.write(nextDisplay, "Hi There".ToCharArray(), 1, 0);
Sharp96.setCursor(nextDisplay, 0, 9);
Sharp96.write(nextDisplay, "Hi There".ToCharArray(), 2, 0);
Sharp96.setCursor(nextDisplay, 0, 26);
Sharp96.write(nextDisplay, "Hi".ToCharArray(), 4,0);
Sharp96.setCursor(nextDisplay, 0, 53);
Sharp96.write(nextDisplay, "Hi".ToCharArray(), 6, 0);
Sharp96.Sharp96x96_Flush(nextDisplay);
await Task.Delay((int)LoopDelay.Value);
nextDisplay = (GenerateRndNumber() % 2 == 1) ? display1 : display2;
for (byte x = 1; x < 9; x++)
{
Sharp96.Sharp96x96_ClearScreen(nextDisplay, 0xFF);
Sharp96.setCursor(nextDisplay, 0, 0);
Sharp96.write(nextDisplay, "Hi".ToCharArray(), x, 0);
Sharp96.Sharp96x96_Flush(nextDisplay);
await Task.Delay((int)LoopDelay.Value);
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
// Exit the program
private void btnExit_Click(object sender, RoutedEventArgs e)
{
App.Current.Exit(); // exit the app
}
// Copy contents of Onscreen display 1 to the real display 1
// not the onscreen is only an approximation and therefor
// the real screen may look slightly different
//You can change the font size with the big slider and edit the text boxes on the displays
private void UpdateDisp1_Click(object sender, RoutedEventArgs e)
{
Sharp96.Sharp96x96_ClearScreen(display1, sharp96Display.SHARP_WHITE);
Sharp96.setCursor(display1, 0, 0);
Sharp96.write(display1, txtDisplay1.Text.ToCharArray(), (byte)font_Size.Value, sharp96Display.SHARP_BLACK);
Sharp96.Sharp96x96_Flush(display1);
}
// Copy contents of Onscreen display 2 to the real display 2
// not the onscreen is only an approximation and therefor
// the real screen may look slightly different
//You can change the font size with the big slider and edit the text boxes on the displays
private void UpdateDisp2_Click(object sender, RoutedEventArgs e)
{
Sharp96.Sharp96x96_ClearScreen(display2, sharp96Display.SHARP_WHITE);
Sharp96.setCursor(display2, 0, 0);
Sharp96.write(display2, txtDisplay2.Text.ToCharArray(), (byte)font_Size.Value, sharp96Display.SHARP_BLACK);
Sharp96.Sharp96x96_Flush(display2);
}
public UInt16 GenerateRndNumber()
{
// Generate a random number.
UInt32 Rnd = CryptographicBuffer.GenerateRandomNumber( ) % 96; //limit between 0 and 96
return (UInt16)Rnd;
}
// Event handler, changes the onscreen FONT size and the setting for screen to real font size
private void font_Size_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
// playing on the fact this is entered as a bug in XAML before anything is changed
// so we capture the base setting for the font to use when we change it
if (fontsizecaptured == false)
{
fontsizeX1 = txtDisplay1.FontSize;
fontsizecaptured = true;
}
txtDisplay1.FontSize = font_Size.Value * fontsizeX1;
txtDisplay2.FontSize = font_Size.Value * fontsizeX1;
}
}
}
<Page
x:Class="SHARP96Test.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SHARP96Test"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="White" BorderBrush="#FF0192B0" BorderThickness="5" CornerRadius="10" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,68,25,68" Width="989" >
<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="59" Margin="490,532,0,0" VerticalAlignment="Top" Width="474" CornerRadius="3"/>
<Button x:Name="button1" Content="Fill 0's" HorizontalAlignment="Left" Margin="396,538,0,0" VerticalAlignment="Top" Click="button1_Click" Height="48" Width="84" Background="#FF0198BB" Foreground="White" BorderBrush="#FFF07C20"/>
<Button x:Name="button2" Content="Fill 1's" HorizontalAlignment="Left" Margin="298,538,0,0" VerticalAlignment="Top" Click="button2_Click" Height="48" Width="84" Background="#FF0198BB" Foreground="White" RenderTransformOrigin="0.293,5.135" BorderBrush="#FFF07C20"/>
<Button x:Name="button4" Content="DEMO" HorizontalAlignment="Left" Margin="498,538,0,0" VerticalAlignment="Top" Click="button4_Click" Height="48" Width="87" Background="#FF0198BB" Foreground="White" BorderBrush="#FFF07C20"/>
<CheckBox x:Name="LoopDemo" Content="Loop" HorizontalAlignment="Left" Margin="598,547,0,0" VerticalAlignment="Top" Height="23" Width="87"/>
<TextBlock x:Name="Text_Status" HorizontalAlignment="Left" Margin="296,591,0,0" TextWrapping="Wrap" Text="Status" VerticalAlignment="Top" Width="463" Height="31"/>
<Image x:Name="image" HorizontalAlignment="Left" Height="254" Margin="298,171,0,0" VerticalAlignment="Top" Width="251" Source="Assets/430BOOST-SHARP96.png"/>
<Image x:Name="image1" HorizontalAlignment="Left" Height="254" Margin="557,171,0,0" VerticalAlignment="Top" Width="251" Source="Assets/430BOOST-SHARP96.png"/>
<Image x:Name="image2" HorizontalAlignment="Left" Height="110" Margin="296,59,0,0" VerticalAlignment="Top" Width="490" Source="Assets/Windows+10+IoT+Raspberry+Pi.jpg"/>
<Button x:Name="UpdateDisp1" Content="Update" HorizontalAlignment="Left" Margin="362,434,0,0" VerticalAlignment="Top" Background="#FF0198BB" Foreground="White" Height="53" Width="126" Click="UpdateDisp1_Click" BorderBrush="#FFF07C20"/>
<Button x:Name="UpdateDisp2" Content="Update" HorizontalAlignment="Left" Margin="606,434,0,0" VerticalAlignment="Top" Background="#FF0198BB" Foreground="White" Height="53" Width="126" Click="UpdateDisp2_Click" BorderBrush="#FFF07C20"/>
<Image x:Name="image3" HorizontalAlignment="Left" Height="110.607" Margin="-95.559,317.454,0,0" VerticalAlignment="Top" Width="487.237" Source="Assets/Element14 LOGO.jpg" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto">
<Image.RenderTransform>
<CompositeTransform Rotation="-90"/>
</Image.RenderTransform>
</Image>
<TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="-4.338,317.65,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="478.828" Height="97.328" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" FontSize="26.667" FontWeight="Bold" FontFamily="Arial Black" Text="Brought to you by theBreadboard.ca Youtube.com/c/thebreadboardca" Foreground="#FFFACAA1" TextAlignment="Center">
<TextBlock.RenderTransform>
<CompositeTransform Rotation="-90"/>
</TextBlock.RenderTransform>
</TextBlock>
<Button x:Name="btnExit" Content="Quit Demo" HorizontalAlignment="Left" Margin="100,64,0,0" VerticalAlignment="Top" Click="btnExit_Click" Height="65" Width="158" Foreground="White" FontSize="26.667" FontWeight="Bold" BorderBrush="#FF0093B9" ToolTipService.ToolTip="Click to exit the application, Come back soon!">
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF0093B9" Offset="0"/>
<GradientStop Color="#FFF57E22" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
<TextBox x:Name="txtDisplay1" HorizontalAlignment="Left" Margin="318,198,0,0" TextWrapping="Wrap" Text="Hello and Welcome to Display 1, you can have up to 144 characters at this font size of 6*8, well actually 16 * 12 lines to be exact, When you edit in this box, it even does spell checking.. nice" VerticalAlignment="Top" Background="{x:Null}" Height="167" Width="182" AcceptsReturn="True" PlaceholderText="" ToolTipService.ToolTip="Type your text here and press upload to send to the display, note it may not render exactly the same on the display due to differences in the scaling etc" MaxLength="144" FontSize="14.667" FontFamily="Global Monospace">
<TextBox.Foreground>
<SolidColorBrush Color="{ThemeResource SystemChromeBlackMediumColor}"/>
</TextBox.Foreground>
</TextBox>
<TextBox x:Name="txtDisplay2" HorizontalAlignment="Left" Margin="577,198,0,0" TextWrapping="Wrap" Text=" Hi There
 Volts = 50.0mV
 Temp = 23C
 Hum = 66%
 
And more can Be shown with ease if you take the time to format it" VerticalAlignment="Top" Background="{x:Null}" Height="167" Width="182" AcceptsReturn="True" PlaceholderText="" ToolTipService.ToolTip="Type your text here and press upload to send to the display, note it may not render exactly the same on the display due to differences in the scaling etc" MaxLength="144" FontSize="14.667" FontFamily="Global Monospace">
<TextBox.Foreground>
<SolidColorBrush Color="{ThemeResource SystemChromeBlackMediumColor}"/>
</TextBox.Foreground>
</TextBox>
<Slider x:Name="font_Size" HorizontalAlignment="Left" Height="38" Margin="298,492,0,0" VerticalAlignment="Top" Width="488" LargeChange="1" Maximum="8" Minimum="1" TickFrequency="1" ToolTipService.ToolTip="Select the Font Size" Value="1" ValueChanged="font_Size_ValueChanged"/>
<Image x:Name="image4" HorizontalAlignment="Left" Height="563" Margin="791,59,0,0" VerticalAlignment="Top" Width="173" Source="Assets/SHARP96 RPI2 HEADER.png"/>
<TextBlock x:Name="textBlock1_Copy" HorizontalAlignment="Left" Margin="67.726,11.67,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="885.939" Height="34.154" RenderTransformOrigin="0.456,0.258" UseLayoutRounding="False" d:LayoutRounding="Auto" FontSize="32" FontWeight="Bold" FontFamily="Arial Black" Foreground="#FFF38323" TextAlignment="Center">
<TextBlock.RenderTransform>
<CompositeTransform Rotation="-0.175" TranslateX="-0.026" TranslateY="0.125"/>
</TextBlock.RenderTransform>
<Run Text="Sharp96 ("/>
<Run Text="DUAL "/>
<Run Text="96*96 pixel "/>
<Run Text="SPI"/>
<Run Text=" displays on RPI2 IoT"/>
</TextBlock>
<Slider x:Name="LoopDelay" HorizontalAlignment="Left" Height="33" Margin="689,543,0,0" VerticalAlignment="Top" Width="229" Background="#FFFD7E28" BorderBrush="#FF0292B7" BorderThickness="2" Maximum="2000" LargeChange="100" ToolTipService.ToolTip="Change Delay between steps from 1mS - 2 Seconds" Value="500" TickPlacement="None" Minimum="1"/>
</Grid>
</Page>
using System;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.Spi;
using Windows.Devices.Gpio;
namespace SHARP96Driver
{
public class sharp96Display
{
public string DefaultText ;
public static UInt16 LCD_VERTICAL_MAX = 96; // Y
public static UInt16 LCD_HORIZONTAL_MAX = 96; // X
public static int SHARP_BLACK = 0x00;
public static int SHARP_WHITE = 0xFF;
public int VCOMbit;
public GpioPin PIN;
public byte cursorX;
public byte cursorY;
public byte[,] DisplayBuffer; // A working pixel buffer for your code
public sharp96Display(string defaultText, int ulValue, int pin)
{
//LCD_VERTICAL_MAX = 96;
//LCD_HORIZONTAL_MAX = 96;
//SHARP_BLACK = 0x00;
//SHARP_WHITE = 0xFF;
PIN = Sharp96.InitGPIO(pin, GpioPinDriveMode.Output, GpioPinValue.Low);
cursorX = 0;
cursorY = 0;
VCOMbit = 0x40; // used to toggle the VCOM bit to prevent screen biasing
DisplayBuffer = new byte[LCD_VERTICAL_MAX, LCD_HORIZONTAL_MAX / 8]; // A working pixel buffer for your code
Sharp96.Sharp96x96_InitializeDisplayBuffer(this, (ulValue==0)? SHARP_BLACK: SHARP_WHITE) ;
DefaultText = defaultText;
Sharp96.write(this, defaultText.ToCharArray(), 1, (ulValue==0) ? SHARP_WHITE : SHARP_BLACK);
if (PIN != null) Sharp96.Sharp96x96_Flush(this);
}
}
public static class Sharp96
{
//*****************************************************************************
//1.3-inch screen has 96x96 resolution (9216 pixels stripe array)
// LCD Screen Dimensions
//*****************************************************************************
public const UInt16 LCD_VERTICAL_MAX = 96; // Y
public const UInt16 LCD_HORIZONTAL_MAX = 96; // X
private static bool AutoWrap = true;
//*****************************************************************************
// for the Display Driver
//*****************************************************************************
public const int SHARP_BLACK = 0x00;
public const int SHARP_WHITE = 0xFF;
private const int SHARP_SEND_TOGGLE_VCOM_COMMAND = 0x01;
private const int SHARP_SKIP_TOGGLE_VCOM_COMMAND = 0x00;
private const int SHARP_LCD_TRAILER_BYTE = 0x00;
private const int SHARP_VCOM_TOGGLE_BIT = 0x40;
private const int SHARP_LCD_CMD_CHANGE_VCOM = 0x00;
private const int SHARP_LCD_CMD_CLEAR_SCREEN = 0x20;
private const int SHARP_LCD_CMD_WRITE_LINE = 0x80;
public static byte[] reverse_data = { 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E, 0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F };
private static int VCOMbit = 0x40;
private static int flagSendToggleVCOMCommand = 0;
private const int ClrBlack = SHARP_BLACK;
public struct Graphics_Rectangle
{
public int XMin;
public int XMax;
public int YMin;
public int YMax;
}
//*****************************************************************************
//RaspBerry Pi2 Parameters
//*****************************************************************************
private const string SPI_CONTROLLER_NAME = "SPI0"; /* For Raspberry Pi 2, use SPI0 */
private const Int32 SPI_CHIP_SELECT_LINE = 0; /* Line 0 maps to physical pin number 24 on the Rpi2, line 1 to pin 26 */
private static byte[] writeBuffer2 = new byte[2];// for simple comands like CLS or VCON
private static byte[] writeBuffer1346 = new byte[1346];// for writing a buffer to the screen
private static byte[] readBuffer4 = new byte[4]; /*this is defined to hold the output data*/
private static byte[] writeBuffer4 = new byte[4];//register, then 16 bit value
private static SpiDevice SpiGPIO;
public static async Task InitSPI()
{
try
{
var settings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);
settings.ClockFrequency = 5000000;// 500kHz;
settings.Mode = SpiMode.Mode0; //Mode0,1,2,3; MCP23S17 needs mode 0
string spiAqs = SpiDevice.GetDeviceSelector(SPI_CONTROLLER_NAME);
var deviceInfo = await DeviceInformation.FindAllAsync(spiAqs);
SpiGPIO = await SpiDevice.FromIdAsync(deviceInfo[0].Id, settings);
}
/* If initialization fails, display the exception and stop running */
catch (Exception ex)
{
//statusText.Text = "\nSPI Initialization Failed";
}
}
public static GpioPin InitGPIO(int GPIOpin, GpioPinDriveMode mode, GpioPinValue HiLow )
{
var gpio = GpioController.GetDefault();
// Show an error if there is no GPIO controller
if (gpio == null)
{
return null;
}
var pin = gpio.OpenPin(GPIOpin);
if (pin == null)
{
return null;
}
pin.SetDriveMode(mode);
pin.Write(HiLow);
return pin;
}
//*******************************************************************************
//
//! Reverses the bit order.- Since the bit reversal function is called
//! frequently by the several driver function this function is implemented
//! to maximize code execution
//
// { 0x00,0x08,0x04,0x0C,0x02,0x0A,0x06,0x0E,0x01,0x09,0x05,0x0D,0x03,0x0B,0x07,0x0F }
//
//*******************************************************************************
static byte Sharp96x96_reverse(byte x)
{
byte b = 0;
b = (byte)(reverse_data[x & 0xF] << 4);
b |= reverse_data[(x & 0xF0) >> 4];
return b;
}
//*****************************************************************************
//
//! Initialize DisplayBuffer.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//!
//! \param ulValue is the foreground color of the buffered data.
//!
//! This function initializes the display buffer and discards any cached data.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_InitializeDisplayBuffer(sharp96Display display, int ulValue)
{
UInt16 i = 0, j = 0;
for (i = 0; i < LCD_VERTICAL_MAX; i++)
for (j = 0; j < (LCD_HORIZONTAL_MAX >> 3); j++)
display.DisplayBuffer[i, j] = (byte)ulValue;
}
//*****************************************************************************
//
//! Draws a pixel on the screen.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX is the X coordinate of the pixel.
//! \param lY is the Y coordinate of the pixel.
//! \param ulValue is the color of the pixel.
//!
//! This function sets the given pixel to a particular color. The coordinates
//! of the pixel are assumed to be within the extents of the display.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_PixelDraw(sharp96Display display, UInt16 lX, UInt16 lY, int ulValue)
{
if ((lY >= LCD_VERTICAL_MAX) || (lX >= LCD_HORIZONTAL_MAX)) return;
if (ClrBlack == ulValue)
{
display.DisplayBuffer[lY, lX >> 3] &= (byte)(~(0x80 >> (lX & 0x7)));
}
else
{
display.DisplayBuffer[lY, lX >> 3] |= (byte)((0x80 >> (lX & 0x7)));
}
}
//*****************************************************************************
//
//! Draws a horizontal line.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX1 is the X coordinate of the start of the line.
//! \param lX2 is the X coordinate of the end of the line.
//! \param lY is the Y coordinate of the line.
//! \param ulValue is the color of the line.
//!
//! This function draws a horizontal line on the display. The coordinates of
//! the line are assumed to be within the extents of the display.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_LineDrawH(sharp96Display display, int lX1, int lX2, int lY, int ulValue)
{
// sanity check
if(lX1 > lX2) { int temp = lX2; lX2 = lX1; lX1 = temp; }
int xi = 0;
int x_index_min = lX1 >> 3;
int x_index_max = lX2 >> 3;
int ucfirst_x_byte, uclast_x_byte;
//calculate first byte
//mod by 8 and shift this # bits
ucfirst_x_byte = (byte)(0xFF >> (lX1 & 0x7));
//calculate last byte
//mod by 8 and shift this # bits
uclast_x_byte = (byte)(0xFF << (7 - (lX2 & 0x7)));
try
{
//check if more than one data byte
if (x_index_min != x_index_max)
{
//black pixels (clear bits)
if (ClrBlack == ulValue)
{
//write first byte
display.DisplayBuffer[lY, x_index_min] &= (byte)(~ucfirst_x_byte);
//write middle bytes
for (xi = x_index_min; xi < x_index_max - 1; xi++)
{
display.DisplayBuffer[lY, xi + 1] = 0x00;
}
//write last byte
display.DisplayBuffer[lY, xi + 1] &= (byte)(~uclast_x_byte);
}
//white pixels (set bits)
else
{
//write first byte
display.DisplayBuffer[lY, xi] |= (byte)(ucfirst_x_byte);
//write middle bytes
for (xi = x_index_min; xi < x_index_max - 1; xi++)
{
display.DisplayBuffer[lY, xi + 1] = 0xFF;
}
//write last byte
display.DisplayBuffer[lY, xi + 1] |= (byte)(uclast_x_byte);
}
}
//only one data byte
else
{
//calculate value of single byte
ucfirst_x_byte &= uclast_x_byte;
//draw black pixels (clear bits)
if (ClrBlack == ulValue)
{
display.DisplayBuffer[lY, xi] &= (byte)(~ucfirst_x_byte);
}
//white pixels (set bits)
else
{
display.DisplayBuffer[lY, xi] |= (byte)(ucfirst_x_byte);
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
//*****************************************************************************
//
//! Draws a vertical line.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX is the X coordinate of the line.
//! \param lY1 is the Y coordinate of the start of the line.
//! \param lY2 is the Y coordinate of the end of the line.
//! \param ulValue is the color of the line.
//!
//! This function draws a vertical line on the display. The coordinates of the
//! line are assumed to be within the extents of the display.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_LineDrawV(sharp96Display display, int lX, int lY1, int lY2, int ulValue)
{
int yi = 0;
int x_index = lX >> 3;
int data_byte;
//calculate data byte
//mod by 8 and shift this # bits
data_byte = (0x80 >> (lX & 0x7));
//write data to the display buffer
for (yi = lY1; yi <= lY2; yi++)
{
//black pixels (clear bits)
if (ClrBlack == ulValue)
{
display.DisplayBuffer[yi,x_index] &= (byte)(~data_byte);
}
//white pixels (set bits)
else
{
display.DisplayBuffer[yi,x_index] |= (byte)(data_byte);
}
}
}
//*****************************************************************************
//
//! Draws a line.
//!
//! \param context is a pointer to the drawing context to use.
//! \param x1 is the X coordinate of the start of the line.
//! \param y1 is the Y coordinate of the start of the line.
//! \param x2 is the X coordinate of the end of the line.
//! \param y2 is the Y coordinate of the end of the line.
//!
//! This function draws a line, utilizing Sharp96x96_LineDrawH() and
//! Sharp96x96_LineDrawV() to draw the line as efficiently as possible.
//! it proceeds using Bresenham's line drawing algorithm.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_drawLine(sharp96Display display, UInt16 x1, UInt16 y1, UInt16 x2, UInt16 y2, int ulValue)
{
UInt16 error, deltaX, deltaY;
int yStep;
bool SwapXY;
// is this a vertical line as we can call an optimized routine for it
if(x1 == x2)
{
Sharp96x96_LineDrawV(display, x1, y1, y2, ulValue);
return;
}
// is this a horizontal line as we can call an optimized routine for it
if (y1 == y2)
{
Sharp96x96_LineDrawH(display, x1, y1, y2, ulValue);
return;
}
// Determine if the line is steep. A steep line has more motion in the Y
// direction than the X direction.
if(((y2 > y1) ? (y2 - y1) : (y1 - y2)) > ((x2 > x1) ? (x2 - x1) : (x1 - x2)))
{
SwapXY = true;
}
else
{
SwapXY = false;
}
// If the line is steep, then swap the X and Y coordinates.
if(SwapXY)
{
error = x1;
x1 = y1;
y1 = error;
error = x2;
x2 = y2;
y2 = error;
}
//
// If the starting X coordinate is larger than the ending X coordinate,
// then swap the start and end coordinates.
//
if(x1 > x2)
{
error = x1;
x1 = x2;
x2 = error;
error = y1;
y1 = y2;
y2 = error;
}
// Compute the difference between the start and end coordinates in each axis.
deltaX = (UInt16)(x2 - x1);
deltaY = (UInt16)((y2 > y1) ? (y2 - y1) : (y1 - y2));
// Initialize the error term to negative half the X delta.
error = (UInt16)(-deltaX / 2);
// Determine the direction to step in the Y axis when required.
if(y1<y2) yStep = 1;
else yStep = -1;
// Loop through all the points along the X axis of the line.
for(; x1 <= x2; x1++)
{
// See if this is a steep line.
if(SwapXY)
{
// Plot this point of the line, swapping the X and Y coordinates.
Sharp96x96_PixelDraw(display, y1, x1, ulValue);
}
else
{
// Plot this point of the line, using the coordinates as is.
Sharp96x96_PixelDraw(display, x1, y1, ulValue);
}
// Increment the error term by the Y delta.
error += deltaY;
// See if the error term is now greater than zero.
if(error > 0)
{
// Take a step in the Y axis.
y1 = (UInt16)(y1 + yStep); // this could be a - or a + step
// Decrement the error term by the X delta.
error -= deltaX;
}
}
}
//*****************************************************************************
//
//! Fills a rectangle.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param pRect is a pointer to the structure describing the rectangle.
//! \param ulValue is the color of the rectangle.
//!
//! This function fills a rectangle on the display. The coordinates of the
//! rectangle are assumed to be within the extents of the display, and the
//! rectangle specification is fully inclusive (in other words, both sXMin and
//! sXMax are drawn, along with sYMin and sYMax).
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_RectFill(sharp96Display display, Graphics_Rectangle pRect, int ulValue)
{
// Bounds Checking, if there all out, simply return, if any are in then do what can be done
if ((pRect.XMin >= LCD_HORIZONTAL_MAX) && (pRect.YMin >= LCD_VERTICAL_MAX) && (pRect.XMax >= LCD_HORIZONTAL_MAX) && (pRect.YMax >= LCD_VERTICAL_MAX)) return;
if (pRect.XMin >= LCD_HORIZONTAL_MAX) pRect.XMin = LCD_HORIZONTAL_MAX-1;
if (pRect.YMin >= LCD_VERTICAL_MAX) pRect.YMin = LCD_VERTICAL_MAX-1;
if (pRect.XMax >= LCD_HORIZONTAL_MAX) pRect.XMax = LCD_HORIZONTAL_MAX-1;
if (pRect.YMax >= LCD_VERTICAL_MAX) pRect.YMax = LCD_VERTICAL_MAX-1;
int xi = 0;
int yi = 0;
int x_index_min = pRect.XMin >> 3;
int x_index_max = pRect.XMax >> 3;
byte ucfirst_x_byte, uclast_x_byte;
//calculate first byte
//mod by 8 and shift this # bits
ucfirst_x_byte = (byte)(0xFF >> (pRect.XMin & 0x7));
//calculate last byte
//mod by 8 and shift this # bits
uclast_x_byte = (byte)(0xFF << (7-(pRect.XMax & 0x7)));
//check if more than one data byte
if(x_index_min != x_index_max)
{
//write bytes
for (yi = pRect.YMin; yi<= pRect.YMax; yi++)
{
//black pixels (clear bits)
if(ClrBlack == ulValue)
{
//write first byte
display.DisplayBuffer[yi, x_index_min] &= (byte)~ucfirst_x_byte;
//write middle bytes
for(xi = x_index_min+1; xi<x_index_max; xi++)
{
display.DisplayBuffer[yi, xi] = 0x00;
}
//write last byte
display.DisplayBuffer[yi, x_index_max] &= (byte)~uclast_x_byte;
}
//white pixels (set bits)
else
{
//write first byte
display.DisplayBuffer[yi, x_index_min] |= (byte)ucfirst_x_byte;
//write middle bytes
for(xi = x_index_min+1; xi<x_index_max; xi++)
{
display.DisplayBuffer[yi, xi] = 0xFF;
}
//write last byte
display.DisplayBuffer[yi, x_index_max] |= (byte)uclast_x_byte;
}
}
}
//only one data byte
else
{
//calculate value of single byte
ucfirst_x_byte &= uclast_x_byte;
//black pixels (clear bits)
if(ClrBlack == ulValue)
{
//write bytes
for (yi = pRect.YMin; yi <= pRect.YMax; yi++)
display.DisplayBuffer[yi, x_index_min] &= (byte)~ucfirst_x_byte;
}
//white pixels (set bits)
else
{
for (yi = pRect.YMin; yi <= pRect.YMax; yi++)
display.DisplayBuffer[yi, x_index_min] |= (byte)ucfirst_x_byte;
}
}
}
//*****************************************************************************
//
//! Flushes any cached drawing operations.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//!
//!
//! This functions flushes any cached drawing operations to the display. This
//! is useful when a local frame buffer is used for drawing operations, and the
//! flush would copy the local frame buffer to the display.
//! it builds a byte array for the call for a single refresh operation
//! for more info see http://www.sharpmemorylcd.com/resources/programming_memory_lcd_app_note.pdf
//! \return None.
//
//*****************************************************************************
public static async Task Sharp96x96_Flush(sharp96Display display)
{
byte xi = 0;
byte yi = 0;
//image update mode(1X000000b)
int bufferIndex = 0;
int command = SHARP_LCD_CMD_WRITE_LINE;
//COM inversion bit
command = command ^ VCOMbit;
writeBuffer1346[bufferIndex++] = (byte)command;
flagSendToggleVCOMCommand = SHARP_SKIP_TOGGLE_VCOM_COMMAND;
for (yi = 0; yi < LCD_VERTICAL_MAX; yi++) // Vertical (y)
{
writeBuffer1346[bufferIndex++] = Sharp96x96_reverse((byte)(yi+1)) ; // Write row Address
for (xi = 0; xi < (LCD_HORIZONTAL_MAX >> 3); xi++) // Horizontal (x) - should be 12 bytes for 96 bits
{
writeBuffer1346[bufferIndex++] = display.DisplayBuffer[yi, xi];
}
writeBuffer1346[bufferIndex++] = SHARP_LCD_TRAILER_BYTE;
}
writeBuffer1346[bufferIndex] = SHARP_LCD_TRAILER_BYTE; // this is the last byte so it will overflow with a ++
HAL_LCD_writeCommandOrData(display, writeBuffer1346);
}
//*****************************************************************************
//
//! Send command to clear screen.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param ulValue is the background color of the buffered data.
//!
//! This function sets every pixel to the background color.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_ClearScreen(sharp96Display display, int ulValue)
{
Sharp96x96_ClearScreen(display);
if (ClrBlack == ulValue)
Sharp96x96_InitializeDisplayBuffer(display, SHARP_BLACK);
else
Sharp96x96_InitializeDisplayBuffer(display, SHARP_WHITE);
Sharp96x96_Flush(display);
}
public static void Sharp96x96_ClearScreen(sharp96Display display)
{
//clear screen mode(0X100000b)
int command = SHARP_LCD_CMD_CLEAR_SCREEN;
//COM inversion bit
command = command ^ VCOMbit;
writeBuffer2[0] = (byte)command;
writeBuffer2[1] = SHARP_LCD_TRAILER_BYTE;
HAL_LCD_writeCommandOrData(display, writeBuffer2);
flagSendToggleVCOMCommand = SHARP_SKIP_TOGGLE_VCOM_COMMAND;
}
//*****************************************************************************
//
// Writes command or data to the LCD Driver
//
// \param ucCmdData is the 8 or 16 bit command to send to the LCD driver
// Uses the SET_LCD_DATA macro
//
// \return None
//
//*****************************************************************************
static void HAL_LCD_writeCommandOrData(sharp96Display display, byte[] command)
{
try
{// ignoring real SS lines CS0 and CS1 as there wrong polarity
display.PIN.Write(GpioPinValue.High);
SpiGPIO.Write(command);
display.PIN.Write(GpioPinValue.Low);
}
catch (Exception e)
{
throw new Exception(e.InnerException.Message); // Help Me !!!!
}
}
//*****************************************************************************
//
//! Send toggle VCOM command.
//!
//! This function toggles the state of VCOM which prevents a DC bias from being
//! built up within the panel.
//!
//! \return None.
//
//*****************************************************************************
public static void Sharp96x96_SendToggleVCOMCommand(sharp96Display display)
{
display.VCOMbit ^= SHARP_VCOM_TOGGLE_BIT;
if (SHARP_SEND_TOGGLE_VCOM_COMMAND == flagSendToggleVCOMCommand)
{
//clear screen mode(0X100000b)
int command = SHARP_LCD_CMD_CHANGE_VCOM;
//COM inversion bit
command = command ^ display.VCOMbit;
writeBuffer2[0] = (byte)command;
writeBuffer2[1] = SHARP_LCD_TRAILER_BYTE;
HAL_LCD_writeCommandOrData(display,writeBuffer2);
}
flagSendToggleVCOMCommand = SHARP_SEND_TOGGLE_VCOM_COMMAND;
}
public static void Arc(sharp96Display display, UInt16 lX, UInt16 lY, UInt16 Radius, UInt16 startAngle, UInt16 endAngle, int ulValue)
{
ArcEx(display, lX, lY, Radius, startAngle, endAngle, 0.1, ulValue);
}
public static void ArcEx(sharp96Display display, UInt16 lX, UInt16 lY, UInt16 Radius, UInt16 startAngle, UInt16 endAngle, double increment, int ulValue)
{
double startA = startAngle;
double endA = endAngle;
if (startA > 360) startA = 360;
if (endA > 360) endA = 360;
if (increment > 10) increment = 10.0;
if (increment < 0.1) increment = 0.1;
for (double i = startA; i < endA; i += increment)
{
double angle = i * System.Math.PI / 180;
byte x = (byte)(lX + Radius * System.Math.Sin(angle));
byte y = (byte)(lY - Radius * System.Math.Cos(angle));
Sharp96x96_PixelDraw(display, x, y, ulValue);
}
}
public static void DrawCircle(sharp96Display display, UInt16 x0, UInt16 y0, UInt16 radius, int ulValue)
{
// Based on https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
int x = radius;
int y = 0;
int decisionOver2 = 1 - x; // Decision criterion divided by 2 evaluated at x=r, y=0
while (x >= y)
{
Sharp96x96_PixelDraw(display, (UInt16)(x + x0), (UInt16)(y + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(y + x0), (UInt16)(x + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(-x + x0), (UInt16)(y + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(-y + x0), (UInt16)(x + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(-x + x0), (UInt16)(-y + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(-y + x0), (UInt16)(-x + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(x + x0), (UInt16)(-y + y0), ulValue);
Sharp96x96_PixelDraw(display, (UInt16)(y + x0), (UInt16)(-x + y0), ulValue);
y++;
if (decisionOver2 <= 0)
{
decisionOver2 += 2 * y + 1; // Change in decision criterion for y -> y+1
}
else
{
x--;
decisionOver2 += 2 * (y - x) + 1; // Change for y -> y+1, x -> x-1
}
}
}
public static void write(sharp96Display display, char[] c, byte textsize, int ulValue)
{
for (int len = 0; len < c.Length; len++)
{
write(display, c[len], textsize, ulValue);
}
}
public static void write(sharp96Display display, char c, byte textsize, int ulValue)
{
// bounds check
if (display.cursorY >= LCD_VERTICAL_MAX) return; // were off the screen
if (c == '\n') // do we have a new line, if so simply adjust cursor position
{
display.cursorY += (byte)(textsize * 8); // next line based on font size
display.cursorX = 0; // back to character 0
}
else if (c == '\r')
{
display.cursorX = 0; // back to character 0
}
else
{
drawChar(display, display.cursorX, display.cursorY, (byte)c, textsize, ulValue);
display.cursorX += (byte)(textsize * 6);
if (AutoWrap && (display.cursorX > (LCD_HORIZONTAL_MAX - textsize * 6)))
{
display.cursorY += (byte)(textsize * 8); // next line based on font size
display.cursorX = 0; // back to character 0
}
}
}
public static void drawChar(sharp96Display display, UInt16 x, UInt16 y, byte c, UInt16 size, int ulValue)
{
if ((x >= LCD_HORIZONTAL_MAX) || (y >= LCD_VERTICAL_MAX) || ((x + 6 * size - 1) < 0) || ((y + 8 * size - 1) < 0))
return; // bounds checks
for (byte i = 0; i < 6; i++) // 5*7 font + space
{
UInt16 line; // index into character font array
if (i == 5) line = 0x0; // space between characters
else line = DisplayFont.font[((c * 5) + i)];
for (byte j = 0; j < 8; j++) // now process each bit of the character
{// we have a bit and normal colour or no bit and inverted
if (
(((line & 0x1)==1) && (ulValue != 0)) || (((line & 0x1) == 0) && (ulValue == 0))
) // do we have a bit and black pixels (clear bits)
{
if (size == 1) // default size
Sharp96x96_PixelDraw(display, (UInt16)(x + i),(UInt16)(y + j), 0xFF);
else
{ // scaled up font
Graphics_Rectangle pRect;
pRect.XMin = x + (i * size);
pRect.YMin = y + (j * size);
pRect.XMax = pRect.XMin + size-1;
pRect.YMax = pRect.YMin + size-1;
Sharp96x96_RectFill(display, pRect, 0xFF);
}
}
else // no bit so clear it, we need to handle reverse colour at some point
{
if (size == 1) // default size
Sharp96x96_PixelDraw(display, (UInt16)(x + i),(UInt16)(y + j), 0x00);
else
{ // big size
Graphics_Rectangle pRect;
pRect.XMin = x + (i * size);
pRect.YMin = y + (j * size);
pRect.XMax = pRect.XMin + size-1;
pRect.YMax = pRect.YMin + size-1;
Sharp96x96_RectFill(display, pRect, 0x00);
}
}
line >>= 1; // next bit in the line
}
}
}
public static void setCursor(sharp96Display display, UInt16 x, UInt16 y)
{
// needs some bounds checking
display.cursorX = (byte)x;
display.cursorY = (byte)y;
}
}
}
Comments