sometimes PWM signal is reversed .

Kyu gravatar image

asked 2015-06-01 13:18:03 +0100

Aryan gravatar image

updated 2015-06-01 16:43:36 +0100

Hi Nordics!

I am using app_pwm.c file in SDK 8.1, and when I use it with softdevice(advertising or data transfer event), sometimes pwm signal is reversed.

I guess, reversed signal's reason is the BLE interrupt. Because this problem is not occurred, when I don't use softdevice ( advertising or data transfer event)

And I need your advice for overcome this problem.

These are my development environment.

SDK 8.1

SD 8.0

nRF51822 AA G0

custom board

Timer1- 2channels

Timer2-1 channel.

3 PWM channels - Red led, Green led, Blue led.

first, I used app_pwm library. (100Hz )

/* */

static void pwm_init(void)


    ret_code_t err_code;

    /* 2-channel PWM, 100Hz, output on LED pins. */
    app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_2CH(10000L, GPIO_LED_RED, GPIO_LED_GREEN);
    app_pwm_config_t pwm2_cfg = APP_PWM_DEFAULT_CONFIG_1CH(10000L, GPIO_LED_BLUE);

    /* Set the polarity of the channel. */
    pwm1_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_HIGH;
    pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_HIGH;
    pwm2_cfg.pin_polarity[0] = APP_PWM_POLARITY_ACTIVE_HIGH;

    /* Initialize and enable PWM. */
    err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_1_ready_callback);
    err_code = app_pwm_init(&PWM2,&pwm2_cfg,pwm_2_ready_callback);


void pwm_1_ready_callback(uint32_t pwm_id)   

    pwm_1_ready_flag = true;

void pwm_2_ready_callback(uint32_t pwm_id)   
    pwm_2_ready_flag = true;

second, I set the pwm chanel duty by using below code. 

(in this function, I didn't wait callback. I just keep trying until PWM is ready)

static void color_setting(uint8_t red, uint8_t green, uint8_t blue)


    pwm_1_ready_flag = false;
    pwm_2_ready_flag = false;

    /* Set the duty cycle - keep trying until PWM is ready... */    
    while (app_pwm_channel_duty_set(&PWM1, 0, (uint32_t)(sin_table[color_red]/2.55)) == NRF_ERROR_BUSY);
    while (app_pwm_channel_duty_set(&PWM1, 1, (uint32_t)(sin_table[color_green]/2.55)) == NRF_ERROR_BUSY);
    while (app_pwm_channel_duty_set(&PWM2, 0, (uint32_t)(sin_table[color_blue]/2.55)) == NRF_ERROR_BUSY);

    /* ... or wait for callback. */
   //    while(!pwm_1_ready_flag);


sin_table's value is from 0 to 255. so I divided it by 2.55

And this color_setting function is called in app_timer call_back function.

my app timer's interval is 40ms.

In other words, PWM values is changed in every 40ms, and it repeat 0~100 value.

This "PWM signal reversed" issue is not appeared, when I don't use softDevice.

Is there any idea for avoiding this problem??

This problem is critical issue on my project.

Please help me.


edit retag flag offensive close delete report spam


Sorry to hear that, goal of this driver was to work with SoftDevice. We'll try to fix it in next release.

Krzysztof Chruściński ( 2015-06-03 06:38:15 +0100 )editconvert to answer

when I use just two pwm, reverse signal doesn't appear.

Kyu ( 2015-06-07 09:05:58 +0100 )editconvert to answer

and when I set the 3 PWM value with same value, reverse signal doesn't appear.

Kyu ( 2015-06-07 09:40:48 +0100 )editconvert to answer

Do you mean when you use a constant value for all 3 PWM values, or do you mean a varying value, but the same for all three?

JohnBrown ( 2015-06-12 09:23:20 +0100 )editconvert to answer

I would like 2 PWM's running on my board (SDK8.1, SD8.0.0, BLE enabled & connected, IC rev2)

I've tried

a) 2 PWM channels on 1 timer and

b) 1 PWM channel on 2 timers (timer1 & timer2)

but am still seeing this.

I tried the nrf_pwm library on GitHub but that results in ON or OFF, no duty cycle. I also tried calling the same as the pwm_stop pwm_start code above when the duty cycle is set to 0 or >0 respectively, this helped, but I think the PWM duty cycles are 'drifting' but are the correct polarity.

It would be good to know if there''s a fix avialbile, even if its not been released yet.IS there?

Thanks Wayne

