From 72d5dd99fbeeec3b42f5949e27a43b2a33c7d199 Mon Sep 17 00:00:00 2001
From: Blipi <gpascualg93@gmail.com>
Date: Fri, 28 Aug 2015 22:24:14 +0200
Subject: [PATCH] Initial PWM implementation from gpascualg

---
 examples/pwm_test/Makefile   |   4 +
 examples/pwm_test/pwm_test.c |  50 ++++++++++
 extras/pwm/component.mk      |   9 ++
 extras/pwm/pwm.c             | 181 +++++++++++++++++++++++++++++++++++
 extras/pwm/pwm.h             |  18 ++++
 5 files changed, 262 insertions(+)
 create mode 100644 examples/pwm_test/Makefile
 create mode 100644 examples/pwm_test/pwm_test.c
 create mode 100644 extras/pwm/component.mk
 create mode 100644 extras/pwm/pwm.c
 create mode 100644 extras/pwm/pwm.h

diff --git a/examples/pwm_test/Makefile b/examples/pwm_test/Makefile
new file mode 100644
index 0000000..36ee7d0
--- /dev/null
+++ b/examples/pwm_test/Makefile
@@ -0,0 +1,4 @@
+# Simple makefile for simple example
+PROGRAM=pwm_test
+COMPONENTS = FreeRTOS lwip core extras/pwm
+include ../../common.mk
diff --git a/examples/pwm_test/pwm_test.c b/examples/pwm_test/pwm_test.c
new file mode 100644
index 0000000..d0372ac
--- /dev/null
+++ b/examples/pwm_test/pwm_test.c
@@ -0,0 +1,50 @@
+/* Very basic example to test the pwm library
+ * Hook up an LED to pin14 and you should see the intensity change
+ *
+ * Part of esp-open-rtos
+ * Copyright (C) 2015 Javier Cardona (https://github.com/jcard0na)
+ * BSD Licensed as described in the file LICENSE
+ */
+#include "espressif/esp_common.h"
+#include "esp/uart.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "pwm.h"
+
+void task1(void *pvParameters)
+{
+    printf("Hello from task1!\r\n");
+    uint32_t const init_count = 0;
+    uint32_t count = init_count;
+    while(1) {
+        vTaskDelay(100);
+        printf("duty cycle set to %d/UINT16_MAX%%\r\n", count);
+        pwm_set_duty(count);
+        count += UINT16_MAX/17;
+        if (count > UINT16_MAX)
+            count = init_count;
+    }
+}
+
+void user_init(void)
+{
+    uint8_t pins[1];
+    uart_set_baud(0, 115200);
+
+    printf("SDK version:%s\n", sdk_system_get_sdk_version());
+
+    printf("pwm_init(1, [14])\n");
+    pins[0] = 14;
+    pwm_init(1, pins);
+
+    printf("pwm_set_freq(1000)     # 1 kHz\n");
+    pwm_set_freq(1000);
+
+    printf("pwm_set_duty(UINT16_MAX/2)     # 50%%\n");
+    pwm_set_duty(UINT16_MAX/2);
+
+    printf("pwm_start()\n");
+    pwm_start();
+
+    xTaskCreate(task1, (signed char *)"tsk1", 256, NULL, 2, NULL);
+}
diff --git a/extras/pwm/component.mk b/extras/pwm/component.mk
new file mode 100644
index 0000000..6c96fbd
--- /dev/null
+++ b/extras/pwm/component.mk
@@ -0,0 +1,9 @@
+# Component makefile for extras/pwm
+
+INC_DIRS += $(ROOT)extras/pwm
+
+# args for passing into compile rule generation
+extras/pwm_INC_DIR =  $(ROOT)extras/pwm
+extras/pwm_SRC_DIR =  $(ROOT)extras/pwm
+
+$(eval $(call component_compile_rules,extras/pwm))
diff --git a/extras/pwm/pwm.c b/extras/pwm/pwm.c
new file mode 100644
index 0000000..338dff0
--- /dev/null
+++ b/extras/pwm/pwm.c
@@ -0,0 +1,181 @@
+/* Implementation of PWM support for the Espressif SDK.
+ *
+ * Part of esp-open-rtos
+ * Copyright (C) 2015 Guillem Pascual Ginovart (https://github.com/gpascualg)
+ * Copyright (C) 2015 Javier Cardona (https://github.com/jcard0na)
+ * BSD Licensed as described in the file LICENSE
+ */
+#include "pwm.h"
+
+#include <espressif/esp_common.h>
+#include <espressif/sdk_private.h>
+#include <FreeRTOS.h>
+#include <esp8266.h>
+
+typedef struct PWMPinDefinition
+{
+    uint8_t pin;
+    uint8_t divider;
+} PWMPin;
+
+typedef enum {
+    PERIOD_ON   = 0,
+    PERIOD_OFF  = 1
+} pwm_step_t;
+
+
+typedef struct pwmInfoDefinition
+{
+    uint8_t running;
+
+    uint16_t freq;
+    uint16_t dutyCicle;
+
+    /* private */
+    uint32_t _maxLoad;
+    uint32_t _onLoad;
+    uint32_t _offLoad;
+    pwm_step_t _step;
+
+    uint16_t usedPins;
+    PWMPin pins[8];
+} PWMInfo;
+
+static PWMInfo pwmInfo;
+
+static void frc1_interrupt_handler(void)
+{
+    uint8_t i = 0;
+    bool out = true;
+    uint32_t load = pwmInfo._onLoad;
+    pwm_step_t step = PERIOD_ON;
+
+    if (pwmInfo._step == PERIOD_ON)
+    {
+        out = false;
+        load = pwmInfo._offLoad;
+        step = PERIOD_OFF;
+    }
+
+    for (; i < pwmInfo.usedPins; ++i)
+    {
+        gpio_write(pwmInfo.pins[i].pin, out);
+    }
+
+    timer_set_load(FRC1, load);
+    pwmInfo._step = step;
+}
+
+void pwm_init(uint8_t npins, uint8_t* pins)
+{
+    /* Assert number of pins is correct */
+    if (npins > MAX_PWM_PINS)
+    {
+        printf("Incorrect number of PWM pins (%d)\n", npins);
+        return;
+    }
+
+    /* Initialize */
+    pwmInfo._maxLoad = 0;
+    pwmInfo._onLoad = 0;
+    pwmInfo._offLoad = 0;
+    pwmInfo._step = PERIOD_ON;
+
+    /* Save pins information */
+    pwmInfo.usedPins = npins;
+
+    uint8_t i = 0;
+    for (; i < npins; ++i)
+    {
+        pwmInfo.pins[i].pin = pins[i];
+
+        /* configure GPIOs */
+        gpio_enable(pins[i], GPIO_OUTPUT);
+    }
+
+    /* Stop timers and mask interrupts */
+    pwm_stop();
+
+    /* set up ISRs */
+    _xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler);
+
+    /* Flag not running */
+    pwmInfo.running = 0;
+}
+
+void pwm_set_freq(uint16_t freq)
+{
+    pwmInfo.freq = freq;
+
+    /* Stop now to avoid load being used */
+    if (pwmInfo.running)
+    {
+        pwm_stop();
+        pwmInfo.running = 1;
+    }
+
+    timer_set_frequency(FRC1, freq);
+    pwmInfo._maxLoad = timer_get_load(FRC1);
+
+    if (pwmInfo.running)
+    {
+        pwm_start();
+    }
+}
+
+void pwm_set_duty(uint16_t duty)
+{
+    bool output;
+
+    pwmInfo.dutyCicle = duty;
+    if (duty > 0 && duty < UINT16_MAX) {
+        pwm_restart();
+	return;
+    }
+
+    // 0% and 100% duty cycle are special cases: constant output.
+    pwm_stop();
+    pwmInfo.running = 1;
+    output = (duty == UINT16_MAX);
+    for (uint8_t i = 0; i < pwmInfo.usedPins; ++i)
+    {
+	gpio_write(pwmInfo.pins[i].pin, output);
+    }
+}
+
+void pwm_restart()
+{
+    if (pwmInfo.running)
+    {
+        pwm_stop();
+        pwm_start();
+    }
+}
+
+void pwm_start()
+{
+    pwmInfo._onLoad = pwmInfo.dutyCicle * pwmInfo._maxLoad / UINT16_MAX;
+    pwmInfo._offLoad = pwmInfo._maxLoad - pwmInfo._onLoad;
+    pwmInfo._step = PERIOD_ON;
+
+    // Trigger ON
+    uint8_t i = 0;
+    for (; i < pwmInfo.usedPins; ++i)
+    {
+        gpio_write(pwmInfo.pins[i].pin, true);
+    }
+
+    timer_set_load(FRC1, pwmInfo._onLoad);
+    timer_set_reload(FRC1, false);
+    timer_set_interrupts(FRC1, true);
+    timer_set_run(FRC1, true);
+
+    pwmInfo.running = 1;
+}
+
+void pwm_stop()
+{
+    timer_set_interrupts(FRC1, false);
+    timer_set_run(FRC1, false);
+    pwmInfo.running = 0;
+}
diff --git a/extras/pwm/pwm.h b/extras/pwm/pwm.h
new file mode 100644
index 0000000..ddf7f2b
--- /dev/null
+++ b/extras/pwm/pwm.h
@@ -0,0 +1,18 @@
+/* Implementation of PWM support for the Espressif SDK.
+ *
+ * Part of esp-open-rtos
+ * Copyright (C) 2015 Guillem Pascual Ginovart (https://github.com/gpascualg)
+ * Copyright (C) 2015 Javier Cardona (https://github.com/jcard0na)
+ * BSD Licensed as described in the file LICENSE
+ */
+#include <stdint.h>
+
+#define MAX_PWM_PINS    8
+
+void pwm_init(uint8_t npins, uint8_t* pins);
+void pwm_set_freq(uint16_t freq);
+void pwm_set_duty(uint16_t duty);
+
+void pwm_restart();
+void pwm_start();
+void pwm_stop();