Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add power domain support, device power management policy and fix display re-init bug #1300

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

infused-kim
Copy link
Contributor

@infused-kim infused-kim commented May 14, 2022

Hey guys, here is my PR for power domain support.

Please review it in conjunction with the zephyr PR: zmkfirmware/zephyr#8

Features

Power Domains

Power Domains are basically Zephyr's notification system for when the power states of devices change.

For example, when the external power is turned off in a nice!nano, power domains allow all devices connected to it to be notified that the power is about to be removed so that they can properly turn off and un-initialize.

Similarly, when power is enabled again, devices are notified and they can re-initialize and resume their work.

The biggest benefit of this is that it's a...

Fix for the Display re-init bug

A longstanding problem was that if external power was turned off and then on again, the display would not resume working, because it needed to be re-initialized.

This PR fixes this issue.

It's currently tested and working for the popular ssd1306 OLED display, but will probably need additional work to fix ePaper and Sharp Memory Displays.

I have ordered a sharp memory display, but the delivery has been delayed and it will probably take another two or three weeks for it to arrive.

If nobody works on this by then, I will add a fix at that time.

Device Power Management Policies

This feature allows you to configure a power management automation policy for a power domain or device.

Currently, the supported policies are:

  1. auto-off-on-idle;, which automatically turns off a power domain when the device goes into idle state and turns it on again when it resumes.
  2. usb-auto-toggle;, which automatically toggles a power domain off when USB is disconnected and toggles it on again when USB is connected.

You can enable this feature by adding the following block to your device tree:

/ {
    dev_pm_policy_1 {
        compatible = "zmk,dev-pm-policy";
        label = "DEV_PM_POLICY_1";
        device = <&pd_ext_power>;
        auto-off-on-idle;
        usb-auto-toggle;
    };
};

To disable one of the policies just comment it out or remove it.

While assigning a power domain with device = <&pd_ext_power>; is the most useful way to use this feature, it's also possible to assign any other device.

For example, you could assign device = <&led_strip>; to only turn off the underglow.

The use of that is somewhat limited though, since it wouldn't cut power to the underglow and it would continue to use up battery.

Multiple Power Domains

This is a feature that probably no keyboard can currently take advantage of, but that will allow us to build really cool wireless keyboards.

For example, the keyboard I am working on will have both a sharp memory display and an rgb underglow / backlight.

The memory display uses very little battery and I want it to be on at all times.

The backlight on the other hand uses a lot of power even when the LEDs are at 0% brightness. I want to be able to turn the power off to the LEDs, but without turning off the power to the display.

Support for multiple power domains allow you to achieve this.

You can add a mosfet between the nice!nano external power pin and the underglow that controls power through another pin.

Then you add a second power domain to your device tree and assign it to the underglow:

/ {
    pd_mosfet: pd_mosfet {
        compatible = "power-domain-gpio";
        power-domain = <&pd_ext_power>;
        label = "PD_MOSFET";
        enable-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
        startup-delay-us = <50000>;
    };
};

&led_strip {
    power-domain = <&pd_mosfet>;
};

As you can see, the pd_mosfet is a child of the pd_ext_power power domain. And the led_strip is a child of the pd_mosfet power domain.

So when you turn off pd_mosfet, it notifies it's child device led_strip and turns it off too. But it doesn't affect any other devices like displays connected to the pd_ext_power power domain.

On the other hand, when you turn off pd_ext_power, it notifies the display connected to it as well as pd_mosfet, which in turn also notifies the led_strip.

In order to control the mosfet power domain, you can add the following behavior to your keymap:

/ {
    behaviors {
        ext_power_mosfet: behavior_ext_power_mosfet {
            compatible = "zmk,behavior-ext-power";
            power-domain = <&pd_mosfet>;
            label = "EXTPOWER_MOSFET";
            #binding-cells = <1>;
        };
    };
};

This is the same behavior as the currently used ext_power behavior that toggles external power, but you are now able to add additional versions of it that take the optional power-domain = <&pd_mosfet>; parameter that configures which power domain should be turned on or off.

