WildernessLabs / Meadow_Issues

Public repo for bugs and issues with Meadow
15 stars 0 forks source link

GPIO State Transitions on Power-up/Start-up/Reset #375

Open E-R-T-W opened 1 year ago

E-R-T-W commented 1 year ago

Describe the bug

On Power-up/Start-up/Reset, GPIO changes state, both prior to configuring GPIO and even post taking control of the GPIO in C# code. In many scenarios this is problematic and unacceptable.

To Reproduce

For example, let's discuss driving a PNP transistor switch as shown below with the Base connected to GPIO. Anytime the GPIO sinks current, the transistor will switch ON. image

Via repeated testing, I've attached the "definitive" GPIO state transitions during Power-up/Start-up/Reset: image

Some explanation: There are really only 2 GPIO cases for each scenario to be discussed:

  1. GPIO A00-A02, A05, D05-D08, D14-D15
  2. GPIO A03-A04, D00-D04, D09-D13

Prior to even discussing the 3 Scenarios once the C# code is up and running, all GPIO for case #1 remain high impedance for ~19 seconds (~14 seconds while blue LED ON and an additional ~5 seconds while blue LED OFF), but all GPIO for case #2 are in a high impedance state for ~14 seconds (while blue LED ON), followed by sinking current for ~5 seconds (while blue LED OFF).

At this point I believe is when the C# code executes. I've examined 3 scenarios:

  1. Configure all GPIO to Inputs
  2. Configure all GPIO to Output false
  3. Configure all GPIO to Output true

Scenario #1 Case #1 GPIO: These GPIO are high impedance for ~14 seconds while blue LED ON, then continue high impedance for ~5 seconds while blue LED OFF. Next the C# code executes; GPIO are configured to Input, so all GPIO are high impedance. There's no problem with Scenario #1 Case #1 GPIO.

Scenario #1 Case #2 GPIO: These GPIO are high impedance for ~14 seconds while blue LED ON, then start to sink current for ~5 seconds while blue LED OFF. Next the C# code executes; GPIO are configured to Input, so all GPIO are high impedance. This is a problem with the change in GPIO state for Case #2 GPIO in this Scenario #1.

Scenario #2 Case #1 GPIO: These GPIO are high impedance for ~14 seconds while blue LED ON, then continue high impedance for ~5 seconds while blue LED OFF. Next the C# code executes; GPIO are configured to Output false, so all GPIO sink current. There's no problem with Scenario #2 Case #1 GPIO.

Scenario #2 Case #2 GPIO: These GPIO are high impedance for ~14 seconds while blue LED ON, then start to sink current for ~5 seconds while blue LED OFF. Next the C# code executes; GPIO are configured to Output false, so all GPIO sink current. This is a problem with the change in Case #2 GPIO state happening ~5 seconds earlier than for Case #1 GPIO in this Scenario #2 Case #2 GPIO.

Scenario #3 Case #1 GPIO: These GPIO are high impedance for ~14 seconds while blue LED ON, then continue high impedance for ~5 seconds while blue LED OFF. Next the C# code executes; GPIO are configured to Output true, so all GPIO go high and should not sink current, however, what happens is that when one first configures the GPIO to Outputs, they sink current for a fraction of a second, until the code sets the Outputs to true. This "blip" of a change in state to sinking current until the Case #1 GPIO are set to true is a problem in this Scenario #3.

Scenario #3 Case #2 GPIO: These GPIO are high impedance for ~14 seconds while blue LED ON, then start to sink current for ~5 seconds while blue LED OFF. Next the C# code executes; GPIO are configured to Output true, so all GPIO go high. There is no "blip" of change in state like Scenario #3 Case #1 because there was already a ~5 second change of state to sinking current while blue LED OFF. This is a problem with the change in GPIO state for Case #2 GPIO in this Scenario #3. Case #2 GPIO should not sink current for ~5 seconds in this Scenario #3.

