1 /* MCPWM sync example
2 
3    This example code is in the Public Domain (or CC0 licensed, at your option.)
4 
5    Unless required by applicable law or agreed to in writing, this
6    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7    CONDITIONS OF ANY KIND, either express or implied.
8 */
9 
10 /*
11  * This example will show you how to use capture function to read HC-SR04 sonar sensor.
12  */
13 
14 #include "freertos/FreeRTOS.h"
15 #include "freertos/task.h"
16 #include "freertos/queue.h"
17 #include "esp_rom_gpio.h"
18 #include "soc/mcpwm_periph.h"
19 #include "hal/gpio_hal.h"
20 #include "esp_log.h"
21 #include "esp_check.h"
22 #include "soc/rtc.h"
23 #include "driver/mcpwm.h"
24 
25 const static char *TAG = "sync_example";
26 
27 #define TARGET_MCPWM_UNIT MCPWM_UNIT_0
28 #define TIMER0_OUTPUT_GPIO GPIO_NUM_16
29 #define TIMER1_OUTPUT_GPIO GPIO_NUM_17
30 #define TIMER2_OUTPUT_GPIO GPIO_NUM_18
31 #define SIMU_GPIO_SYNC_SOURCE_GPIO GPIO_NUM_21
32 #define SIMU_GPIO_SYNC_SIMULATE_GPIO GPIO_NUM_19
33 
app_main(void)34 void app_main(void) {
35     ESP_LOGI(TAG, "MCPWM sync example");
36 
37     // init MCPWM: 10% duty cycle on all three timers in MCPWM unit 0 (currently not synchronous)
38     mcpwm_config_t pwm_config = {
39         .frequency = 1000,
40         .cmpr_a = 10,
41         .cmpr_b = 0,
42         .counter_mode = MCPWM_UP_COUNTER,
43         .duty_mode = MCPWM_DUTY_MODE_0,
44     };
45     ESP_ERROR_CHECK(mcpwm_init(TARGET_MCPWM_UNIT, MCPWM_TIMER_0, &pwm_config));
46     ESP_ERROR_CHECK(mcpwm_init(TARGET_MCPWM_UNIT, MCPWM_TIMER_1, &pwm_config));
47     ESP_ERROR_CHECK(mcpwm_init(TARGET_MCPWM_UNIT, MCPWM_TIMER_2, &pwm_config));
48 
49     // bind output to GPIO
50     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
51     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
52     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
53     ESP_LOGI(TAG, "PWM started, not synchronized");
54 
55     vTaskDelay(pdMS_TO_TICKS(1000));
56     // temporarily disable GPIO output, by binding to GenBs which have 0 output
57     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0B, TIMER0_OUTPUT_GPIO));
58     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1B, TIMER1_OUTPUT_GPIO));
59     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2B, TIMER2_OUTPUT_GPIO));
60     vTaskDelay(pdMS_TO_TICKS(2000));
61 
62     ESP_LOGI(TAG, "Sync timers with GPIO approach");
63     // first configure sync source
64     mcpwm_sync_config_t sync_conf = {
65         .sync_sig = MCPWM_SELECT_GPIO_SYNC0,
66         .timer_val = 0,
67         .count_direction = MCPWM_TIMER_DIRECTION_UP,
68     };
69     ESP_ERROR_CHECK(mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_0, &sync_conf));
70     ESP_ERROR_CHECK(mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_1, &sync_conf));
71     ESP_ERROR_CHECK(mcpwm_sync_configure(TARGET_MCPWM_UNIT, MCPWM_TIMER_2, &sync_conf));
72     // then configure GPIO
73     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM_SYNC_0, SIMU_GPIO_SYNC_SOURCE_GPIO));
74     gpio_config_t io_conf = {};
75     io_conf.intr_type = GPIO_INTR_DISABLE;
76     io_conf.mode = GPIO_MODE_OUTPUT;
77     io_conf.pin_bit_mask = BIT64(SIMU_GPIO_SYNC_SIMULATE_GPIO);
78     io_conf.pull_down_en = 0;
79     io_conf.pull_up_en = 0;
80     ESP_ERROR_CHECK(gpio_config(&io_conf));
81     ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 0));
82     ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 1));
83     // wait for at least one TEP
84     vTaskDelay(pdMS_TO_TICKS(10));
85     // re-enable GPIO output, to see the result
86     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
87     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
88     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
89     ESP_LOGI(TAG, "Output should already be synchronized");
90 
91     vTaskDelay(pdMS_TO_TICKS(1000));
92 
93     // stop and restart timers to mess them
94     ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_2));
95     ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_1));
96     ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_0));
97     vTaskDelay(pdMS_TO_TICKS(2000));
98     ESP_ERROR_CHECK(mcpwm_start(TARGET_MCPWM_UNIT, MCPWM_TIMER_0));
99     ESP_ERROR_CHECK(mcpwm_start(TARGET_MCPWM_UNIT, MCPWM_TIMER_1));
100     ESP_ERROR_CHECK(mcpwm_start(TARGET_MCPWM_UNIT, MCPWM_TIMER_2));
101     ESP_LOGI(TAG, "force synchronous lost");
102 
103     vTaskDelay(pdMS_TO_TICKS(1000));
104     // temporarily disable GPIO output, by binding to GenBs which have 0 output
105     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0B, TIMER0_OUTPUT_GPIO));
106     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1B, TIMER1_OUTPUT_GPIO));
107     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2B, TIMER2_OUTPUT_GPIO));
108     vTaskDelay(pdMS_TO_TICKS(2000));
109 
110 #ifdef SOC_MCPWM_SWSYNC_CAN_PROPAGATE
111     // use the trick that only available on esp32s3
112     mcpwm_set_timer_sync_output(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SWSYNC_SOURCE_SYNCIN);
113     sync_conf.sync_sig = MCPWM_SELECT_TIMER0_SYNC;
114     mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_0, &sync_conf);
115     mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_1, &sync_conf);
116     mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_2, &sync_conf);
117     // then send soft sync event to timer0
118     mcpwm_timer_trigger_soft_sync(MCPWM_UNIT_0, MCPWM_TIMER_0);
119     // re-enable GPIO output
120     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
121     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
122     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
123     ESP_LOGI(TAG, "Output should already be synchronized on esp32s3");
124 
125     vTaskDelay(pdMS_TO_TICKS(1000));
126 #endif
127 
128     // temporarily disable GPIO output, by binding to GenBs which have 0 output
129     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0B, TIMER0_OUTPUT_GPIO));
130     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1B, TIMER1_OUTPUT_GPIO));
131     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2B, TIMER2_OUTPUT_GPIO));
132     vTaskDelay(pdMS_TO_TICKS(2000));
133     // create phase between each timer.
134     // for this case all timers has 10% of period phase between each other
135     sync_conf.sync_sig = MCPWM_SELECT_GPIO_SYNC0;
136     sync_conf.timer_val = 0;  // no phase applied
137     mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_0, &sync_conf);
138     sync_conf.timer_val = 900;  // fill the counter with 90.0% of period will cause next pulse being delayed 10% period
139     mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_1, &sync_conf);
140     sync_conf.timer_val = 800;  // fill the counter with 80.0% of period will cause next pulse being delayed 20% period
141     mcpwm_sync_configure(MCPWM_UNIT_0, MCPWM_TIMER_2, &sync_conf);
142     // trigger positive edge
143     ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 0));
144     ESP_ERROR_CHECK(gpio_set_level(SIMU_GPIO_SYNC_SIMULATE_GPIO, 1));
145     // wait for at least one TEP
146     vTaskDelay(pdMS_TO_TICKS(10));
147     // re-enable GPIO output, to see the result
148     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM0A, TIMER0_OUTPUT_GPIO));
149     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM1A, TIMER1_OUTPUT_GPIO));
150     ESP_ERROR_CHECK(mcpwm_gpio_init(TARGET_MCPWM_UNIT, MCPWM2A, TIMER2_OUTPUT_GPIO));
151     ESP_LOGI(TAG, "Each output pulse should be placed with 10 percents of period");
152 
153     vTaskDelay(pdMS_TO_TICKS(1000));
154 
155     ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_2));
156     ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_1));
157     ESP_ERROR_CHECK(mcpwm_stop(TARGET_MCPWM_UNIT, MCPWM_TIMER_0));
158 }
159