/// <summary> /// Writes the "on" and "off" values of a channel together, and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15) or 16 for the "all call" channel.</param> /// <param name="value"><see cref="Pca9685ChannelValue"/> to write.</param> /// <returns> /// Updated channel value or null when all channels were updated. /// </returns> public Pca9685ChannelValue?WriteChannel(int index, Pca9685ChannelValue value) { // Validate if (index <0 | index> ChannelCount) { throw new ArgumentOutOfRangeException(nameof(index)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } // Calculate register address var register = GetChannelAddress(index); // Convert and write value var bytes = value.ToByteArray(); I2cExtensions.WriteJoinBytes(_hardware, register, bytes); // Read and return result when single channel if (index < ChannelCount) { return(ReadChannel(index)); } // Read all channels when "all call". ReadAllChannels(); return(null); }
/// <summary> /// Reads the PWM "off" (falling) value of a channel, and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15).</param> /// <returns>Channel value.</returns> public int ReadChannelOff(int index) { // Validate if (index < 0 | index >= ChannelCount) { throw new ArgumentOutOfRangeException(nameof(index)); } // Calculate register address of second word value var register = (byte)(GetChannelAddress(index) + sizeof(ushort)); // Read and convert value var bytes = I2cExtensions.WriteReadBytes(_hardware, register, sizeof(ushort)); var value = BitConverter.ToUInt16(bytes, 0); // Update channel when changed var oldValue = _channels[index]; if (oldValue.Off != value) { _channels[index] = new Pca9685ChannelValue(oldValue.On, value); } // Return result return(value); }
/// <summary> /// Calculates the prescale value from the frequency (according to <see cref="ClockSpeed"/>) /// then writes that register, then calls <see cref="ReadFrequency"/> to update properties. /// Note the actual frequency may differ to the requested frequency due to clock scale (rounding). /// </summary> /// <remarks> /// The prescale can only be set during sleep mode. This method enters <see cref="Sleep"/> if necessary, /// then only if the device was awake before, calls <see cref="Wake"/> afterwards. It's important not to /// start output unexpectedly to avoid damage, i.e. if the device was sleeping before, the frequency is /// changed without starting the oscillator. /// </remarks> /// <param name="frequency">Desired frequency to set in Hz.</param> /// <returns> /// Effective frequency in Hz, read-back and recalculated after setting the desired frequency. /// Frequency in Hz. Related properties are also updated. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown when <paramref name="frequency"/> is less than <see cref="FrequencyMinimum"/> or greater than /// <see cref="FrequencyMaximum"/>. /// </exception> public int WriteFrequency(int frequency) { // Validate if (frequency < FrequencyMinimum || frequency > FrequencyMaximum) { throw new ArgumentOutOfRangeException(nameof(frequency)); } // Calculate prescale var prescale = CalculatePrescale(frequency, ClockSpeed); // Enter sleep mode and record wake status var wasAwake = Sleep(); // Write prescale I2cExtensions.WriteJoinByte(_hardware, (byte)Pca9685Register.Prescale, prescale); // Read result var actual = ReadFrequency(); // Wake-up if previously running if (wasAwake) { Wake(); } // Update related properties PwmMsMinimum = Pca9685ChannelValue.CalculateWidthMs(frequency, 0); PwmMsMaximum = Pca9685ChannelValue.CalculateWidthMs(frequency, Pca9685ChannelValue.Maximum); // Return actual frequency return(actual); }
/// <summary> /// Reads all channels and updates <see cref="Channels"/>. /// </summary> public void ReadAllChannels() { // Read all channels as one block of data var data = I2cExtensions.WriteReadBytes(_hardware, ChannelStartAddress, ChannelSize * ChannelCount); // Update properties for (var index = 0; index < ChannelCount; index++) { _channels[index] = Pca9685ChannelValue.FromByteArray(data, ChannelSize * index); } }
/// <summary> /// Reads the prescale register and calculates the <see cref="Frequency"/> (and related properties) /// based on <see cref="ClockSpeed"/>. /// </summary> /// <returns> /// Frequency in Hz. Related properties are also updated. /// </returns> public int ReadFrequency() { // Read prescale register var prescale = I2cExtensions.WriteReadByte(_hardware, (byte)Pca9685Register.Prescale); // Calculate frequency var frequency = CalculateFrequency(prescale, ClockSpeed); // Update related properties Frequency = frequency; PwmMsMinimum = Pca9685ChannelValue.CalculateWidthMs(frequency, 0); PwmMsMaximum = Pca9685ChannelValue.CalculateWidthMs(frequency, Pca9685ChannelValue.Maximum); // Return result return(frequency); }
/// <summary> /// Reads a whole channel value (on and off), and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15).</param> /// <returns>Channel value</returns> public Pca9685ChannelValue ReadChannel(int index) { // Validate if (index < 0 | index >= ChannelCount) { throw new ArgumentOutOfRangeException(nameof(index)); } // Calculate register address var register = GetChannelAddress(index); // Read value var bytes = I2cExtensions.WriteReadBytes(_hardware, register, sizeof(ushort) * 2); // Update channel property and return result var value = Pca9685ChannelValue.FromByteArray(bytes); return(_channels[index] = value); }
/// <summary> /// Writes multiple channels together (both "on" and "off" values), and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15) or 16 for the "all call" channel.</param> /// <param name="values">Collection of <see cref="Pca9685ChannelValue"/>s to write.</param> public void WriteChannels(int index, IList <Pca9685ChannelValue> values) { // Validate if (index <0 | index> ChannelCount) { throw new ArgumentOutOfRangeException(nameof(index)); } if (values == null || values.Count == 0) { throw new ArgumentNullException(nameof(values)); } var count = values.Count; if (index + count > ChannelCount) { throw new ArgumentOutOfRangeException(nameof(values)); } // Build I2C packet var data = new byte[1 + ChannelSize * count]; // Calculate first register address var register = GetChannelAddress(index); data[0] = register; // Write channels and update properties for (int dataIndex = 0, dataOffset = 1; dataIndex < count; dataIndex++, dataOffset += ChannelSize) { // Get channel data var channelData = values[dataIndex].ToByteArray(); // Copy to buffer Array.ConstrainedCopy(channelData, 0, data, dataOffset, ChannelSize); // Update property _channels[index + dataIndex] = Pca9685ChannelValue.FromByteArray(channelData); } // Send packet I2cExtensions.WriteBytes(_hardware, data); }
/// <summary> /// Reads multiple channel values (both on and off for each), and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15).</param> /// <param name="count">Number of channels to read.</param> /// <returns>Channel values</returns> public Collection <Pca9685ChannelValue> ReadChannels(int index, int count) { // Validate if (index < 0 | index >= ChannelCount) { throw new ArgumentOutOfRangeException(nameof(index)); } if (count < 1 || index + count > ChannelCount) { throw new ArgumentOutOfRangeException(nameof(count)); } // Calculate register address var register = GetChannelAddress(index); // Send I2C command to read channels in one operation var data = I2cExtensions.WriteReadBytes(_hardware, register, ChannelSize * count); // Update channel properties and add to results var results = new Collection <Pca9685ChannelValue>(); for (int channelIndex = index, offset = 0; count > 0; count--, channelIndex++, offset += ChannelSize) { // Calculate value var value = Pca9685ChannelValue.FromByteArray(data, offset); // Update property _channels[channelIndex] = value; // Add to results results.Add(value); } // Return results return(results); }
/// <summary> /// Calculates the "on" and "off" values of a channel from milliseconds (and optional delay), /// then writes them together, and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15) or 16 for the "all call" channel.</param> /// <param name="width"> /// Pulse width in milliseconds. Cannot be greater than one clock interval (1000 / frequency). /// </param> /// <param name="delay">Optional delay in milliseconds. Cannot be greater than one clock interval (1000 / frequency).</param> /// <returns> /// Updated channel value or null when all channels were updated. /// </returns> public Pca9685ChannelValue?WriteChannelMs(int index, decimal width, int delay = 0) { // Call overloaded method return(WriteChannel(index, Pca9685ChannelValue.FromWidthMs(width, Frequency, delay))); }
/// <summary> /// Calculates the "on" and "off" values of a channel from length (and optional delay), /// then writes them together, and updates it in <see cref="Channels"/>. /// </summary> /// <param name="index">Zero based channel number (0-15) or 16 for the "all call" channel.</param> /// <param name="width">Pulse width in clock ticks.</param> /// <param name="delay">Optional delay in clock ticks.</param> /// <returns> /// Updated channel value or null when all channels were updated. /// </returns> public Pca9685ChannelValue?WriteChannelLength(int index, int width, int delay = 0) { // Call overloaded method return(WriteChannel(index, Pca9685ChannelValue.FromWidth(width, delay))); }