Commit aab22528 authored by Martí Bolívar's avatar Martí Bolívar Committed by Carles Cufi
Browse files

samples: blink_led: cleanups and fixups



Align this sample's source code and documentation with other basic
samples.

Adjust the logging configuration and output some additional printk()s
at startup to make it more obvious that initial errors are device
specific and related to calibration.

Fix the period calculation logic so the sample does not stick at the
maximum and minimum periods for two consecutive four-second periods.
That is, instead of setting the pin period to MIN_PERIOD_USEC twice in
a row, do it only once before doubling again, and similarly for
max_period.

Signed-off-by: default avatarMartí Bolívar <marti.bolivar@nordicsemi.no>
parent dff93ae0
Loading
Loading
Loading
Loading
+49 −40
Original line number Diff line number Diff line
.. _blink-led-sample:
.. _pwm-blinky-sample:

PWM: Blink LED
##############
PWM Blinky
##########

Overview
********

This is a sample app which blinks a LED using PWM.
This application blinks a LED using the :ref:`PWM API <pwm_api>`. See
:ref:`blinky-sample` for a GPIO-based sample.

The LED will start at a blinking frequency of 1 Hz. Every 4 seconds,
the blinking frequency will double. When the blinking frequency
reaches 64 Hz, the blinking frequency will be halved every 4 seconds
until the blinking frequency reaches 1 Hz. This completes a whole
blinking cycle. This faster-then-slower LED blinking cycle repeats forever.
The LED starts blinking at a 1 Hz frequency. The frequency doubles every 4
seconds until it reaches 64 Hz. The frequency will then be halved every 4
seconds until it returns to 1 Hz, completing a single blinking cycle. This
faster-then-slower blinking cycle then repeats forever.

Since for some PWM hardware it might be not possible to set the PWM period of
1 second (to achieve the blinking frequency of 1 Hz), this application at its
beginning tries to determine what is available for the used PWM hardware,
and accordingly decreases the maximum PWM period (thus increases the initial
blinking frequency) if needed.
Some PWM hardware cannot set the PWM period to 1 second to achieve the blinking
frequency of 1 Hz. This sample calibrates itself to what the hardware supports
at startup. The maximum PWM period is decreased appropriately until a value
supported by the hardware is found.

Requirements
************

Wiring
******
The board must have an LED connected to a PWM output channel. The PWM
controlling this LED must be configured using the ``pwm_led0`` :ref:`devicetree
<dt-guide>` alias, usually in the :ref:`BOARD.dts file
<devicetree-in-out-files>`.

Nucleo_F401RE, Nucleo_L476RG, STM32F4_DISCOVERY, Nucleo_F302R8
==============================================================
Connect PWM2(PA0) to LED
You will see this error if you try to build this sample for an unsupported
board:

Nucleo_F103RB
=============
Connect PWM1(PA8) to LED
.. code-block:: none

Nucleo_L496ZG
=============
No special board setup is necessary because there are three on-board LEDs (red,
green, blue) connected to the Nucleo's PWM.
   Unsupported board: pwm_led0 devicetree alias is not defined

Nucleo_H743ZI
=============
No special board setup is necessary because the on-board red LED is connected
to PWM output 12 (channel 1).
Wiring
******

Hexiwear K64
============
No special board setup is necessary because there is an on-board RGB LED
connected to the K64 PWM.
No additional wiring is necessary if ``pwm_led0`` refers to hardware that is
already connected to an LED on the board.

nrf52840dk_nrf52840
===================
No special board setup is necessary because there is an on-board LED connected.
In these other cases, however, manual wiring is necessary:

.. list-table::
   :header-rows: 1

   * - Board
     - Wiring
   * - :ref:`nucleo_f401re_board`
     - connect PWM2 (PA0) to an LED
   * - :ref:`nucleo_l476rg_board`
     - connect PWM2 (PA0) to an LED
   * - :ref:`stm32f4_disco_board`
     - connect PWM2 (PA0) to an LED
   * - :ref:`nucleo_f302r8_board`
     - connect PWM2 (PA0) to an LED
   * - :ref:`nucleo_f103rb_board`
     - connect PWM1 (PA8) to an LED

Building and Running
********************

This sample can be built for multiple boards, in this example we will build it
for the nrf52840dk_nrf52840 board:
To build and flash this sample for the :ref:`nrf52840dk_nrf52840`:

.. zephyr-app-commands::
   :zephyr-app: samples/basic/blink_led
@@ -63,5 +70,7 @@ for the nrf52840dk_nrf52840 board:
   :goals: build flash
   :compact:

After flashing the image to the board, the user LED on the board should start to
blinking as discussed in overview
Change ``nrf52840dk_nrf52840`` appropriately for other supported boards.

After flashing, the sample starts blinking the LED as described above. It also
prints information to the board's console.
+2 −0
Original line number Diff line number Diff line
@@ -2,4 +2,6 @@ CONFIG_STDOUT_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_PWM=y
CONFIG_LOG=y
CONFIG_LOG_PRINTK=y
CONFIG_LOG_IMMEDIATE=y
CONFIG_PWM_LOG_LEVEL_DBG=y
+56 −48
Original line number Diff line number Diff line
/*
 * Copyright (c) 2016 Intel Corporation
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file Sample app to demonstrate PWM.
 *
 * This app uses PWM[0].
 */

