Only the last created PWM works on my spider with io60p16

My aim is to use Spider with IO60P16 (on socket X) to drive servos. I’m at the earliest steps, just using an oscilloscope to prove if the PWM is set up correctly. Using one PWM worked fine (producing a 1ms to 2ms pulse every 10ms). I added a second PWM and the first PWM stopped working - i.e. no PWM output. I’ve reduced this to as simple a program as I can.


        private const byte PERIOD = 234; // ticks for 10ms period

        private IO60P16.PWM servo0;
        private IO60P16.PWM servo1;

        private void ProgramStarted()
        {
            this.servo0 = new IO60P16.PWM(IO60P16.PWMPin.PWM0, IO60P16.PWM.TickWidth.TickWidth_Servo_23438hz_42666ns);
            this.servo0.SetPulse(36, PERIOD);
            this.servo0.SetPulse(24, PERIOD); // breakpoint here and step to see change            

            this.servo1 = new IO60P16.PWM(IO60P16.PWMPin.PWM1, IO60P16.PWM.TickWidth.TickWidth_Servo_23438hz_42666ns);
            this.servo1.SetPulse(24, PERIOD);
            
            // try to change servo0 and it fails to change
            this.servo0.SetPulse(48, PERIOD);


Using the debugger, I put a breakpoint on the last statement - the scope showed 1.5ms pulse (correct for setting of 36) - then execute the statement and the pulse doesn’t change.

If I swap the servo creation statements then changing the pulse does work.


 // swapped order of creation....
        private void ProgramStarted()
        {
            this.servo1 = new IO60P16.PWM(IO60P16.PWMPin.PWM1, IO60P16.PWM.TickWidth.TickWidth_Servo_23438hz_42666ns);
            this.servo1.SetPulse(24, PERIOD);

            this.servo0 = new IO60P16.PWM(IO60P16.PWMPin.PWM0, IO60P16.PWM.TickWidth.TickWidth_Servo_23438hz_42666ns);
            this.servo0.SetPulse(36, PERIOD);
            this.servo0.SetPulse(24, PERIOD); // breakpoint here and step to see change            
            this.servo0.SetPulse(48, PERIOD); // step again to see change

A bit of experimenting showed me that it didn’t matter which PWM pins I used, but whichever PWM I created last was the one that worked (for changes) and the other won’t change.

I hope I’m using the latest and greatest drivers because I downloaded and installed them yesterday:
Microsoft .Net Micro Framework SDK 4.2 (QDE2)
GHI .Net Gadgeteer SDK 1.6.10.0

Any suggestions as to why the order of creation makes a difference?

Investigating further in to the issue using a IKALOGIC cheap n cheerful I2C decoder

SetPulse(48) was interesting; it sets the pulse width but unexpectedly it also sets the period to 255.
On the bus it looked like W20 x2A xFF to set the period and W20 x2B x30 to set the pulse width.
I’ll stick to SetPulse(48,234) so that I don’t lose my chosen 10ms period.


this.servo0 = new IO60P16.PWM(IO60P16.PWMPin.PWM0, IO60P16.PWM.TickWidth.TickWidth_Servo_23438hz_42666ns);

When the code creates a PWM it sends out a W20 x28 00 for PWM0 and W20 x28 x01 for PWM1. From the CY8C9560A datasheet I can see that this is a PWM select register. Ah! Tracing through the I2C bus activity, I can see that the PWM select register isn’t getting written again, so I thnk that whichever PWM is created last stays as the only PWM being changed.

Confirmed the behaviour in the source code IO60p16.cs


// Type: Gadgeteer.Modules.GHIElectronics.IO60P16
// Assembly: GTM.GHIElectronics.IO60P16, Version=1.1.2.0, Culture=neutral, PublicKeyToken=null
// Assembly location: C:\Program Files\GHI Electronics\GHI .NET Gadgeteer SDK\Modules\IO60P16\NETMF 4.2\GTM.GHIElectronics.IO60P16.dll

Just setting the pulse will make the period 255 (max value)


      public void SetPulse(byte highPulseWidthTicks)
      {
        this.SetPulse(highPulseWidthTicks, byte.MaxValue);
      }

Setting the pulse doesn’t make sure that the correct PWM is being addressed


      public void SetPulse(byte highPulseWidthTicks, byte periodTicks)
      {
        if ((int) highPulseWidthTicks >= (int) periodTicks)
          throw new Exception("Invalid param. highPulseWidthTicks should be smaller than " + (object) periodTicks);
        if (!IO60P16.IsPinReserved(this._portId, this._pinId))
          throw new Exception("This pin has already been reset or has not been initialized.");
        this._periodTicks = periodTicks;
        this._highPulseWidthTicks = highPulseWidthTicks;
        IO60P16.WriteRegister((byte) 42, this._periodTicks);
        IO60P16.WriteRegister((byte) 43, this._highPulseWidthTicks);
      }

I just need to figure out how to manually write to the PWM select register (The IO60P16 WriteRegister function is private).

@ ColinGrant -

That is a bug of IO60P16 driver. Next release will fix it.
Now you can fix by changing a little bit in function “public void SetPulse(byte highPulseWidthTicks, byte periodTicks)” on the file IO60P16_42.cs


public void SetPulse(byte highPulseWidthTicks, byte periodTicks)
            {
                if (highPulseWidthTicks > periodTicks)
                {
                    throw new Exception("Invalid param. highPulseWidthTicks should be smaller than " + periodTicks);
                }

                if (!_io60p16.IsPinReserved(_portId, _pinId))
                {
                    throw new Exception("This pin has already been reset or has not been initialized.");// reserve already
                }

                _periodTicks = periodTicks;
                _highPulseWidthTicks = highPulseWidthTicks;
                SetPWM(); // =>>>>>>> added

            }

@ Dat : Thanks for the confirmation.

I didn’t know how to change the driver as I’d only installed the packages. It didn’t take long to find the sources, downloading the current latest sources from http://gadgeteer.codeplex.com/SourceControl/changeset/view/24955 which I unpacked to my E:\ and then added the IO60P16_42.csproj project from E:\gadgeteer-24955\Main\Modules\GHIElectronics\IO60P16\Software\IO60P16\IO60P16_42 in to my current gadgeteer solution (using VS2010).

That gave me direct access to the driver source. I like the simplicity of calling SetPWM() but I wanted a bit less I2C traffic to the module so as a temporary measure until the next release fixes the PWM selection, I opted to make a few changes.

I added a function that just selects the PWM:


            /// <summary>
            /// No checks - just tell the IO60P16 to work with this PWM.
            /// </summary>
            private void SelectThisPWM()
            {
                WriteRegister(0x28, (byte)(_pinId + (_portId - 6) * 8));
            }

Then added a call to it from SetPWM()


            /// <summary>
            /// Sets the PWM frequency
            /// </summary>
            private void SetPWM()
            {
                WriteRegister(PORT_SELECT_REGISTER, _portId);
                this.SelectThisPWM(); // ==  WriteRegister(0x28, (byte)(_pinId + (_portId - 6) * 8));
                byte pwm_output_register = ReadRegister(ENABLE_PWM_REGISTER);
                pwm_output_register |= (byte)(1 << _pinId);
                WriteRegister(ENABLE_PWM_REGISTER, pwm_output_register);
                WriteRegister(DIVIDER_REGISTER, _divider);
                WriteRegister(SELECT_CLOCK_SRC, (byte)_tickWidth);
                WriteRegister(PERIOD_REGISTER, _periodTicks);      // (Period rising : 255)
                WriteRegister(PULSE_WIDTH_REGISTER, _highPulseWidthTicks);        //(DutyCycle )
            }

Finally, a new function that just changes the pulse width


            /// <summary>
            /// Just set the pulse high width in ticks, selecting the PWM first.
            /// </summary>
            /// <param name="highPulseWidthTicks">
            /// Number of ticks width. Must be less than previously set period ticks.
            /// </param>
            public void SetPulseWidth(byte highPulseWidthTicks)
            {
                if (highPulseWidthTicks >= _periodTicks)
                {
                    throw new Exception("Invalid param. highPulseWidthTicks should be smaller than " + _periodTicks);
                }

                if (!IsPinReserved(_portId, _pinId))
                {
                    throw new Exception("This pin has already been reset or has not been initialized.");// reserve already
                }

                _highPulseWidthTicks = highPulseWidthTicks;

                SelectThisPWM();
                WriteRegister(PULSE_WIDTH_REGISTER, _highPulseWidthTicks);
            }

I was really impressed by how easy it was to update the library; well, once I knew I could. Thanks for the great little IO60P16 module and hats off to all the developers that put this framework together.