Skip to content

Commit bcfb4ef

Browse files
committed
initialize.
0 parents  commit bcfb4ef

File tree

7 files changed

+330
-0
lines changed

7 files changed

+330
-0
lines changed

.gitignore

Whitespace-only changes.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 yuuu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# mruby-esp32-pwm
2+
3+
PWM library for mruby-esp32.
4+
5+
## Installation
6+
7+
You need [esp-idf v5.0](https://docs.espressif.com/projects/esp-idf/en/release-v5.0/esp32/index.html) to use this mrbgems.
8+
9+
Add the line below to your `build_config.rb`:
10+
11+
```ruby
12+
conf.gem :github => 'yuuu/mruby-esp32-pwm'
13+
```
14+
15+
## Examples
16+
17+
```ruby
18+
pwm = PWM.new(1) # GPIO pin = 1
19+
pwm.frequency(50) # set frequency(Hz) of PWM
20+
pwm.duty(10) # set duty(%) of PWM
21+
# => drive with frequency=50Hz(Period=20_000us), duby=10%(Pulse Width=2000us)
22+
23+
pwm = PWM.new(1, freq: 100, duty: 40)
24+
# => drive with frequency=100Hz(Period=10_000us), duby=40%(Pulse Width=4000us)
25+
26+
pwm = PWM.new(1)
27+
pwm.period_us(25_000)
28+
pwm.pulse_width_us(5_000)
29+
# => drive with frequency=40Hz(Period=25_000us), duby=20%(Pulse Width=5000us)
30+
```
31+
32+
## LICENSE
33+
34+
MIT License.

mrbgem.rake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
MRuby::Gem::Specification.new('mruby-esp32-pwm') do |spec|
2+
spec.license = 'MIT'
3+
spec.author = 'yuuu'
4+
spec.summary = 'PWM class on ESP32'
5+
6+
spec.add_dependency('mruby-rational', :core => 'mruby-rational')
7+
end

mrblib/pwm.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class PWM
2+
attr_accessor :pin, :pulse_width_ticks, :period_ticks
3+
4+
def initialize(pin = nil, **kwargs)
5+
self.pin = pin
6+
missing_argument_error(:pin) if self.pin.nil?
7+
8+
self.period_ticks = Rational(1_000_000, (kwargs[:freq] || kwargs[:frequency] || 1)).to_i
9+
self.pulse_width_ticks = (self.period_ticks * Rational((kwargs[:duty] || 0), 100)).to_i
10+
11+
__initialize(self.pin).__update(self.period_ticks, self.pulse_width_ticks)
12+
end
13+
14+
def frequency(freq)
15+
self.period_ticks = Rational(1_000_000, freq).to_i if freq != 0
16+
17+
__update(self.period_ticks, self.pulse_width_ticks)
18+
end
19+
20+
def period_us(micro_sec)
21+
self.period_ticks = micro_sec
22+
23+
__update(self.period_ticks, self.pulse_width_ticks)
24+
end
25+
26+
def duty(percent)
27+
self.pulse_width_ticks = (self.period_ticks * Rational(percent, 100)).to_i
28+
29+
__update(self.period_ticks, self.pulse_width_ticks)
30+
end
31+
32+
def pulse_width_us(micro_sec)
33+
self.pulse_width_ticks = micro_sec
34+
35+
__update(self.period_ticks, self.pulse_width_ticks)
36+
end
37+
38+
def missing_argument_error(name)
39+
ArgumentError.new("must pass #{key}")
40+
end
41+
end

src/mruby_esp32_pwm.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include <mruby.h>
2+
#include <mruby/data.h>
3+
#include <mruby/variable.h>
4+
#include <mruby/string.h>
5+
#include <mruby/array.h>
6+
#include <mruby/presym.h>
7+
8+
#include <stdio.h>
9+
#include <string.h>
10+
11+
#if ESP_PLATFORM
12+
#include "freertos/FreeRTOS.h"
13+
#include "freertos/task.h"
14+
#include "driver/mcpwm_prelude.h"
15+
#include "esp_log.h"
16+
#else
17+
#define ESP_LOGI(tag, fmt, ...) printf(fmt, __VA_ARGS__)
18+
#endif
19+
20+
#define TAG ("mruby-esp32-pwm")
21+
22+
typedef struct pwm_t {
23+
mrb_int pulse_width_ticks;
24+
mrb_int pin;
25+
mrb_int period_ticks;
26+
#if ESP_PLATFORM
27+
mcpwm_timer_handle_t *timer;
28+
mcpwm_cmpr_handle_t *comparator;
29+
mcpwm_oper_handle_t *operator;
30+
mcpwm_gen_handle_t *generator;
31+
#endif
32+
mrb_state *mrb;
33+
} pwm_t;
34+
35+
static void mrb_pwm_free(mrb_state *mrb, void *p);
36+
37+
static const struct mrb_data_type mrb_pwm = {
38+
"mrb_mruby_esp32_pwm", mrb_pwm_free
39+
};
40+
41+
static void
42+
mrb_pwm_new_mcpwm(mrb_state *mrb, pwm_t *pwm) {
43+
#if ESP_PLATFORM
44+
if((pwm->period_ticks <= 0) || (pwm->pulse_width_ticks <= 0)) {
45+
return;
46+
}
47+
48+
pwm->timer = mrb_malloc(mrb, sizeof(mcpwm_timer_handle_t));
49+
mcpwm_timer_config_t timer_config = {
50+
.group_id = 0,
51+
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
52+
.resolution_hz = 1000000,
53+
.period_ticks = pwm->period_ticks,
54+
.count_mode = MCPWM_TIMER_COUNT_MODE_UP,
55+
};
56+
ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, pwm->timer));
57+
58+
pwm->operator = mrb_malloc(mrb, sizeof(mcpwm_oper_handle_t));
59+
mcpwm_operator_config_t operator_config = {
60+
.group_id = 0,
61+
};
62+
ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, pwm->operator));
63+
ESP_ERROR_CHECK(mcpwm_operator_connect_timer(*pwm->operator, *pwm->timer));
64+
65+
pwm->comparator = mrb_malloc(mrb, sizeof(mcpwm_cmpr_handle_t));
66+
ESP_LOGI(TAG, "!(%p)", pwm->comparator);
67+
68+
mcpwm_comparator_config_t comparator_config = {
69+
.flags.update_cmp_on_tez = true,
70+
};
71+
ESP_ERROR_CHECK(mcpwm_new_comparator(*pwm->operator, &comparator_config, pwm->comparator));
72+
73+
pwm->generator = mrb_malloc(mrb, sizeof(mcpwm_gen_handle_t));
74+
mcpwm_generator_config_t generator_config = {
75+
.gen_gpio_num = pwm->pin,
76+
};
77+
ESP_ERROR_CHECK(mcpwm_new_generator(*pwm->operator, &generator_config, pwm->generator));
78+
ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(*pwm->comparator, pwm->pulse_width_ticks));
79+
80+
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(*pwm->generator, MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)));
81+
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(*pwm->generator, MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, *pwm->comparator, MCPWM_GEN_ACTION_LOW)));
82+
83+
ESP_ERROR_CHECK(mcpwm_timer_enable(*pwm->timer));
84+
ESP_ERROR_CHECK(mcpwm_timer_start_stop(*pwm->timer, MCPWM_TIMER_START_NO_STOP));
85+
#endif
86+
}
87+
88+
static void
89+
mrb_pwm_del_mcpwm(mrb_state *mrb, pwm_t *pwm) {
90+
#if ESP_PLATFORM
91+
if(pwm->generator != NULL) {
92+
ESP_ERROR_CHECK(mcpwm_del_generator(*pwm->generator));
93+
mrb_free(mrb, pwm->generator);
94+
pwm->generator = NULL;
95+
}
96+
if(pwm->comparator != NULL) {
97+
ESP_ERROR_CHECK(mcpwm_del_comparator(*pwm->comparator));
98+
mrb_free(mrb, pwm->comparator);
99+
pwm->comparator = NULL;
100+
}
101+
if(pwm->operator != NULL) {
102+
ESP_ERROR_CHECK(mcpwm_del_operator(*pwm->operator));
103+
mrb_free(mrb, pwm->operator);
104+
pwm->operator = NULL;
105+
}
106+
if(pwm->timer != NULL) {
107+
ESP_ERROR_CHECK(mcpwm_timer_start_stop(*pwm->timer, MCPWM_TIMER_STOP_EMPTY));
108+
ESP_ERROR_CHECK(mcpwm_timer_disable(*pwm->timer));
109+
ESP_ERROR_CHECK(mcpwm_del_timer(*pwm->timer));
110+
mrb_free(mrb, pwm->timer);
111+
pwm->timer = NULL;
112+
}
113+
#endif
114+
}
115+
116+
static void
117+
mrb_pwm_free(mrb_state *mrb, void *p) {
118+
pwm_t *pwm = (pwm_t *)p;
119+
120+
#if ESP_PLATFORM
121+
mrb_pwm_del_mcpwm(mrb, pwm);
122+
#endif
123+
124+
mrb_free(mrb, p);
125+
}
126+
127+
static mrb_value
128+
mrb_pwm_init(mrb_state *mrb, mrb_value self) {
129+
pwm_t *pwm = mrb_malloc(mrb, sizeof(pwm_t));
130+
mrb_get_args(mrb, "i", &pwm->pin);
131+
ESP_LOGI(TAG, "__initialize(pin: %d)", pwm->pin);
132+
133+
pwm->period_ticks = 0;
134+
pwm->pulse_width_ticks = 0;
135+
136+
#if ESP_PLATFORM
137+
pwm->timer = NULL;
138+
pwm->comparator = NULL;
139+
pwm->operator = NULL;
140+
pwm->generator = NULL;
141+
142+
mrb_pwm_new_mcpwm(mrb, pwm);
143+
#endif
144+
145+
mrb_data_init(self, pwm, &mrb_pwm);
146+
147+
return self;
148+
}
149+
150+
static mrb_value
151+
mrb_pwm_update(mrb_state *mrb, mrb_value self) {
152+
pwm_t *pwm = (pwm_t *) DATA_PTR(self);
153+
mrb_get_args(mrb, "ii", &pwm->period_ticks, &pwm->pulse_width_ticks);
154+
ESP_LOGI(TAG, "__update(%d, %d)", pwm->period_ticks, pwm->pulse_width_ticks);
155+
156+
#if ESP_PLATFORM
157+
mrb_pwm_del_mcpwm(mrb, pwm);
158+
mrb_pwm_new_mcpwm(mrb, pwm);
159+
#endif
160+
161+
return self;
162+
}
163+
164+
void
165+
mrb_mruby_esp32_pwm_gem_init(mrb_state* mrb) {
166+
struct RClass *client_class = mrb_define_class(mrb, "PWM", mrb->object_class);
167+
168+
mrb_define_method(mrb, client_class, "__initialize", mrb_pwm_init, MRB_ARGS_REQ(1));
169+
mrb_define_method(mrb, client_class, "__update", mrb_pwm_update, MRB_ARGS_REQ(2));
170+
}
171+
172+
void
173+
mrb_mruby_esp32_pwm_gem_final(mrb_state* mrb) {
174+
}

