SDK 14.2, NRF52840, S140
Hi,
Thank you so much in advance for answering my very long question. I am a little confused by the low power pwm example and app_PWM documentation and have some questions:
1) The pwm handler is called very frequently -- is it called on each app timer tick? Is that 32768 times per second? Why is the timer configured as a single_shot, if the timeout needs to be called so repetitively?
err_code = app_timer_create(p_pwm_instance->p_timer_id, APP_TIMER_MODE_SINGLE_SHOT, pwm_timeout_handler); // from low_power_pwm.c line 177
2) The pwm handler, however, seems to only be responsible for changing the duty cycle of the pin (I am pwm-ing an LED). Does this mean that if I don't want to change the duty cycle of the LED (that is, if I want to keep the same duty cycle always), does this mean I don't need the pwm handler? Or I still need it, but it can be an empty function?
3) If I understand properly, the bitmask is supposed to be (1 << PIN_NUMBER)? So if I want to PWM GPIO P0.15, then I have to set bitmask = (1 << 15)?
What if I wanted to PWM a pin in P1?
4) It seems like the higher the duty cycle, the dimmer the LED gets. Is that right? This seems backwards. What units are the period and duty cycle in, and how is duty cycle defined relative to period? Is {period = 100, duty cycle = 50} functionally the same as {period = 200, duty cycle = 100}? How do these numbers relate to the timer -- are they ticks?
5) The LED doesn't seem to be at a very constant brightness level while it is being PWM'd; every so often, I will get a brighter flash, which is a problem because it makes the light distractingly uneven. This is especially evident when the LED is relatively dim. The bright pulses are kind of like this: ______-_______---______---______. Why does this happen?
6) If I am turning on and off different LEDs, and I don't want them to be on all at the same time, before, I would call nrf_gpio_pin_set(LED1); nrf_gpio_pin_clear(LED2);
Now, do I have to change these to low_power_pwm_start(LED1); low_power_pwm_stop(LED2); ? Would that result in the same behavior -- e.g, LED1 turning on while LED2 turns off, and when each is on, it is PWM'd to the correct respective level (as set by low_power_pwm_init and duty_set)?
----
Here are my questions about the app_pwm, so I can make a more informed decision about which to choose.
7) I notice that I need TIMER0 and TIMER1 to both be enabled, even if I am PWM-ing only one LED. I copied the code from the SDK PWM example, which uses TIMER1 for both LEDs. What is TIMER0 for then? If I want to PWM 3 LEDs with 3 different duty cycles, and have them be on and off at different times, it seems like I need a separate timer for each one (3 timers). And if I wanted them to all have the same duty cycle always, the only way to group them up is to use a 2 channel PWM and a separate 1 channel PWM (2 timers). Is this right?
8) Is the equivalent of nrf_gpio_pin_set(LED1); nrf_gpio_pin_clear(LED2); using the app_pwm to do this:
while (app_pwm_channel_duty_set(&PWM1, 0, 100) == NRF_ERROR_BUSY); while (app_pwm_channel_duty_set(&PWM2, 0, 0) == NRF_ERROR_BUSY); // where both are set to APP_PWM_POLARITY_ACTIVE_HIGH
The behavior I am experiencing is:
- if I have only LED1 set to be PWM'd, calling nrf_gpio_pin_set(LED2); nrf_gpio_pin_clear(LED1); and also nrf_gpio_pin_set(LED1); nrf_gpio_pin_clear(LED2); seems to work, in that LED2 is turned on to full brightness while LED1 is turned off, and then LED2 is turned off while LED1 is turned on to the PWM brightness.
- If I have both LED1 and LED2 set to be PWM'd, nrf_gpio_pin_set/clear() don't work anymore. Calls to _set() and _clear() don't do anything. For example, in the following code, where PWM1 is config'd for LED1, and PWM2 is config'd for LED2, both LED1 and LED2 stay on indefinitely:
while (app_pwm_channel_duty_set(&PWM1, 0, 100) == NRF_ERROR_BUSY); // start off at 100% duty cycle while (app_pwm_channel_duty_set(&PWM2, 0, 50) == NRF_ERROR_BUSY); nrf_gpio_pin_clear(LED2);
9) I am confused that there are 4 different compare channels specified here (PWM Module documentation, p. 263). But each PWM instance can only have 2 channels, based on the code. EDIT: It looks like the hardware PWM HAL module is something completely different. Is there an example for how to use it? What are the benefits/drawbacks for each of the 3 PWM options (lpp, app_pwm, hardware pwm)?
I notice the app PWM library lets you specify period in us, and also duty cycle out of 100%. Does this mean there are possibly rounding errors (even if they are small) when specifying a period that doesn't correspond with a frequency that is an even divisor of the clock frequency? I also assume that the duty cycle gets translated to a compare value, and that this may result in some rounding as well -- is this the case? I am wondering if it is possible to utilize the full dynamic range of the PWM hardware feature, which seems like it has 15-bit resolution. is it possible to specify COUNTERTOP and the COMPARE register values directly?
I searched but didn't see that COUNTERTOP was even referenced in the app_pwm library, so maybe I am misunderstanding how this works:
10) Related to #9, I thought duty cycle is a number out of 100, but I see that app_pwm_duty_t is a uint16_t. But if I set duty cycle = 5000, somehow app_pwm_channel_duty_get(...) shows that the 5000 got turned into 84. how did this happen, and why is app_pwm_duty_t a uint16?
Thanks!