NxpPca9685Device channel value.
        /// <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="NxpPca9685ChannelValue"/> to write.</param>
        /// <returns>
        /// Updated channel value or null when all channels were updated.
        /// </returns>
        public NxpPca9685Channel WriteChannel(int index, NxpPca9685ChannelValue value)
        {
            // Validate
            if (index <0 | index> ChannelCount)
            {
                throw new ArgumentOutOfRangeException(nameof(index));
            }

            // Validate
            if (value.On > NxpPca9685ChannelValue.Maximum + 1 ||
                value.Off > NxpPca9685ChannelValue.Maximum + 1)
            {
                throw new ArgumentOutOfRangeException(nameof(value));
            }

            // Calculate register address
            var register = GetChannelAddress(index);

            // Convert and write value
            var bytes = value.ToByteArray();

            Hardware.WriteJoinBytes(register, bytes);

            // Read and return result
            if (index < ChannelCount)
            {
                return(ReadChannel(index));
            }
            ReadAllChannels();
            return(null);
        }
        /// <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="length">
        /// Pulse length 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 NxpPca9685Channel WriteChannelMilliseconds(int index, float length, float delay = 0)
        {
            // Read current frequency
            var frequency = ReadFrequency();

            // Call overloaded method
            return(WriteChannel(index, NxpPca9685ChannelValue.FromMilliseconds(length, frequency, delay)));
        }
        /// <summary>
        /// Reads the pre-scale register and calculates the <see cref="Frequency"/> (and related properties)
        /// based on <see cref="ClockSpeed"/>.
        /// </summary>
        /// <returns>
        /// Frequency in Hz. Related properties are also udpated.
        /// </returns>
        public float ReadFrequency()
        {
            // Read pre-scale register
            var preScale = Hardware.WriteReadByte((byte)NxpPca9685Register.PreScale);

            // Calculate frequency
            var frequency = CalculateFrequency(preScale, ClockSpeed);

            // Update related properties
            Frequency    = frequency;
            PwmMsMinimum = NxpPca9685ChannelValue.CalculateMilliseconds(Frequency, 0);
            PwmMsMaximum = NxpPca9685ChannelValue.CalculateMilliseconds(Frequency, NxpPca9685ChannelValue.Maximum);

            // Return result
            return(frequency);
        }
        /// <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="NxpPca9685ChannelValue"/> to write.</param>
        /// <returns>
        /// Updated channel value or null when all channels were updated.
        /// </returns>
        public NxpPca9685Channel WriteChannel(int index, NxpPca9685ChannelValue value)
        {
            // Validate
            if (index < 0 | index > ChannelCount) throw new ArgumentOutOfRangeException(nameof(index));

            // Validate
            if (value.On > NxpPca9685ChannelValue.Maximum + 1 ||
            value.Off > NxpPca9685ChannelValue.Maximum + 1)
                throw new ArgumentOutOfRangeException(nameof(value));

            // Calculate register address
            var register = GetChannelAddress(index);

            // Convert and write value
            var bytes = value.ToByteArray();
            Hardware.WriteJoinBytes(register, bytes);

            // Read and return result
            if (index < ChannelCount)
                return ReadChannel(index);
            ReadAllChannels();
            return null;
        }
 /// <summary>
 /// Creates an instance with the specified values.
 /// </summary>
 public NxpPca9685Channel(int index, NxpPca9685ChannelValue value)
 {
     Index = index;
     Value = value;
     Value.Changed += OnValueChanged;
 }
 /// <summary>
 /// Creates an instance with the specified values.
 /// </summary>
 public NxpPca9685Channel(int index, NxpPca9685ChannelValue value)
 {
     Index          = index;
     Value          = value;
     Value.Changed += OnValueChanged;
 }
 /// <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="length">Pulse length 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 NxpPca9685Channel WriteChannelLength(int index, int length, int delay = 0)
 {
     // Call overloaded method
     return(WriteChannel(index, NxpPca9685ChannelValue.FromLength(length, delay)));
 }