For this exercise, we will use the Tower Pro Micro Servo SG90 servo motor, but you can also follow along without any additional hardware by using the LED’s to simulate a motor similar to what we did in exercise 1.
Important
The nRF91x1 DKs and the nRF7002 DK only have 1.8 V output, which is not enough to control the servo motor used in this course. If you are using one of these boards, you can follow along by using one of the LEDs to simulate a motor or use a level shifter, which we will not cover in this exercise.
The first thing we will do is use the pwm_led instance we used in the first exercise and create a custom variation of this that we will use for our PWM signal that will drive another GPIO other than LED 1. If you don’t have a motor available, follow along with the steps in this exercise, but don’t change the GPIO that this PWM instance will drive.
The datasheet shows that the PWM period is 20ms (50Hz), and the duty cycle is between 1ms and 2ms. Recall that these are the same values we used in the previous exercise when we tested the PWM peripheral for controlling LED1, and we observed that these values only resulted in a dimly lit LED. For the specific motor used in this exercise the values must be within the parameters to ensure that the motor only moves within the range it is physically able to move within.
The servo can rotate approximately 90 degrees in each direction. 1ms pulse corresponds to -90 degrees, 1.5ms corresponds to 0 degrees and 2ms corresponds to 90 degrees rotation.
In Exercise 1, we used LED1 as our PWM LED, which is connected to P0.13 for the nRF52840 DK. We need to use one of the available GPIOs for our motor. To do this, we need to make some modifications to the device tree.
Exercise steps
Open the code base of the exercise by navigating to Create a new application in the nRF Connect for VS Code extension, select Copy a sample, and search for Lesson 4 – Exercise 2.
You must build the application before beginning for some of the VS Code devicetree functionality to work properly.
1. Creating a custom PWM device
A useful resource when learning about devicetree is to inspect a board’s devicetree to see what nodes/devices are predefined on a board. For this exercise, we will focus on the devicetree for the nRF52840DK, nRF52840dk_nrf52840.dts, which we will open and inspect in the following sections. If you’re using the VS Code extension directly, you may find the .dts file under “Config files-> Devicetree” in the details view of the extension or in the relative path <install_path>\ncs\zephyr\boards\arm.
1.1 Create an overlay for your board
In the template of this exercise we’ve created a folder named boards which contains an .overlay file. Open this directory, inter_less4_exer2/boards, and rename the overlay file to the name of the build target for the board you are using, for example nrf52840dk_nrf52840.overlay. If you’ve created your own project from scratch instead of using the template, create a new overlay file located in inter_less4_exer2/boards and follow the same naming convention as just mentioned.
Open the .dts for your board, for instance nRF52840dk_nrf52840.dts for the nRF52840DK, or locate the board file for your board as stated in the introduction for step 1. When opening the .dts in the extension, you should be able to locate compatibles such as the predefined LEDs and buttons as well as the predefined node pwmleds.
The .dts contains the default definitions of what the DKs nodes/compatibles/devices should do and what GPIOs they are connected to. Every project in this version of the SDK that uses one of these boards will use this exact .dts file, so we will not make our modifications here. Instead, we will make the modifications in an overlay file that is specific to this project.
In the image above, you can see that the overlay file we have created will place itself in the devicetree folder located under the Config files item in the extension.
1.2 Add a pwm_led instance and change the polarity.
We want to create a custom device that should drive a GPIO with a PWM signal. From the nrf52840dk_nrf52840.dts, L:48-53, we know that we have a predefined pwm_led instance, which we can modify to fit our needs.
We will copy the pwm_led instance from the .dts and paste it into your project’s overlay file under step 1.2. Regardless of which board you’re using, you can copy this step as long asit has a PWM peripheral. You can verify this by reading the product specification for your specific board.
In addition to adding this node, we want to change the polarity to normal. This means that the PWM signal will have a high output for the duty cycle instead of a low.
Add the following code snippet to the overlay file
This snippet means that we now have a node in our overlay that will overwrite what is defined in the .dts file. This node is a pwmleds node that uses channel 0 on the pwm0 peripheral of the board, and has an alias named “pwm_led0”. For now, this is not any different from what is already defined in the .dts file other than that the polarity has been changed, but we will further modify this node in the next two steps.
We will use the alias pwm_led0 to showcase that this alias is just a name and that the properties can easily be modified to drive other devices on another GPIO.
1.3 Add your own custom pwm0 instance.
In your overlay file, right-click on “&pwm0” in the instance of pwmleds in and click Go to Definition. This will take you to the definition in the devicetree file we had a look at earlier, and you will see something like this:
Note
This will only work if you have tried to build the application before this step.
Notice that the pwm0 instance has two states, pwm0_default and pwm0_sleep. This is what is sent out on the GPIO connected to this pin control instance. We want to copy the &pwm0 instance into your overlay file, but create our own custom states by changing the names to pwm0_custom and pwm0_csleep.
Add the following code snippet in the overlay file
1.4 Configure which pins your custom pwm0 instance should use through pinctrl
Inspect pwm0_default and pwm0_sleep in your boards devicetree file by right-clicking on one of them and clicking Go to Definition again.
It should look something like this:
We will use the same format showcased in line 77-89 in the image above in our overlay file but change the names to pwm0_custom and pwm0_csleep, see the following code snippet as an example
Up until this step, we’ve created a custom pwm0 instance that does exactly the same as the pwm_led0 instance, only with different state names. For the nRF52840 DK, our new custom pwm0 is set to use P0.13 (port 0, pin 13) for its output. Since pin 13 is used by LED 1 on the nRF52840 DK, we need to change the output pin if we want to control anything other than LED1.
For instance, set <NRF_PSEL(PWM_OUT0, 0, 14)>; to have the PWM output go to GPIO 14, i.e LED 2, instead of GPIO 13, i.e LED 1. If you don’t have a motor available, then this step is sufficient to showcase how to drive a GPIO with a PWM signal.
If you’re using a motor, we instead want to use a free available GPIO on the DK. This means that if you wish to use anything other than what we’ve described here, you need to consult with the product specification of the device you’re using and check if that GPIO is available for use. A good starting point is to inspect the product specification for your development kit to find a GPIO that is not used for anything else. For instance, in the User Guide for the nRF52840 DK, in section “8.6 Connector interface”, you can see that all the GPIOs in P6 and P24 are already in use for the items listed in the figure.
The solution in this exercise will use the following GPIOs for the 3V boards and the GPIO connected to LED2 for the case of the 1.8V boards (nRF7002 DK and nRF91x1 DK)
Board
GPIOsin the overlay solution
nRF52 DK
P0.03
nRF52833 DK
P0.03
nRF52840 DK
P0.03
nRF5340 DK
P0.05
nRF7002 DK
P0.07 (LED2)
nRF9160 DK
P0.10
nRF91x1 DK
P0.01 (LED2)
Add the snippet corresponding to your board into your overlay file.
We have now set up our device to have a PWM output on pin 3, but we still need to decide how to set the pin. Let’s create the function set_motor_angle() that sets the motor angle based on the input parameter duty_cycle_ns.
This input parameter will be decided based on the motor’s datasheet. For the SG90 servo motor, you can see that here.
From the datasheet we can see that the duty cycle ranges from 1-2 ms while the PWM period is 20 ms (50Hz).
2.1 Define the function set_motor_angle() to set the motor angle.
Define a function to set the motor angle. This function should take in the PWM duty cycle or an angle between 0 and 180 degrees as the input parameter and use pwm_set_dt()to set it accordingly.
if (!pwm_is_ready_dt(&pwm_led0)) {LOG_ERR("Error: PWM device %s is not ready", pwm_led0.dev->name);return0;}err = pwm_set_dt(&pwm_led0, PWM_PERIOD, PWM_MIN_DUTY_CYCLE);if (err) {LOG_ERR("pwm_set_dt returned %d", err);return0;}
C
2.4 Change motor angle when a button is pressed.
Now we want to change the button_handler(), to call set_motor_angle() when button 1 or button 2 is pressed.
Add the following code snippet in button_handler()
Connect the motor’s ground to ground, the VCC to a voltage source on the DK, and the PWM wire to the GPIO pin you set in step 1.4.
Wire color
Servo
DK
Brown
Ground
Ground
Red
VCC
Voltage source
Yellow
PWM
GPIO pin
This is how the wiring looks if you’ve used the GPIO on port 0, pin 3
Important
For the nRF9160 DK, you must set SW9 on the board to 3V for the servo motor to work.
3.2 Build and flash the application to your board.
You should now be able to change the motor’s angle by pressing button 1 or button 2.
If you’ve followed this exercise without the motor and instead configured everything for the PWM LED from exercise 1, you should now be able to make the LED blink with two different frequencies. Note that the parameters chosen for the motor control may not work for an LED.
4. Configure pwm0 to drive LED 1.
Before we can add another PMW instance, we need to revert the PWM instance we’ve modified from driving a GPIO that controls an LED to a general GPIO, back to driving an LED.
4.1 Configure pwm0 to drive the GPIO pin for LED 1
We want to configure pwm0 to drive one of the LEDs on the board. We will change our previous overlay by changing NRF_PSEL to the GPIO connected to LED1.
Replace the pin control for pwm0_custom and pwm0_csleep code with the following code snippet, changing the pin number back to the GPIO corresponding to LED1
This is doing the same as set_led_blink() did initially. The integer we multiply PWM_PERIOD with is selected as a semi-arbitrary value to showcase how the LED blinking frequency varies with different input.
Note
The nRF7002 DK only has two buttons on the board, meaning the button handler can only have action on two buttons. In the solution, it controls pwm0, i.e LED 2.
The nRF9160DK only has two buttons, but it has two slide switches that will act similarly to the buttons in the button handler callback. To make the application work, ensure the switches are in the GND position and then switch to the right and back to the left position to simulate a button press.
5. Add another PWM instance to drive the servo.
Now, we want to add another PWM instance. While doing this, we will also show you how to create a custom device that will act as a servo instead of modifying the existing node, which is, by default, enabled to control pwm0.
5.1 Create a device binding for the servo, called pwm-servo.
Since there is no preexisting device binding for the device we want to use, we will create our own generic device binding in the application directory called pwm-servo.
This device binding will have the properties
pwms: The PWM specifier driving the servo motor, in our case &pwm1
min-pulse: The minimum pulse width in nanoseconds
max-pulse: The maximum pulse width in nanoseconds.
The pwms property is of type phandle-array, which is a property type used to specify a resource that is owned by another node.
In the folder dts/bindings, open the file called pwm-servo.yaml, and add the following code snippet
Now that we have a devicetree binding to describe it, let’s add the servo motor as a node in the devicetree. We want our servo motor to be driven by a signal generated from PWM instance pwm1.
We’ve now defined our device with the nodelabel “servo”, it’s a compatible of “pwm-servo” which is driven by the pwm1 instance, period of 20 ms and a normal polarity. In addition, we have defined it to have a minimum and maximum duty cycle of 1 ms and 2 ms, respectively
5.3 Configure which pins pwm1 should use
Similar to what we did previously, we need to use pin control to select what GPIO pins pwm1 should drive. First by defining the pin control states pwm1_custom_motor and pwm1_csleep_motor, then defining these states and which GPIO pins they are associated with.
Select your board and add the following in the overlay file
5.4 Retrieve the device structure for the servo motor
Initialize and populate structpwm_dt_spec by using PWM_DT_SPEC_GET(), and DT_NODELABEL() with the node label servo, that we defined in the overlay file.
5.7 Check if the motor device is ready and set its initial value.
Add the following code
Copy
if (!pwm_is_ready_dt(&pwm_servo)) {LOG_ERR("Error: PWM device %s is not ready", pwm_servo.dev->name);return0;}err = pwm_set_dt(&pwm_servo, PWM_PERIOD, PWM_SERVO_MIN_DUTY_CYCLE);if (err) {LOG_ERR("pwm_set_dt returned %d", err);return0;}
C
5.8 Change set_motor_angle() to use the pwm_servo device.
Replace the set_motor_angle() function with the following code snippet
After programming your device, you should now observe that LED1 starts blinking, and if your motor is not already in the initial position, it should move there. When you press the various buttons, you should now observe that the frequency of the LED changes or that the motor changes position depending on which button.
What we’ve now created is a device that has two PWM instances that each drive its own GPIO.
The solution for this exercise can be found in the GitHub repository, lesson4/inter_less4_exer2_solution.
Nordic Developer Academy Privacy Policy
1. Introduction
In this Privacy Policy you will find information on Nordic Semiconductor ASA (“Nordic Semiconductor”) processes your personal data when you use the Nordic Developer Academy.
References to “we” and “us” in this document refers to Nordic Semiconductor.
2. Our processing of personal data when you use the Nordic Developer Academy
2.1 Nordic Developer Academy
Nordic Semiconductor processes personal data in order to provide you with the features and functionality of the Nordic Developer Academy. Creating a user account is optional, but required if you want to track you progress and view your completed courses and obtained certificates. If you choose to create a user account, we will process the following categories of personal data:
Email
Name
Password (encrypted)
Course progression (e.g. which course you have completely or partly completed)
Certificate information, which consists of name of completed course and the validity of the certificate
Course results
During your use of the Nordic Developer Academy, you may also be asked if you want to provide feedback. If you choose to respond to any such surveys, we will also process the personal data in your responses in that survey.
The legal basis for this processing is GDPR article 6 (1) b. The processing is necessary for Nordic Semiconductor to provide the Nordic Developer Academy under the Terms of Service.
2.2 Analytics
If you consent to analytics, Nordic Semiconductor will use Google Analytics to obtain statistics about how the Nordic Developer Academy is used. This includes collecting information on for example what pages are viewed, the duration of the visit, the way in which the pages are maneuvered, what links are clicked, technical information about your equipment. The information is used to learn how Nordic Developer Academy is used and how the user experience can be further developed.
2.2 Newsletter
You can consent to receive newsletters from Nordic from within the Nordic Developer Academy. How your personal data is processed when you sign up for our newsletters is described in the Nordic Semiconductor Privacy Policy.
3. Retention period
We will store your personal data for as long you use the Nordic Developer Academy. If our systems register that you have not used your account for 36 months, your account will be deleted.
4. Additional information
Additional information on how we process personal data can be found in the Nordic Semiconductor Privacy Policy and Cookie Policy.
Nordic Developer Academy Terms of Service
1. Introduction
These terms and conditions (“Terms of Use”) apply to the use of the Nordic Developer Academy, provided by Nordic Semiconductor ASA, org. nr. 966 011 726, a public limited liability company registered in Norway (“Nordic Semiconductor”).
Nordic Developer Academy allows the user to take technical courses related to Nordic Semiconductor products, software and services, and obtain a certificate certifying completion of these courses. By completing the registration process for the Nordic Developer Academy, you are agreeing to be bound by these Terms of Use.
These Terms of Use are applicable as long as you have a user account giving you access to Nordic Developer Academy.
2. Access to and use of Nordic Developer Academy
Upon acceptance of these Terms of Use you are granted a non-exclusive right of access to, and use of Nordic Developer Academy, as it is provided to you at any time. Nordic Semiconductor provides Nordic Developer Academy to you free of charge, subject to the provisions of these Terms of Use and the Nordic Developer Academy Privacy Policy.
To access select features of Nordic Developer Academy, you need to create a user account. You are solely responsible for the security associated with your user account, including always keeping your login details safe.
You will able to receive an electronic certificate from Nordic Developer Academy upon completion of courses. By issuing you such a certificate, Nordic Semiconductor certifies that you have completed the applicable course, but does not provide any further warrants or endorsements for any particular skills or professional qualifications.
Nordic Semiconductor will continuously develop Nordic Developer Academy with new features and functionality, but reserves the right to remove or alter any existing functions without notice.
3. Acceptable use
You undertake that you will use Nordic Developer Academy in accordance with applicable law and regulations, and in accordance with these Terms of Use. You must not modify, adapt, or hack Nordic Developer Academy or modify another website so as to falsely imply that it is associated with Nordic Developer Academy, Nordic Semiconductor, or any other Nordic Semiconductor product, software or service.
You agree not to reproduce, duplicate, copy, sell, resell or in any other way exploit any portion of Nordic Developer Academy, use of Nordic Developer Academy, or access to Nordic Developer Academy without the express written permission by Nordic Semiconductor. You must not upload, post, host, or transmit unsolicited email, SMS, or \”spam\” messages.
You are responsible for ensuring that the information you post and the content you share does not;
contain false, misleading or otherwise erroneous information
infringe someone else’s copyrights or other intellectual property rights
contain sensitive personal data or
contain information that might be received as offensive or insulting.
Such information may be removed without prior notice.
Nordic Semiconductor reserves the right to at any time determine whether a use of Nordic Developer Academy is in violation of its requirements for acceptable use.
Violation of the at any time applicable requirements for acceptable use may result in termination of your account. We will take reasonable steps to notify you and state the reason for termination in such cases.
4. Routines for planned maintenance
Certain types of maintenance may imply a stop or reduction in availability of Nordic Developer Academy. Nordic Semiconductor does not warrant any level of service availability but will provide its best effort to limit the impact of any planned maintenance on the availability of Nordic Developer Academy.
5. Intellectual property rights
Nordic Semiconductor retains all rights to all elements of Nordic Developer Academy. This includes, but is not limited to, the concept, design, trademarks, know-how, trade secrets, copyrights and all other intellectual property rights.
Nordic Semiconductor receives all rights to all content uploaded or created in Nordic Developer Academy. You do not receive any license or usage rights to Nordic Developer Academy beyond what is explicitly stated in this Agreement.
6. Liability and damages
Nothing within these Terms of Use is intended to limit your statutory data privacy rights as a data subject, as described in the Nordic Developer Academy Privacy Policy. You acknowledge that errors might occur from time to time and waive any right to claim for compensation as a result of errors in Nordic Developer Academy. When an error occurs, you shall notify Nordic Semiconductor of the error and provide a description of the error situation.
You agree to indemnify Nordic Semiconductor for any loss, including indirect loss, arising out of or in connection with your use of Nordic Developer Academy or violations of these Terms of Use. Nordic Semiconductor shall not be held liable for, and does not warrant that (i) Nordic Developer Academy will meet your specific requirements, (ii) Nordic Developer Academy will be uninterrupted, timely, secure, or error-free, (iii) the results that may be obtained from the use of Nordic Developer Academy will be accurate or reliable, (iv) the quality of any products, services, information, or other material purchased or obtained by you through Nordic Developer Academy will meet your expectations, or that (v) any errors in Nordic Developer Academy will be corrected.
You accept that this is a service provided to you without any payment and hence you accept that Nordic Semiconductor will not be held responsible, or liable, for any breaches of these Terms of Use or any loss connected to your use of Nordic Developer Academy. Unless otherwise follows from mandatory law, Nordic Semiconductor will not accept any such responsibility or liability.
7. Change of terms
Nordic Semiconductor may update and change the Terms of Use from time to time. Nordic Semiconductor will seek to notify you about significant changes before such changes come into force and give you a possibility to evaluate the effects of proposed changes. Continued use of Nordic Developer Academy after any such changes shall constitute your acceptance of such changes. You can review the current version of the Terms of Use at any time at https://academy.nordicsemi.com/terms-of-service/
8. Transfer of rights
Nordic Semiconductor is entitled to transfer its rights and obligation pursuant to these Terms of Use to a third party as part of a merger or acquisition process, or as a result of other organizational changes.
9. Third Party Services
To the extent Nordic Developer Academy facilitates access to services provided by a third party, you agree to comply with the terms governing such third party services. Nordic Semiconductor shall not be held liable for any errors, omissions, inaccuracies, etc. related to such third party services.
10. Dispute resolution
The Terms of Use and any other legally binding agreement between yourself and Nordic Semiconductor shall be subject to Norwegian law and Norwegian courts’ exclusive jurisdiction.