User avatar
By eriksl
#51101 I think it still needs a bit of work ;)

I am using period = 330 kHz, 65536 step, approximate, defined SDK_PWM_PERIOD_COMPAT_MODE, removed libpwm.a and included your version instead.

Nothing happens ;)

Output of my application says both period and duty are set like they should (by inquiering get_duty and get_period).

I had to make a few trivial adjustments, because my compiler warning/error flags are a tad higher than usual, I will post the diff here, so you can judge.

The local ESP8266_new_pwm/pwm.h file has the definitions like in SDK version, but I felt they should not be re-used from a file that is not used (in the SDK). The PWM_MAX_CHANNELS #defiine moved there as well.

Probably clear in itself, but with libpwm.a my code works. I am not using any hardware timers myself, just the UART interrupts and pin change interrupts, I don't think that should matter?
Code: Select alldiff --git a/pwm.c b/pwm.c
index fb9ed99..a439528 100644
--- a/pwm.c
+++ b/pwm.c
@@ -16,31 +16,33 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include "ESP8266_new_pwm/pwm.h"
+
 /* Set the following three defines to your needs */
 
 #ifndef SDK_PWM_PERIOD_COMPAT_MODE
   #define SDK_PWM_PERIOD_COMPAT_MODE 0
 #endif
-#ifndef PWM_MAX_CHANNELS
-  #define PWM_MAX_CHANNELS 8
-#endif
 #define PWM_DEBUG 0
 
 /* no user servicable parts beyond this point */
 
-#if SDK_PWM_PERIOD_COMPAT_MODE
+//#if SDK_PWM_PERIOD_COMPAT_MODE
 #define PWM_PERIOD_TO_TICKS(x) (x * 0.2)
 #define PWM_DUTY_TO_TICKS(x) (x * 5)
-#else
-#define PWM_PERIOD_TO_TICKS(x) (x)
-#define PWM_DUTY_TO_TICKS(x) (x)
-#endif
+//#else
+//#define PWM_PERIOD_TO_TICKS(x) (x)
+//#define PWM_DUTY_TO_TICKS(x) (x)
+//#endif
 
 #include <c_types.h>
-#include <pwm.h>
 #include <eagle_soc.h>
 #include <ets_sys.h>
 
+void ets_isr_attach(int, void *, void *);
+void ets_isr_mask(unsigned int);
+void ets_isr_unmask(unsigned int);
+
 // from SDK hw_timer.c
 #define TIMER1_DIVIDE_BY_16             0x0004
 #define TIMER1_ENABLE_TIMER             0x0080
@@ -199,7 +201,6 @@ _pwm_phases_prep(struct pwm_phase* pwm)
 {
    uint8_t n, phases;
 
-   uint16_t off_mask = 0;
    for (n = 0; n < pwm_channels + 2; n++) {
       pwm[n].ticks = 0;
       pwm[n].on_mask = 0;
@@ -210,10 +211,10 @@ _pwm_phases_prep(struct pwm_phase* pwm)
       int32_t ticks = PWM_DUTY_TO_TICKS(pwm_duty[n]);
       if (ticks == 0) {
          pwm[0].off_mask |= gpio_mask[n];
-      } else if (ticks >= pwm_period) {
+      } else if (ticks >= (int32_t)pwm_period) {
          pwm[0].on_mask |= gpio_mask[n];
       } else {
-         if (ticks < (pwm_period_ticks/2)) {
+         if (ticks < ((int32_t)pwm_period_ticks/2)) {
             pwm[phases].ticks = ticks;
             pwm[0].on_mask |= gpio_mask[n];
             pwm[phases].off_mask = gpio_mask[n];
@@ -356,7 +357,7 @@ pwm_start(void)
    uint8_t phases = _pwm_phases_prep(*pwm);
 
         // all with 0% / 100% duty - stop timer
-   if ((*pwm)[phases].ticks == pwm_period_ticks) {
+   if ((*pwm)[phases].ticks == (int32_t)pwm_period_ticks) {
       if (pwm_state.next_set)
          ETS_FRC1_INTR_DISABLE();
       pwm_state.next_set = NULL;
@@ -387,7 +388,7 @@ pwm_set_duty(uint32_t duty, uint8_t channel)
    pwm_duty[channel] = duty;
 }
 
-uint32_t ICACHE_FLASH_ATTR
+__attribute__((pure)) uint32_t ICACHE_FLASH_ATTR
 pwm_get_duty(uint8_t channel)
 {
    if (channel > PWM_MAX_CHANNELS)
@@ -406,21 +407,20 @@ pwm_set_period(uint32_t period)
 
 }
 
-uint32_t ICACHE_FLASH_ATTR
+__attribute__((pure)) uint32_t ICACHE_FLASH_ATTR
 pwm_get_period(void)
 {
    return pwm_period;
 }
 
-uint32_t ICACHE_FLASH_ATTR
+__attribute__((const)) uint32_t ICACHE_FLASH_ATTR
 get_pwm_version(void)
 {
    return 1;
 }
 
-void ICACHE_FLASH_ATTR
+__attribute__((const)) void ICACHE_FLASH_ATTR
 set_pwm_debug_en(uint8_t print_en)
 {
    (void) print_en;
 }
-

pwm.h
Code: Select all#ifndef __pwm_h__
#define __pwm_h__

#ifndef PWM_MAX_CHANNELS
  #define PWM_MAX_CHANNELS 8
#endif

#define PWM_CHANNEL_NUM_MAX PWM_MAX_CHANNELS

#include <stdint.h>

void pwm_init(uint32_t period, uint32_t *duty,uint32_t pwm_channel_num, uint32_t (*pin_info_list)[3]);
void pwm_start(void);

void pwm_set_duty(uint32_t duty, uint8_t channel);
uint32_t pwm_get_duty(uint8_t channel);
void pwm_set_period(uint32_t period);
uint32_t pwm_get_period(void);

uint32_t get_pwm_version(void);
void set_pwm_debug_en(uint8_t print_en);

#endif
User avatar
By StefanB
#51104 @eriksl, Cicero: Thanks, glad you like it :-)

@eriksl: There are two different aspects when it comes to interrupts:

1. Driving the GPIO pins - this is done from the normal timer interrupt. As any other interrupt (but NMI) it can be delayed by a currently running interrupt handler. This is a matter of fact for any software driven PWM. You can use it for LED dimming, fan speed control, ..., but better not for robotics or tooling machines.

2. Setting of the duty values - new values are committed by the pwm_start() function (this had better been named pwm_apply or _commit, but I wanted to the with the ESP SDK names). All the real work is done in a scratch buffer. After any calculations, a single pointer referencing this buffer is updated, which is atomic. Actually there are three buffers, one for serving the GPIO pins, one becoming valid start of the next PWM period (which might be the same as the first), and the scratch buffer.

So to answer your question, the worst that can happen if you are trying to change duty values faster than a PWM period, some immediate changes may not be visible. The code itself is interrupt safe - you can set the duty values (and period) whenever you want and from any context, only pwm_start should be called from a single context.
User avatar
By eriksl
#51112 .. as confirmed from browsing through the code (trying to find why it doesn't work in my application) ;)

I don't directly see the problem. I guess I will have to enable/add some debug code. Problem is that the pcb I am now using for testing code, has the UART in use for something else (driving an LCD display), so I can't use it for debugging.

When I have one of the other testboards ready, I will try it with debugging. But maybe you see the problem in a blink, you never know ;)