added ulp-pwm logic for timedlight

This commit is contained in:
Your Name 2021-12-05 02:36:03 +01:00
parent 80828eadd7
commit a745896643
9 changed files with 455 additions and 240 deletions

View File

@ -22,7 +22,8 @@
"array": "cpp", "array": "cpp",
"tuple": "cpp", "tuple": "cpp",
"utility": "cpp", "utility": "cpp",
"fstream": "cpp" "fstream": "cpp",
"system_error": "cpp"
} }
} }
} }

View File

@ -82,9 +82,13 @@
/** \addtogroup Configuration /** \addtogroup Configuration
* @{ * @{
*/ */
#define CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
#define CONFIG_COMPILER_CXX_EXCEPTIONS
#define FIRMWARE_VERSION "sw 2.0 hw 0.10b" #define FIRMWARE_VERSION "sw 2.0 hw 0.10b"
#define TIMED_LIGHT_PIN CUSTOM1_PIN5 #define TIMED_LIGHT_PIN CUSTOM1_PIN7
//#define FLOWMETER_PIN CUSTOM1_PIN1 //#define FLOWMETER_PIN CUSTOM1_PIN1
#ifdef FLOWMETER_PIN #ifdef FLOWMETER_PIN
#define FLOWMETER_FLOWFACTOR 23 /** F = 22 * Q;Q = L/min */ #define FLOWMETER_FLOWFACTOR 23 /** F = 22 * Q;Q = L/min */

View File

@ -86,6 +86,7 @@ HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as d
HomieSetting<long> timedLightStart("LightStart", "hour to start light"); HomieSetting<long> timedLightStart("LightStart", "hour to start light");
HomieSetting<long> timedLightEnd("LightEnd", "hour to end light"); HomieSetting<long> timedLightEnd("LightEnd", "hour to end light");
HomieSetting<bool> timedLightOnlyWhenDark("LightOnlyDark", "only enable light, if solar is low"); HomieSetting<bool> timedLightOnlyWhenDark("LightOnlyDark", "only enable light, if solar is low");
HomieSetting<long> timedLightPowerLevel("LightPowerLevel", "0-255 power level");
#endif // TIMED_LIGHT_PIN #endif // TIMED_LIGHT_PIN

View File

@ -32,3 +32,4 @@
#define LOG_SLEEP_DAY 101 #define LOG_SLEEP_DAY 101
#define LOG_SLEEP_CYCLE 102 #define LOG_SLEEP_CYCLE 102
#define LOG_MISSING_PUMP -4 #define LOG_MISSING_PUMP -4
#define LOG_BOOT_ERROR_DETECTION 10000

188
esp32/include/ulp-pwm.h Normal file
View File

@ -0,0 +1,188 @@
#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;
}
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_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

View File