test/pwm.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
assert('PWM.new') do
2+
pwm = PWM.new(1)
3+
assert_equal(1, pwm.pin)
4+
assert_equal(0, pwm.pulse_width_ticks)
5+
assert_equal(1_000_000, pwm.period_ticks)
6+
7+
pwm = PWM.new(1, freq: 50, duty: 50)
8+
assert_equal(pwm.pin, 1)
9+
assert_equal(pwm.pulse_width_ticks, 10_000)
10+
assert_equal(pwm.period_ticks, 20_000)
11+
end
12+
13+
assert('PWM#freq') do
14+
pwm = PWM.new(1)
15+
pwm.frequency(50)
16+
assert_equal(pwm.pulse_width_ticks, 0)
17+
assert_equal(pwm.period_ticks, 20_000)
18+
end
19+
20+
assert('PWM#period_us') do
21+
pwm = PWM.new(1)
22+
pwm.period_us(5000)
23+
assert_equal(pwm.pulse_width_ticks, 0)
24+
assert_equal(pwm.period_ticks, 5_000)
25+
pwm.period_us(2000)
26+
assert_equal(pwm.pulse_width_ticks, 0)
27+
assert_equal(pwm.period_ticks, 2_000)
28+
end
29+
30+
assert('PWM#duty') do
31+
pwm = PWM.new(1)
32+
pwm.duty(20)
33+
assert_equal(pwm.pulse_width_ticks, 200_000)
34+
assert_equal(pwm.period_ticks, 1_000_000)
35+
36+
pwm = PWM.new(1, freq: 20)
37+
pwm.duty(20)
38+
assert_equal(pwm.pin, 1)
39+
assert_equal(pwm.pulse_width_ticks, 10_000)
40+
assert_equal(pwm.period_ticks, 50_000)
41+
end
42+
43+
assert('PWM#pulse_width_us') do
44+
pwm = PWM.new(1, freq: 100)
45+
pwm.pulse_width_us(1)
46+
assert_equal(pwm.pulse_width_ticks, 1)
47+
assert_equal(pwm.period_ticks, 10_000)
48+
49+
pwm = PWM.new(1, freq: 50)
50+
pwm.pulse_width_us(20)
51+
assert_equal(pwm.pulse_width_ticks, 20)
52+
assert_equal(pwm.period_ticks, 20_000)
53+
end

0 commit comments

Comments
 (0)