1 /*
2 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <securec.h>
17
18 #include "iot_errno.h"
19 #include "iot_pwm.h"
20
21 #include "driver/ledc.h"
22 #include "esp_err.h"
23
24 #define CLK_40M (40000000)
25 #define DUTY_MIN (0)
26 #define DUTY_MAX (100)
27 #define DUTY_RES_MIN (128)
28 #define LEDC_OUTPUT_IO (5)
29 #define LEDC_TIMER LEDC_TIMER_0
30
31 #define NUM2 2
32 #define DUTY_RESOLUTION_MAX (NUM2 << ((LEDC_TIMER_BIT_MAX) - 1))
33 #if SOC_LEDC_SUPPORT_HS_MODE
34 #define LEDC_MODE LEDC_HIGH_SPEED_MODE
35 #else
36 #define LEDC_MODE LEDC_LOW_SPEED_MODE
37 #endif
38
39 typedef enum {
40 PWM_UNINIT = 0,
41 PWM_INIT = 1
42 } pwm_status_e;
43
44 typedef struct {
45 pwm_status_e pwm_state;
46 ledc_channel_config_t pwm_attr;
47 } pwm_driver_data_t;
48
49 static pwm_driver_data_t g_pwm[LEDC_CHANNEL_MAX] = {0};
50
PwmDutyCalc(uint8_t TimerBit,uint32_t duty)51 static unsigned int PwmDutyCalc(uint8_t TimerBit, uint32_t duty)
52 {
53 return ((((NUM2 >> TimerBit) - 1) * duty) / DUTY_MAX);
54 }
55
InitPwm(ledc_channel_t num,ledc_channel_config_t * pwm_conf)56 static void InitPwm(ledc_channel_t num, ledc_channel_config_t *pwm_conf)
57 {
58 assert(num < LEDC_CHANNEL_MAX);
59 assert(pwm_conf != NULL);
60 pwm_conf->speed_mode = LEDC_MODE;
61 pwm_conf->channel = num;
62 pwm_conf->timer_sel = LEDC_TIMER;
63 pwm_conf->intr_type = LEDC_INTR_DISABLE;
64 pwm_conf->gpio_num = LEDC_OUTPUT_IO;
65 pwm_conf->duty = 0;
66 pwm_conf->hpoint = 0;
67 }
68
ESPErrToHoErr(esp_err_t ret)69 static uint32_t ESPErrToHoErr(esp_err_t ret)
70 {
71 if (ret == ESP_OK) {
72 return IOT_SUCCESS;
73 } else {
74 return IOT_FAILURE;
75 }
76 }
77
IoTPwmInit(unsigned int port)78 unsigned int IoTPwmInit(unsigned int port)
79 {
80 if (port >= LEDC_CHANNEL_MAX) {
81 return IOT_FAILURE;
82 }
83
84 pwm_driver_data_t *pwm = &g_pwm[port];
85 if (pwm->pwm_state == PWM_INIT) {
86 return IOT_FAILURE;
87 }
88
89 pwm->pwm_state = PWM_INIT;
90 InitPwm((ledc_channel_t)port, &(pwm->pwm_attr));
91 return IOT_SUCCESS;
92 }
93
IoTPwmDeinit(unsigned int port)94 unsigned int IoTPwmDeinit(unsigned int port)
95 {
96 if (port >= LEDC_CHANNEL_MAX) {
97 return IOT_FAILURE;
98 }
99
100 pwm_driver_data_t *pwm = &g_pwm[port];
101 if (pwm->pwm_state == PWM_UNINIT) {
102 return IOT_FAILURE;
103 }
104
105 memset_s(pwm, sizeof(pwm_driver_data_t), 0, sizeof(pwm_driver_data_t));
106 return IOT_SUCCESS;
107 }
108
IoTPwmStart(unsigned int port,unsigned short duty,unsigned int freq)109 unsigned int IoTPwmStart(unsigned int port, unsigned short duty, unsigned int freq)
110 {
111 if (port >= LEDC_CHANNEL_MAX) {
112 return IOT_FAILURE;
113 }
114
115 pwm_driver_data_t *pwm = &g_pwm[port];
116 if (pwm->pwm_state == PWM_UNINIT) {
117 return IOT_FAILURE;
118 }
119
120 if ((freq == 0) || (duty >= DUTY_MAX) || (duty == DUTY_MIN)) {
121 return IOT_FAILURE;
122 }
123
124 uint32_t DutyResolution = CLK_40M / freq;
125 if (DutyResolution < DUTY_RES_MIN || DutyResolution > DUTY_RESOLUTION_MAX) {
126 return IOT_FAILURE;
127 }
128
129 uint8_t TimerBit = 0;
130 while (DutyResolution) {
131 DutyResolution = DutyResolution >> 1;
132 TimerBit++;
133 }
134
135 ledc_timer_config_t ledc_timer = {
136 .speed_mode = LEDC_MODE,
137 .timer_num = LEDC_TIMER,
138 .duty_resolution = (TimerBit - 1),
139 .freq_hz = freq,
140 .clk_cfg = LEDC_AUTO_CLK};
141
142 esp_err_t ret = ledc_timer_config(&ledc_timer);
143 if (ret != ESP_OK) {
144 return IOT_FAILURE;
145 }
146
147 ret = ledc_channel_config(&(pwm->pwm_attr));
148 if (ret != ESP_OK) {
149 return IOT_FAILURE;
150 }
151
152 uint32_t PwmDuty = PwmDutyCalc(TimerBit, duty);
153 ret = ledc_set_duty(LEDC_MODE, port, PwmDuty);
154 if (ret != ESP_OK) {
155 return IOT_FAILURE;
156 }
157
158 ret = ledc_update_duty(LEDC_MODE, port);
159 return ESPErrToHoErr(ret);
160 }
161
IoTPwmStop(unsigned int port)162 unsigned int IoTPwmStop(unsigned int port)
163 {
164 if (port >= LEDC_CHANNEL_MAX) {
165 return IOT_FAILURE;
166 }
167
168 pwm_driver_data_t *pwm = &g_pwm[port];
169 if (pwm->pwm_state == PWM_UNINIT) {
170 return IOT_FAILURE;
171 }
172
173 esp_err_t ret = ledc_stop(LEDC_MODE, port, 0);
174 return ESPErrToHoErr(ret);
175 }
176