private bool UsageCanSharePins(PinUsage usage) { switch (usage) { // Several SPI devices can share the same Pins (because we're allocating devices, not buses) case PinUsage.Spi: return(true); default: return(false); } }
/// <summary> /// Gets the current alternate pin mode. (ALTn mode) /// </summary> /// <param name="pinNumber">Pin number, in the logical scheme</param> /// <returns>The current pin usage</returns> /// <remarks>This also works for closed pins, but then uses a bit of heuristics to get the correct mode</remarks> public override PinUsage DetermineCurrentPinUsage(int pinNumber) { if (_managedGpioController == null) { throw new InvalidOperationException("Board not initialized"); } PinUsage cached = base.DetermineCurrentPinUsage(pinNumber); if (cached != PinUsage.Unknown) { return(cached); } if (_raspberryPi3Driver == null || !_raspberryPi3Driver.AlternatePinModeSettingSupported) { throw new NotSupportedException("Alternate pin mode setting not supported by driver"); } var pinMode = _raspberryPi3Driver.GetAlternatePinMode(pinNumber); if (pinMode == RaspberryPi3Driver.AltMode.Input || pinMode == RaspberryPi3Driver.AltMode.Output) { return(PinUsage.Gpio); } // Do some heuristics: If the given pin number can be used for I2C with the same Alt mode, we can assume that's what it // it set to. var possibleAltMode = GetHardwareModeForPinUsage(pinNumber, PinUsage.I2c, DefaultPinNumberingScheme); if (possibleAltMode == pinMode) { return(PinUsage.I2c); } possibleAltMode = GetHardwareModeForPinUsage(pinNumber, PinUsage.Spi, DefaultPinNumberingScheme); if (possibleAltMode == pinMode) { return(PinUsage.Spi); } possibleAltMode = GetHardwareModeForPinUsage(pinNumber, PinUsage.Pwm, DefaultPinNumberingScheme); if (possibleAltMode == pinMode) { return(PinUsage.Pwm); } return(PinUsage.Unknown); }
/// <summary> /// Removes the reservation for a pin. /// See <see cref="ReservePin"/> for details. /// </summary> /// <param name="pinNumber">The pin number to free, in the numbering scheme of the board</param> /// <param name="usage">The current pin usage</param> /// <param name="owner">The current pin owner</param> /// <exception cref="InvalidOperationException">The pin is not reserved, or the owner is not correct</exception> public virtual void ReleasePin(int pinNumber, PinUsage usage, object owner) { if (!_initialized) { throw new InvalidOperationException("Cannot release a pin if board is not initialized."); } lock (_pinReservationsLock) { if (_pinReservations.TryGetValue(pinNumber, out List <PinReservation>?reservations)) { if (!UsageCanSharePins(usage)) { PinReservation reservation = reservations.Single(); if (reservation.Owner != owner || reservation.Usage != usage) { throw new InvalidOperationException($"Cannot release Pin {pinNumber}, because you are not the owner or the usage is wrong. Class {reservation.Owner} has reserved the Pin for {reservation.Usage}"); } _pinReservations.Remove(pinNumber); } else { PinReservation?reservation = reservations.FirstOrDefault(x => x.Owner == owner); if (reservation == null) { throw new InvalidOperationException($"Cannot release Pin {pinNumber}, because you are not a valid owner."); } if (reservation.Usage != usage) { throw new InvalidOperationException($"Cannot release Pin {pinNumber}, because you are not the owner or the usage is wrong. Class {reservation.Owner} has reserved the Pin for {reservation.Usage}"); } reservations.Remove(reservation); if (reservations.Count == 0) { _pinReservations.Remove(pinNumber); } } } else { throw new InvalidOperationException($"Cannot release Pin {pinNumber}, because it is not reserved."); } } }
/// <summary> /// Reserves a pin for a specific usage. This is done automatically if a known interface (i.e. GpioController) is /// used to open the pin, but may be used to block a pin explicitly, i.e. for UART. /// </summary> /// <param name="pinNumber">The pin number, in the boards default numbering scheme</param> /// <param name="usage">Intended usage of the pin</param> /// <param name="owner">Class that owns the pin (use "this")</param> /// <exception cref="InvalidOperationException">The pin is already reserved</exception> public virtual void ReservePin(int pinNumber, PinUsage usage, object owner) { if (!_initialized) { Initialize(); } lock (_pinReservationsLock) { if (!UsageCanSharePins(usage)) { if (_pinReservations.TryGetValue(pinNumber, out List <PinReservation>?reservations)) { PinReservation reservation = reservations.First(); throw new InvalidOperationException($"Pin {pinNumber} has already been reserved for {reservation.Usage} by class {reservation.Owner}."); } PinReservation rsv = new PinReservation(pinNumber, usage, owner); _pinReservations.Add(pinNumber, new List <PinReservation>() { rsv }); } else { if (_pinReservations.TryGetValue(pinNumber, out List <PinReservation>?reservations)) { PinReservation reservation = reservations.First(); if (reservation.Usage != usage) { throw new InvalidOperationException($"Pin {pinNumber} has already been reserved for {reservation.Usage} by class {reservation.Owner}."); } } else { reservations = new List <PinReservation>(); _pinReservations.Add(pinNumber, reservations); } PinReservation rsv = new PinReservation(pinNumber, usage, owner); reservations.Add(rsv); } } ActivatePinMode(pinNumber, usage); }
/// <summary> /// Switches a pin to a certain alternate mode. (ALTn mode) /// </summary> /// <param name="pinNumber">The pin number in the logical scheme</param> /// <param name="usage">The desired usage</param> protected override void ActivatePinMode(int pinNumber, PinUsage usage) { if (_managedGpioController == null) { throw new InvalidOperationException("Board not initialized"); } if (_raspberryPi3Driver == null || !_raspberryPi3Driver.AlternatePinModeSettingSupported) { throw new NotSupportedException("Alternate pin mode setting not supported by driver"); } var modeToSet = GetHardwareModeForPinUsage(pinNumber, usage, PinNumberingScheme.Logical); if (modeToSet != RaspberryPi3Driver.AltMode.Unknown) { _raspberryPi3Driver.SetAlternatePinMode(pinNumber, modeToSet); } base.ActivatePinMode(pinNumber, usage); }
public PinReservation(int pin, PinUsage usage, object owner) { Pin = pin; Usage = usage; Owner = owner; }
/// <summary> /// Override this method if something special needs to be done to use the pin for the given device. /// Many devices support multiple functions per Pin, but not at the same time, so that some kind of /// multiplexer needs to be set accordingly. /// </summary> /// <param name="pinNumber">The logical pin number to use.</param> /// <param name="usage">The intended usage</param> protected virtual void ActivatePinMode(int pinNumber, PinUsage usage) { _knownUsages[pinNumber] = usage; }
/// <summary> /// Gets the board-specific hardware mode for a particular pin and pin usage (i.e. the different ALTn modes on the raspberry pi) /// </summary> /// <param name="pinNumber">Pin number to use</param> /// <param name="usage">Requested usage</param> /// <param name="pinNumberingScheme">Pin numbering scheme for the pin provided (logical or physical)</param> /// <param name="bus">Optional bus argument, for SPI and I2C pins</param> /// <returns> /// A member of <see cref="RaspberryPi3Driver.AltMode"/> describing the mode the pin is in.</returns> private RaspberryPi3Driver.AltMode GetHardwareModeForPinUsage(int pinNumber, PinUsage usage, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical, int bus = 0) { if (pinNumber >= PinCount) { throw new InvalidOperationException($"Invalid pin number {pinNumber}"); } if (usage == PinUsage.Gpio) { // all pins support GPIO return(RaspberryPi3Driver.AltMode.Input); } if (usage == PinUsage.I2c) { // The Pi4 has a big number of pins that can become I2C pins switch (pinNumber) { // Busses 0 and 1 run on Alt0 case 0: case 1: case 2: case 3: return(RaspberryPi3Driver.AltMode.Alt0); case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: return(RaspberryPi3Driver.AltMode.Alt5); case 22: case 23: return(RaspberryPi3Driver.AltMode.Alt5); } throw new NotSupportedException($"No I2C support on Pin {pinNumber}."); } if (usage == PinUsage.Pwm) { if (pinNumber == 12 || pinNumber == 13) { return(RaspberryPi3Driver.AltMode.Alt0); } if (pinNumber == 18 || pinNumber == 19) { return(RaspberryPi3Driver.AltMode.Alt5); } throw new NotSupportedException($"No Pwm support on Pin {pinNumber}."); } if (usage == PinUsage.Spi) { switch (pinNumber) { case 7: // Pin 7 can be assigned to either SPI0 or SPI4 return(bus == 0 ? RaspberryPi3Driver.AltMode.Alt0 : RaspberryPi3Driver.AltMode.Alt3); case 8: case 9: case 10: case 11: return(RaspberryPi3Driver.AltMode.Alt0); case 0: case 1: case 2: case 3: return(RaspberryPi3Driver.AltMode.Alt3); case 4: case 5: case 6: return(RaspberryPi3Driver.AltMode.Alt3); case 12: case 13: case 14: case 15: return(RaspberryPi3Driver.AltMode.Alt3); case 16: case 17: return(RaspberryPi3Driver.AltMode.Alt4); case 18: case 19: case 20: case 21: return(bus == 6 ? RaspberryPi3Driver.AltMode.Alt3 : RaspberryPi3Driver.AltMode.Alt4); case 24: case 25: case 26: case 27: return(RaspberryPi3Driver.AltMode.Alt5); } throw new NotSupportedException($"No SPI support on Pin {pinNumber}."); } if (usage == PinUsage.Uart) { switch (pinNumber) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: return(RaspberryPi3Driver.AltMode.Alt4); case 14: case 15: if (bus == 0) { return(RaspberryPi3Driver.AltMode.Alt0); } else if (bus == 5) { return(RaspberryPi3Driver.AltMode.Alt4); } else if (bus == 1) { return(RaspberryPi3Driver.AltMode.Alt5); } break; case 16: case 17: return((bus == 0) ? RaspberryPi3Driver.AltMode.Alt3 : RaspberryPi3Driver.AltMode.Alt5); } throw new NotSupportedException($"No Uart support on Pin {pinNumber}."); } throw new NotSupportedException($"There are no known pins for {usage}."); }