#include <zephyr.h>
@@ -15,79 +14,88 @@
#include <device.h>
#include <drivers/pwm.h>

#if DT_NODE_HAS_PROP(DT_ALIAS(pwm_led0), pwms) && \
    DT_PHA_HAS_CELL(DT_ALIAS(pwm_led0), pwms, channel)
/* get the defines from dt (based on alias 'pwm-led0') */
#define PWM_DRIVER	DT_PWMS_LABEL(DT_ALIAS(pwm_led0))
#define PWM_CHANNEL	DT_PWMS_CHANNEL(DT_ALIAS(pwm_led0))
#if DT_PHA_HAS_CELL(DT_ALIAS(pwm_led0), pwms, flags)
#define PWM_FLAGS	DT_PWMS_FLAGS(DT_ALIAS(pwm_led0))
#define PWM_LED0_NODE	DT_ALIAS(pwm_led0)

/*
 * Devicetree helper macro which gets the 'flags' cell from the node's
 * pwms property, or returns 0 if the property has no 'flags' cell.
 */

#define FLAGS_OR_ZERO(node)						\
	COND_CODE_1(DT_PHA_HAS_CELL(node, pwms, flags),		\
		    (DT_PWMS_FLAGS(node)),				\
		    (0))

#if DT_NODE_HAS_STATUS(PWM_LED0_NODE, okay)
#define PWM_LABEL	DT_PWMS_LABEL(PWM_LED0_NODE)
#define PWM_CHANNEL	DT_PWMS_CHANNEL(PWM_LED0_NODE)
#define PWM_FLAGS	FLAGS_OR_ZERO(PWM_LED0_NODE)
#else
#error "Unsupported board: pwm-led0 devicetree alias is not defined"
#define PWM_LABEL	""
#define PWM_CHANNEL	0
#define PWM_FLAGS	0
#endif
#else
#error "Choose supported PWM driver"
#endif

/* in microseconds */
#define MIN_PERIOD	(USEC_PER_SEC / 64U)

/* in microseconds */
#define MAX_PERIOD	USEC_PER_SEC
#define MIN_PERIOD_USEC	(USEC_PER_SEC / 64U)
#define MAX_PERIOD_USEC	USEC_PER_SEC

void main(void)
{
	struct device *pwm_dev;
	struct device *pwm;
	u32_t max_period;
	u32_t period;
	u8_t dir = 0U;
	int ret;

	printk("PWM demo app-blink LED\n");
	printk("PWM-based blinky\n");

	pwm_dev = device_get_binding(PWM_DRIVER);
	if (!pwm_dev) {
		printk("Cannot find %s!\n", PWM_DRIVER);
	pwm = device_get_binding(PWM_LABEL);
	if (!pwm) {
		printk("Error: didn't find %s device\n", PWM_LABEL);
		return;
	}

	/* In case the default MAX_PERIOD value cannot be set for some PWM
	 * hardware, try to decrease the value until it fits, but no further
	 * than to the value of MIN_PERIOD muliplied by four (to allow the
	 * sample to actually show some blinking with changing frequency).
	/*
	 * In case the default MAX_PERIOD_USEC value cannot be set for
	 * some PWM hardware, decrease its value until it can.
	 *
	 * Keep its value at least MIN_PERIOD_USEC * 4 to make sure
	 * the sample changes frequency at least once.
	 */
	max_period = MAX_PERIOD;
	while (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
	printk("Calibrating for device %s channel %d...\n",
	       PWM_LABEL, PWM_CHANNEL);
	max_period = MAX_PERIOD_USEC;
	while (pwm_pin_set_usec(pwm, PWM_CHANNEL,
				max_period, max_period / 2U, PWM_FLAGS)) {
		max_period /= 2U;
		if (max_period < (4U * MIN_PERIOD)) {
			printk("This sample needs to set a period that is "
			       "not supported by the used PWM driver.");
		if (max_period < (4U * MIN_PERIOD_USEC)) {
			printk("Error: PWM device %s "
			       "does not support a period at least %u\n",
			       PWM_LABEL, 4U * MIN_PERIOD_USEC);
			return;
		}
	}

	printk("Done calibrating; maximum/minimum periods %u/%u usec\n",
	       max_period, MIN_PERIOD_USEC);

	period = max_period;
	while (1) {
		if (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
				     period, period / 2U, PWM_FLAGS)) {
			printk("pwm pin set fails\n");
		ret = pwm_pin_set_usec(pwm, PWM_CHANNEL,
				       period, period / 2U, PWM_FLAGS);
		if (ret) {
			printk("Error %d: failed to set pulse width\n", ret);
			return;
		}

		if (dir) {
			period *= 2U;

		period = dir ? (period * 2U) : (period / 2U);
		if (period > max_period) {
			period = max_period / 2U;
			dir = 0U;
				period = max_period;
			}
		} else {
			period /= 2U;

			if (period < MIN_PERIOD) {
		} else if (period < MIN_PERIOD_USEC) {
			period = MIN_PERIOD_USEC * 2U;
			dir = 1U;
				period = MIN_PERIOD;
			}
		}

		k_sleep(K_SECONDS(4U));