Wayne ( 2015-06-15 13:02:45 +0100 )editconvert to answer

I have created automatic tests that can catch such sporadic errors. I can confirm the bug. Expect the solution in the following week :)

Radosław Koppel ( 2015-06-26 14:20:38 +0100 )editconvert to answer

I have the solution that seems to work - we are going to run overnight tests to prove it. Stay informed :)

Radosław Koppel ( 2015-06-29 14:40:41 +0100 )editconvert to answer

Hi Rodoslaw, I kind of have a theory that I think should work. Can you see if this could be a posibility https://devzone.nordicsemi.com/questi...

Aryan ( 2015-06-29 14:54:29 +0100 )editconvert to answer

Hi Aryan. The whole PWM code complexity is here because we do not want the timer stopped because it would generate glitches in generated signal. See the answer below.

Radosław Koppel ( 2015-06-30 07:44:23 +0100 )editconvert to answer

The user on the other question is already restarting the timer. But I see that you have attached new library code. Great work.

Aryan ( 2015-06-30 11:24:54 +0100 )editconvert to answer

4 answers

Sort by » oldest newest most voted
Radoslaw Koppel gravatar image

answered 2015-06-30 07:40:46 +0100

olba gravatar image

updated 2016-09-13 10:47:31 +0100

Update: This issue is fixed since SDK 10.

This is fresh copy from our development repository: (This is obsolete. See the newest driver attached below) pwm.7z This fix is going to be presented in the nearest release. There are also some functions added to give the possibility to set PWM duty in cycles - it was something I needed for testing and probably it would be left in official API.

The error is really subtle and quite hard to find - the order of some operations gives a possibility to break the synchronisation if interrupt occurs between them. This version was simulated whole night with simulated interrupting in random places and in the morning today no errors was found.

Beside of that this is a real masterpiece of code and as I have told - the error was really subtle.

Ok - it was a really crazy work. Yesterday I have detected that it is changing channel 1 that breaks channel 2, and additionally it was only after second change from 0 of 100% duty to another value. I mean - first change from 0 to 1... or from 100 to 99 and then second change to any value that is not 0 nor 100% and BAM - because of some event to interrupt race sometimes phase was switched.

So god news everyone: hardware looks ok. Updated software preview here: pwm_20150708.7z

edit flag offensive delete publish link more


Does this version of the pwm project come from nordic? Also, is the PWM run completely in hardware once initialized or does it required a software interrupt each reset of the timer?

Lucas ( 2015-06-30 19:08:15 +0100 )editconvert to answer

Hello, I am using this updated code and while it seems to have reduced the problem, it has not eliminated it. I still have the polarity of my PWM signal flipping every couple minutes. Not sure if I can provide any helpful information...

StudioNeat ( 2015-06-30 23:16:13 +0100 )editconvert to answer

I checked my code several times. I built my own outside of nordics app_pwm before their release. It seems to me that PPI events are being lost in hardware.

Lucas ( 2015-07-01 00:00:55 +0100 )editconvert to answer

@Lucas, so are you saying you are having this issue outside of the Nordic provided PWM library?

StudioNeat ( 2015-07-01 00:14:24 +0100 )editconvert to answer

Ya I made my own library because I wanted a complete hardware pwm before nordic released their app_pwm.c library. I am experiencing the same pwm inversion as seen with the library. I believe hardware events are being lost. If you miss a toggle event the pwm would become inverted.

Lucas ( 2015-07-01 00:26:17 +0100 )editconvert to answer

Lucas - Yes, this library comes from Nordic. I was waiting for my status update and as you can see now - I am one of the Nordic employee.

This library works in hardware but it uses interrupts for internal flags to mark if update process is finished. So it is not safe to call duty setting function from high priority interrupt. The interrupt timing is not critical for library to work.

StudioNeat - Could you provide some code for testing? This version was tested overnight using testing code in witch the original version did not survive more than 15 seconds.

I am not saying I could not miss something. The fact is that in nrf51 it is quite complex to provide reliable PWM output - in nrf52 the GPIOTE it is much better and it can provide a reliable PWM output even without dedicated PWM peripheral.

Radosław Koppel ( 2015-07-01 09:55:16 +0100 )editconvert to answer

I will try to post some code today. I did make one observation that may be helpful: the issue seems to be related to the PWM period. I am trying to use a 64us period, with 64us the polarity reverses somewhat frequently. If I double the period to 128us it rarely seems to happen.

In my program I am updating the duty cycle every 10ms.

