PlantCtrl/esp32/include/ulp-pwm.h

177 lines
5.1 KiB
C

#ifndef ULP_PWM_h
#define ILP_PWM_h
#include <Arduino.h>
#include "driver/rtc_io.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "esp32/ulp.h"
#include "ControllerConfiguration.h"
#define LBL_START 1
#define LBL_DELAY_ON 2
#define LBL_DELAY_OFF 3
#define LBL_SKIP_ON 4
#define LBL_SKIP_OFF 5
#define REGISTER_DELAY_LOOP_COUNTER R0
#define REGISTER_TICKS_ON R1
#define REGISTER_TICKS_OFF R2
#define TOTAL_TICKS_DELAY 255
#define PIN TIMED_LIGHT_PIN
//support 20 vars
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
//use the first for dimming
const size_t ulp_dimm_offset = ulp_var_offset + 1;
const size_t ulp_alive_offset = ulp_var_offset + 2;
//see https://github.com/perseus086/ESP32-notes
const uint32_t rtc_bit[40] = {
25, //gpio0
0, //gpio1
26, //gpio2
0, //gpio3
24, //gpio4
0, //gpio5
0, //gpio6
0, //gpio7
0, //gpio8
0, //gpio9
0, //gpio10
0, //gpio11
29, //gpio12
28, //gpio13
30, //gpio14
27, //gpio15
0, //gpio16
31, //gpio17
0, //gpio18
0, //gpio19
0, //gpio20
0, //gpio21
0, //gpio22
0, //gpio23
0, //gpio24
20, //gpio25
21, //gpio26
0, //gpio27
0, //gpio28
0, //gpio29
0, //gpio30
0, //gpio31
23, //gpio32
22, //gpio33
18, //gpio34
19, //gpio35
14, //gpio36
15, //gpio37
16, //gpio38
17 //gpio39
};
static inline void ulp_internal_data_write(size_t offset, uint16_t value)
{
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
{
Serial.print("Invalid ULP offset detected, refusing write!");
Serial.print(offset);
Serial.print("-");
Serial.print(ulp_var_offset);
Serial.print("-");
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
return;
}
else
{
RTC_SLOW_MEM[offset] = value;
}
}
static inline uint16_t ulp_internal_data_read(size_t offset)
{
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
{
Serial.print("Invalid ULP offset detected");
Serial.print(offset);
Serial.print("-");
Serial.print(ulp_var_offset);
Serial.print("-");
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
}
return RTC_SLOW_MEM[offset] & 0xffff;
}
void ulp_internal_start(void)
{
rtc_gpio_init(PIN);
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(PIN, 0);
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
// Define ULP program
const ulp_insn_t ulp_prog[] = {
M_LABEL(LBL_START),
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
I_MOVI(REGISTER_TICKS_ON, 0),
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
//in total there is always 255 delay loop iterations, but in different duty cycle
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
//on phase
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
M_LABEL(LBL_DELAY_ON),
I_DELAY(1), //wait 1 clock
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
M_LABEL(LBL_SKIP_ON),
//off phase
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
M_LABEL(3),
I_DELAY(1), //wait 1 clock
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
M_BGE(3, 1), //if time left, goto start of on loop
M_LABEL(LBL_SKIP_OFF),
M_BX(LBL_START),
};
// Run ULP program
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
Serial.print("ULP bootstrap status ");
Serial.println(error);
//allow glitchless start
ulp_internal_data_write(ulp_alive_offset, 0);
error = ulp_run(0);
Serial.print("ULP start status ");
Serial.println(error);
}
static inline void ulp_pwm_set_level(uint8_t level)
{
ulp_internal_data_write(ulp_dimm_offset, level);
}
static inline void ulp_pwm_init()
{
ulp_internal_data_write(ulp_alive_offset, 0);
delay(10);
if (ulp_internal_data_read(ulp_alive_offset) == 0)
{
ulp_internal_start();
}
}
#endif