/// <summary> /// Displays nodes in their current configuration for the specified duration. /// </summary> /// <param name="duration">Time to display segment.</param> public void DisplaySegment(TimeSpan duration) { /* * Cases to consider * node.Cathode == _lastNode.Cathode * node.Cathode == _lastNode.Anode -- drop low * node.Anode == _lastNode.Cathode * node.Anode == _lastNode.Anode * node.Anode != _lastNode.Cathode | _lastNode.Anode * node.Cathode != _lastNode.Cathode | _lastNode.Anode */ Stopwatch watch = Stopwatch.StartNew(); do { for (int i = 0; i < _nodes.Length; i++) { CharlieplexSegmentNode node = _nodes[i]; // skip updating pinmode when possible if (_lastNode.Anode != node.Anode && _lastNode.Anode != node.Cathode) { _gpioController.SetPinMode(_lastNode.Anode, PinMode.Input); } if (_lastNode.Cathode != node.Anode && _lastNode.Cathode != node.Cathode) { _gpioController.SetPinMode(_lastNode.Cathode, PinMode.Input); } if (node.Cathode != _lastNode.Anode && node.Cathode != _lastNode.Cathode) { _gpioController.SetPinMode(node.Cathode, PinMode.Output); } if (node.Anode != _lastNode.Anode && node.Anode != _lastNode.Cathode) { _gpioController.SetPinMode(node.Anode, PinMode.Output); } _gpioController.Write(node.Anode, node.Value); // It is necessary to sleep for the LED to be seen with full brightness // It may be possible to sleep less than 1ms -- this API has ms granularity Thread.Sleep(1); _gpioController.Write(node.Anode, 0); _lastNode.Anode = node.Anode; _lastNode.Cathode = node.Cathode; } }while (watch.Elapsed < duration); }
/// <summary> /// Displays nodes in their current configuration for the specified duration. /// </summary> /// <param name="token">CancellationToken used to signal when method should exit.</param> public void Display(CancellationToken token) { /* * Cases to consider * node.Cathode == _lastNode.Cathode * node.Cathode == _lastNode.Anode -- drop low * node.Anode == _lastNode.Cathode * node.Anode == _lastNode.Anode * node.Anode != _lastNode.Cathode | _lastNode.Anode * node.Cathode != _lastNode.Cathode | _lastNode.Anode */ while (!token.IsCancellationRequested) { for (int i = 0; i < _nodes.Length; i++) { CharlieplexSegmentNode node = _nodes[i]; // skip updating pinmode when possible if (_lastNode.Anode != node.Anode && _lastNode.Anode != node.Cathode) { _gpioController.SetPinMode(_lastNode.Anode, PinMode.Input); } if (_lastNode.Cathode != node.Anode && _lastNode.Cathode != node.Cathode) { _gpioController.SetPinMode(_lastNode.Cathode, PinMode.Input); } if (node.Cathode != _lastNode.Anode && node.Cathode != _lastNode.Cathode) { _gpioController.SetPinMode(node.Cathode, PinMode.Output); } if (node.Anode != _lastNode.Anode && node.Anode != _lastNode.Cathode) { _gpioController.SetPinMode(node.Anode, PinMode.Output); } _gpioController.Write(node.Anode, node.Value); // It is necessary to sleep for the LED to be seen with full brightness Thread.SpinWait(1); _gpioController.Write(node.Anode, 0); _lastNode.Anode = node.Anode; _lastNode.Cathode = node.Cathode; } } }
/// <summary> /// Initializes a new Charlieplex type that can be use for multiplex over a relatively small number of GPIO pins. /// </summary> /// <param name="pins">The set of pins to use.</param> /// <param name="nodeCount">The count of nodes (like LEDs) that will be addressable. If 0, then the Charlieplex maximum is used for the pins provided (n^2-n).</param> /// <param name="gpioController">The GPIO Controller used for interrupt handling.</param> /// <param name="shouldDispose">True (the default) if the GPIO controller shall be disposed when disposing this instance.</param> public CharlieplexSegment(int[] pins, int nodeCount = 0, GpioController?gpioController = null, bool shouldDispose = true) { if (pins.Length < 2) { throw new ArgumentException($"{nameof(CharlieplexSegment)}: 2 or more pins must be provided."); } int charlieCount = (int)Math.Pow(pins.Length, 2) - pins.Length; if (nodeCount > charlieCount) { throw new ArgumentException($"{nameof(CharlieplexSegment)}: maximum count is {charlieCount} based on {pins.Length} pins. {nodeCount} was specified as the count."); } if (nodeCount == 0) { nodeCount = charlieCount; } if (gpioController is null) { gpioController = new GpioController(); } // first two pins will be needed as Output. gpioController.OpenPin(pins[0], PinMode.Output); gpioController.OpenPin(pins[1], PinMode.Output); // remaining pins should be input type // prevents participating in the circuit until needed for (int i = 2; i < pins.Length; i++) { gpioController.OpenPin(pins[i], PinMode.Input); } _lastNode = new CharlieplexSegmentNode() { Anode = pins[1], Cathode = pins[0] }; _controller = gpioController; _shouldDispose = shouldDispose; _pins = pins; _nodeCount = nodeCount; _nodes = GetNodes(pins, nodeCount); }
/// <summary> /// Provides the set of Charlie nodes given the set of pins and the count provided. /// If count = 0, then the Charlieplex maximum is used for the pins provided (n^2-n). /// </summary> /// <param name="pins">The pins to use for the segment.</param> /// <param name="nodeCount">The number of nodes to use. Default is the Charlieplex maximum.</param> public static CharlieplexSegmentNode[] GetNodes(int[] pins, int nodeCount = 0) { int pinCount = pins.Length; if (nodeCount == 0) { nodeCount = (int)Math.Pow(pinCount, 2) - pinCount; } CharlieplexSegmentNode[] nodes = new CharlieplexSegmentNode[nodeCount]; int pin = 0; int pinJump = 1; int resetCount = pinCount - 1; bool firstLeg = false; for (int i = 0; i < nodeCount; i++) { if ((pin > 0 && pin % resetCount == 0) || pin + pinJump > resetCount) { pin = 0; pinJump++; } CharlieplexSegmentNode node = new CharlieplexSegmentNode(); if (!firstLeg) { node.Anode = pins[pin]; node.Cathode = pins[pin + pinJump]; firstLeg = true; } else { node.Anode = pins[pin + pinJump]; node.Cathode = pins[pin]; firstLeg = false; pin++; } nodes[i] = node; } return(nodes); }