Pico-Arduino
PicoPWM.h
1 #pragma once
2 
3 #include "pico/stdlib.h"
4 #include <inttypes.h>
5 #include "hardware/gpio.h"
6 #include "hardware/adc.h"
7 #include "hardware/pwm.h"
8 #include "hardware/clocks.h"
9 #include "PicoPinFunction.h"
10 
18 #ifndef PWM_MAX_NUMER
19 #define PWM_MAX_NUMER 65535
20 #endif
21 
22 class PicoPWMNano {
23  public:
24  // Constructor: Defines the length of a full cycle in nanoseconds (mio of seconds)
25  PicoPWMNano(uint64_t periodNanoSeconds){
26  Logger.debug("PicoPWMNano");
27  period_nano_sec = periodNanoSeconds;
28  // the maximum number that the pwm_config_set_wrap & and pwm_set_chan_level supports is 65535
29  rangeDivider = (1.0 * period_nano_sec) / PWM_MAX_NUMER;
30  }
31 
32  // setup a pin for pwm write
33  void begin(pin_size_t pin, uint64_t initialDutyCyleNanoSeconds=0){
34  Logger.debug("PicoPWMNano::begin");
35  if (setupConfig()){
36  Logger.debug("gpio_set_function");
37  gpio_set_function(pin, GPIO_FUNC_PWM);
38  // determine the slice number and channel
39  uint8_t slice_num = pwm_gpio_to_slice_num(pin);
40  // uint8_t channel = pwm_gpio_to_channel(pinNumber);
41  Logger.debug("pwm_init");
42  pwm_init(slice_num, &config, true);
43  setDutyCycle(pin, initialDutyCyleNanoSeconds);
44  }
45  }
46 
47  // sets the pin to low
48  void end(pin_size_t pin){
49  Logger.debug("PicoPWMNano::end");
50  setDutyCycle(pin, 0);
51  PinFunction.clear(pin);
52  }
53 
54  // Defines the active period is nanoseconds
55  void setDutyCycle(pin_size_t pin, uint64_t dutyCyleNanoSeconds){
56  uint slice_num = pwm_gpio_to_slice_num(pin);
57  uint channel = pwm_gpio_to_channel(pin);
58  uint16_t value = (1.0 * dutyCyleNanoSeconds) / rangeDivider;
59 
60  if (Logger.isLogging(PicoLogger::Debug)){
61  char str[40];
62  sprintf(str,"%lu", dutyCyleNanoSeconds);
63  Logger.debug("PWM duty cycle ns:",str);
64  sprintf(str,"%lu", value);
65  Logger.debug("PWM duty cycle(internal):",str);
66  }
67  pwm_set_chan_level(slice_num, channel, value);
68  }
69 
70  // converts the indicated period is nanoseconds to hz
71  uint64_t frequency(uint64_t periodNanoSeconds){
72  return 1000000000l / period_nano_sec;
73  }
74 
75  // converts the PWM period to hz
76  uint64_t frequency(){
77  return frequency(period_nano_sec);
78  }
79 
80  // provides the full cycle period in nanoseconds
81  uint64_t period() {
82  return period_nano_sec;
83  }
84 
85 
86  protected:
87  uint64_t period_nano_sec;
88  pwm_config config;
89  float rangeDivider; // values must be <= 65535
90  PicoPinFunction pinFunction = PicoPinFunction::instance();
91  bool is_config_done = false;
92 
93  // provides the configuration - returns true if we have a new configuration
94  bool setupConfig(){
95  if (!is_config_done) {
96  Logger.debug("PicoPWMNano::setupConfig");
97  is_config_done = true;
98  config = pwm_get_default_config();
99 
100  uint32_t freq = clock_get_hz(clk_sys);
101  float dividerClock = (1.0 * freq) / frequency();
102  // divider for a full cycle
103  float divider = dividerClock / PWM_MAX_NUMER;
104  // this should actually be identical with PWM_MAX_NUMER
105  uint16_t wrap = PWM_MAX_NUMER ;
106 
107  if (Logger.isLogging(PicoLogger::Debug)){
108  char str[80];
109  sprintf(str,"%ul", period_nano_sec);
110  Logger.debug("Period ns:", str);
111  sprintf(str,"%f", rangeDivider);
112  Logger.debug("Range divider:", str);
113  sprintf(str,"%d", frequency());
114  Logger.debug("PWM hz:", str);
115  sprintf(str,"%d", freq);
116  Logger.debug("Systemclock hz:", str);
117  sprintf(str,"%f", divider);
118  Logger.debug("PWM divider:", str);
119  sprintf(str,"%d", wrap);
120  Logger.debug("PWM wrap:", str);
121  }
122 
123  // Set divider, reduces counter clock to sysclock/this value
124  pwm_config_set_clkdiv(&config, divider);
125  pwm_config_set_wrap(&config, wrap);
126  return true;
127  }
128  return false;
129  }
130 
131 };
132 
133 
139 class PicoPWM {
140  public:
141  PicoPWM(uint64_t frequency, uint64_t maxValue){
142  // convert frequency to period
143  period_nano_sec = 1000000000l / frequency;
144  nano = new PicoPWMNano(period_nano_sec);
145  max_value = maxValue;
146  }
147 
148  ~PicoPWM(){
149  delete nano;
150  }
151 
152  void begin(pin_size_t pin, uint64_t initalValue=0){
153  nano->begin(pin, initalValue);
154  }
155 
156  // sets the pin to low
157  void end(pin_size_t pin){
158  nano->end(pin);
159  }
160 
161  // Defines the active period is nanoseconds
162  void write(pin_size_t pin, uint64_t value){
163  nano->setDutyCycle(pin, valueToDutyCycle(value));
164  }
165 
166  // provides the full cycle period in nanoseconds
167  uint64_t period() {
168  return nano->period();
169  }
170 
171  // converts the PWM period to hz
172  uint64_t frequency(){
173  return nano->frequency();
174  }
175 
176  protected:
177  PicoPWMNano *nano;
178  int64_t max_value;
179  uint64_t period_nano_sec;
180 
181  uint64_t valueToDutyCycle(uint64_t value){
182  return map(value, 0, max_value, 0, period_nano_sec);
183  }
184 
185 
186 };
187 
188 
This is an even more powerfull PWM API where we can specify a user defined input range and the cycle ...
Definition: PicoPWM.h:139
Definition: PicoPWM.h:22
Definition: PicoPinFunction.h:30