// Raspberry Pi LPC1114 I/O Processor Expansion Board
// SPI Agent Firmware Extension for Microsoft Small Basic
// Copyright (C)2015-2018, Philip Munts, President, Munts AM Corp.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
using Microsoft.SmallBasic.Library;
using System;
using System.Collections.Generic;
using System.Net;
namespace SPIAgent
{
///
/// The SPIAgent object allows you to dispatch commands to the Raspberry Pi LPC1114 I/O Processor Expansion Board SPI Agent Firmware.
///
[SmallBasicType]
public static partial class SPIAgent
{
private static Transport server;
private static int fwversion = 0;
private static int response_cmd = 0;
private static int response_pin = 0;
private static int response_data = 0;
private static int response_error = 0;
// The following lookup tables map strings from Small Basic to integers
private static Dictionary command_table = new Dictionary(StringComparer.OrdinalIgnoreCase);
private static Dictionary pin_table = new Dictionary(StringComparer.OrdinalIgnoreCase);
private static Dictionary data_table = new Dictionary(StringComparer.OrdinalIgnoreCase);
private static SPIAGENT_COMMAND_MSG_t cmdmsg = new SPIAGENT_COMMAND_MSG_t();
private static SPIAGENT_RESPONSE_MSG_t respmsg = new SPIAGENT_RESPONSE_MSG_t();
static SPIAgent()
{
// Populate the command lookup table
command_table.Add("NOP", (int)Commands.SPIAGENT_CMD_NOP);
command_table.Add("LOOPBACK", (int)Commands.SPIAGENT_CMD_LOOPBACK);
command_table.Add("CONFIGURE_ANALOG_INPUT", (int)Commands.SPIAGENT_CMD_CONFIGURE_ANALOG_INPUT);
command_table.Add("CONFIGURE_GPIO_INPUT", (int)Commands.SPIAGENT_CMD_CONFIGURE_GPIO_INPUT);
command_table.Add("CONFIGURE_GPIO_OUTPUT", (int)Commands.SPIAGENT_CMD_CONFIGURE_GPIO_OUTPUT);
command_table.Add("CONFIGURE_PWM_OUTPUT", (int)Commands.SPIAGENT_CMD_CONFIGURE_PWM_OUTPUT);
command_table.Add("GET_ANALOG", (int)Commands.SPIAGENT_CMD_GET_ANALOG);
command_table.Add("GET_GPIO", (int)Commands.SPIAGENT_CMD_GET_GPIO);
command_table.Add("PUT_GPIO", (int)Commands.SPIAGENT_CMD_PUT_GPIO);
command_table.Add("PUT_PWM", (int)Commands.SPIAGENT_CMD_PUT_PWM);
command_table.Add("CONFIGURE_GPIO_INTERRUPT", (int)Commands.SPIAGENT_CMD_CONFIGURE_GPIO_INTERRUPT);
command_table.Add("CONFIGURE_GPIO", (int)Commands.SPIAGENT_CMD_CONFIGURE_GPIO);
command_table.Add("PUT_LEGORC", (int)Commands.SPIAGENT_CMD_PUT_LEGORC);
command_table.Add("GET_SFR", (int)Commands.SPIAGENT_CMD_GET_SFR);
command_table.Add("PUT_SFR", (int)Commands.SPIAGENT_CMD_PUT_SFR);
command_table.Add("CONFIGURE_TIMER_MODE", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MODE);
command_table.Add("CONFIGURE_TIMER_PRESCALER", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_PRESCALER);
command_table.Add("CONFIGURE_TIMER_CAPTURE", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_CAPTURE);
command_table.Add("CONFIGURE_TIMER_MATCH0", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH0);
command_table.Add("CONFIGURE_TIMER_MATCH1", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH1);
command_table.Add("CONFIGURE_TIMER_MATCH2", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH2);
command_table.Add("CONFIGURE_TIMER_MATCH3", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH3);
command_table.Add("CONFIGURE_TIMER_MATCH0_VALUE", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH0_VALUE);
command_table.Add("CONFIGURE_TIMER_MATCH1_VALUE", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH1_VALUE);
command_table.Add("CONFIGURE_TIMER_MATCH2_VALUE", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH2_VALUE);
command_table.Add("CONFIGURE_TIMER_MATCH3_VALUE", (int)Commands.SPIAGENT_CMD_CONFIGURE_TIMER_MATCH3_VALUE);
command_table.Add("GET_TIMER_VALUE", (int)Commands.SPIAGENT_CMD_GET_TIMER_VALUE);
command_table.Add("GET_TIMER_CAPTURE", (int)Commands.SPIAGENT_CMD_GET_TIMER_CAPTURE);
command_table.Add("GET_TIMER_CAPTURE_DELTA", (int)Commands.SPIAGENT_CMD_GET_TIMER_CAPTURE_DELTA);
command_table.Add("INIT_TIMER", (int)Commands.SPIAGENT_CMD_INIT_TIMER);
// Populate the pin lookup table
pin_table.Add("GPIO0", Pins.LPC1114_GPIO0);
pin_table.Add("GPIO1", Pins.LPC1114_GPIO1);
pin_table.Add("GPIO2", Pins.LPC1114_GPIO2);
pin_table.Add("GPIO3", Pins.LPC1114_GPIO3);
pin_table.Add("GPIO4", Pins.LPC1114_GPIO4);
pin_table.Add("GPIO5", Pins.LPC1114_GPIO5);
pin_table.Add("GPIO6", Pins.LPC1114_GPIO6);
pin_table.Add("GPIO7", Pins.LPC1114_GPIO7);
pin_table.Add("INT", Pins.LPC1114_INT);
pin_table.Add("LED", Pins.LPC1114_LED);
pin_table.Add("AD1", Pins.LPC1114_AD1);
pin_table.Add("AD2", Pins.LPC1114_AD2);
pin_table.Add("AD3", Pins.LPC1114_AD3);
pin_table.Add("AD4", Pins.LPC1114_AD4);
pin_table.Add("AD5", Pins.LPC1114_AD5);
pin_table.Add("PWM1", Pins.LPC1114_PWM1);
pin_table.Add("PWM2", Pins.LPC1114_PWM2);
pin_table.Add("PWM3", Pins.LPC1114_PWM3);
pin_table.Add("PWM4", Pins.LPC1114_PWM4);
pin_table.Add("CT32B0_CAP0", Pins.LPC1114_CT32B0_CAP0);
pin_table.Add("CT32B1_CAP0", Pins.LPC1114_CT32B1_CAP0);
pin_table.Add("CT32B1_MAT0", Pins.LPC1114_CT32B1_MAT0);
pin_table.Add("CT32B1_MAT1", Pins.LPC1114_CT32B1_MAT1);
pin_table.Add("CT32B1_MAT2", Pins.LPC1114_CT32B1_MAT2);
pin_table.Add("CT32B1_MAT3", Pins.LPC1114_CT32B1_MAT3);
// Timer ID's
pin_table.Add("CT32B0", (int)Timer.ID.CT32B0);
pin_table.Add("CT32B1", (int)Timer.ID.CT32B1);
// Special function registers
pin_table.Add("DEVICE_ID", (int)SFR.LPC1114_DEVICE_ID);
pin_table.Add("GPIO1DATA", (int)SFR.LPC1114_GPIO1DATA);
pin_table.Add("U0SCR", (int)SFR.LPC1114_U0SCR);
// Populate the data value lookup table
// GPIO pin states
data_table.Add("OFF", 0);
data_table.Add("ON", 1);
data_table.Add("FALSE", 0);
data_table.Add("TRUE", 1);
data_table.Add("PULLDOWN", 0);
data_table.Add("PULLUP", 1);
// GPIO pin modes
data_table.Add("INPUT", (int)GPIO.MODE.INPUT);
data_table.Add("INPUT_PULLDOWN", (int)GPIO.MODE.INPUT_PULLDOWN);
data_table.Add("INPUT_PULLUP", (int)GPIO.MODE.INPUT_PULLUP);
data_table.Add("OUTPUT", (int)GPIO.MODE.OUTPUT);
data_table.Add("OUTPUT_OPENDRAIN", (int)GPIO.MODE.OUTPUT_OPENDRAIN);
// Timer settings
data_table.Add("ENABLED", 1);
data_table.Add("DISABLED", (int)Timer.MODE.DISABLED);
data_table.Add("RESET", (int)Timer.MODE.RESET);
data_table.Add("PCLK", (int)Timer.MODE.PCLK);
data_table.Add("CAP0_RISING", (int)Timer.MODE.CAP0_RISING);
data_table.Add("CAP0_FALLING", (int)Timer.MODE.CAP0_FALLING);
data_table.Add("CAP0_BOTH", (int)Timer.MODE.CAP0_BOTH);
data_table.Add("CLEAR", (int)Timer.MATCH_OUTPUT.CLEAR);
data_table.Add("SET", (int)Timer.MATCH_OUTPUT.SET);
data_table.Add("TOGGLE", (int)Timer.MATCH_OUTPUT.TOGGLE);
// LEGO(R) RC motors
data_table.Add("All Stop", (int)LEGORC.MOTOR.ALLSTOP);
data_table.Add("Motor A", (int)LEGORC.MOTOR.MOTORA);
data_table.Add("Motor B", (int)LEGORC.MOTOR.MOTORB);
data_table.Add("Combo Direct", (int)LEGORC.MOTOR.COMBODIRECT);
data_table.Add("Comobo PWM", (int)LEGORC.MOTOR.COMBOPWM);
// LEGO(R) RC directions
data_table.Add("Reverse", (int)LEGORC.DIRECTION.REVERSE);
data_table.Add("Forward", (int)LEGORC.DIRECTION.FORWARD);
}
private static void ClearResponse(int error)
{
response_cmd = 0;
response_pin = 0;
response_data = 0;
response_error = error;
}
private static int ConvertPrimitive(Primitive Item, Dictionary table, int MinValue, int MaxValue)
{
string s = Item.ToString();
int result;
// Convert from string to integer
try
{
if (s.StartsWith("0x"))
{
result = Convert.ToInt32(s, 16);
}
else if (Char.IsDigit(s[0]))
{
result = int.Parse(s);
}
else if (table != null)
{
result = table[s];
}
else
{
ClearResponse((int)errno.EINVAL);
return 0;
}
}
catch
{
ClearResponse((int)errno.EINVAL);
return 0;
}
// Check bounds
if ((result < MinValue) || (result > MaxValue))
{
ClearResponse((int)errno.EINVAL);
return 0;
}
// Return result
ClearResponse((int)errno.EOK);
return result;
}
private static void CommandWrapper(int cmd, int pin, int data)
{
// Check for open connection to the server
if (server == null)
{
ClearResponse((int)errno.EBADF);
return;
}
// Try to dispatch the command message
try
{
cmdmsg.command = cmd;
cmdmsg.pin = pin;
cmdmsg.data = data;
server.Command(cmdmsg, ref respmsg);
response_cmd = respmsg.command;
response_pin = respmsg.pin;
response_data = respmsg.data;
response_error = respmsg.error;
}
catch
{
ClearResponse((int)errno.EIO);
}
}
///
/// Opens a connection to the Raspberry Pi SPI Agent server.
///
/// Domain name or IP address of the Raspberry Pi SPI Agent server.
public static void Open(Primitive servername)
{
// Check for previous open
if (server != null)
{
ClearResponse((int)errno.EBUSY);
return;
}
try
{
server = new Transport(servername);
}
catch
{
server = null;
fwversion = 0;
ClearResponse((int)errno.EIO);
return;
}
// Query the LPC1114 SPI Agent firmware version number
CommandWrapper((int)Commands.SPIAGENT_CMD_NOP, 0, 0);
if (response_error != 0)
{
server = null;
fwversion = 0;
return;
}
// Initialize subsystem state variables
AnalogInitialize();
PWMInitialize();
// Save firmware version, and clear flotsam
fwversion = response_data;
ClearResponse((int)errno.EOK);
}
///
/// Closes the connection to the Raspberry Pi SPI Agent server.
///
public static void Close()
{
// Check for previous open
if (server == null)
{
fwversion = 0;
ClearResponse((int)errno.EBADF);
return;
}
// Initialize subsystem state variables
AnalogInitialize();
PWMInitialize();
// Discard the transport object, and clear flotsam
server = null;
fwversion = 0;
ClearResponse((int)errno.EOK);
}
///
/// Dispatches a command to the SPI Agent Firmware.
///
/// SPI Agent command code
/// LPC1114 GPIO pin number
/// Data value for the command
public static void Command(Primitive cmd, Primitive pin, Primitive data)
{
// Convert parameters to integer
int c = ConvertPrimitive(cmd, command_table, 0, (int)Commands.SENTINEL - 1);
if (response_error != (int)errno.EOK) return;
int p = ConvertPrimitive(pin, pin_table, 0, 0x7FFFFFFF);
if (response_error != (int)errno.EOK) return;
int d = ConvertPrimitive(data, data_table, 0, 0x7FFFFFFF);
if (response_error != (int)errno.EOK) return;
// Try to dispatch the command message
CommandWrapper(c, p, d);
}
///
/// SPI Agent firmware version number
///
public static Primitive version
{
get { return fwversion; }
}
///
/// Command code returned by the SPI Agent Firmware.
///
public static Primitive cmd
{
get { return response_cmd; }
}
///
/// Pin number returned by the SPI Agent Firmware.
///
public static Primitive pin
{
get { return response_pin; }
}
///
/// Data value returned by the SPI Agent Firmware.
///
public static Primitive data
{
get { return response_data; }
}
///
/// Data value in hexadecimal returned by the SPI Agent Firmware.
///
public static Primitive datahex
{
get { return "0x" + response_data.ToString("X"); }
}
///
/// Error code returned by the SPI Agent Firmware. Zero if the last command executed successfully.
///
public static Primitive error
{
get { return response_error; }
}
}
}