StudioNeat ( 2015-07-01 15:42:33 +0100 )editconvert to answer

I have rewrite the testing environment to test 2 channels simultaneously. I have also set 64us of PWM cycle. I can say now is that I can confirm that channel 2 gets inverted about a dozen of seconds. I am working on it now.

Radosław Koppel ( 2015-07-06 11:22:38 +0100 )editconvert to answer

It seems that I have located another error in the library - wait for the results of the overnight testing. In my current test, the original code was reversing PWM about every minute. Now it works above 20 minutes and still no error - promising :)

Radosław Koppel ( 2015-07-07 14:53:48 +0100 )editconvert to answer

Great, looking forward to testing it. Tom

StudioNeat ( 2015-07-07 23:53:58 +0100 )editconvert to answer

New software included. Waiting any feedback. I hope the problem would be solved for good - no error detected during tests on our side.

Radosław Koppel ( 2015-07-08 09:46:09 +0100 )editconvert to answer

I have been testing the updated PWM library in my code and it is better, but I still get frequent 'glitches' when updating the PWM signal. If I have the period set to 64us I have glitches, if I set the period to 128us then it runs fine. I however have not ruled out that there might be some error in my code that is causing these glitches. Certainly the work you have done to improve the PWM library has helped. It has reduced these glitches in general, but I am still having some. My hunch is it could be the frequency and method I am using to set the duty cycle on the PWMs? I am using two PWM instances, each with one channel. Do you have any best practices for setting channels on two instances?

StudioNeat ( 2015-07-09 17:30:36 +0100 )editconvert to answer

What do you mean by "glitches"? If it is possible - please include some waveforms. Is the signal inverting? On what channel this "glitches" can be seen? How often it happens? The method you use to update the PWM seems OK.

Radosław Koppel ( 2015-07-10 07:38:20 +0100 )editconvert to answer

Ok, so I ran the signal through an logic analyzer to see exactly what is happening. And the 'glitches' I described before are in fact the PWM polarity reversing. It is actually reversing back and forth several times in a row, sometimes like 50 times in a row. I made a few observations: 1. The signal reverses at a predicable timing. When a pattern of reversal starts it happens every 10ms on the dot. I am updating the PWM duty every 10ms, so that timing seems to align. For example: I have one recording where the flipping started at 1s 354ms into the recording, at 364ms it would reverse polarity, at 374ms reverse again, and like that for 40 times and then it was fine. During that pattern the duty value is just gradually changing from ~6% to 11%, so no big changes (besides the polarity). 2. The problem doesn ...(more)

StudioNeat ( 2015-07-11 01:20:24 +0100 )editconvert to answer
  1. Sometimes it flips once. Sometimes 100 times in a row. But always at that 10ms interval, so it is easy to see on the signal trace. In case you have access to Saleae Logic, here is adropbox link to a logic file with the recorded signal, the flipping is right at the beginning. Logic File
StudioNeat ( 2015-07-11 01:22:33 +0100 )editconvert to answer

Hi, I can see it. It is some clue to build new test. If I understand you correctly during this changes you was rising values from 6 to 11%? What was the step? Correct polarity is positive?

I am not able to recreate this problem. A few questions:

  1. Why do you need such small period? Code suggests that you are using it for LED driving. 64us gives over 15kHz period.
  2. Can you include the code that generates this glitch?
  3. What exact version of the chip are you using? Please include also the production code from the case.
Radosław Koppel ( 2015-07-13 09:50:34 +0100 )editconvert to answer

Yes, the values in that example were raising from 6 to 11%, but the problem exists in other cases and at other rates/steps. 1. The reason I need such a small period is I am driving a induction coil to activate an electromagnet. If the frequency is lower, say with a 128us period, the PWM is audible. 2. The code is somewhat complex and relies on a BLE connection to an iOS app. I can make a simplified version to demonstrate the glitch, what part of the code are you most interested in? 3. I am using the nRF51-DK dev board. And on the nRF51 chip itself these numbers are printed: N51422 QFACA1 1503AD. I also have the same issue on a Red Bear Lab Nano module I have. Also, on the DK there is a sticker that has 'PCA10028 V1.1.0 2015.11' not sure if that ...(more)

StudioNeat ( 2015-07-13 22:41:08 +0100 )editconvert to answer

I found another possible clue, and hopefully it isn't just me being stupid:

