// 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; } } } }