Code for Scenario #1:

        IDigitalInputPort inputA00;
        IDigitalInputPort inputA01;
        IDigitalInputPort inputA02;
        IDigitalInputPort inputA03;
        IDigitalInputPort inputA04;
        IDigitalInputPort inputA05;
        IDigitalInputPort inputD00;
        IDigitalInputPort inputD01;
        IDigitalInputPort inputD02;
        IDigitalInputPort inputD03;
        IDigitalInputPort inputD04;
        IDigitalInputPort inputD05;
        IDigitalInputPort inputD06;
        IDigitalInputPort inputD07;
        IDigitalInputPort inputD08;
        IDigitalInputPort inputD09;
        IDigitalInputPort inputD10;
        IDigitalInputPort inputD11;
        IDigitalInputPort inputD12;
        IDigitalInputPort inputD13;
        IDigitalInputPort inputD14;
        IDigitalInputPort inputD15;

        public override Task Initialize()
        {
            Resolver.Log.Info("Initialize...");

            inputA00 = Device.CreateDigitalInputPort(Device.Pins.A00);
            inputA01 = Device.CreateDigitalInputPort(Device.Pins.A01);
            inputA02 = Device.CreateDigitalInputPort(Device.Pins.A02);
            inputA03 = Device.CreateDigitalInputPort(Device.Pins.A03);
            inputA04 = Device.CreateDigitalInputPort(Device.Pins.A04);
            inputA05 = Device.CreateDigitalInputPort(Device.Pins.A05);
            inputD00 = Device.CreateDigitalInputPort(Device.Pins.D00);
            inputD01 = Device.CreateDigitalInputPort(Device.Pins.D01);
            inputD02 = Device.CreateDigitalInputPort(Device.Pins.D02);
            inputD03 = Device.CreateDigitalInputPort(Device.Pins.D03);
            inputD04 = Device.CreateDigitalInputPort(Device.Pins.D04);
            inputD05 = Device.CreateDigitalInputPort(Device.Pins.D05);
            inputD06 = Device.CreateDigitalInputPort(Device.Pins.D06);
            inputD07 = Device.CreateDigitalInputPort(Device.Pins.D07);
            inputD08 = Device.CreateDigitalInputPort(Device.Pins.D08);
            inputD09 = Device.CreateDigitalInputPort(Device.Pins.D09);
            inputD10 = Device.CreateDigitalInputPort(Device.Pins.D10);
            inputD11 = Device.CreateDigitalInputPort(Device.Pins.D11);
            inputD12 = Device.CreateDigitalInputPort(Device.Pins.D12);
            inputD13 = Device.CreateDigitalInputPort(Device.Pins.D13);
            inputD14 = Device.CreateDigitalInputPort(Device.Pins.D14);
            inputD15 = Device.CreateDigitalInputPort(Device.Pins.D15);

            return base.Initialize();
        }