So I need to set the duty at a higher resolution than the 0-100 provided by the app_pwm_channel_duty_set function in the PWM library, so I added a function to the PWM library that accepts 0-1024. I have been using that function, and it mirrors the original function except 100UL is replaced with 1024UL. Today when I was creating a test program to send I went back to the original app_pwm_channel_duty_set function and the polarity reversals went away as far as I can tell with my testing.

I am not sure how the function I added would create the problem. Here is the function I added and used called 'app_pwm_channel_duty_set_hires':

StudioNeat ( 2015-07-14 04:12:33 +0100 )editconvert to answer

ret_code_t app_pwm_channel_duty_set_hires(app_pwm_t const * const p_instance, uint8_t channel, app_pwm_duty_t duty) { uint32_t ticks = ((uint32_t)app_pwm_cycle_ticks_get(p_instance) * (uint32_t)duty) / 1024UL; return app_pwm_channel_duty_ticks_set(p_instance, channel, ticks); }

StudioNeat ( 2015-07-14 04:12:46 +0100 )editconvert to answer

I make sure that only 0-1024 is ever passed to the function. Not sure why this would create the problem.

StudioNeat ( 2015-07-14 04:13:51 +0100 )editconvert to answer

I will check it, but no earlier than the end of next week. Currently I have some new implementations to do. Watching the waveforms it looks that the problem is when changing the PWM duty with the step of 1 tick. If having steady PWM is really important did you thing about using nrf52? It has SET and CLEAR tasks for GPIOTE.

Radosław Koppel ( 2015-07-16 11:18:52 +0100 )editconvert to answer

I looked into your idea of it happening only when chaning the duty by one tick and that seems to be the issue. I set up a test where I am setting the PWM via ticks directly. Lots of inversions if ramp the PWM by 1 tick increments. However, if I ramp by 2 ticks, no errors. It would be nice to figure out how to fix this issue as it reduces the effective PWM resolution by half. I'll let you know if I find anything else. Thanks again for your assistance on this!

StudioNeat ( 2015-07-16 16:32:28 +0100 )editconvert to answer

Forget changing the PWM duty cycle at 10ms intervals. If you leave it a constant rate the duty cycle inverts randomly. IN SUMMARY THE NRF51822 DOES NOT HAVE A HARDWARE PWM. Some one screwed up and now you guys are beating around the bush trying to fix a problem in software that cant be fixed.

Lucas ( 2015-07-24 18:21:50 +0100 )editconvert to answer

Hey Lucas, with the updated PWM library from above, and making sure the duty is changed by at least 2 clock ticks, I haven't experienced any inversion with a routine that has been running for days. Still not perfect, but they have made some progress for sure.

StudioNeat ( 2015-07-24 18:30:31 +0100 )editconvert to answer

Are you running the bluetooth stack? What is your connection interval? The only way I have found to mitigate the problem is to reduce the frequency of the pwm significantly say 250Hz. Also lower the rate that that timer counts at. I pretty certain that when the bluetooth stack is running the GPIO_TE toggle events are being missed. I don't know if it is the PPI failing to deliver the event to the GPIO_TE. Or the GPIO_TE missing the event.

Lucas ( 2015-07-24 19:01:05 +0100 )editconvert to answer

Yes, the 110 Softdevice. The connection interval is between 20 and 75 ms. My use case is a bit different possibly... I connect to a iOS device and stay connected for awhile. Then it might not connect for days, all the while the PWM is constantly changing. I am updating the duty now every 4ms, nearly constantly. My period is 64us, or 15.625kHz. I was having almost constant inversions if I would change the duty value by only one clock tick. I changed the code so that it would only update if it the new duty value was 2 ticks away and that is working fine, no issues that I have seen. I am using the updated PWM library included above, with one additional function that lets me set the duty from 0-512 because I need the resolution. I want to set it from 0-1024, but at my frequency ...(more)

StudioNeat ( 2015-07-24 20:02:15 +0100 )editconvert to answer

I set it to a constant duty cycle of 25%. Don't change it constant 25% and inversions still happen. Very infrequently, but they do.

Lucas ( 2015-07-24 20:16:30 +0100 )editconvert to answer

Hey Lucas - are you using updated driver (pwm_20150708) from this topic?

Radosław Koppel ( 2015-07-27 07:52:38 +0100 )editconvert to answer

Yes, but I had to back port it to SDK 8.0. SDK 8.1 broke bluetooth connectivity for select android phones our Google Nexus 5 doesn't work with SDK 8.1 GG. Iphone and Google Nexus 4 dont seem to have a problem.

