fixed drive strength

Dependents:   capstone_i2c

Fork of mbed-dev by mbed official

Revision:
149:156823d33999
Parent:
144:ef7eb2e8f9f7
Child:
150:02e0a0aed4ec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_NORDIC/TARGET_NRF5/TARGET_MCU_NRF52832/pwmout_api.c	Fri Oct 28 11:17:30 2016 +0100
@@ -0,0 +1,376 @@
+/* 
+ * Copyright (c) 2013 Nordic Semiconductor ASA
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 
+ *   1. Redistributions of source code must retain the above copyright notice, this list 
+ *      of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA 
+ *      integrated circuit in a product or a software update for such product, must reproduce 
+ *      the above copyright notice, this list of conditions and the following disclaimer in 
+ *      the documentation and/or other materials provided with the distribution.
+ *
+ *   3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be 
+ *      used to endorse or promote products derived from this software without specific prior 
+ *      written permission.
+ *
+ *   4. This software, with or without modification, must only be used with a 
+ *      Nordic Semiconductor ASA integrated circuit.
+ *
+ *   5. Any software provided in binary or object form under this license must not be reverse 
+ *      engineered, decompiled, modified and/or disassembled. 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+
+#include "mbed_assert.h"
+#include "mbed_error.h"
+#include "pwmout_api.h"
+#include "cmsis.h"
+#include "pinmap.h"
+
+#if DEVICE_PWMOUT
+
+#include "app_util_platform.h"
+#include "nrf_drv_pwm.h"
+
+#define MAX_PWM_COUNTERTOP  (0x7FFF)                 // 0x7FFF is the max of COUNTERTOP value for the PWM peripherial of the nRF52.
+#define MAX_PWM_PERIOD_US   (MAX_PWM_COUNTERTOP * 8) // PWM hw is driven by 16 MHz clock, hence the tick is 1_us/16,
+                                                     // and 128 is the max prescaler value.
+#define MAX_PWM_PERIOD_MS   ((MAX_PWM_PERIOD_US / 1000) + 1)    // approximations advance
+#define MAX_PWM_PERIOD_S    ((MAX_PWM_PERIOD_US / 1000000) + 1) // approximations advance
+
+
+#define PWM_INSTANCE_COUNT  (PWM_COUNT) // import from the nrf_drv_config.h file
+
+///> instances of nRF52 PWM driver
+static const nrf_drv_pwm_t m_pwm_driver[PWM_INSTANCE_COUNT] =
+{
+#if PWM0_ENABLED
+    NRF_DRV_PWM_INSTANCE(0),
+#endif
+#if PWM1_ENABLED
+    NRF_DRV_PWM_INSTANCE(1),
+#endif
+#if PWM2_ENABLED
+    NRF_DRV_PWM_INSTANCE(2)
+#endif
+};
+
+typedef struct
+{
+    uint32_t period_us;
+    uint32_t duty_us;
+    float    duty;
+} pwm_signal_t; /// PWM signal description type
+
+typedef struct
+{
+    nrf_drv_pwm_t * p_pwm_driver;
+    pwm_signal_t signal;
+    volatile nrf_pwm_values_common_t seq_values[1];
+} pwm_t; /// internal PWM instance support type
+
+static pwm_t m_pwm[PWM_INSTANCE_COUNT] =
+{
+#if PWM0_ENABLED
+    {.p_pwm_driver = NULL},
+#endif
+#if PWM1_ENABLED
+    {.p_pwm_driver = NULL},
+#endif
+#if PWM2_ENABLED
+    {.p_pwm_driver = NULL}
+#endif
+};  /// Array of internal PWM instances.
+
+typedef struct
+{
+    uint16_t       period_hwu; // unit related to pwm_clk
+    uint16_t       duty_hwu;   // unit related to pwm_clk
+    nrf_pwm_clk_t  pwm_clk;
+} pulsewidth_set_t; /// helper type for timing calculations
+    
+    
+static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization);
+    
+void pwmout_init(pwmout_t *obj, PinName pin)
+{
+    uint32_t i;
+    
+    for (i = 0; PWM_INSTANCE_COUNT; i++)
+    {
+        if (m_pwm[i].p_pwm_driver == NULL) // a driver instance not assigned to the obj?
+        {
+            obj->pin         = pin;
+            
+            obj->pwm_channel = i;
+            
+            m_pwm[i].p_pwm_driver = (nrf_drv_pwm_t *) &m_pwm_driver[i];
+            m_pwm[i].signal.period_us = 200000; // 0.02 s
+            m_pwm[i].signal.duty_us   = 100000;
+            m_pwm[i].signal.duty      = 0.5f;
+            
+            obj->pwm_struct  = &m_pwm[i];
+
+            internal_pwmout_exe(obj, true, true);
+
+            break;
+        }
+    }
+    
+    MBED_ASSERT(i != PWM_INSTANCE_COUNT); // assert if free instance was not found.
+}
+
+void pwmout_free(pwmout_t *obj)
+{
+    nrf_drv_pwm_uninit( (nrf_drv_pwm_t*) obj->pwm_struct );
+
+    m_pwm[obj->pwm_channel].p_pwm_driver = NULL;
+}
+
+void pwmout_write(pwmout_t *obj, float percent)
+{
+    
+    if (percent < 0)
+    {
+        percent = 0;
+    }
+    else if (percent > 1)
+    {
+        percent = 1;
+    }
+    
+    pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
+    
+    p_pwm_signal->duty = percent;
+    
+    int us  = (((int)p_pwm_signal->period_us) * percent);
+    
+    pwmout_pulsewidth_us(obj, us);
+}
+
+float pwmout_read(pwmout_t *obj)
+{
+    pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
+    
+    return (float)p_pwm_signal->duty_us / (float)p_pwm_signal->period_us;
+}
+
+void pwmout_period(pwmout_t *obj, float seconds)
+{
+    // raught saturation < 0, quasi-max>
+    if (seconds > MAX_PWM_PERIOD_S)
+    {
+        seconds = MAX_PWM_PERIOD_S;
+    }
+    else if (seconds < 0)
+    {
+        seconds = 0; // f. pwmout_period_us will set period to min. value
+    }
+    
+    int us = seconds * 1000000;
+    
+    pwmout_period_us(obj, us);
+}
+
+void pwmout_period_ms(pwmout_t *obj, int ms)
+{
+    // reught saturation < 0, quasi-max>
+    if (ms > MAX_PWM_PERIOD_MS)
+    {
+        ms = MAX_PWM_PERIOD_MS;
+    }
+    else if (ms < 0)
+    {
+        ms = 0; // f. pwmout_period_us will set period to min. value
+    }
+    
+    int us = ms * 1000;
+    
+    pwmout_period_us(obj, us);
+}
+
+
+void pwmout_period_us(pwmout_t *obj, int us)
+{
+    pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
+    
+    // saturation <1, real-max>
+    if (us > MAX_PWM_PERIOD_US)
+    {
+        us = MAX_PWM_PERIOD_US;
+    }
+    else if (us < 1)
+    {
+        us = 1;
+    }
+    
+    p_pwm_signal->duty_us = (int)((float)us * p_pwm_signal->duty);
+    
+    p_pwm_signal->period_us  = us;
+    
+    internal_pwmout_exe(obj, true, false);
+}
+
+void pwmout_pulsewidth(pwmout_t *obj, float seconds)
+{
+    // raught saturation < 0, quasi-max>
+    if (seconds > MAX_PWM_PERIOD_S)
+    {
+        seconds = MAX_PWM_PERIOD_S;
+    }
+    else if (seconds < 0)
+    {
+        seconds = 0;
+    }
+    
+    int us = seconds * 1000000;
+    
+    pwmout_pulsewidth_us(obj,us);
+}
+
+void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
+{
+    // raught saturation < 0, quasi-max>
+    if (ms > MAX_PWM_PERIOD_MS)
+    {
+        ms = MAX_PWM_PERIOD_MS;
+    }
+    else if (ms < 0)
+    {
+        ms = 0;
+    }
+    
+    int us = ms * 1000;
+    
+    pwmout_pulsewidth_us(obj, us);
+}
+
+void pwmout_pulsewidth_us(pwmout_t *obj, int us)
+{
+    // saturation <0, real-max>
+    if (us > MAX_PWM_PERIOD_US)
+    {
+        us = MAX_PWM_PERIOD_US;
+    }
+    else if (us < 0)
+    {
+        us = 0;
+    }
+    
+    pwm_signal_t * p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
+    
+    p_pwm_signal->duty_us  = us;
+    p_pwm_signal->duty     = us / p_pwm_signal->period_us;
+    
+    internal_pwmout_exe(obj, false, false);
+}
+
+
+
+
+
+
+static ret_code_t pulsewidth_us_set_get(int period_hwu, int duty_hwu, pulsewidth_set_t * p_settings)
+{
+    uint16_t      div;
+    nrf_pwm_clk_t pwm_clk = NRF_PWM_CLK_16MHz;
+    
+    for(div = 1; div <= 128 ; div <<= 1) // 128 is the maximum of clock prescaler for PWM peripherial
+    {
+        if (MAX_PWM_COUNTERTOP >= period_hwu)
+        {
+            p_settings->period_hwu  = period_hwu; // unit [us/16 * div]
+            p_settings->duty_hwu = duty_hwu;       // unit [us/16 * div]
+            p_settings->pwm_clk   = pwm_clk;
+    
+            return NRF_SUCCESS;
+        }
+        
+        period_hwu >>= 1;
+        duty_hwu >>= 1;
+        pwm_clk++;
+    }
+    
+    return NRF_ERROR_INVALID_PARAM;
+}
+
+
+static void internal_pwmout_exe(pwmout_t *obj, bool new_period, bool initialization)
+{
+    pulsewidth_set_t          pulsewidth_set;
+    pwm_signal_t            * p_pwm_signal;
+    nrf_drv_pwm_t           * p_pwm_driver;
+    ret_code_t                ret_code;
+    
+    p_pwm_signal = &(((pwm_t*)obj->pwm_struct)->signal);
+    
+    if (NRF_SUCCESS == pulsewidth_us_set_get(p_pwm_signal->period_us * 16, // base clk for PWM is 16 MHz
+                                             p_pwm_signal->duty_us * 16,   // base clk for PWM is 16 MHz
+                                             &pulsewidth_set))
+    {
+        p_pwm_driver = (((pwm_t*)obj->pwm_struct)->p_pwm_driver);
+        
+        const nrf_pwm_sequence_t seq =
+        {
+            .values.p_common = (nrf_pwm_values_common_t*) (((pwm_t*)obj->pwm_struct)->seq_values),
+            .length          = 1,
+            .repeats         = 0,
+            .end_delay       = 0
+        };
+        
+        (((pwm_t*)obj->pwm_struct)->seq_values)[0] = pulsewidth_set.duty_hwu | 0x8000;
+        
+        if (new_period)
+        {
+            nrf_drv_pwm_config_t config0 =
+            {
+                .output_pins =
+                {
+                    obj->pin | NRF_DRV_PWM_PIN_INVERTED, // channel 0
+                    NRF_DRV_PWM_PIN_NOT_USED,            // channel 1
+                    NRF_DRV_PWM_PIN_NOT_USED,            // channel 2
+                    NRF_DRV_PWM_PIN_NOT_USED,            // channel 3
+                },
+                .irq_priority = APP_IRQ_PRIORITY_LOW,
+                .base_clock   = pulsewidth_set.pwm_clk,
+                .count_mode   = NRF_PWM_MODE_UP,
+                .top_value    = pulsewidth_set.period_hwu,
+                .load_mode    = NRF_PWM_LOAD_COMMON,
+                .step_mode    = NRF_PWM_STEP_AUTO
+            };
+            
+            if (!initialization)
+            {
+                nrf_drv_pwm_uninit(p_pwm_driver);
+            }
+        
+            ret_code = nrf_drv_pwm_init( p_pwm_driver, &config0, NULL);
+        
+            MBED_ASSERT(ret_code == NRF_SUCCESS); // assert if free instance was not found.
+        }
+
+        nrf_drv_pwm_simple_playback(p_pwm_driver, &seq, 0, NRF_DRV_PWM_FLAG_LOOP);
+    }
+    else
+    {
+        MBED_ASSERT(0); // force assertion
+    }
+    
+}
+
+#endif // DEVICE_PWMOUT