You can then add &ext_power_mosfet EXT_POWER_TOGGLE_CMD to your keymap to toggle the mosfet.

Testing

1. Use the correct branch

This PR depends on changes to the zephyr code, which can be found here:
https://github.com/infused-kim/zmk-zephyr/commits/v3.0.0%2Bzmk-fixes%2Bpd-oled

You won't be able to build against the PR branch until these changes have been incorporated into zmk's zephyr fork.

This PR also depends on Nicell's fix for ext power disabling when going to sleep: #1291

The best way to currently test is to use this branch:
https://github.com/infused-kim/zmk/commits/testing/power-domain

It includes #1291 and uses my zephyr work that has all necessary changes.

If you have a local dev environment...

Just switch to that branch and you should be good to go.

If you want to use github build tools with your zmk-config...

Adjust your west.yml:

manifest:
  remotes:
    - name: zmkfirmware
      url-base: https://github.com/zmkfirmware
    - name: infused-kim
      url-base: https://github.com/infused-kim
  projects:
    # - name: zmk
    #   remote: zmkfirmware
    #   revision: main
    #   import: app/west.yml
    - name: zmk
      remote: infused-kim
      revision: testing/power-domain
      import: app/west.yml
  self:
    path: config

And then run west update.

2. Add the power domain setting to your shield

You can add these directly to your my_keyboard.keymap:

// Enable power domain on oled display
&oled {
    power-domain = <&pd_ext_power>;
};

// Enable power domain on rgb underglow led strip
&led_strip {
    power-domain = <&pd_ext_power>;
};

// Enable device power management policy on pd_ext_power
/ {
    dev_pm_policy_1 {
        compatible = "zmk,dev-pm-policy";
        label = "DEV_PM_POLICY_1";
        device = <&pd_ext_power>;
        auto-off-on-idle;
        usb-auto-toggle;
    };
};

If you are using a nice!nano that's all you need to do. If you are using a different board, then you will need to add the power domain itself to it.

Check the PD commit for a reference on how to do it.

3. Add other useful settings

You may also want to add these settings to your my_keyboard.conf to help with debugging:

# Enable debug logging
CONFIG_ZMK_USB_LOGGING=y
CONFIG_ZMK_LOG_LEVEL_DBG=y

# Delay the config process so that early logs are not cut out
CONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS=4000

# Enable logging inside relevant zephyr modules
CONFIG_LOG_MAX_LEVEL=4
CONFIG_I2C_LOG_LEVEL_DBG=y
CONFIG_DISPLAY_LOG_LEVEL_DBG=y
CONFIG_PM_DEVICE_LOG_LEVEL_DBG=y

# Enable deep sleep after 30s
CONFIG_ZMK_SLEEP=y
CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=30000

4. Build the firmware

Build the firmware as usual-- either push into github or build locally.

Follow the direction here to read the logs if necessary:
https://zmk.dev/docs/development/usb-logging

Merging

Required changes

Update shields

One of the things that is missing is that the .dtsi files of all keyboards need to be updated to use the power domain for displays and underglow.

But I didn't want to do this until the naming conventions were locked in.

Other changes

Please let me know what other changes need to be made.

Dependencies

I suggest we merge this after merging Nicell's PR #1291.

I have also opened a separate PR for the zephyr changes: zmkfirmware/zephyr#8

@infused-kim infused-kim changed the title My changes/power domain Add power domain support, device power management policy and fix display re-init bug May 14, 2022
@infused-kim infused-kim force-pushed the my-changes/power-domain branch from 9e073c9 to cb0eeca Compare May 14, 2022 15:16
@infused-kim infused-kim force-pushed the my-changes/power-domain branch from cb0eeca to 843b99b Compare May 18, 2022 14:53
@caksoylar caksoylar added enhancement New feature or request core Core functionality/behavior of ZMK displays labels Jul 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Core functionality/behavior of ZMK displays enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants