Skip to content

Commit ebc8a15

Browse files
Add new ULP example for ESP32 (espressif#7221)
Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>
1 parent 74964e1 commit ebc8a15

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

Diff for: libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/.skip.esp32c3

Whitespace-only changes.

Diff for: libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/.skip.esp32s2

Whitespace-only changes.

Diff for: libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/.skip.esp32s3

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
This example smothly blinks GPIO_2 using different frequencies changed after Deep Sleep Time
3+
The PWM and control of blink frequency is done by ULP exclusively
4+
This is an example about how to program the ULP using Arduino
5+
It also demonstrates use of RTM MEMORY to persist data and states
6+
*/
7+
8+
#include <Arduino.h>
9+
#include "esp32/ulp.h"
10+
#include "driver/rtc_io.h"
11+
12+
// RTC Memory used for ULP internal variable and Sketch interfacing
13+
#define RTC_dutyMeter 0
14+
#define RTC_dir 4
15+
#define RTC_fadeDelay 12
16+
// *fadeCycleDelay is used to pass values to ULP and change its behaviour
17+
uint32_t *fadeCycleDelay = &RTC_SLOW_MEM[RTC_fadeDelay];
18+
#define ULP_START_OFFSET 32
19+
20+
// For ESP32 Arduino, it is usually at offeset 512, defined in sdkconfig
21+
RTC_DATA_ATTR uint32_t ULP_Started = 0; // 0 or 1
22+
23+
//Time-to-Sleep
24+
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
25+
#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in microseconds); multiplied by above conversion to achieve seconds*/
26+
27+
28+
void ulp_setup() {
29+
if (ULP_Started) {
30+
return;
31+
}
32+
*fadeCycleDelay = 5; // 5..200 works fine for a full Fade In + Out cycle
33+
ULP_Started = 1;
34+
35+
// GPIO2 initialization (set to output and initial value is 0)
36+
const gpio_num_t MeterPWMPin = GPIO_NUM_2;
37+
rtc_gpio_init(MeterPWMPin);
38+
rtc_gpio_set_direction(MeterPWMPin, RTC_GPIO_MODE_OUTPUT_ONLY);
39+
rtc_gpio_set_level(MeterPWMPin, 0);
40+
41+
// if LED is connected to GPIO2 (specify by +RTC_GPIO_OUT_DATA_S : ESP32 is 14, S2/S3 is 10)
42+
const uint32_t MeterPWMBit = rtc_io_number_get(MeterPWMPin) + RTC_GPIO_OUT_DATA_S;
43+
44+
enum labels {
45+
INIFINITE_LOOP,
46+
RUN_PWM,
47+
NEXT_PWM_CYCLE,
48+
PWM_ON,
49+
PWM_OFF,
50+
END_PWM_CYCLE,
51+
POSITIVE_DIR,
52+
DEC_DUTY,
53+
INC_DUTY,
54+
};
55+
56+
// Define ULP program
57+
const ulp_insn_t ulp_prog[] = {
58+
// Initial Value setup
59+
I_MOVI(R0, 0), // R0 = 0
60+
I_ST(R0, R0, RTC_dutyMeter), // RTC_SLOW_MEM[RTC_dutyMeter] = 0
61+
I_MOVI(R1, 1), // R1 = 1
62+
I_ST(R1, R0, RTC_dir), // RTC_SLOW_MEM[RTC_dir] = 1
63+
64+
M_LABEL(INIFINITE_LOOP), // while(1) {
65+
66+
// run certain PWM Duty for about (RTC_fadeDelay x 100) microseconds
67+
I_MOVI(R3, 0), // R3 = 0
68+
I_LD(R3, R3, RTC_fadeDelay), // R3 = RTC_SLOW_MEM[RTC_fadeDelay]
69+
M_LABEL(RUN_PWM), // do { // repeat RTC_fadeDelay times:
70+
71+
// execute about 10KHz PWM on GPIO2 using as duty cycle = RTC_SLOW_MEM[RTC_dutyMeter]
72+
I_MOVI(R0, 0), // R0 = 0
73+
I_LD(R0, R0, RTC_dutyMeter), // R0 = RTC_SLOW_MEM[RTC_dutyMeter]
74+
M_BL(NEXT_PWM_CYCLE, 1), // if (R0 > 0) turn on LED
75+
I_WR_REG(RTC_GPIO_OUT_W1TS_REG, MeterPWMBit, MeterPWMBit, 1), // W1TS set bit to clear GPIO - GPIO2 on
76+
M_LABEL(PWM_ON), // while (R0 > 0) // repeat RTC_dutyMeter times:
77+
M_BL(NEXT_PWM_CYCLE, 1), // {
78+
//I_DELAY(8), // // 8 is about 1 microsecond based on 8MHz
79+
I_SUBI(R0, R0, 1), // R0 = R0 - 1
80+
M_BX(PWM_ON), // }
81+
M_LABEL(NEXT_PWM_CYCLE), // // toggle GPIO_2
82+
I_MOVI(R0, 0), // R0 = 0
83+
I_LD(R0, R0, RTC_dutyMeter), // R0 = RTC_SLOW_MEM[RTC_dutyMeter]
84+
I_MOVI(R1, 100), // R1 = 100
85+
I_SUBR(R0, R1, R0), // R0 = 100 - dutyMeter
86+
M_BL(END_PWM_CYCLE, 1), // if (R0 > 0) turn off LED
87+
I_WR_REG(RTC_GPIO_OUT_W1TC_REG, MeterPWMBit, MeterPWMBit, 1), // W1TC set bit to clear GPIO - GPIO2 off
88+
M_LABEL(PWM_OFF), // while (R0 > 0) // repeat (100 - RTC_dutyMeter) times:
89+
M_BL(END_PWM_CYCLE, 1), // {
90+
//I_DELAY(8), // // 8 is about 1us: ULP fetch+execution time
91+
I_SUBI(R0, R0, 1), // R0 = R0 - 1
92+
M_BX(PWM_OFF), // }
93+
M_LABEL(END_PWM_CYCLE), //
94+
95+
I_SUBI(R3, R3, 1), // R3 = R3 - 1 // RTC_fadeDelay
96+
I_MOVR(R0, R3), // R0 = R3 // only R0 can be used to compare and branch
97+
M_BGE(RUN_PWM, 1), // } while (R3 > 0) // ESP32 repeatinf RTC_fadeDelay times
98+
99+
// increase/decrease DutyMeter to apply Fade In/Out loop
100+
I_MOVI(R1, 0), // R1 = 0
101+
I_LD(R1, R1, RTC_dutyMeter), // R1 = RTC_SLOW_MEM[RTC_dutyMeter]
102+
I_MOVI(R0, 0), // R0 = 0
103+
I_LD(R0, R0, RTC_dir), // R0 = RTC_SLOW_MEM[RTC_dir]
104+
105+
M_BGE(POSITIVE_DIR, 1), // if(dir == 0) { // decrease duty by 2
106+
// Dir is 0, means decrease Duty by 2
107+
I_MOVR(R0, R1), // R0 = Duty
108+
M_BGE(DEC_DUTY, 1), // if (duty == 0) { // change direction and increase duty
109+
I_MOVI(R3, 0), // R3 = 0
110+
I_MOVI(R2, 1), // R2 = 1
111+
I_ST(R2, R3, RTC_dir), // RTC_SLOW_MEM[RTC_dir] = 1 // increasing direction
112+
M_BX(INC_DUTY), // goto "increase Duty"
113+
M_LABEL(DEC_DUTY), // } "decrease Duty":
114+
I_SUBI(R0, R0, 2), // Duty -= 2
115+
I_MOVI(R2, 0), // R2 = 0
116+
I_ST(R0, R2, RTC_dutyMeter), // RTC_SLOW_MEM[RTC_dutyMeter] += 2
117+
M_BX(INIFINITE_LOOP), // }
118+
119+
M_LABEL(POSITIVE_DIR), // else { // dir == 1 // increase duty by 2
120+
// Dir is 1, means increase Duty by 2
121+
I_MOVR(R0, R1), // R0 = Duty
122+
M_BL(INC_DUTY, 100), // if (duty == 100) { // change direction and decrease duty
123+
I_MOVI(R2, 0), // R2 = 0
124+
I_ST(R2, R2, RTC_dir), // RTC_SLOW_MEM[RTC_dir] = 0 // decreasing direction
125+
M_BX(DEC_DUTY), // goto "decrease Duty"
126+
M_LABEL(INC_DUTY), // } "increase Duty":
127+
I_ADDI(R0, R0, 2), // Duty += 2
128+
I_MOVI(R2, 0), // R2 = 0
129+
I_ST(R0, R2, RTC_dutyMeter), // RTC_SLOW_MEM[RTC_dutyMeter] -= 2
130+
// } // if (dir == 0)
131+
M_BX(INIFINITE_LOOP), // } // while(1)
132+
};
133+
// Run ULP program
134+
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
135+
ulp_process_macros_and_load(ULP_START_OFFSET, ulp_prog, &size);
136+
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
137+
ulp_run(ULP_START_OFFSET);
138+
}
139+
140+
141+
void setup() {
142+
Serial.begin(115200);
143+
while (!Serial) {} // wait for Serial to start
144+
145+
ulp_setup(); // it really only runs on the first ESP32 boot
146+
Serial.printf("\nStarted smooth blink with delay %d\n", *fadeCycleDelay);
147+
148+
// *fadeCycleDelay resides in RTC_SLOW_MEM and persists along deep sleep waking up
149+
// it is used as a delay time parameter for smooth blinking, in the ULP processing code
150+
if (*fadeCycleDelay < 195) {
151+
*fadeCycleDelay += 10;
152+
} else {
153+
*fadeCycleDelay = 5; // 5..200 works fine for a full Fade In + Out cycle
154+
}
155+
Serial.println("Entering in Deep Sleep");
156+
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR /*/ 4*/); // time set with variable above
157+
esp_deep_sleep_start();
158+
// From this point on, no code is executed in DEEP SLEEP mode
159+
}
160+
161+
162+
void loop() {
163+
// It never reaches this code because it enters in Deep Sleep mode at the end of setup()
164+
}
165+

0 commit comments

Comments
 (0)