@ -35,12 +35,18 @@
#include <VL53L0X.h> #include <VL53L0X.h>
#include "driver/pcnt.h" #include "driver/pcnt.h"
#include "MQTTUtils.h" #include "MQTTUtils.h"
#include "esp_ota_ops.h"
#if defined(TIMED_LIGHT_PIN)
#include "ulp-pwm.h"
#endif
/****************************************************************************** /******************************************************************************
* DEFINES * DEFINES
******************************************************************************/ ******************************************************************************/
#define AMOUNT_SENOR_QUERYS 8 #define AMOUNT_SENOR_QUERYS 8
#define MAX_TANK_DEPTH 2000 #define MAX_TANK_DEPTH 2000
#define REBOOT_LOOP_DETECTION_ERROR 5
/****************************************************************************** /******************************************************************************
* FUNCTION PROTOTYPES * FUNCTION PROTOTYPES
@ -54,9 +60,7 @@ bool determineTimedLightState(bool lowLight);
/****************************************************************************** /******************************************************************************
* NON VOLATILE VARIABLES in DEEP SLEEP * NON VOLATILE VARIABLES in DEEP SLEEP
******************************************************************************/ ******************************************************************************/
#if defined(TIMED_LIGHT_PIN) #if defined(TIMED_LIGHT_PIN)
RTC_DATA_ATTR bool timedLightOn = false; /**< allow fast recovery after poweron */
RTC_DATA_ATTR bool timedLightLowVoltageTriggered = false; /**remember if it was shut down due to voltage level */ RTC_DATA_ATTR bool timedLightLowVoltageTriggered = false; /**remember if it was shut down due to voltage level */
#endif // TIMED_LIGHT_PIN #endif // TIMED_LIGHT_PIN
@ -104,11 +108,29 @@ Plant mPlants[MAX_PLANTS] = {
* LOCAL FUNCTIONS * LOCAL FUNCTIONS
******************************************************************************/ ******************************************************************************/
void finsihedCycleSucessfully()
{
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
log(LOG_LEVEL_INFO, "Get State Partition was Successfull", LOG_BOOT_ERROR_DETECTION);
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
log(LOG_LEVEL_INFO, "Diagnostics completed successfully! Marking as valid", LOG_BOOT_ERROR_DETECTION);
esp_ota_mark_app_valid_cancel_rollback();
}
}
}
void espDeepSleep(bool afterPump = false) void espDeepSleep(bool afterPump = false)
{ {
if (mDownloadMode) if (mDownloadMode)
{ {
log(LOG_LEVEL_DEBUG, "abort deepsleep, DownloadMode active", LOG_DEBUG_CODE); log(LOG_LEVEL_DEBUG, "abort deepsleep, DownloadMode active", LOG_DEBUG_CODE);
//if we manage to get to the download mode, the device can be restored
finsihedCycleSucessfully();
return; return;
} }
if (aliveWasRead()) if (aliveWasRead())
@ -139,10 +161,6 @@ void espDeepSleep(bool afterPump = false)
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON);
#if defined(TIMED_LIGHT_PIN)
gpio_hold_en(TIMED_LIGHT_PIN);
#endif // TIMED_LIGHT_PIN
long secondsToSleep = -1; long secondsToSleep = -1;
if (afterPump) if (afterPump)
@ -165,6 +183,7 @@ void espDeepSleep(bool afterPump = false)
} }
esp_sleep_enable_timer_wakeup((secondsToSleep * 1000U * 1000U)); esp_sleep_enable_timer_wakeup((secondsToSleep * 1000U * 1000U));
finsihedCycleSucessfully();
if (aliveWasRead()) if (aliveWasRead())
{ {
delay(1000); delay(1000);
@ -639,22 +658,19 @@ void pumpActiveLoop()
} }
} }
void safeSetup() void setup()
{ {
/* reduce power consumption */
setCpuFrequencyMhz(80); setCpuFrequencyMhz(80);
Serial.begin(115200); Serial.begin(115200);
Serial << "Wifi mode set to " << WIFI_OFF << " to allow analog2 useage " << endl; Serial << "Wifi mode set to " << WIFI_OFF << " to allow analog2 useage " << endl;
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
Serial.flush(); Serial.flush();
//restore state before releasing pin, to prevent flickering //restore state before releasing pin, to prevent flickering
#if defined(TIMED_LIGHT_PIN) #if defined(TIMED_LIGHT_PIN)
pinMode(TIMED_LIGHT_PIN, OUTPUT); ulp_pwm_init();
digitalWrite(TIMED_LIGHT_PIN, timedLightOn);
gpio_hold_dis(TIMED_LIGHT_PIN);
#endif // TIMED_LIGHT_PIN #endif // TIMED_LIGHT_PIN
/* Intialize Plant */ /* Intialize Plant */
@ -733,6 +749,8 @@ void safeSetup()
{ return (candidate > 0) && (candidate < (20)); }); { return (candidate > 0) && (candidate < (20)); });
#if defined(TIMED_LIGHT_PIN) #if defined(TIMED_LIGHT_PIN)
timedLightPowerLevel.setDefaultValue(25).setValidator([](long candidate)
{ return (candidate > 0) && (candidate <= (255)); });
timedLightStart.setDefaultValue(18).setValidator([](long candidate) timedLightStart.setDefaultValue(18).setValidator([](long candidate)
{ return (candidate > 0) && (candidate < (25)); }); { return (candidate > 0) && (candidate < (25)); });
timedLightEnd.setDefaultValue(23).setValidator([](long candidate) timedLightEnd.setDefaultValue(23).setValidator([](long candidate)
@ -825,26 +843,6 @@ void safeSetup()
setupFinishedTimestamp = millis(); setupFinishedTimestamp = millis();
} }
/**
* @brief Startup function
* Is called once, the controller is started
*/
void setup()
{
try
{
safeSetup();
}
catch (const std::exception &e)
{
Serial.printf("Exception thrown: \"%s\"", e.what());
}
catch (...)
{
Serial.println("Other exception thrown.");
}
}
void selfTest() void selfTest()
{ {
@ -974,8 +972,18 @@ void plantcontrol()
Serial.println("Skipping MQTT, offline mode"); Serial.println("Skipping MQTT, offline mode");
Serial.flush(); Serial.flush();
} }
bool isLowLight = (mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE); bool isLowLight = (mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE);
#if defined(TIMED_LIGHT_PIN)
bool shouldLight = determineTimedLightState(isLowLight);
if(shouldLight){
ulp_pwm_set_level(timedLightPowerLevel.get());
}else {
ulp_pwm_set_level(0);
}
#endif // TIMED_LIGHT_PIN
bool hasWater = true; //FIXME remaining > waterLevelMin.get(); bool hasWater = true; //FIXME remaining > waterLevelMin.get();
//FIXME no water warning message //FIXME no water warning message
pumpToRun = determineNextPump(isLowLight); pumpToRun = determineNextPump(isLowLight);
@ -1002,12 +1010,6 @@ void plantcontrol()
{ {
espDeepSleep(); espDeepSleep();
} }
#if defined(TIMED_LIGHT_PIN)
bool shouldLight = determineTimedLightState(isLowLight);
timedLightOn = shouldLight;
digitalWrite(TIMED_LIGHT_PIN, shouldLight);
#endif // TIMED_LIGHT_PIN
} }
/** @}*/ /** @}*/
@ -1032,11 +1034,14 @@ bool determineTimedLightState(bool lowLight)
return false; return false;
} }
if (((hoursStart > hoursEnd) && int curHour = getCurrentHour();
(getCurrentHour() >= hoursStart || getCurrentHour() <= hoursEnd)) || bool condition1 = ((hoursStart > hoursEnd) &&
/* Handle e.g. start = 8, end = 21 */ (curHour >= hoursStart || curHour <= hoursEnd));
bool condition2 = /* Handle e.g. start = 8, end = 21 */
((hoursStart < hoursEnd) && ((hoursStart < hoursEnd) &&
(getCurrentHour() >= hoursStart && getCurrentHour() <= hoursEnd))) (curHour >= hoursStart && curHour <= hoursEnd));
timedLightNode.setProperty("debug").send(String(curHour) + " " + String(hoursStart) + " " + String(hoursEnd) + " " + String(condition1) + " " + String(condition2));
if (condition1 || condition2)
{ {
bool voltageOk = !timedLightLowVoltageTriggered && battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get(); bool voltageOk = !timedLightLowVoltageTriggered && battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get();
if (voltageOk || equalish(timedLightVoltageCutoff.get(), -1)) if (voltageOk || equalish(timedLightVoltageCutoff.get(), -1))

View File

@ -1,112 +0,0 @@
/*
* DS2438.h
*
* by Joe Bechter
*
* (C) 2012, bechter.com
*
* All files, software, schematics and designs are provided as-is with no warranty.
* All files, software, schematics and designs are for experimental/hobby use.
* Under no circumstances should any part be used for critical systems where safety,
* life or property depends upon it. You are responsible for all use.
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
* 2. Use is non-commercial.
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
*
*/
#ifndef DS2438_h
#define DS2438_h
#include <Arduino.h>
#include <OneWire.h>
#define DS2438_TEMPERATURE_CONVERSION_COMMAND 0x44
#define DS2438_VOLTAGE_CONVERSION_COMMAND 0xb4
#define DS2438_WRITE_SCRATCHPAD_COMMAND 0x4e
#define DS2438_COPY_SCRATCHPAD_COMMAND 0x48
#define DS2438_READ_SCRATCHPAD_COMMAND 0xbe
#define DS2438_RECALL_MEMORY_COMMAND 0xb8
#define PAGE_MIN 0
#define PAGE_MAX 7
#define DS2438_CHA 0
#define DS2438_CHB 1
#define DS2438_MODE_CHA 0x01
#define DS2438_MODE_CHB 0x02
#define DS2438_MODE_TEMPERATURE 0x04
#define DS2438_TEMPERATURE_DELAY 10
#define DS2438_VOLTAGE_CONVERSION_DELAY 8
#define DEFAULT_PAGE0(var) uint8_t var[8] { \
0b00001011 /* X, ADB=0, NVB=0, TB=0, AD=1, EE=0, CA=1, IAD=1 */, \
0, /* Temperatur */ \
0, /* Temperatur */ \
0, /* Voltage */ \
0, /* Voltage */ \
0, /* Current */ \
0, /* Current */ \
0 /* Threashold */ \
}
typedef struct PageOne {
uint8_t eleapsedTimerByte0; /**< LSB of timestamp */
uint8_t eleapsedTimerByte1;
uint8_t eleapsedTimerByte2;
uint8_t eleapsedTimerByte3; /**< MSB of timestamp */
uint8_t ICA; /**< Integrated Current Accumulator (current flowing into and out of the battery) */
uint8_t offsetRegisterByte0; /**< Offset for ADC calibdation */
uint8_t offsetRegisterByte1; /**< Offset for ADC calibdation */
uint8_t reserved;
} PageOne_t;
typedef struct PageSeven {
uint8_t userByte0;
uint8_t userByte1;
uint8_t userByte2;
uint8_t userByte3;
uint8_t CCA0; /**< Charging Current Accumulator (CCA) */
uint8_t CCA1; /**< Charging Current Accumulator (CCA) */
uint8_t DCA0; /**< Discharge Current Accumulator (DCA) */
uint8_t DCA1; /**< Discharge Current Accumulator (DCA) */
} PageSeven_t;
typedef uint8_t DeviceAddress[8];
class DS2438 {
public:
DS2438(OneWire *ow, float currentShunt);
DS2438(OneWire *ow, uint8_t *address);
void begin();
void update();
double getTemperature();
float getVoltage(int channel=DS2438_CHA);
float getCurrent();
boolean isError();
boolean isFound();
private:
bool validAddress(const uint8_t*);
bool validFamily(const uint8_t* deviceAddress);
bool deviceFound = false;
OneWire *_ow;
DeviceAddress _address;
uint8_t _mode;
double _temperature;
float _voltageA;
float _voltageB;
float _current;
float _currentShunt;
boolean _error;
boolean startConversion(int channel, boolean doTemperature);
boolean selectChannel(int channel);
void writePage(int page, uint8_t *data);
boolean readPage(int page, uint8_t *data);
};
#endif

View File

@ -0,0 +1,187 @@
#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

View File

@ -1,90 +1,30 @@
#include <Arduino.h> #include <Arduino.h>
#include "driver/pcnt.h" #include "driver/pcnt.h"
#include "driver/rtc_io.h" #include "ulp-pwm.h"
#include "esp32/ulp.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#define ULP_DATA_OFFSET 200 RTC_SLOW_ATTR uint8_t tick = 0;
RTC_SLOW_ATTR bool dir = true;
#define ULP_START_OFFSET 0 void setup()
void ulp_start(void) {
// Slow memory initialization
//memset(RTC_SLOW_MEM, ULP_START_OFFSET, (8192-ULP_START_OFFSET));
// GPIO32 initialization (set to output and initial value is 0)
rtc_gpio_init(GPIO_NUM_12);
rtc_gpio_set_direction(GPIO_NUM_12, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(GPIO_NUM_12, 0);
// Define ULP program
const ulp_insn_t ulp_prog[] = {
M_LABEL(1),
I_MOVI(R2, 0),
I_MOVI(R3, 0),
I_LD(R2, R2, ULP_DATA_OFFSET),
I_LD(R3, R3, ULP_DATA_OFFSET+1),
I_WR_REG(RTC_GPIO_OUT_REG, 29, 29, 1), // on
//wait for 100*r2
I_MOVR(R0,R2),
M_LABEL(2),
I_DELAY(1),
I_SUBI(R0,R0,1),
M_BGE(2,1),
I_WR_REG(RTC_GPIO_OUT_REG, 29, 29, 0), // off
//wait for 100*R3
I_MOVR(R0,R3),
M_LABEL(3),
I_DELAY(1),
I_SUBI(R0,R0,1),
M_BGE(3,1),
M_BX(1),
};
// Run ULP program
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
ulp_process_macros_and_load(ULP_START_OFFSET, ulp_prog, &size);
assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
ulp_run(ULP_START_OFFSET);
}
static inline void ulp_data_write(size_t offset, uint16_t value)
{ {
RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] = value;
}
static inline uint16_t ulp_data_read(size_t offset)
{
return RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] & 0xffff;
}
void setup() {
Serial.begin(115200); Serial.begin(115200);
ulp_data_write(0,1000); Serial.println(rtc_io_number_get(GPIO_NUM_12));
ulp_data_write(1,1000); ulp_pwm_init();
ulp_start();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
//esp_deep_sleep_start();
}
if (dir) {
tick += 10;
} else {
tick -= 10;
}
ulp_pwm_set_level(tick);
if (tick == 250 || tick == 0) {
dir = !dir;
}
esp_sleep_enable_timer_wakeup(1000000);
Serial.println(ulp_data_read(ulp_dimm_offset));
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
esp_deep_sleep_start();
}
void loop(){ void loop(){
delay(1000);
Serial.println(ulp_data_read(0));
ulp_data_write(0, 50);
ulp_data_write(1, 150);
//test = 10;
//test2 = 255-test;
delay(1000);
//test = 50;
//test2 = 255 - test;
ulp_data_write(0, 150);
ulp_data_write(1, 50);
//Serial.print();
Serial.println("loop");
} }