187 lines
5.4 KiB
C
187 lines
5.4 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"
|
|
|
|
#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 GPIO_NUM_12
|
|
|
|
//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_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_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;
|
|
}
|
|
|
|
static inline uint32_t rtc_io_number_get(gpio_num_t gpio_num)
|
|
{
|
|
assert(rtc_gpio_is_valid_gpio(gpio_num) && "Invalid GPIO for RTC");
|
|
uint32_t bit = rtc_bit[gpio_num];
|
|
Serial.print("Resolved GPIO ");
|
|
Serial.print(gpio_num);
|
|
Serial.print(" to rtc bit ");
|
|
Serial.println(bit);
|
|
return bit;
|
|
}
|
|
|
|
void ulp_pwm_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_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_data_write(ulp_dimm_offset, level);
|
|
}
|
|
|
|
static inline void ulp_pwm_init()
|
|
{
|
|
ulp_data_write(ulp_alive_offset, 0);
|
|
delay(10);
|
|
if (ulp_data_read(ulp_alive_offset) == 0)
|
|
{
|
|
ulp_pwm_start();
|
|
}
|
|
}
|
|
|
|
#endif |