ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
ruuvi_nrf5_sdk15_gpio_pwm.c
Go to the documentation of this file.
1 
15 #if (RUUVI_NRF5_SDK15_GPIO_PWM_ENABLED || DOXYGEN || CEEDLING)
16 #include "ruuvi_driver_error.h"
17 #include "ruuvi_interface_gpio.h"
18 
19 #include <stdbool.h>
20 #include "nrf_pwm.h"
21 #include "nrf_drv_pwm.h"
22 
23 #define RI_GPIO_PWM_DRIVER 0
24 #define RI_GPIO_PWM_PLAYBACK_OK (0)
25 #define RI_GPIO_PWM_PLAYBACK_COUNT (1)
26 #define RI_GPIO_PWM_DRIVER_PIN_POINT (0)
27 #define RI_GPIO_PWM_SEQ_DELAY (0)
28 #define RI_GPIO_PWM_SEQ_REPEATS (1)
29 
30 #define RI_GPIO_PWM_MAX_FREQ (16000000.00f)
31 #define RI_GPIO_PWM_MIN_FREQ (2.00f)
32 
33 #define RI_GPIO_PWM_MAX_DUTY (1.00f)
34 #define RI_GPIO_PWM_MIN_DUTY (0.00f)
35 
36 #define RI_GPIO_PWM_CHANNEL_UNUSED (0)
37 
38 #define RI_GPIO_PWM_MIN_TOP_VALUE (1U)
39 #define RI_GPIO_PWM_MIN_REST_FOR_TOP (0.999999f)
40 
41 #define RI_GPIO_PWM_FREQ_COUNT (8)
42 
43 #define RI_GPIO_PWM_BASE_FREQ_16MHZ (RI_GPIO_PWM_MAX_FREQ)
44 #define RI_GPIO_PWM_BASE_FREQ_8MHZ (8000000.00f)
45 #define RI_GPIO_PWM_BASE_FREQ_4MHZ (4000000.00f)
46 #define RI_GPIO_PWM_BASE_FREQ_2MHZ (2000000.00f)
47 #define RI_GPIO_PWM_BASE_FREQ_1MHZ (1000000.00f)
48 #define RI_GPIO_PWM_BASE_FREQ_500KHZ (500000.00f)
49 #define RI_GPIO_PWM_BASE_FREQ_250KHZ (250000.00f)
50 #define RI_GPIO_PWM_BASE_FREQ_125KHZ (125000.00f)
51 
53 static bool m_gpio_pwm_is_init = false;
54 static bool m_gpio_pwm_is_start = false;
55 static nrf_drv_pwm_t m_pwm = NRF_DRV_PWM_INSTANCE (RI_GPIO_PWM_DRIVER);
56 
57 static float const freq[RI_GPIO_PWM_FREQ_COUNT] =
58 {
67 };
68 
69 typedef enum
70 {
80 
81 
83 {
85 
86  if (!m_gpio_pwm_is_init)
87  {
88  m_gpio_pwm_is_init = true;
89  res = RD_SUCCESS;
90  }
91 
92  return res;
93 }
94 
96 {
98 
99  if (false == m_gpio_pwm_is_init)
100  {
101  res = RD_SUCCESS;
102  }
103  else
104  {
105  m_gpio_pwm_is_init = false;
106  res = RD_SUCCESS;
107  }
108 
109  return res;
110 }
111 
113 {
114  return m_gpio_pwm_is_init;
115 }
116 
117 static bool ri_gpio_pwm_is_start (void)
118 {
119  return m_gpio_pwm_is_start;
120 }
121 
122 
126 static inline uint8_t ruuvi_to_nrf_pin_pwm_map (const ri_gpio_id_t pin)
127 {
128  return ( (pin >> 3) & 0xE0) + (pin & 0x1F);
129 }
130 
131 static nrf_pwm_clk_t ruuvi_get_base_config (float * const frequency,
132  float * const duty_cycle,
133  uint16_t * p_top)
134 {
135  nrf_pwm_clk_t clock = NRF_PWM_CLK_16MHz;
136  float f_rest_min = RI_GPIO_PWM_MIN_REST_FOR_TOP;
137  float m_freq = (*frequency);
138  float m_duty = (*duty_cycle);
139  float m_top;
140 
141  for (uint8_t i = 0; i < RI_GPIO_PWM_FREQ_COUNT ; i++)
142  {
143  if (freq[i] >= m_freq)
144  {
145  float f_rest = (freq[i] / m_freq) -
146  (uint32_t) (freq[i] / m_freq);
147 
148  if (f_rest_min >= f_rest)
149  {
150  m_top = (freq[i] / m_freq) * m_duty;
151 
152  if (m_top >= RI_GPIO_PWM_MIN_TOP_VALUE)
153  {
154  f_rest_min = f_rest;
155  clock = (nrf_pwm_clk_t) i;
156  (*p_top) = (freq[i] / m_freq);
157  }
158  }
159  }
160  }
161 
162  return clock;
163 }
164 
166  float * const frequency, float * const duty_cycle)
167 {
169 
170  if ( (true == ri_gpio_pwm_is_init()) &&
171  (true == ri_gpio_is_init()))
172  {
173  if (true == ri_gpio_pwm_is_start ())
174  {
176  {
177  return RD_ERROR_INVALID_STATE;
178  }
179  }
180 
181  if ( (duty_cycle == NULL) || (frequency == NULL))
182  {
183  res = RD_ERROR_NULL;
184  }
185  else
186  {
187  if ( (RI_GPIO_PWM_MIN_DUTY > (*duty_cycle)) ||
188  (RI_GPIO_PWM_MAX_DUTY < (*duty_cycle)) ||
189  (RI_GPIO_PWM_MAX_FREQ < (*frequency)) ||
190  (RI_GPIO_PWM_MIN_FREQ > (*frequency)) ||
191  ( (RI_GPIO_MODE_OUTPUT_STANDARD != mode) &&
193  {
195  }
196  else
197  {
198  uint16_t top = RI_GPIO_PWM_MIN_TOP_VALUE;
199  nrf_pwm_clk_t clock = ruuvi_get_base_config (frequency, duty_cycle, &top);
200  uint8_t out_pin = (ruuvi_to_nrf_pin_pwm_map (pin) | NRF_DRV_PWM_PIN_INVERTED);
201  nrf_drv_pwm_config_t config = NRF_DRV_PWM_DEFAULT_CONFIG;
202  config.output_pins[RI_GPIO_PWM_DRIVER_PIN_POINT] = out_pin;
203  config.base_clock = clock;
204  config.top_value = top;
205 
206  if ( (NRF_SUCCESS == ri_gpio_configure (pin, mode)) &&
207  (NRF_SUCCESS == nrf_drv_pwm_init (&m_pwm, &config, NULL)))
208  {
209  uint16_t steps_duty = (uint16_t) (top * (*duty_cycle));
210  static nrf_pwm_values_individual_t seq_values;
211  seq_values.channel_0 = steps_duty;
212  seq_values.channel_1 = RI_GPIO_PWM_CHANNEL_UNUSED;
213  seq_values.channel_2 = RI_GPIO_PWM_CHANNEL_UNUSED;
214  seq_values.channel_3 = RI_GPIO_PWM_CHANNEL_UNUSED;
215  nrf_pwm_sequence_t const seq =
216  {
217  .values.p_individual = &seq_values,
218  .length = NRF_PWM_VALUES_LENGTH (seq_values),
219  .repeats = RI_GPIO_PWM_SEQ_REPEATS,
220  .end_delay = RI_GPIO_PWM_SEQ_DELAY
221  };
222 
223  if (RI_GPIO_PWM_PLAYBACK_OK == nrf_drv_pwm_simple_playback (&m_pwm, &seq,
225  NRF_DRV_PWM_FLAG_LOOP))
226  {
227  res = RD_SUCCESS;
228  m_gpio_pwm_is_start = true;
229  }
230  }
231  }
232  }
233  }
234 
235  return res;
236 }
237 
239 {
241  bool wait = true;
242 
243  if (true == nrf_drv_pwm_stop (&m_pwm, wait))
244  {
245  nrf_drv_pwm_uninit (&m_pwm);
246  m_gpio_pwm_is_start = false;
247  res = RD_SUCCESS;
248  }
249 
250  return res;
251 }
253 #endif
#define RD_ERROR_INVALID_PARAM
Invalid Parameter.
#define RD_ERROR_NULL
Null Pointer.
uint32_t rd_status_t
bitfield for representing errors
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
#define RI_GPIO_PWM_MIN_DUTY
#define RI_GPIO_PWM_BASE_FREQ_4MHZ
#define RI_GPIO_PWM_MIN_TOP_VALUE
#define RI_GPIO_PWM_PLAYBACK_OK
#define RI_GPIO_PWM_CHANNEL_UNUSED
#define RI_GPIO_PWM_PLAYBACK_COUNT
#define RI_GPIO_PWM_MAX_DUTY
#define RI_GPIO_PWM_BASE_FREQ_500KHZ
#define RI_GPIO_PWM_SEQ_REPEATS
#define RI_GPIO_PWM_MIN_REST_FOR_TOP
bool ri_gpio_is_init(void)
return true if GPIO is init, false otherwise.
rd_status_t ri_gpio_configure(const ri_gpio_id_t pin, const ri_gpio_mode_t mode)
Configure a pin of a port into a mode. If there are several ports the platform driver must implement ...
rd_status_t ri_gpio_pwm_stop(const ri_gpio_id_t pin)
Stop PWM on given pin.
#define RI_GPIO_PWM_MIN_FREQ
#define RI_GPIO_PWM_BASE_FREQ_2MHZ
#define RI_GPIO_PWM_DRIVER
#define RI_GPIO_PWM_BASE_FREQ_125KHZ
#define RI_GPIO_PWM_MAX_FREQ
bool ri_gpio_pwm_is_init(void)
Check if PWM is initialized.
#define RI_GPIO_PWM_BASE_FREQ_250KHZ
#define RI_GPIO_PWM_SEQ_DELAY
#define RI_GPIO_PWM_BASE_FREQ_8MHZ
#define RI_GPIO_PWM_FREQ_COUNT
#define RI_GPIO_PWM_DRIVER_PIN_POINT
rd_status_t ri_gpio_pwm_init(void)
Run any necessary initialization for PWM.
#define RI_GPIO_PWM_BASE_FREQ_16MHZ
rd_status_t ri_gpio_pwm_start(const ri_gpio_id_t pin, const ri_gpio_mode_t mode, float *const frequency, float *const duty_cycle)
Start PWM on given pin at given frequency and duty cycle.
rd_status_t ri_gpio_pwm_uninit(void)
Uninitialize PWM.
#define RI_GPIO_PWM_BASE_FREQ_1MHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_500KHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_8MHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_1MHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_125KHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_250KHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_2MHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_4MHZ
@ RI_GPIO_PWM_BASE_NUM_FREQ_16MHZ
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
uint16_t ri_gpio_id_t
port<<8 + pin
ri_gpio_mode_t
@ RI_GPIO_MODE_OUTPUT_STANDARD
Push-pull output, can be written.
@ RI_GPIO_MODE_OUTPUT_HIGHDRIVE
Push-pull output, can be written. Higher current drive than standard.