├── rpitest
├── run.sh
├── global.json
├── nuget.config
├── rpitest.csproj
├── publish.bat
├── .vscode
│ ├── tasks.json
│ └── launch.json
└── Program.cs
├── LCDDisplay
├── run.sh
├── global.json
├── nuget.config
├── Mcp23xxx
│ ├── Port.cs
│ ├── OpCode.cs
│ ├── Bank.cs
│ ├── Mcp23017.cs
│ ├── Mcp23018.cs
│ ├── Mcp23S18.cs
│ ├── Mcp23S17.cs
│ ├── Mcp23009.cs
│ ├── Mcp23S09.cs
│ ├── Mcp23S08.cs
│ ├── Mcp230xx.cs
│ ├── Mcp23Sxx.cs
│ ├── Mcp23008.cs
│ ├── Mcp23xxx.cs
│ └── Register.cs
├── LCDDisplay.csproj
├── publish.bat
├── Lcm1602a1
│ ├── Commands.cs
│ ├── Flags.cs
│ └── Lcm1602a1.cs
├── .vscode
│ ├── tasks.json
│ └── launch.json
└── Program.cs
├── LICENSE
└── .gitignore
/rpitest/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | sudo chmod +x rpitest
3 | ./rpitest
--------------------------------------------------------------------------------
/LCDDisplay/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | sudo chmod +x LCDDisplay
3 | ./LCDDisplay
--------------------------------------------------------------------------------
/rpitest/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "2.2.100"
4 | }
5 | }
--------------------------------------------------------------------------------
/LCDDisplay/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "2.2.100"
4 | }
5 | }
--------------------------------------------------------------------------------
/rpitest/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LCDDisplay/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Port.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | namespace Iot.Device.Mcp23xxx
6 | {
7 | public enum Port
8 | {
9 | PortA,
10 | PortB
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/rpitest/rpitest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.2
6 |
7 |
8 |
9 | -->
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/LCDDisplay/LCDDisplay.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/rpitest/publish.bat:
--------------------------------------------------------------------------------
1 | dotnet publish -r linux-arm /p:ShowLinkerSizeComparison=true
2 | pushd .\bin\Debug\netcoreapp2.2\linux-arm\publish
3 |
4 | REM Publish the .NET Core Framework ONCE, then just move the EXE
5 |
6 | REM Putty
7 | REM pscp -pw raspberry -v -r .\* pi@crowpi.lan:/home/pi/Desktop/rpitest
8 | REM pscp -pw raspberry -v -r .\rpitest pi@crowpi.lan:/home/pi/Desktop/rpitest
9 |
10 | REM Built-in OpenSSH
11 | REM scp -r .\* pi@crowpi.lan:/home/pi/Desktop/rpitest
12 | scp -r .\rpitest* pi@crowpi.lan:/home/pi/Desktop/rpitest
13 |
14 | popd
--------------------------------------------------------------------------------
/LCDDisplay/publish.bat:
--------------------------------------------------------------------------------
1 | dotnet publish -r linux-arm /p:ShowLinkerSizeComparison=true
2 | pushd .\bin\Debug\netcoreapp2.2\linux-arm\publish
3 |
4 | REM Publish the .NET Core Framework ONCE, then just move the EXE
5 |
6 | REM Putty
7 | REM pscp -pw raspberry -v -r .\* pi@crowpi.lan:/home/pi/Desktop/LCDDisplay
8 | REM pscp -pw raspberry -v -r .\LCDDisplay pi@crowpi.lan:/home/pi/Desktop/LCDDisplay
9 |
10 | REM Built-in OpenSSH
11 | REM scp -r .\* pi@crowpi.lan:/home/pi/Desktop/LCDDisplay
12 | scp -r .\LCDDisplay* pi@crowpi.lan:/home/pi/Desktop/LCDDisplay
13 |
14 | popd
--------------------------------------------------------------------------------
/LCDDisplay/Lcm1602a1/Commands.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | namespace Iot.Device.Lcm1602a1
6 | {
7 | internal enum Commands
8 | {
9 | LCD_CLEARDISPLAY = 0x01,
10 | LCD_RETURNHOME = 0x02,
11 | LCD_ENTRYMODESET = 0x04,
12 | LCD_DISPLAYCONTROL = 0x08,
13 | LCD_CURSORSHIFT = 0x10,
14 | LCD_FUNCTIONSET = 0x20,
15 | LCD_SETCGRAMADDR = 0x40,
16 | LCD_SETDDRAMADDR = 0x80
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/OpCode.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | namespace Iot.Device.Mcp23xxx
6 | {
7 | public static class OpCode
8 | {
9 | public static byte GetOpCode(int deviceAddress, bool isReadCommand)
10 | {
11 | int opCode = deviceAddress << 1;
12 |
13 | if (isReadCommand)
14 | {
15 | opCode |= 0b000_0001; // Set read bit.
16 | }
17 |
18 | return (byte)opCode;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/rpitest/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/rpitest.csproj"
11 | ],
12 | "problemMatcher": "$msCompile"
13 | },
14 | {
15 | "label": "publish",
16 | "type": "shell",
17 | "dependsOn": "build",
18 | "presentation": {
19 | "reveal": "always",
20 | "panel": "new"
21 | },
22 | "options": {
23 | "cwd": "${workspaceFolder}"
24 | },
25 | "windows": {
26 | "command": "${cwd}\\publish.bat"
27 | },
28 | "problemMatcher": []
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/LCDDisplay/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/LCDDisplay.csproj"
11 | ],
12 | "problemMatcher": "$msCompile"
13 | },
14 | {
15 | "label": "publish",
16 | "type": "shell",
17 | "dependsOn": "build",
18 | "presentation": {
19 | "reveal": "always",
20 | "panel": "new"
21 | },
22 | "options": {
23 | "cwd": "${workspaceFolder}"
24 | },
25 | "windows": {
26 | "command": "${cwd}\\publish.bat"
27 | },
28 | "problemMatcher": []
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Bank.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | namespace Iot.Device.Mcp23xxx
6 | {
7 | ///
8 | /// The MCP28XXX family has an address mapping concept for accessing registers.
9 | /// This provides a way to easily address registers by group or type.
10 | ///
11 | public enum Bank
12 | {
13 | ///
14 | /// This mode is used specifically for 16-bit devices where it causes the
15 | /// address pointer to toggle between associated A/B register pairs.
16 | ///
17 | Bank0 = 0,
18 | ///
19 | /// This mode is used to group each port's registers together.
20 | /// This mode is the default since 8-bit devices only have one port and
21 | /// 16-bit devices are initialized in this state.
22 | ///
23 | Bank1 = 1
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Scott Hanselman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/rpitest/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Device.Gpio;
4 | using System.Device.Gpio.Drivers;
5 |
6 | namespace rpitest
7 | {
8 | class Program
9 | {
10 | static void Main(string[] args)
11 | {
12 | Console.WriteLine("Hello World!");
13 | GpioController controller = new GpioController(PinNumberingScheme.Board);
14 | var pin = 37;
15 | var lightTime = 300;
16 | var dimTime = 300;
17 |
18 | controller.OpenPin(pin, PinMode.Output);
19 | try
20 | {
21 | while (true)
22 | {
23 | Console.WriteLine($"Light for {lightTime}ms");
24 | controller.Write(pin, PinValue.High);
25 | Thread.Sleep(lightTime);
26 | Console.WriteLine($"Dim for {dimTime}ms");
27 | controller.Write(pin, PinValue.Low);
28 | Thread.Sleep(dimTime);
29 | }
30 | }
31 | finally
32 | {
33 | controller.ClosePin(pin);
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23017.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.I2c;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23017 : Mcp230xx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23017 device.
13 | /// A general purpose parallel I/O expansion for I2C applications.
14 | ///
15 | /// I2C device used for communication.
16 | /// Output pin number that is connected to the hardware reset.
17 | /// Input pin number that is connected to the interrupt for Port A (INTA).
18 | /// Input pin number that is connected to the interrupt for Port B (INTB).
19 | public Mcp23017(I2cDevice i2cDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
20 | : base(i2cDevice, reset, interruptA, interruptB)
21 | {
22 | }
23 |
24 | public override int PinCount => 16;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23018.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.I2c;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23018 : Mcp230xx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23018 device.
13 | /// A general purpose parallel I/O expansion for I2C applications.
14 | ///
15 | /// I2C device used for communication.
16 | /// Output pin number that is connected to the hardware reset.
17 | /// Input pin number that is connected to the interrupt for Port A (INTA).
18 | /// Input pin number that is connected to the interrupt for Port B (INTB).
19 | public Mcp23018(I2cDevice i2cDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
20 | : base(i2cDevice, reset, interruptA, interruptB)
21 | {
22 | }
23 |
24 | public override int PinCount => 16;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/LCDDisplay/Lcm1602a1/Flags.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System;
6 |
7 | namespace Iot.Device.Lcm1602a1
8 | {
9 | [Flags]
10 | public enum DisplayFlags : byte
11 | {
12 | // Flags for display entry mode
13 | LCD_ENTRYRIGHT = 0x00,
14 | LCD_ENTRYLEFT = 0x02,
15 | LCD_ENTRYSHIFTINCREMENT = 0x01,
16 | LCD_ENTRYSHIFTDECREMENT = 0x00,
17 |
18 | // Flags for display on/off control
19 | LCD_DISPLAYON = 0x04,
20 | LCD_DISPLAYOFF = 0x00,
21 | LCD_CURSORON = 0x02,
22 | LCD_CURSOROFF = 0x00,
23 | LCD_BLINKON = 0x01,
24 | LCD_BLINKOFF = 0x00,
25 |
26 | // Flags for display/cursor shift
27 | LCD_DISPLAYMOVE = 0x08,
28 | LCD_CURSORMOVE = 0x00,
29 | LCD_MOVERIGHT = 0x04,
30 | LCD_MOVELEFT = 0x00,
31 |
32 | // Flags for function set
33 | LCD_8BITMODE = 0x10,
34 | LCD_4BITMODE = 0x00,
35 | LCD_2LINE = 0x08,
36 | LCD_1LINE = 0x00,
37 | LCD_5x10DOTS = 0x04,
38 | LCD_5x8DOTS = 0x00
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23S18.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.Spi;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23S18 : Mcp23Sxx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23S18 device.
13 | /// A general purpose parallel I/O expansion for SPI applications.
14 | ///
15 | /// The device address for the connection on the SPI bus.
16 | /// SPI device used for communication.
17 | /// Output pin number that is connected to the hardware reset.
18 | /// Input pin number that is connected to the interrupt for Port A (INTA).
19 | /// Input pin number that is connected to the interrupt for Port B (INTB).
20 | public Mcp23S18(int deviceAddress, SpiDevice spiDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
21 | : base(deviceAddress, spiDevice, reset, interruptA, interruptB)
22 | {
23 | }
24 |
25 | public override int PinCount => 16;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23S17.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.Spi;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23S17 : Mcp23Sxx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23xxx device.
13 | /// A general purpose parallel I/O expansion for I2C or SPI applications.
14 | ///
15 | /// The device address for the connection on the I2C or SPI bus.
16 | /// SPI device used for communication.
17 | /// Output pin number that is connected to the hardware reset.
18 | /// Input pin number that is connected to the interrupt for Port A (INTA).
19 | /// Input pin number that is connected to the interrupt for Port B (INTB).
20 | public Mcp23S17(int deviceAddress, SpiDevice spiDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
21 | : base(deviceAddress, spiDevice, reset, interruptA, interruptB)
22 | {
23 | }
24 |
25 | public override int PinCount => 16;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23009.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.I2c;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23009 : Mcp230xx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23009 device.
13 | /// A general purpose parallel I/O expansion for I2C applications.
14 | ///
15 | /// I2C device used for communication.
16 | /// Output pin number that is connected to the hardware reset.
17 | /// Input pin number that is connected to the interrupt for Port A (INTA).
18 | /// Input pin number that is connected to the interrupt for Port B (INTB).
19 | public Mcp23009(I2cDevice i2cDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
20 | : base(i2cDevice, reset, interruptA, interruptB)
21 | {
22 | }
23 |
24 | public override int PinCount => 8;
25 |
26 | public byte Read(Register.Address registerAddress)
27 | {
28 | return Read(registerAddress, Port.PortA, Bank.Bank1);
29 | }
30 |
31 | public byte[] Read(Register.Address startingRegisterAddress, byte byteCount)
32 | {
33 | return Read(startingRegisterAddress, byteCount, Port.PortA, Bank.Bank1);
34 | }
35 |
36 | public void Write(Register.Address registerAddress, byte data)
37 | {
38 | Write(registerAddress, data, Port.PortA, Bank.Bank1);
39 | }
40 |
41 | public void Write(Register.Address startingRegisterAddress, byte[] data)
42 | {
43 | Write(startingRegisterAddress, data, Port.PortA, Bank.Bank1);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/rpitest/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (remote console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "publish",
12 | "program": "/home/pi/dotnet/dotnet",
13 | "args": ["/home/pi/Desktop/rpitest/rpitest.dll"],
14 | "cwd": "/home/pi/Desktop/rpitest",
15 | "stopAtEntry": false,
16 | "console": "internalConsole",
17 | "pipeTransport": {
18 | "pipeCwd": "${workspaceFolder}",
19 | "pipeProgram": "ssh.exe",
20 | "pipeArgs": [
21 | "root@crowpi.lan"
22 | ],
23 | "debuggerPath": "/home/pi/vsdbg/vsdbg"
24 | }
25 | },
26 | {
27 | "name": ".NET Core Launch (console)",
28 | "type": "coreclr",
29 | "request": "launch",
30 | "preLaunchTask": "build",
31 | // If you have changed target frameworks, make sure to update the program path.
32 | "program": "${workspaceFolder}/bin/Debug/netcoreapp2.2/rpitest.dll",
33 | "args": [],
34 | "cwd": "${workspaceFolder}",
35 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
36 | "console": "internalConsole",
37 | "stopAtEntry": false,
38 | "internalConsoleOptions": "openOnSessionStart"
39 | },
40 | {
41 | "name": ".NET Core Attach",
42 | "type": "coreclr",
43 | "request": "attach",
44 | "processId": "${command:pickProcess}"
45 | }
46 | ,]
47 | }
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23S09.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.Spi;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23S09 : Mcp23Sxx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23S09 device.
13 | /// A general purpose parallel I/O expansion for SPI applications.
14 | ///
15 | /// The device address for the connection on the SPI bus.
16 | /// SPI device used for communication.
17 | /// Output pin number that is connected to the hardware reset.
18 | /// Input pin number that is connected to the interrupt for Port A (INTA).
19 | /// Input pin number that is connected to the interrupt for Port B (INTB).
20 | public Mcp23S09(int deviceAddress, SpiDevice spiDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
21 | : base(deviceAddress, spiDevice, reset, interruptA, interruptB)
22 | {
23 | }
24 |
25 | public override int PinCount => 8;
26 |
27 | public byte Read(Register.Address registerAddress)
28 | {
29 | return Read(registerAddress, Port.PortA, Bank.Bank1);
30 | }
31 |
32 | public byte[] Read(Register.Address startingRegisterAddress, byte byteCount)
33 | {
34 | return Read(startingRegisterAddress, byteCount, Port.PortA, Bank.Bank1);
35 | }
36 |
37 | public void Write(Register.Address registerAddress, byte data)
38 | {
39 | Write(registerAddress, data, Port.PortA, Bank.Bank1);
40 | }
41 |
42 | public void Write(Register.Address startingRegisterAddress, byte[] data)
43 | {
44 | Write(startingRegisterAddress, data, Port.PortA, Bank.Bank1);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23S08.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.Spi;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public class Mcp23S08 : Mcp23Sxx
10 | {
11 | ///
12 | /// Initializes new instance of Mcp23S08 device.
13 | /// A general purpose parallel I/O expansion for I2C or SPI applications.
14 | ///
15 | /// The device address for the connection on the SPI bus.
16 | /// SPI device used for communication.
17 | /// Output pin number that is connected to the hardware reset.
18 | /// Input pin number that is connected to the interrupt for Port A (INTA).
19 | /// Input pin number that is connected to the interrupt for Port B (INTB).
20 | public Mcp23S08(int deviceAddress, SpiDevice spiDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
21 | : base(deviceAddress, spiDevice, reset, interruptA, interruptB)
22 | {
23 | }
24 |
25 | public override int PinCount => 8;
26 |
27 | public byte Read(Register.Address registerAddress)
28 | {
29 | return Read(registerAddress, Port.PortA, Bank.Bank1);
30 | }
31 |
32 | public byte[] Read(Register.Address startingRegisterAddress, byte byteCount)
33 | {
34 | return Read(startingRegisterAddress, byteCount, Port.PortA, Bank.Bank1);
35 | }
36 |
37 | public void Write(Register.Address registerAddress, byte data)
38 | {
39 | Write(registerAddress, data, Port.PortA, Bank.Bank1);
40 | }
41 |
42 | public void Write(Register.Address startingRegisterAddress, byte[] data)
43 | {
44 | Write(startingRegisterAddress, data, Port.PortA, Bank.Bank1);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp230xx.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System.Device.I2c;
6 |
7 | namespace Iot.Device.Mcp23xxx
8 | {
9 | public abstract class Mcp230xx : Mcp23xxx
10 | {
11 | private readonly I2cDevice _i2cDevice;
12 |
13 | ///
14 | /// A general purpose parallel I/O expansion for I2C applications.
15 | ///
16 | /// I2C device used for communication.
17 | /// Output pin number that is connected to the hardware reset.
18 | /// Input pin number that is connected to the interrupt for Port A (INTA).
19 | /// Input pin number that is connected to the interrupt for Port B (INTB).
20 | public Mcp230xx(I2cDevice i2cDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
21 | : base(i2cDevice.ConnectionSettings.DeviceAddress, reset, interruptA, interruptB)
22 | {
23 | _i2cDevice = i2cDevice;
24 | }
25 |
26 | public override byte[] Read(Register.Address startingRegisterAddress, byte byteCount, Port port = Port.PortA, Bank bank = Bank.Bank1)
27 | {
28 | Write(startingRegisterAddress, new byte[] { }, port, bank); // Set address to register first.
29 |
30 | byte[] readBuffer = new byte[byteCount];
31 | _i2cDevice.Read(readBuffer);
32 | return readBuffer;
33 | }
34 |
35 | public override void Write(Register.Address startingRegisterAddress, byte[] data, Port port = Port.PortA, Bank bank = Bank.Bank1)
36 | {
37 | byte[] writeBuffer = new byte[data.Length + 1]; // Include Register Address.
38 | writeBuffer[0] = Register.GetMappedAddress(startingRegisterAddress, port, bank);
39 | data.CopyTo(writeBuffer, 1);
40 | _i2cDevice.Write(writeBuffer);
41 | }
42 |
43 | public override void Dispose()
44 | {
45 | _i2cDevice?.Dispose();
46 | base.Dispose();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/LCDDisplay/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Device.Gpio;
3 | using System.Device.Gpio.Drivers;
4 | using System.Device.I2c;
5 | using System.Device.I2c.Drivers;
6 | using System.Threading;
7 | using Iot.Device.Lcm1602a1;
8 | using Iot.Device.Mcp23xxx;
9 |
10 | namespace LCDDisplay
11 | {
12 | class Program
13 | {
14 | static void Main(string[] args)
15 | {
16 | // int[] dataPins = { 12, 11, 10, 9 };
17 | // int registerSelectPin = 15;
18 | // int enablePin = 13;
19 | // int readAndWritePin = 14;
20 | // DateTime xmas = new DateTime(2019, 12, 25);
21 | // CancellationTokenSource cts = new CancellationTokenSource();
22 | // using (var lcd = new Lcm1602c(registerSelectPin, enablePin, dataPins))
23 | // {
24 | // lcd.Clear(); //Clear in case there was a previous program that left some text on the screen
25 | // lcd.Begin(16, 2); //Initialize the lcd to use 2 rows, each with 16 characters.
26 |
27 | // lcd.Print("X-Mas Countdown"); //Print string on first row.
28 |
29 | // Console.CancelKeyPress += (o, e) => // Add handler for when the program should be terminated.
30 | // {
31 | // cts.Cancel();
32 | // };
33 |
34 | // while (!cts.Token.IsCancellationRequested) // Loop until Ctr-C is pressed.
35 | // {
36 | // lcd.SetCursor(0, 1);
37 | // TimeSpan countdown = xmas - DateTime.Now;
38 | // lcd.Print($"");
39 | // }
40 | // }
41 |
42 | Mcp23008 mcpDevice = new Mcp23008(new UnixI2cDevice(new I2cConnectionSettings(1, 0x21)));
43 | int[] dataPins = { 3, 4, 5, 6 };
44 | int registerSelectPin = 1;
45 | int enablePin = 2;
46 | int backlightPin = 7;
47 | using (Lcm1602a1 lcd = new Lcm1602a1(mcpDevice, registerSelectPin, -1, enablePin, backlightPin, dataPins))
48 | {
49 | lcd.Clear();
50 | lcd.Begin(16, 2);
51 |
52 | lcd.Print("Hello World!");
53 | lcd.SetCursor(0, 1);
54 | lcd.Print(".NET Core");
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/LCDDisplay/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (remote console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "publish",
12 | "program": "/home/pi/dotnet/dotnet",
13 | "args": [
14 | "/home/pi/Desktop/LCDDisplay/LCDDisplay.dll"
15 | ],
16 | "cwd": "/home/pi/Desktop/LCDDisplay",
17 | "stopAtEntry": false,
18 | "console": "internalConsole",
19 | "pipeTransport": {
20 | "pipeCwd": "${workspaceFolder}",
21 | "pipeProgram": "ssh.exe",
22 | "pipeArgs": [
23 | "root@crowpi.lan"
24 | ],
25 | "debuggerPath": "/home/pi/vsdbg/vsdbg"
26 | }
27 | //"pipeTransport": {
28 | // "pipeCwd": "${workspaceFolder}",
29 | // "pipeProgram": "${env:ChocolateyInstall}\\bin\\PLINK.EXE",
30 | // "pipeArgs": [
31 | // "-pw",
32 | // "raspberry",
33 | // "root@crowpi.lan"
34 | // ],
35 | // "debuggerPath": "/home/pi/vsdbg/vsdbg"
36 | },
37 | {
38 | "name": ".NET Core Launch (console)",
39 | "type": "coreclr",
40 | "request": "launch",
41 | "preLaunchTask": "build",
42 | // If you have changed target frameworks, make sure to update the program path.
43 | "program": "${workspaceFolder}/bin/Debug/netcoreapp2.2/LCDDisplay.dll",
44 | "args": [],
45 | "cwd": "${workspaceFolder}",
46 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
47 | "console": "internalConsole",
48 | "stopAtEntry": false,
49 | "internalConsoleOptions": "openOnSessionStart"
50 | },
51 | {
52 | "name": ".NET Core Attach",
53 | "type": "coreclr",
54 | "request": "attach",
55 | "processId": "${command:pickProcess}"
56 | },
57 | ]
58 | }
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23Sxx.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System;
6 | using System.Device.Spi;
7 |
8 | namespace Iot.Device.Mcp23xxx
9 | {
10 | public abstract class Mcp23Sxx : Mcp23xxx
11 | {
12 | private readonly SpiDevice _spiDevice;
13 |
14 | ///
15 | /// A general purpose parallel I/O expansion for SPI applications.
16 | ///
17 | /// The device address for the connection on the SPI bus.
18 | /// SPI device used for communication.
19 | /// Output pin number that is connected to the hardware reset.
20 | /// Input pin number that is connected to the interrupt for Port A (INTA).
21 | /// Input pin number that is connected to the interrupt for Port B (INTB).
22 | public Mcp23Sxx(int deviceAddress, SpiDevice spiDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
23 | : base(deviceAddress, reset, interruptA, interruptB)
24 | {
25 | _spiDevice = spiDevice;
26 | }
27 |
28 | public override byte[] Read(Register.Address startingRegisterAddress, byte byteCount, Port port = Port.PortA, Bank bank = Bank.Bank1)
29 | {
30 | byteCount += 2; // Include OpCode and Register Address.
31 | byte[] writeBuffer = new byte[byteCount];
32 | writeBuffer[0] = OpCode.GetOpCode(DeviceAddress, true);
33 | writeBuffer[1] = Register.GetMappedAddress(startingRegisterAddress, port, bank);
34 | byte[] readBuffer = new byte[byteCount];
35 |
36 | _spiDevice.TransferFullDuplex(writeBuffer, readBuffer);
37 | return readBuffer.AsSpan().Slice(2).ToArray(); // First 2 bytes are from sending OpCode and Register Address.
38 | }
39 |
40 | public override void Write(Register.Address startingRegisterAddress, byte[] data, Port port = Port.PortA, Bank bank = Bank.Bank1)
41 | {
42 | byte[] writeBuffer = new byte[data.Length + 2]; // Include OpCode and Register Address.
43 | writeBuffer[0] = OpCode.GetOpCode(DeviceAddress, false);
44 | writeBuffer[1] = Register.GetMappedAddress(startingRegisterAddress, port, bank);
45 | data.CopyTo(writeBuffer, 2);
46 |
47 | _spiDevice.Write(writeBuffer);
48 | }
49 |
50 | public override void Dispose()
51 | {
52 | _spiDevice?.Dispose();
53 | base.Dispose();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23008.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System;
6 | using System.Device.Gpio;
7 | using System.Device.I2c;
8 |
9 | namespace Iot.Device.Mcp23xxx
10 | {
11 | public class Mcp23008 : Mcp230xx
12 | {
13 | private byte _iodir;
14 | private byte _gpio;
15 |
16 | ///
17 | /// Initializes new instance of Mcp23008 device.
18 | /// A general purpose parallel I/O expansion for I2C applications.
19 | ///
20 | /// I2C device used for communication.
21 | /// Output pin number that is connected to the hardware reset.
22 | /// Input pin number that is connected to the interrupt for Port A (INTA).
23 | /// Input pin number that is connected to the interrupt for Port B (INTB).
24 | public Mcp23008(I2cDevice i2cDevice, int? reset = null, int? interruptA = null, int? interruptB = null)
25 | : base(i2cDevice, reset, interruptA, interruptB)
26 | {
27 | _iodir = 0xFF;
28 | _gpio = 0x00;
29 | Write(Register.Address.IODIR, _iodir);
30 | Write(Register.Address.GPIO, _gpio);
31 | }
32 |
33 | public override int PinCount => 8;
34 |
35 | public byte Read(Register.Address registerAddress)
36 | {
37 | return Read(registerAddress, Port.PortA, Bank.Bank1);
38 | }
39 |
40 | public byte[] Read(Register.Address startingRegisterAddress, byte byteCount)
41 | {
42 | return Read(startingRegisterAddress, byteCount, Port.PortA, Bank.Bank1);
43 | }
44 |
45 | public void SetPinMode(int pinNumber, PinMode mode)
46 | {
47 | ValidateMode(mode);
48 | ValidatePin(pinNumber);
49 |
50 | switch (mode)
51 | {
52 | case PinMode.Input:
53 | _iodir |= (byte)(1 << (pinNumber % 8));
54 | break;
55 | case PinMode.Output:
56 | _iodir &= (byte)(~(1 << (pinNumber % 8)));
57 | break;
58 | }
59 |
60 | Write(Register.Address.IODIR, _iodir);
61 | }
62 |
63 | public PinValue ReadPin(int pinNumber)
64 | {
65 | ValidatePin(pinNumber);
66 |
67 | _gpio = Read(Register.Address.GPIO);
68 | return ((_gpio & (1 << (pinNumber % 8))) > 0) ? PinValue.High : PinValue.Low;
69 | }
70 |
71 | public void WritePin(int pinNumber, PinValue value)
72 | {
73 | ValidatePin(pinNumber);
74 |
75 | switch (value)
76 | {
77 | case PinValue.High:
78 | _gpio |= (byte)(1 << (pinNumber % 8));
79 | break;
80 | case PinValue.Low:
81 | _gpio &= (byte)(~(1 << (pinNumber % 8)));
82 | break;
83 | }
84 | Write(Register.Address.GPIO, _gpio);
85 | }
86 |
87 | private static void ValidateMode(PinMode mode)
88 | {
89 | if (mode != PinMode.Input && mode != PinMode.Output)
90 | {
91 | throw new ArgumentException("Mcp supports Input and Output modes only.");
92 | }
93 | }
94 |
95 | private void ValidatePin(int pinNumber)
96 | {
97 | if (pinNumber >= PinCount || pinNumber < 0)
98 | {
99 | throw new ArgumentOutOfRangeException($"{pinNumber} is not a valid pin on the Mcp controller.");
100 | }
101 | }
102 |
103 | public void Write(Register.Address registerAddress, byte data)
104 | {
105 | Write(registerAddress, data, Port.PortA, Bank.Bank1);
106 | }
107 |
108 | public void Write(Register.Address startingRegisterAddress, byte[] data)
109 | {
110 | Write(startingRegisterAddress, data, Port.PortA, Bank.Bank1);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 | rpitest/crowpi.key
332 | rpitest/crowpi.key.pub
333 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Mcp23xxx.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System;
6 | using System.Device.Gpio;
7 |
8 | namespace Iot.Device.Mcp23xxx
9 | {
10 | public abstract class Mcp23xxx : IDisposable
11 | {
12 | protected int DeviceAddress { get; }
13 | private GpioController _masterGpioController;
14 | private readonly int? _reset;
15 | private readonly int? _interruptA;
16 | private readonly int? _interruptB;
17 |
18 | ///
19 | /// A general purpose parallel I/O expansion for I2C or SPI applications.
20 | ///
21 | /// The device address for the connection on the I2C or SPI bus.
22 | /// Output pin number that is connected to the hardware reset.
23 | /// Input pin number that is connected to the interrupt for Port A (INTA).
24 | /// Input pin number that is connected to the interrupt for Port B (INTB).
25 | public Mcp23xxx(int deviceAddress, int? reset = null, int? interruptA = null, int? interruptB = null)
26 | {
27 | ValidateDeviceAddress(deviceAddress);
28 |
29 | DeviceAddress = deviceAddress;
30 | _reset = reset;
31 | _interruptA = interruptA;
32 | _interruptB = interruptB;
33 |
34 | InitializeMasterGpioController();
35 | }
36 |
37 | internal static void ValidateBitNumber(int bitNumber)
38 | {
39 | if (bitNumber < 0 || bitNumber > 7)
40 | {
41 | throw new IndexOutOfRangeException("Invalid bit index.");
42 | }
43 | }
44 |
45 | internal static void ClearBit(ref byte data, int bitNumber)
46 | {
47 | ValidateBitNumber(bitNumber);
48 | data &= (byte)~(1 << bitNumber);
49 | }
50 |
51 | internal void SetBit(ref byte data, int bitNumber)
52 | {
53 | ValidateBitNumber(bitNumber);
54 | data |= (byte)(1 << bitNumber);
55 | }
56 |
57 | internal static bool GetBit(byte data, int bitNumber)
58 | {
59 | ValidateBitNumber(bitNumber);
60 | return ((data >> bitNumber) & 1) == 1;
61 | }
62 |
63 | private void ValidateDeviceAddress(int deviceAddress)
64 | {
65 | if (deviceAddress < 0x20 || deviceAddress > 0x27)
66 | {
67 | throw new ArgumentOutOfRangeException(nameof(deviceAddress), deviceAddress, "The Mcp23xxx address must be a value of 32 (0x20) - 39 (0x27).");
68 | }
69 | }
70 |
71 | private void InitializeMasterGpioController()
72 | {
73 | // Only need master controller if there are external pins provided.
74 | if (_reset != null || _interruptA != null || _interruptB != null)
75 | {
76 | _masterGpioController = new GpioController();
77 |
78 | if (_interruptA != null)
79 | {
80 | _masterGpioController.OpenPin((int)_interruptA, PinMode.Input);
81 | }
82 |
83 | if (_interruptB != null)
84 | {
85 | _masterGpioController.OpenPin((int)_interruptB, PinMode.Input);
86 | }
87 |
88 | if (_reset != null)
89 | {
90 | _masterGpioController.OpenPin((int)_reset, PinMode.Output);
91 | Disable();
92 | }
93 | }
94 | }
95 |
96 | public abstract int PinCount { get; }
97 |
98 | public abstract byte[] Read(Register.Address startingRegisterAddress, byte byteCount, Port port = Port.PortA, Bank bank = Bank.Bank1);
99 | public abstract void Write(Register.Address startingRegisterAddress, byte[] data, Port port = Port.PortA, Bank bank = Bank.Bank1);
100 |
101 | public virtual void Dispose()
102 | {
103 | if (_masterGpioController != null)
104 | {
105 | _masterGpioController.Dispose();
106 | _masterGpioController = null;
107 | }
108 | }
109 |
110 | public void Disable()
111 | {
112 | if (_masterGpioController == null)
113 | {
114 | throw new Exception("Master controller has not been initialized.");
115 | }
116 |
117 | if (_reset == null)
118 | {
119 | throw new Exception("Reset pin has not been initialized.");
120 | }
121 |
122 | _masterGpioController.Write((int)_reset, PinValue.Low);
123 | }
124 |
125 | public void Enable()
126 | {
127 | if (_masterGpioController == null)
128 | {
129 | throw new Exception("Master controller has not been initialized.");
130 | }
131 |
132 | if (_reset == null)
133 | {
134 | throw new Exception("Reset pin has not been initialized.");
135 | }
136 |
137 | _masterGpioController.Write((int)_reset, PinValue.High);
138 | }
139 |
140 | public byte Read(Register.Address registerAddress, Port port = Port.PortA, Bank bank = Bank.Bank1)
141 | {
142 | byte[] data = Read(registerAddress, 1, port, bank);
143 | return data[0];
144 | }
145 |
146 | public bool ReadBit(Register.Address registerAddress, int bitNumber, Port port = Port.PortA, Bank bank = Bank.Bank1)
147 | {
148 | ValidateBitNumber(bitNumber);
149 | byte data = Read(registerAddress, port, bank);
150 | return GetBit(data, bitNumber);
151 | }
152 |
153 | ///
154 | /// Read the pin value of interrupt for Port A (INTA).
155 | ///
156 | /// Pin value of interrupt for Port A (INTA).
157 | public PinValue ReadInterruptA()
158 | {
159 | if (_masterGpioController == null)
160 | {
161 | throw new Exception("Master controller has not been initialized.");
162 | }
163 |
164 | if (_reset == null)
165 | {
166 | throw new Exception("INTA pin has not been initialized.");
167 | }
168 |
169 | return _masterGpioController.Read((int)_interruptA);
170 | }
171 |
172 | ///
173 | /// Read the pin value of interrupt for Port B (INTB).
174 | ///
175 | /// Pin value of interrupt for Port B (INTB).
176 | public PinValue ReadInterruptB()
177 | {
178 | if (_masterGpioController == null)
179 | {
180 | throw new Exception("Master controller has not been initialized.");
181 | }
182 |
183 | if (_reset == null)
184 | {
185 | throw new Exception("INTB pin has not been initialized.");
186 | }
187 |
188 | return _masterGpioController.Read((int)_interruptB);
189 | }
190 |
191 | public void Write(Register.Address registerAddress, byte data, Port port = Port.PortA, Bank bank = Bank.Bank1)
192 | {
193 | Write(registerAddress, new byte[] { data }, port, bank);
194 | }
195 |
196 | public void WriteBit(Register.Address registerAddress, int bitNumber, bool bit, Port port = Port.PortA, Bank bank = Bank.Bank1)
197 | {
198 | ValidateBitNumber(bitNumber);
199 | byte data = Read(registerAddress, port, bank);
200 |
201 | if (bit)
202 | {
203 | SetBit(ref data, bitNumber);
204 | }
205 | else
206 | {
207 | ClearBit(ref data, bitNumber);
208 | }
209 |
210 | Write(registerAddress, data, port, bank);
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/LCDDisplay/Mcp23xxx/Register.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | namespace Iot.Device.Mcp23xxx
6 | {
7 | public class Register
8 | {
9 | public enum Address
10 | {
11 | ///
12 | /// Controls the direction of the data I/O.
13 | /// When a bit is set, the corresponding pin becomes an input.
14 | /// When a bit is clear, the corresponding pin becomes an output.
15 | ///
16 | IODIR = 0b0000_0000,
17 | ///
18 | /// Configures the polarity on the corresponding GPIO port bits.
19 | /// When a bit is set, the corresponding GPIO register bit will reflect the inverted value on the pin.
20 | ///
21 | IPOL = 0b0000_0001,
22 | ///
23 | /// Controls the interrupt-on-change feature for each pin.
24 | /// When a bit is set, the corresponding pin is enabled for interrupt-on-change.
25 | /// The DEFVAL and INTCON registers must also be configured if any pins are enabled for interrupt-on-change.
26 | ///
27 | GPINTEN = 0b0000_0010,
28 | ///
29 | /// Configures the default comparison value.
30 | /// If enabled (via GPINTEN and INTCON) to compare against the DEFVAL register,
31 | /// an opposite value on the associated pin will cause an interrupt to occur.
32 | ///
33 | DEFVAL = 0b0000_0011,
34 | ///
35 | /// Controls how the associated pin value is compared for the interrupt-on-change feature.
36 | /// When a bit is set, the corresponding I/O pin is compared against the associated bit in the DEFVAL register.
37 | /// When a bit value is clear, the corresponding I/O pin is compared against the previous value.
38 | ///
39 | INTCON = 0b0000_0100,
40 | ///
41 | /// Contains several bits for configuring the device. See respective datasheet for more details.
42 | ///
43 | IOCON = 0b0000_0101,
44 | ///
45 | /// Controls the pull-up resistors for the port pins.
46 | /// When a bit is set and the corresponding pin is configured as an input,
47 | /// the corresponding port pin is internally pulled up with a 100 kΩ resistor.
48 | ///
49 | GPPU = 0b0000_0110,
50 | ///
51 | /// Reflects the interrupt condition on the port pins of any pin that is enabled for interrupts via the GPINTEN register.
52 | /// A 'set' bit indicates that the associated pin caused the interrupt.
53 | /// This register is read-only. Writes to this register will be ignored.
54 | ///
55 | INTF = 0b0000_0111,
56 | ///
57 | /// The INTCAP register captures the GPIO port value at the time the interrupt occurred.
58 | /// The register is read-only and is updated only when an interrupt occurs.
59 | /// The register will remain unchanged until the interrupt is cleared via a read of INTCAP or GPIO.
60 | ///
61 | INTCAP = 0b0000_1000,
62 | ///
63 | /// Reflects the value on the port. Reading from this register reads the port.
64 | /// Writing to this register modifies the Output Latch (OLAT) register.
65 | ///
66 | GPIO = 0b0000_1001,
67 | ///
68 | /// Provides access to the output latches.
69 | /// A read from this register results in a read of the OLAT and not the port itself.
70 | /// A write to this register modifies the output latches that modify the pins configured as outputs.
71 | ///
72 | OLAT = 0b0000_1010
73 | }
74 |
75 | public static byte GetMappedAddress(Address address, Port port = Port.PortA, Bank bank = Bank.Bank1)
76 | {
77 | byte mappedAddress;
78 |
79 | if (bank == Bank.Bank1)
80 | {
81 | mappedAddress = GetMappedAddressBank1(address, port);
82 | }
83 | else
84 | {
85 | mappedAddress = GetMappedAddressBank0(address, port);
86 | }
87 |
88 | return mappedAddress;
89 | }
90 |
91 | private static byte GetMappedAddressBank0(Address address, Port port)
92 | {
93 | byte mappedAddress;
94 |
95 | if (port == Port.PortA)
96 | {
97 | mappedAddress = GetMappedAddressBank0PortA(address);
98 | }
99 | else
100 | {
101 | mappedAddress = GetMappedAddressBank0PortB(address);
102 | }
103 |
104 | return mappedAddress;
105 | }
106 |
107 | private static byte GetMappedAddressBank0PortA(Address address)
108 | {
109 | byte mappedAddress = 0;
110 |
111 | switch (address)
112 | {
113 | case Address.IODIR:
114 | mappedAddress = 0b0000_0000;
115 | break;
116 | case Address.IPOL:
117 | mappedAddress = 0b0000_0010;
118 | break;
119 | case Address.GPINTEN:
120 | mappedAddress = 0b0000_0100;
121 | break;
122 | case Address.DEFVAL:
123 | mappedAddress = 0b0000_0110;
124 | break;
125 | case Address.INTCON:
126 | mappedAddress = 0b0000_1000;
127 | break;
128 | case Address.IOCON:
129 | mappedAddress = 0b0000_1010;
130 | break;
131 | case Address.GPPU:
132 | mappedAddress = 0b0000_1100;
133 | break;
134 | case Address.INTF:
135 | mappedAddress = 0b0000_1110;
136 | break;
137 | case Address.INTCAP:
138 | mappedAddress = 0b0001_0000;
139 | break;
140 | case Address.GPIO:
141 | mappedAddress = 0b0001_0010;
142 | break;
143 | case Address.OLAT:
144 | mappedAddress = 0b0001_0100;
145 | break;
146 | }
147 |
148 | return mappedAddress;
149 | }
150 |
151 | private static byte GetMappedAddressBank0PortB(Address address)
152 | {
153 | byte mappedAddress = 0;
154 |
155 | switch (address)
156 | {
157 | case Address.IODIR:
158 | mappedAddress = 0b0000_0001;
159 | break;
160 | case Address.IPOL:
161 | mappedAddress = 0b0000_0011;
162 | break;
163 | case Address.GPINTEN:
164 | mappedAddress = 0b0000_0101;
165 | break;
166 | case Address.DEFVAL:
167 | mappedAddress = 0b0000_0111;
168 | break;
169 | case Address.INTCON:
170 | mappedAddress = 0b0000_1001;
171 | break;
172 | case Address.IOCON:
173 | mappedAddress = 0b0000_1011;
174 | break;
175 | case Address.GPPU:
176 | mappedAddress = 0b0000_1101;
177 | break;
178 | case Address.INTF:
179 | mappedAddress = 0b0000_1111;
180 | break;
181 | case Address.INTCAP:
182 | mappedAddress = 0b0001_0001;
183 | break;
184 | case Address.GPIO:
185 | mappedAddress = 0b0001_0011;
186 | break;
187 | case Address.OLAT:
188 | mappedAddress = 0b0001_0101;
189 | break;
190 | }
191 |
192 | return mappedAddress;
193 | }
194 |
195 | private static byte GetMappedAddressBank1(Address address, Port port)
196 | {
197 | byte mappedAddress = (byte)address;
198 |
199 | if (port == Port.PortB)
200 | {
201 | mappedAddress |= 0b0001_0000;
202 | }
203 |
204 | return mappedAddress;
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/LCDDisplay/Lcm1602a1/Lcm1602a1.cs:
--------------------------------------------------------------------------------
1 | // Licensed to the .NET Foundation under one or more agreements.
2 | // The .NET Foundation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System;
6 | using System.Device.Gpio;
7 | using System.Device.Gpio.Drivers;
8 | using System.Device.I2c;
9 | using System.Device.I2c.Drivers;
10 | using System.Diagnostics;
11 | using System.Runtime.CompilerServices;
12 | using System.Runtime.InteropServices;
13 | using Iot.Device.Mcp23xxx;
14 |
15 | namespace Iot.Device.Lcm1602a1
16 | {
17 | ///
18 | /// Supports Lcm1602c LCD controller. Ported from: https://github.com/adafruit/Adafruit_Python_CharLCD/blob/master/Adafruit_CharLCD/Adafruit_CharLCD.py
19 | ///
20 | public class Lcm1602a1 : IDisposable
21 | {
22 | // When the display powers up, it is configured as follows:
23 | //
24 | // 1. Display clear
25 | // 2. Function set:
26 | // DL = 1; 8-bit interface data
27 | // N = 0; 1-line display
28 | // F = 0; 5x8 dot character font
29 | // 3. Display on/off control:
30 | // D = 0; Display off
31 | // C = 0; Cursor off
32 | // B = 0; Blinking off
33 | // 4. Entry mode set:
34 | // I/D = 1; Increment by 1
35 | // S = 0; No shift
36 | //
37 | // Note, however, that resetting the device doesn't reset the LCD, so we
38 | // can't assume that its in that state when a sketch starts (and the
39 | // LiquidCrystal constructor is called).
40 |
41 | private readonly int _rsPin; // LOW: command. HIGH: character.
42 | private readonly int _rwPin; // LOW: write to LCD. HIGH: read from LCD.
43 | private readonly int _backlight;
44 | private readonly int _enablePin; // Activated by a HIGH pulse.
45 | private readonly int[] _dataPins;
46 | private readonly bool _usingMcp;
47 |
48 | private GpioController _controller;
49 | private readonly Mcp23008 _mcpController;
50 |
51 | private DisplayFlags _displayFunction;
52 | private DisplayFlags _displayControl;
53 | private DisplayFlags _displayMode;
54 |
55 | private byte _numLines;
56 | private readonly byte[] _rowOffsets;
57 |
58 | ///
59 | /// Initializes a new Lcm1602a1 display object without using a readWrite or backlight pin.
60 | /// This object will use the board's GPIO pins.
61 | ///
62 | /// The pin that controls the regsiter select.
63 | /// The pin that controls the enable switch.
64 | /// Collection of pins holding the data that will be printed on the screen.
65 | public Lcm1602a1(int registerSelect, int enable, int[] data)
66 | : this(registerSelect, -1, enable, -1, data)
67 | {
68 | // Do nothing
69 | }
70 |
71 | ///
72 | /// Initializes a new Lcm1602a1 display object.
73 | /// This object will use the board's GPIO pins.
74 | ///
75 | /// The pin that controls the regsiter select.
76 | /// The pin that controls the read and write switch.
77 | /// The pin that controls the enable switch.
78 | /// The pin that controls the backlight of the display.
79 | /// Collection of pins holding the data that will be printed on the screen.
80 | public Lcm1602a1(int registerSelect, int readWrite, int enable, int backlight, int[] data)
81 | : this (null, registerSelect, readWrite, enable, backlight, data)
82 | {
83 | // Do Nothing
84 | }
85 |
86 | ///
87 | /// Initializes a new Lcm1602a1 display object.
88 | /// This object will use the board's GPIO pins, or the McpController pins if one is provided.
89 | ///
90 | /// McpController that is used to provide the pins for the display. Null if we should use board's GPIO pins instead.
91 | /// The pin that controls the regsiter select.
92 | /// The pin that controls the read and write switch.
93 | /// The pin that controls the enable switch.
94 | /// The pin that controls the backlight of the display.
95 | /// Collection of pins holding the data that will be printed on the screen.
96 | public Lcm1602a1(Mcp23008 mcpController, int registerSelect, int readWrite, int enable, int backlight, int[] data)
97 | {
98 | _rwPin = readWrite;
99 | _rsPin = registerSelect;
100 | _enablePin = enable;
101 | _dataPins = data;
102 | _backlight = backlight;
103 |
104 | _rowOffsets = new byte[4];
105 |
106 | _displayFunction = DisplayFlags.LCD_1LINE | DisplayFlags.LCD_5x8DOTS;
107 |
108 | if (data.Length == 4)
109 | {
110 | _displayFunction |= DisplayFlags.LCD_4BITMODE;
111 | }
112 | else if (data.Length == 8)
113 | {
114 | _displayFunction |= DisplayFlags.LCD_8BITMODE;
115 | }
116 | else
117 | {
118 | throw new ArgumentException($"The length of the array given to parameter {nameof(data)} must be 4 or 8");
119 | }
120 |
121 | if (mcpController == null)
122 | {
123 | _controller = (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) ?
124 | new GpioController(PinNumberingScheme.Logical, new UnixDriver()) :
125 | new GpioController(PinNumberingScheme.Logical, new Windows10Driver());
126 | _usingMcp = false;
127 | }
128 | else
129 | {
130 | _mcpController = mcpController;
131 | _usingMcp = true;
132 | }
133 |
134 | OpenPin(_rsPin, PinMode.Input);
135 | if (_rwPin != -1)
136 | {
137 | OpenPin(_rwPin, PinMode.Input);
138 | }
139 | if (_backlight != -1)
140 | {
141 | OpenPin(_backlight, PinMode.Input);
142 | }
143 | OpenPin(_enablePin, PinMode.Input);
144 | foreach (int i in _dataPins)
145 | OpenPin(i, PinMode.Input);
146 | // By default, initialize the display with one row and 16 characters.
147 | Begin(16, 1);
148 | }
149 |
150 | private void OpenPin(int pinNumber, PinMode mode)
151 | {
152 | if (_usingMcp)
153 | {
154 | _mcpController.SetPinMode(pinNumber, mode);
155 | }
156 | else
157 | {
158 | _controller.OpenPin(pinNumber, mode);
159 | }
160 | }
161 |
162 | private void SetPinMode(int pinNumber, PinMode mode)
163 | {
164 | if (_usingMcp)
165 | {
166 | _mcpController.SetPinMode(pinNumber, mode);
167 | }
168 | else
169 | {
170 | _controller.SetPinMode(pinNumber, mode);
171 | }
172 | }
173 |
174 | private void Write(int pinNumber, PinValue value)
175 | {
176 | if (_usingMcp)
177 | {
178 | _mcpController.WritePin(pinNumber, value);
179 | }
180 | else
181 | {
182 | _controller.Write(pinNumber, value);
183 | }
184 | }
185 |
186 | public void Dispose()
187 | {
188 | if (_controller != null)
189 | {
190 | _controller.Dispose();
191 | _controller = null;
192 | }
193 | }
194 |
195 | public void Begin(byte cols, byte lines, DisplayFlags dotSize = DisplayFlags.LCD_5x8DOTS)
196 | {
197 | if (lines > 1)
198 | {
199 | _displayFunction |= DisplayFlags.LCD_2LINE;
200 | }
201 |
202 | _numLines = lines;
203 |
204 | SetRowOffsets(0x00, 0x40, 0x14, 0x54);
205 |
206 | // for some 1 line displays you can select a 10 pixel high font
207 | if ((dotSize != DisplayFlags.LCD_5x8DOTS) && (lines == 1))
208 | {
209 | _displayFunction |= DisplayFlags.LCD_5x10DOTS;
210 | }
211 |
212 | _displayControl = DisplayFlags.LCD_DISPLAYON | DisplayFlags.LCD_CURSOROFF | DisplayFlags.LCD_BLINKOFF;
213 | _displayMode = DisplayFlags.LCD_ENTRYLEFT | DisplayFlags.LCD_ENTRYSHIFTDECREMENT;
214 |
215 | SetPinMode(_rsPin, PinMode.Output);
216 | // we can save 1 pin by not using RW. Indicate by passing null instead of a pin
217 | if (_rwPin != -1)
218 | {
219 | SetPinMode(_rwPin, PinMode.Output);
220 | }
221 | if (_backlight != -1)
222 | {
223 | SetPinMode(_backlight, PinMode.Output);
224 | Write(_backlight, PinValue.High);
225 | }
226 | SetPinMode(_enablePin, PinMode.Output);
227 |
228 | // Do this just once, instead of every time a character is drawn (for speed reasons).
229 | for (int i = 0; i < _dataPins.Length; ++i)
230 | {
231 | SetPinMode(_dataPins[i], PinMode.Output);
232 | }
233 |
234 | // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
235 | // according to datasheet, we need at least 40ms after power rises above 2.7V
236 | // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
237 | DelayMicroseconds(50000);
238 |
239 | // Initialize the display.
240 | Write((byte)0x33);
241 | Write((byte)0x32);
242 |
243 | Write((byte)((byte)Commands.LCD_DISPLAYCONTROL| (byte)_displayControl));
244 | Write((byte)((byte)Commands.LCD_FUNCTIONSET | (byte)_displayFunction));
245 | Write((byte)((byte)Commands.LCD_ENTRYMODESET | (byte)_displayMode));
246 | Clear();
247 | }
248 |
249 | private void SetRowOffsets(byte row0, byte row1, byte row2, byte row3)
250 | {
251 | _rowOffsets[0] = row0;
252 | _rowOffsets[1] = row1;
253 | _rowOffsets[2] = row2;
254 | _rowOffsets[3] = row3;
255 | }
256 |
257 | #region High Level Surface Area
258 | public void Clear()
259 | {
260 | Command((byte)Commands.LCD_CLEARDISPLAY); // clear display, set cursor position to zero
261 | DelayMicroseconds(3000); // this command takes a long time!
262 | }
263 |
264 | public void Home()
265 | {
266 | Command((byte)Commands.LCD_RETURNHOME); // set cursor position to zero
267 | DelayMicroseconds(3000); // this command takes a long time!
268 | }
269 |
270 | public void SetCursor(byte col, byte row)
271 | {
272 | if (row >= _rowOffsets.Length)
273 | {
274 | row = (byte)(_rowOffsets.Length - 1); // we count rows starting w/0
275 | }
276 | if (row >= _numLines)
277 | {
278 | row = (byte)(_numLines - 1); // we count rows starting w/0
279 | }
280 |
281 | Command((byte)Commands.LCD_SETDDRAMADDR | (col + _rowOffsets[row]));
282 | }
283 |
284 | // Turn the display on/off (quickly)
285 | public void NoDisplay()
286 | {
287 | _displayControl &= ~DisplayFlags.LCD_DISPLAYON;
288 | Command((byte)Commands.LCD_DISPLAYCONTROL | (byte)_displayControl);
289 | }
290 |
291 | public void Display()
292 | {
293 | _displayControl |= DisplayFlags.LCD_DISPLAYON;
294 | Command((byte)Commands.LCD_DISPLAYCONTROL | (byte)_displayControl);
295 | }
296 |
297 | // Turns the underline cursor on/off
298 | public void NoCursor()
299 | {
300 | _displayControl &= ~DisplayFlags.LCD_CURSORON;
301 | Command((byte)Commands.LCD_DISPLAYCONTROL | (byte)_displayControl);
302 | }
303 |
304 | public void Cursor()
305 | {
306 | _displayControl |= DisplayFlags.LCD_CURSORON;
307 | Command((byte)Commands.LCD_DISPLAYCONTROL | (byte)_displayControl);
308 | }
309 |
310 | // Turn on and off the blinking cursor
311 | public void NoBlink()
312 | {
313 | _displayControl &= ~DisplayFlags.LCD_BLINKON;
314 | Command((byte)Commands.LCD_DISPLAYCONTROL | (byte)_displayControl);
315 | }
316 |
317 | public void Blink()
318 | {
319 | _displayControl |= DisplayFlags.LCD_BLINKON;
320 | Command((byte)Commands.LCD_DISPLAYCONTROL | (byte)_displayControl);
321 | }
322 |
323 | // These commands scroll the display without changing the RAM
324 | public void ScrollDisplayLeft()
325 | {
326 | Command((byte)Commands.LCD_CURSORSHIFT | (byte)DisplayFlags.LCD_DISPLAYMOVE | (byte)DisplayFlags.LCD_MOVELEFT);
327 | }
328 |
329 | public void ScrollDisplayRight()
330 | {
331 | Command((byte)Commands.LCD_CURSORSHIFT | (byte)DisplayFlags.LCD_DISPLAYMOVE | (byte)DisplayFlags.LCD_MOVERIGHT);
332 | }
333 |
334 | // This is for text that flows Left to Right
335 | public void LeftToRight()
336 | {
337 | _displayMode |= DisplayFlags.LCD_ENTRYLEFT;
338 | Command((byte)Commands.LCD_ENTRYMODESET | (byte)_displayMode);
339 | }
340 |
341 | // This is for text that flows Right to Left
342 | public void RightToLeft()
343 | {
344 | _displayMode &= ~DisplayFlags.LCD_ENTRYLEFT;
345 | Command((byte)Commands.LCD_ENTRYMODESET | (byte)_displayMode);
346 | }
347 |
348 | // This will 'right justify' text from the cursor
349 | public void Autoscroll()
350 | {
351 | _displayMode |= DisplayFlags.LCD_ENTRYSHIFTINCREMENT;
352 | Command((byte)Commands.LCD_ENTRYMODESET | (byte)_displayMode);
353 | }
354 |
355 | // This will 'left justify' text from the cursor
356 | public void NoAutoscroll()
357 | {
358 | _displayMode &= ~DisplayFlags.LCD_ENTRYSHIFTINCREMENT;
359 | Command((byte)Commands.LCD_ENTRYMODESET | (byte)_displayMode);
360 | }
361 |
362 | // Allows us to fill the first 8 CGRAM locations
363 | // with custom characters
364 | public void CreateChar(byte location, params byte[] charmap)
365 | {
366 | if (charmap.Length != 8)
367 | {
368 | throw new ArgumentException(nameof(charmap));
369 | }
370 |
371 | location &= 0x7; // we only have 8 locations 0-7
372 | Command((byte)Commands.LCD_SETCGRAMADDR | (location << 3));
373 |
374 | for (int i = 0; i < 8; i++)
375 | {
376 | Write(charmap[i]);
377 | }
378 | }
379 |
380 | public void BacklightOn()
381 | {
382 | if (_backlight != -1)
383 | {
384 | Write(_backlight, PinValue.High);
385 | }
386 | }
387 |
388 | public void BacklightOff()
389 | {
390 | if (_backlight != -1)
391 | {
392 | Write(_backlight, PinValue.Low);
393 | }
394 | }
395 |
396 | public void Print(string value)
397 | {
398 | for (int i = 0; i < value.Length; ++i)
399 | {
400 | Write(value[i]);
401 | }
402 | }
403 | #endregion // High Level Surface Area
404 |
405 | #region Mid Level Methods
406 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
407 | private void Command(int value)
408 | {
409 | Command((byte)value);
410 | }
411 |
412 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
413 | private void Command(byte value)
414 | {
415 | Send(value, PinValue.Low);
416 | }
417 |
418 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
419 | private void Write(char value)
420 | {
421 | Write((byte)value);
422 | }
423 |
424 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
425 | private void Write(byte value)
426 | {
427 | Send(value, PinValue.High);
428 | }
429 | #endregion // Mid Level Methods
430 |
431 | #region Low Level Methods
432 | // write either command or data, with automatic 4/8-bit selection
433 | private void Send(byte value, PinValue mode)
434 | {
435 | Write(_rsPin, mode);
436 |
437 | // if there is a RW pin indicated, set it low to Write
438 | if (_rwPin != -1)
439 | {
440 | Write(_rwPin, PinValue.Low);
441 | }
442 |
443 | if (_displayFunction.HasFlag(DisplayFlags.LCD_8BITMODE))
444 | {
445 | Write8bits(value);
446 | }
447 | else
448 | {
449 | Write4bits((byte)(value >> 4));
450 | Write4bits(value);
451 | }
452 | }
453 |
454 | private void PulseEnable()
455 | {
456 | Write(_enablePin, PinValue.Low);
457 | DelayMicroseconds(1);
458 | Write(_enablePin, PinValue.High);
459 | DelayMicroseconds(1); // enable pulse must be >450ns
460 | Write(_enablePin, PinValue.Low);
461 | DelayMicroseconds(100); // commands need > 37us to settle
462 | }
463 |
464 | private void Write4bits(byte value)
465 | {
466 | for (int i = 0; i < 4; i++)
467 | {
468 | DigitalWrite(_dataPins[i], ((value >> i) & 1));
469 | }
470 |
471 | PulseEnable();
472 | }
473 |
474 | private void Write8bits(byte value)
475 | {
476 | for (int i = 0; i < 8; i++)
477 | {
478 | DigitalWrite(_dataPins[i], ((value >> i) & 1));
479 | }
480 |
481 | PulseEnable();
482 | }
483 |
484 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
485 | private void DigitalWrite(int pin, int value)
486 | {
487 | PinValue state = (value == 1) ? PinValue.High : PinValue.Low;
488 | Write(pin, state);
489 | }
490 |
491 | private static void DelayMicroseconds(int microseconds)
492 | {
493 | Stopwatch sw = Stopwatch.StartNew();
494 | long v = (microseconds * System.Diagnostics.Stopwatch.Frequency) / 1000000;
495 | while (sw.ElapsedTicks < v)
496 | {
497 | // Do nothing
498 | }
499 | }
500 | #endregion // Low Level Methods
501 | }
502 | }
503 |
--------------------------------------------------------------------------------