diff options
author | Paul Sokolovsky <pfalcon@users.sourceforge.net> | 2016-11-06 22:08:35 +0300 |
---|---|---|
committer | Paul Sokolovsky <pfalcon@users.sourceforge.net> | 2016-11-06 22:08:35 +0300 |
commit | 8f068e84eedd90146b66b9ffd8193e2e37a24b4d (patch) | |
tree | b7d770ec9a15680d15d61bce6588511238f952b1 | |
parent | 5c3d75c937c1e7d220dcb8fb4230dd8280b1453f (diff) | |
download | micropython-8f068e84eedd90146b66b9ffd8193e2e37a24b4d.tar.gz micropython-8f068e84eedd90146b66b9ffd8193e2e37a24b4d.zip |
examples/hwapi: Example showing best practices for HW API usage in apps.
Showing and providing detailed instructions and motivation.
-rw-r--r-- | examples/hwapi/README.md | 126 | ||||
-rw-r--r-- | examples/hwapi/hwconfig_esp8266_esp12.py | 5 | ||||
-rw-r--r-- | examples/hwapi/hwconfig_z_frdm_k64f.py | 5 | ||||
-rw-r--r-- | examples/hwapi/soft_pwm.py | 38 |
4 files changed, 174 insertions, 0 deletions
diff --git a/examples/hwapi/README.md b/examples/hwapi/README.md new file mode 100644 index 0000000000..1992eb6609 --- /dev/null +++ b/examples/hwapi/README.md @@ -0,0 +1,126 @@ +This directory shows the best practices for using MicroPython hardware API +(`machine` module). `machine` module strives to provide consistent API +across various boards, with the aim to enable writing portable applications, +which would work from a board to board, from a system to another systems. +This is inherently a hard problem, because hardware is different from one +board type to another, and even from examplar of board to another. For +example, if your app requires an external LED, one user may connect it +to one GPIO pin, while another user may find it much more convinient to +use another pin. This of course applies to relays, buzzers, sensors, etc. + +With complications above in mind, it's still possible to write portable +applications by using "low[est] denominator" subset of hardware API and +following simple rules outlined below. The applications won't be able +to rely on advanced hardware capabilities of a particular board and +will be limited to generic capabilities, but it's still possible to +write many useful applications in such a way, with the obvious benefit of +"write once - run everywhere" approach (only configuration for a particular +board is required). + +The key to this approach is splitting your application into (at least) +2 parts: + +* main application logic +* hardware configuration + +The key point is that hardware configuration should be a separate file +(module in Python terms). A good name would be `hwconfig.py`, and that's +how we'll call it from now on. Another key point is that main application +should never instantiate (construct) hardware objects directly. Instead, +they should be defined in `hwconfig.py`, and main application should +import and reference hardware objects via this module. The simplest +application of this idea would look like: + +`hwconfig.py`: + + from machine import Pin + + LED = Pin("A3", Pin.OUT) + +`app.py`: + + from hwconfig import * + import utime + + while True: + LED.value(1) + utime.sleep_ms(500) + LED.value(0) + utime.sleep_ms(500) + + +To deploy this application to a particular board, a user will need: + +1. Edit `hwconfig.py` to adjust Pin and other hardware peripheral + parameters and locations. +2. Actually deploy `hwconfig.py` and `app.py` to a board (e.g. copy to + board's filesystem, or build new firmware with these modules frozen + into it). + +Note that there's no need to edit the main application code! (Which may +be complex, while `hwconfig.py` should usually remain short enough, and +focused solely on hardware configuration). + +An obvious improvement to this approach is the following. There're few +well-known boards which run MicroPython, and most of them include an +onboard LED. So, to help users of these boards to do configuration +quickly (that's especially important for novice users, for who may +be stumped by the need to reach out to a board reference to find LED +pin assignments), `hwconfig.py` your application ships may include +commented out sections with working configurations for different +boards. The step 1 above then will be: + +1. Look thru `hwconfig.py` to find a section which either exactly + matches your board, or the closest to it. Uncomment, and if any + adjustments required, apply them. + +It's important to keep in mind that adjustments may be always required, +and that there may be users whose configuration doesn't match any of +the available. So, always include a section or instructions for them. +Consider for example that even on a supported board, user may want to +blink not an on-board LED, but the one they connected externally. +MicroPython's Hardware API offers portability not just among "supported" +boards, but to any board at all, so make sure users can enjoy it. + +There's next step of improvement to make. While having one `hwconfig.py` +with many sections would work for smaller projects with few hardware +objects, it may become more cumbersome to maintain both on programmer's +and user's sides for larger projects. Then instead of single +`hwconfig.py` file, you can provide few "template" ones for well-known +boards: + +* `hwconfig_pyboard.py` +* `hwconfig_wipy.py` +* `hwconfig_esp8266.py` +* etc. + +Then step 1 above will be: + +1. Look thru available `hwconfig_*.py` files and find one which matches + your board the best, then rename to `hwconfig.py` and make adjustments, + if any. + +Again, please keep in mind that there may be users whose hardware will be +completely unlike you heard of. Give them some helpful hints too, perhaps +provide `hwconfig_custom.py` with some instructions. + +That's where we stop with improvements to the "separate file for hardware +configuration" idea, as it is already pretty flexible and viable. An +application in this directory shows it in practice, using slightly less +trivial example than just a blinking LED: `soft_pwm.py` implements a +software PWM (pulse width modulation) to produce an LED fade-in/fade-out +effect - without any dependence on hardware PWM availability. + +Note that improvements to board configuration handling may continue further. +For example, one may invent a "configuration manager" helper module which will +try to detect current board (among well-known ones), and load appropriate +`hwconfig_*.py` - this assumes that a user would lazily deploy them all +(or that application will be automatically installed, e.g. using MicroPython's +`upip` package manager). The key point in this case remains the same as +elaborated above - always assume there can, and will be a custom configuration, +and it should be well supported. So, any automatic detection should be +overridable by a user, and instructions how to do so are among the most +important you may provide for your application. + +By following these best practices, you will use MicroPython at its full +potential, and let users enjoy it too. Good luck! diff --git a/examples/hwapi/hwconfig_esp8266_esp12.py b/examples/hwapi/hwconfig_esp8266_esp12.py new file mode 100644 index 0000000000..e8cf2d12e1 --- /dev/null +++ b/examples/hwapi/hwconfig_esp8266_esp12.py @@ -0,0 +1,5 @@ +from machine import Pin + +# ESP12 module as used by many boards +# Blue LED on pin 2 +LED = Pin(2, Pin.OUT) diff --git a/examples/hwapi/hwconfig_z_frdm_k64f.py b/examples/hwapi/hwconfig_z_frdm_k64f.py new file mode 100644 index 0000000000..c45e3e7567 --- /dev/null +++ b/examples/hwapi/hwconfig_z_frdm_k64f.py @@ -0,0 +1,5 @@ +from machine import Pin + +# Freescale/NXP FRDM-K64F board +# Blue LED on port B, pin 21 +LED = Pin(("GPIO_1", 21), Pin.OUT) diff --git a/examples/hwapi/soft_pwm.py b/examples/hwapi/soft_pwm.py new file mode 100644 index 0000000000..a9a5561717 --- /dev/null +++ b/examples/hwapi/soft_pwm.py @@ -0,0 +1,38 @@ +import utime +from hwconfig import LED + + +# Using sleep_ms() gives pretty poor PWM resolution and +# brightness control, but we use it in the attempt to +# make this demo portable to even more boards (e.g. to +# those which don't provide sleep_us(), or provide, but +# it's not precise, like would be on non realtime OSes). +# We otherwise use 20ms period, to make frequency not less +# than 50Hz to avoid visible flickering (you may still see +# if you're unlucky). +def pwm_cycle(led, duty, cycles): + duty_off = 20 - duty + for i in range(cycles): + if duty: + led.value(1) + utime.sleep_ms(duty) + if duty_off: + led.value(0) + utime.sleep_ms(duty_off) + + +# At the duty setting of 1, an LED is still pretty bright, then +# at duty 0, it's off. This makes rather unsmooth transition, and +# breaks fade effect. So, we avoid value of 0 and oscillate between +# 1 and 20. Actually, highest values like 19 and 20 are also +# barely distinguishible (like, both of them too bright and burn +# your eye). So, improvement to the visible effect would be to use +# more steps (at least 10x), and then higher frequency, and use +# range which includes 1 but excludes values at the top. +while True: + # Fade in + for i in range(1, 21): + pwm_cycle(LED, i, 2) + # Fade out + for i in range(20, 0, -1): + pwm_cycle(LED, i, 2) |