Code for Scenario #2:

        IDigitalOutputPort outputA00;
        IDigitalOutputPort outputA01;
        IDigitalOutputPort outputA02;
        IDigitalOutputPort outputA03;
        IDigitalOutputPort outputA04;
        IDigitalOutputPort outputA05;
        IDigitalOutputPort outputD00;
        IDigitalOutputPort outputD01;
        IDigitalOutputPort outputD02;
        IDigitalOutputPort outputD03;
        IDigitalOutputPort outputD04;
        IDigitalOutputPort outputD05;
        IDigitalOutputPort outputD06;
        IDigitalOutputPort outputD07;
        IDigitalOutputPort outputD08;
        IDigitalOutputPort outputD09;
        IDigitalOutputPort outputD10;
        IDigitalOutputPort outputD11;
        IDigitalOutputPort outputD12;
        IDigitalOutputPort outputD13;
        IDigitalOutputPort outputD14;
        IDigitalOutputPort outputD15;

        public override Task Initialize()
        {
            Resolver.Log.Info("Initialize...");

            outputA00 = Device.CreateDigitalOutputPort(Device.Pins.A00);
            outputA01 = Device.CreateDigitalOutputPort(Device.Pins.A01);
            outputA02 = Device.CreateDigitalOutputPort(Device.Pins.A02);
            outputA03 = Device.CreateDigitalOutputPort(Device.Pins.A03);
            outputA04 = Device.CreateDigitalOutputPort(Device.Pins.A04);
            outputA05 = Device.CreateDigitalOutputPort(Device.Pins.A05);
            outputA00 = Device.CreateDigitalOutputPort(Device.Pins.A00);
            outputA01 = Device.CreateDigitalOutputPort(Device.Pins.A01);
            outputA02 = Device.CreateDigitalOutputPort(Device.Pins.A02);
            outputA03 = Device.CreateDigitalOutputPort(Device.Pins.A03);
            outputA04 = Device.CreateDigitalOutputPort(Device.Pins.A04);
            outputA05 = Device.CreateDigitalOutputPort(Device.Pins.A05);
            outputD00 = Device.CreateDigitalOutputPort(Device.Pins.D00);
            outputD01 = Device.CreateDigitalOutputPort(Device.Pins.D01);
            outputD02 = Device.CreateDigitalOutputPort(Device.Pins.D02);
            outputD03 = Device.CreateDigitalOutputPort(Device.Pins.D03);
            outputD04 = Device.CreateDigitalOutputPort(Device.Pins.D04);
            outputD05 = Device.CreateDigitalOutputPort(Device.Pins.D05);
            outputD06 = Device.CreateDigitalOutputPort(Device.Pins.D06);
            outputD07 = Device.CreateDigitalOutputPort(Device.Pins.D07);
            outputD08 = Device.CreateDigitalOutputPort(Device.Pins.D08);
            outputD09 = Device.CreateDigitalOutputPort(Device.Pins.D09);
            outputD10 = Device.CreateDigitalOutputPort(Device.Pins.D10);
            outputD11 = Device.CreateDigitalOutputPort(Device.Pins.D11);
            outputD12 = Device.CreateDigitalOutputPort(Device.Pins.D12);
            outputD13 = Device.CreateDigitalOutputPort(Device.Pins.D13);
            outputD14 = Device.CreateDigitalOutputPort(Device.Pins.D14);
            outputD15 = Device.CreateDigitalOutputPort(Device.Pins.D15);

            outputA00.State = false;
            outputA01.State = false;
            outputA02.State = false;
            outputA03.State = false;
            outputA04.State = false;
            outputA05.State = false;
            outputD00.State = false;
            outputD00.State = false;
            outputD01.State = false;
            outputD02.State = false;
            outputD03.State = false;
            outputD04.State = false;
            outputD05.State = false;
            outputD06.State = false;
            outputD07.State = false;
            outputD08.State = false;
            outputD09.State = false;
            outputD10.State = false;
            outputD11.State = false;
            outputD12.State = false;
            outputD13.State = false;
            outputD14.State = false;
            outputD15.State = false;

            return base.Initialize();
        }

Code for Scenario #3:

        IDigitalOutputPort outputA00;
        IDigitalOutputPort outputA01;
        IDigitalOutputPort outputA02;
        IDigitalOutputPort outputA03;
        IDigitalOutputPort outputA04;
        IDigitalOutputPort outputA05;
        IDigitalOutputPort outputD00;
        IDigitalOutputPort outputD01;
        IDigitalOutputPort outputD02;
        IDigitalOutputPort outputD03;
        IDigitalOutputPort outputD04;
        IDigitalOutputPort outputD05;
        IDigitalOutputPort outputD06;
        IDigitalOutputPort outputD07;
        IDigitalOutputPort outputD08;
        IDigitalOutputPort outputD09;
        IDigitalOutputPort outputD10;
        IDigitalOutputPort outputD11;
        IDigitalOutputPort outputD12;
        IDigitalOutputPort outputD13;
        IDigitalOutputPort outputD14;
        IDigitalOutputPort outputD15;

        public override Task Initialize()
        {
            Resolver.Log.Info("Initialize...");

            outputA00 = Device.CreateDigitalOutputPort(Device.Pins.A00);
            outputA01 = Device.CreateDigitalOutputPort(Device.Pins.A01);
            outputA02 = Device.CreateDigitalOutputPort(Device.Pins.A02);
            outputA03 = Device.CreateDigitalOutputPort(Device.Pins.A03);
            outputA04 = Device.CreateDigitalOutputPort(Device.Pins.A04);
            outputA05 = Device.CreateDigitalOutputPort(Device.Pins.A05);
            outputA00 = Device.CreateDigitalOutputPort(Device.Pins.A00);
            outputA01 = Device.CreateDigitalOutputPort(Device.Pins.A01);
            outputA02 = Device.CreateDigitalOutputPort(Device.Pins.A02);
            outputA03 = Device.CreateDigitalOutputPort(Device.Pins.A03);
            outputA04 = Device.CreateDigitalOutputPort(Device.Pins.A04);
            outputA05 = Device.CreateDigitalOutputPort(Device.Pins.A05);
            outputD00 = Device.CreateDigitalOutputPort(Device.Pins.D00);
            outputD01 = Device.CreateDigitalOutputPort(Device.Pins.D01);
            outputD02 = Device.CreateDigitalOutputPort(Device.Pins.D02);
            outputD03 = Device.CreateDigitalOutputPort(Device.Pins.D03);
            outputD04 = Device.CreateDigitalOutputPort(Device.Pins.D04);
            outputD05 = Device.CreateDigitalOutputPort(Device.Pins.D05);
            outputD06 = Device.CreateDigitalOutputPort(Device.Pins.D06);
            outputD07 = Device.CreateDigitalOutputPort(Device.Pins.D07);
            outputD08 = Device.CreateDigitalOutputPort(Device.Pins.D08);
            outputD09 = Device.CreateDigitalOutputPort(Device.Pins.D09);
            outputD10 = Device.CreateDigitalOutputPort(Device.Pins.D10);
            outputD11 = Device.CreateDigitalOutputPort(Device.Pins.D11);
            outputD12 = Device.CreateDigitalOutputPort(Device.Pins.D12);
            outputD13 = Device.CreateDigitalOutputPort(Device.Pins.D13);
            outputD14 = Device.CreateDigitalOutputPort(Device.Pins.D14);
            outputD15 = Device.CreateDigitalOutputPort(Device.Pins.D15);

            outputA00.State = true;
            outputA01.State = true;
            outputA02.State = true;
            outputA03.State = true;
            outputA04.State = true;
            outputA05.State = true;
            outputD00.State = true;
            outputD00.State = true;
            outputD01.State = true;
            outputD02.State = true;
            outputD03.State = true;
            outputD04.State = true;
            outputD05.State = true;
            outputD06.State = true;
            outputD07.State = true;
            outputD08.State = true;
            outputD09.State = true;
            outputD10.State = true;
            outputD11.State = true;
            outputD12.State = true;
            outputD13.State = true;
            outputD14.State = true;
            outputD15.State = true;

            return base.Initialize();
        }

Expected behaviour

GPIO state should not change during the Start-up/Power-up/Reset phase.

Screenshots

image

Developer tools (please complete the following information as best as you can):

Meadow (please complete the following information as best as you can):

Most of these values can be found by running meadow device info using the Meadow CLI.

Additional context

Further details and information can be found on Slack in a discussion with chris.tacke@wildernesslabs.co (he asked me to create this issue) at: https://wildernesslabspublic.slack.com/archives/CQWD660E8/p1698019417572109.

ctacke commented 1 year ago

Release 1.4 changes how Meadow.Core initializes GPIOs. They are now set as inputs with no resistor, so should effectively become high-z. For all of the pons in the table above where it's "OFF OFF OFF" these should behave as expected. We're still investigating the others

E-R-T-W commented 1 year ago

I've had a chance to test - looking good - thanks for the progress!!

Just one last issue (as you already know). I've updated the table as follows: GPIO Startup State post 1 4 0 3

There is only one issue left for all GPIO (they now all consistently behave the same): When configure all GPIO to output true, there's a short "blip" for a few milliSeconds where the GPIO sink current, between the time the GPIO are configured to outputs, and when they are set to true. It would be nice to be able to configure the state of the outputs prior to configuring them to outputs.

All other cases/scenarios have been resolved - thanks again!!

E-R-T-W commented 1 year ago

By the way, the blue RGB LED has this same short "blip" upon Power-up/Start-up/Reset (after it goes through it's regular ~14 seconds blue LED ON followed by ~5 seconds blue LED OFF prior to the C# code executing). I assume it will be resolved too, along with all other GPIO that have the same issue...