Lucas ( 2015-07-28 07:57:24 +0100 )editconvert to answer

I have more findings. This error only starts occurring for me after the system I am working with runs out of batteries. Once I recharge the system the issue is persistent. Even after a system software reset the inversion continues to happen. If i reprogram the system there are no inversions until the system runs out of batteries again and is recharged.

Lucas ( 2015-07-29 03:00:40 +0100 )editconvert to answer
Aryan gravatar image

answered 2015-06-11 11:05:07 +0100

This is a known issue on nRF51 chips with PWM. There is actually no easy way to make sure that the signals are not inverted when using softdevice. The engineers are trying their best to try to avoid it in the driver. Hopefully it will be addressed in the future SDK releases.

edit flag offensive delete publish link more


I have the same setup and am seeing this using 2 channes on timer2. is there any news on when a fix might appear?

SDK 8.1

SD 8.0

nRF51822 AA G0

thanks Wayne

Wayne ( 2015-06-14 13:43:37 +0100 )editconvert to answer

I would also like to add that i do see this problem. In addition, after a while of transitioning my 2 channel PWMs at different duty cycles, and then I set only the duty cycle for 1 side of the channel while the other is set to zero, it seems like both PWM channel seems to be running even when my software explicitly setting one of the channel to be 0.

Regards, William

gohak ( 2015-06-17 06:18:53 +0100 )editconvert to answer

The team is working on it

Aryan ( 2015-06-25 14:02:57 +0100 )editconvert to answer
siroslay gravatar image

answered 2015-07-27 00:20:42 +0100

updated 2015-07-27 00:22:02 +0100

I was having the same issue with SDK 9.0.0: on power-up the PWM polarity would sometimes flip. Based on my previous experience finding a bug in the PWM library, I decided to set values to all of the fields in the app_pwm_cb_t struct created by APP_PWM_INSTANCE() (they aren't initialized right away in the code). As far as I can tell, the problem went away entirely.

My workaround looks like this:

PWM1.p_cb->channels_cb[0].gpio_pin = 0;
PWM1.p_cb->channels_cb[0].pulsewidth = 0x00000000;
PWM1.p_cb->channels_cb[0].ppi_channels[0] = NRF_PPI_CHANNEL26;
PWM1.p_cb->channels_cb[0].ppi_channels[1] = NRF_PPI_CHANNEL27;
PWM1.p_cb->channels_cb[0].polarity = APP_PWM_POLARITY_ACTIVE_LOW ;
PWM1.p_cb->channels_cb[0].initialized = 0;

PWM1.p_cb->channels_cb[1].gpio_pin = 1;
PWM1.p_cb->channels_cb[1].pulsewidth = 0x00000000;
PWM1.p_cb->channels_cb[1].ppi_channels[0] = NRF_PPI_CHANNEL28;
PWM1.p_cb->channels_cb[1].ppi_channels[1] = NRF_PPI_CHANNEL29;
PWM1.p_cb->channels_cb[1].polarity = APP_PWM_POLARITY_ACTIVE_LOW;
PWM1.p_cb->channels_cb[1].initialized = 0;

PWM1.p_cb->period = 0xFFFFFFFF;
PWM1.p_cb->p_ready_callback = pwm_ready_callback;
PWM1.p_cb->ppi_channels[0] = NRF_PPI_CHANNEL30;
PWM1.p_cb->ppi_channels[1] = NRF_PPI_CHANNEL31;
PWM1.p_cb->ppi_group = NRF_PPI_CHANNEL_GROUP3;

I guessed at many of the values (mainly the PPI values), so YMMV.

edit flag offensive delete publish link more
mbosma gravatar image

answered 2015-08-03 10:53:41 +0100

I was having polarity issues as well on my nRF51822 using SDv8 and SDK 8.1.0. I applied the patch Radosław made just now, so now await testing.

I was wondering if there is a way to detect the polarity invertion and then call nrf_drv_gpiote_out_task_trigger to correct it. This because my application relies on the pwm.

Also, instead of using gpiote toggle isn't it possible to use NRF_GPIOTE_POLARITY_LOTOHI/NRF_GPIOTE_POLARITY_HITOLO. This way, if you miss a gpiote event, at least the polarity isn't inverted. Not sure if it is even possbile, but just a brainfart.

edit flag offensive delete publish link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer. Do not ask a new question or reply to an answer here.

[hide preview]

Question Tools



Asked: 2015-06-01 13:18:03 +0100

Seen: 3,353 times

Last updated: sep. 13 '16