Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ description: Unified library for M5Stack series
issues: https://github.com/m5stack/M5Unified/issues
repository: https://github.com/m5stack/M5Unified.git
url: https://github.com/m5stack/M5Unified.git
version: 0.2.27
version: 0.2.28
dependencies:
m5stack/m5gfx:
version: ">=0.2.31"
version: ">=0.2.35"
4 changes: 2 additions & 2 deletions library.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
"dependencies": [
{
"name": "M5GFX",
"version": ">=0.2.31"
"version": ">=0.2.35"
}
],
"version": "0.2.27",
"version": "0.2.28",
"frameworks": ["arduino", "espidf", "*"],
"platforms": ["espressif32", "native"],
"headers": "M5Unified.h"
Expand Down
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=M5Unified
version=0.2.27
version=0.2.28
author=M5Stack
maintainer=M5Stack
sentence=Unified library for M5Stack series
Expand All @@ -8,4 +8,4 @@ category=Display
url=https://github.com/m5stack/M5Unified.git
architectures=esp32
includes=M5Unified.h
depends=M5GFX (>=0.2.31)
depends=M5GFX (>=0.2.35)
16 changes: 8 additions & 8 deletions src/M5Unified.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ namespace m5
#endif
{
M5ModuleDisplay dsp(cfg.module_display);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
}
}
Expand Down Expand Up @@ -520,7 +520,7 @@ namespace m5
#endif
{
M5UnitOLED dsp(cfg.unit_oled);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
port_a_used = true;
}
Expand All @@ -544,7 +544,7 @@ namespace m5
#endif
{
M5UnitMiniOLED dsp(cfg.unit_mini_oled);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
port_a_used = true;
}
Expand All @@ -568,7 +568,7 @@ namespace m5
#endif
{
M5UnitGLASS dsp(cfg.unit_glass);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
port_a_used = true;
}
Expand All @@ -592,7 +592,7 @@ namespace m5
#endif
{
M5UnitGLASS2 dsp(cfg.unit_glass2);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
port_a_used = true;
}
Expand All @@ -619,7 +619,7 @@ namespace m5
int retry = 8;
do {
m5gfx::delay(32);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
port_a_used = true;
break;
Expand All @@ -645,7 +645,7 @@ namespace m5
// When ModuleRCA is used, UnitRCA is not used.
unit_rca = false;
M5ModuleRCA dsp(cfg.module_rca);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
}
}
Expand All @@ -668,7 +668,7 @@ namespace m5
)))
{
M5UnitRCA dsp(cfg.unit_rca);
if (dsp.init()) {
if (cfg.clear_display ? dsp.init() : dsp.init_without_reset(false)) {
addDisplay(dsp);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/gitTagVersion.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#define M5UNIFIED_VERSION_MAJOR 0
#define M5UNIFIED_VERSION_MINOR 2
#define M5UNIFIED_VERSION_PATCH 27
#define M5UNIFIED_VERSION_PATCH 28
#define M5UNIFIED_VERSION F( M5UNIFIED_VERSION_MAJOR "." M5UNIFIED_VERSION_MINOR "." M5UNIFIED_VERSION_PATCH )
6 changes: 3 additions & 3 deletions src/utility/IMU_Class.inl
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ namespace m5
}
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
if (board == m5::board_t::board_M5CoreP4X)
{
_internal_axisorder_fixed[sensor_index_accel] = (internal_axisorder_t)(axis_invert_z);
_internal_axisorder_fixed[sensor_index_gyro ] = (internal_axisorder_t)(axis_invert_z);
{ // CoreP4X BMI270: X=-X, Y=+Y, Z=-Z.
_internal_axisorder_fixed[sensor_index_accel] = (internal_axisorder_t)(axis_invert_x | axis_invert_z);
_internal_axisorder_fixed[sensor_index_gyro ] = (internal_axisorder_t)(axis_invert_x | axis_invert_z);
}
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
if ((board == m5::board_t::board_M5StackCoreS3 || board == m5::board_t::board_M5StackChan)
Expand Down
3 changes: 3 additions & 0 deletions src/utility/Power_Class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,22 @@ namespace m5
/// @note PowerHub paces alarm writes by 50 ms, settles for 500 ms before requesting
/// power-off, then enters light sleep and restarts if it wakes.
/// @param seconds Number of seconds to boot.
/// @note On Tab5, returns without sleeping; use timerSleep(time).
void timerSleep(int seconds);

/// sleep and timer boot. The boot condition can be specified by the argument.
/// @param time Time to boot. (only minutes and hours can be specified. Ignore seconds)
/// @attention CoreInk and M5Paper can't alarm boot because it can't be turned off while connected to USB.
/// @attention CoreInk と M5Paper は USB接続中はRTCタイマー起動が出来ない。;
/// @note Returns without sleeping if no RTC is available or the RTC driver reports that the alarm could not be set.
void timerSleep(const rtc_time_t& time);

/// sleep and timer boot. The boot condition can be specified by the argument.
/// @param date Date to boot. (only date and weekDay can be specified. Ignore year and month)
/// @param time Time to boot. (only minutes and hours can be specified. Ignore seconds)
/// @attention CoreInk and M5Paper can't alarm boot because it can't be turned off while connected to USB.
/// @attention CoreInk と M5Paper は USB接続中はRTCタイマー起動が出来ない。;
/// @note Returns without sleeping if no RTC is available or the RTC driver reports that the alarm could not be set.
void timerSleep(const rtc_date_t& date, const rtc_time_t& time);

/// Value for micro_seconds of deepSleep / lightSleep, meaning "sleep without a timer wakeup".
Expand Down
25 changes: 23 additions & 2 deletions src/utility/Power_Class.inl
Original file line number Diff line number Diff line change
Expand Up @@ -2084,6 +2084,13 @@ namespace m5

void Power_Class::timerSleep( int seconds )
{
#if defined (CONFIG_IDF_TARGET_ESP32P4)
if (M5.getBoard() == board_t::board_M5Tab5)
{
M5_LOGE("timerSleep(seconds) is not supported on Tab5, use timerSleep(time). not sleeping.");
return;
}
#endif
M5.Rtc.disableIRQ();
M5.Rtc.clearIRQ();
M5.Rtc.setAlarmIRQ(seconds);
Expand All @@ -2097,15 +2104,29 @@ namespace m5
{
M5.Rtc.disableIRQ();
M5.Rtc.clearIRQ();
M5.Rtc.setAlarmIRQ(time);
// An alarm setup failure must not enter sleep without an RTC wake source.
if (M5.Rtc.setAlarmIRQ(time) <= 0)
{
M5_LOGE("timerSleep: RTC alarm could not be set. not sleeping.");
M5.Rtc.disableIRQ();
M5.Rtc.clearIRQ();
return;
}
_timerSleep();
}

void Power_Class::timerSleep( const rtc_date_t& date, const rtc_time_t& time)
{
M5.Rtc.disableIRQ();
M5.Rtc.clearIRQ();
M5.Rtc.setAlarmIRQ(date, time);
// An alarm setup failure must not enter sleep without an RTC wake source.
if (M5.Rtc.setAlarmIRQ(date, time) <= 0)
{
M5_LOGE("timerSleep: RTC alarm could not be set. not sleeping.");
M5.Rtc.disableIRQ();
M5.Rtc.clearIRQ();
return;
}
_timerSleep();
}

Expand Down
1 change: 1 addition & 0 deletions src/utility/RTC_Class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace m5
/// Set timer IRQ
/// @param timer_msec
/// @return the set number of msec. 0 == disabled, or the timer could not be set (communication failure).
/// @note The built-in RTC driver on Tab5 rejects positive periods with 0; 0 still requests a stop.
std::uint32_t setTimerIRQ(std::uint32_t timer_msec);

// deprecated
Expand Down
17 changes: 12 additions & 5 deletions src/utility/RTC_Class.inl
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,19 @@ namespace m5
switch (board)
{
#if defined (CONFIG_IDF_TARGET_ESP32P4)
case board_t::board_M5Tab5: // VBAT: supercapacitor through 1 kOhm
// Periodic timer IRQs are not supported for the built-in RTC on Tab5.
instance.reset(new RX8130_Class(RX8130_Class::DEFAULT_ADDRESS, 400000, i2c, true, false));
break;

case board_t::board_M5CoreP4X:
case board_t::board_M5Tab5:
case board_t::board_M5Tab5X:
instance.reset(new RX8130_Class(RX8130_Class::DEFAULT_ADDRESS, 400000, i2c));
break;
#endif

#if defined (CONFIG_IDF_TARGET_ESP32C5)
case board_t::board_M5ToughC5:
case board_t::board_M5ToughC5: // VBAT: CR2032 primary cell, never charged
instance.reset(new RX8130_Class(RX8130_Class::DEFAULT_ADDRESS, 400000, i2c));
break;
#endif
Expand All @@ -75,11 +79,14 @@ namespace m5
instance.reset(new RTC_PowerHub_Class(RTC_PowerHub_Class::DEFAULT_ADDRESS, 400000));
break;

case board_t::board_M5StampPLC: // VBAT: rechargeable backup cell through 1 kOhm
case board_t::board_M5StopWatch: // VBAT: 10 uF capacitor
case board_t::board_M5PaperMono: // VBAT: 1 uF capacitor
instance.reset(new RX8130_Class(RX8130_Class::DEFAULT_ADDRESS, 400000, i2c, true));
break;

case board_t::board_M5StickS3:
case board_t::board_M5StopWatch:
case board_t::board_M5StampPLC:
case board_t::board_M5PaperColor:
case board_t::board_M5PaperMono:
case board_t::board_M5ChainCaptain:
instance.reset(new RX8130_Class(RX8130_Class::DEFAULT_ADDRESS, 400000, i2c));
break;
Expand Down
65 changes: 50 additions & 15 deletions src/utility/Speaker_Class.inl
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,36 @@ namespace m5

int32_t* sound_buf32 = (int32_t*)alloca(dma_buf_len * sizeof(int32_t));

#if !defined (SDL_h_)
// Fill one write of sound_buf32 with a DAC level: the hold level (dac_offset), or a smoothstep ramp
// (x^2 (3 - 2x): flat at both ends like a half-cosine, in integer math) between dac_offset and 0.
// The fraction below one DAC step is carried per sample like the playback path does; without the
// carry a small bias is written as whole steps, an audible staircase of clicks on the 8-bit DAC.
enum dac_fill_t { dac_hold, dac_fall, dac_rise };
uint8_t* const carry = surplus; // a lambda cannot capture a member of the anonymous union
auto fill_dac = [&](size_t words, dac_fill_t mode)
{
const uint32_t steps = out_stereo ? dma_buf_len : dma_buf_len << 1;
auto level = [&](size_t t) -> int32_t
{
if (mode == dac_hold) { return dac_offset; }
uint32_t x = (uint32_t)((t << 15) / steps); // 0 .. <1 in Q15
uint32_t y = ((x * x >> 15) * (98304 - 2 * x)) >> 15; // x^2 (3 - 2x) in Q15
if (mode == dac_fall) { y = 32768 - y; }
return (int32_t)(((uint32_t)dac_offset * y) >> 15);
};
for (size_t idx = 0; idx < words; ++idx)
{ // mono packs two time steps per word; stereo packs L/R of the same step
size_t t = out_stereo ? idx : idx << 1;
int32_t v1 = level(t) + carry[0];
carry[0] = v1;
int32_t v2 = level(out_stereo ? t : t + 1) + carry[out_stereo];
carry[out_stereo] = v2;
sound_buf32[idx] = v1 << 16 | v2;
}
};
#endif


while (self->_task_running.load(std::memory_order_acquire))
{
Expand All @@ -583,9 +613,15 @@ namespace m5
{ // Keep the DMA fed at the hold level instead of sleeping. A timed wait can let the
// DMA reach the auto-cleared buffers, which output 0 (on the DAC, a drop to the zero
// level and a jump back to the bias). The blocking write paces this loop.
uint32_t hold = self->_cfg.use_dac ? (uint32_t)(dac_offset | dac_offset << 16) : 0u;
size_t words = dma_buf_len >> 1;
for (size_t i = 0; i < words; ++i) { sound_buf32[i] = hold; }
if (self->_cfg.use_dac)
{
fill_dac(words, dac_hold);
}
else
{
memset(sound_buf32, 0, words * sizeof(int32_t));
}
size_t write_bytes;
_spk_i2s_write(i2s_port, sound_buf32, words * sizeof(int32_t), &write_bytes, portMAX_DELAY);
flg_nodata = (0 == ulTaskNotifyTake( pdFALSE, 0 ));
Expand All @@ -597,12 +633,7 @@ namespace m5
if (self->_cfg.use_dac && dac_offset)
{ // Gradual transition of DAC output to 0;
flg_i2s_started = spk_i2s_mute;
size_t idx = 0;
do
{
auto tmp = (uint32_t)((1.0f + cosf(idx * M_PI / dma_buf_len)) * (dac_offset >> 1));
sound_buf32[idx] = tmp | tmp << 16;
} while (++idx < dma_buf_len);
fill_dac(dma_buf_len, dac_fall);
size_t write_bytes;
_spk_i2s_write(i2s_port, sound_buf32, dma_buf_len * sizeof(int32_t), &write_bytes, portMAX_DELAY);
if (self->_cfg.dac_zero_level == 0)
Expand All @@ -615,6 +646,13 @@ namespace m5
memset(sound_buf32, 0, dma_buf_len * sizeof(uint32_t));
// DAC使用時は長めに設定する
size_t retry = (self->_cfg.dma_buf_count << self->_cfg.use_dac) + 1;
if (self->_cfg.use_dac && dma_buf_len)
{ // Keep I2S running at the zero level for about a second before stopping it. Stopping and
// restarting I2S steps the DAC output (a pop at both ends of a sound), so short pauses
// between sounds (melodies, key clicks) stay free of it.
size_t per_sec = (spk_sample_rate_x256 / SAMPLERATE_MUL) / (out_stereo ? dma_buf_len : dma_buf_len << 1);
if (retry < per_sec) { retry = per_sec; }
}
while (!ulTaskNotifyTake( pdTRUE, 0 ) && --retry)
{
size_t write_bytes;
Expand Down Expand Up @@ -664,14 +702,11 @@ namespace m5
_spk_i2s_start(i2s_port);
}

if (self->_cfg.use_dac && self->_cfg.dac_zero_level != 0)
// not when end() woke the task from the zero level: raising the bias just before the pin
// is pulled low would add a pop
if (self->_cfg.use_dac && self->_cfg.dac_zero_level != 0 && self->_task_running.load(std::memory_order_acquire))
{
size_t idx = 0;
do
{
auto tmp = (uint32_t)((1.0f - cosf(idx * M_PI / dma_buf_len)) * (dac_offset >> 1));
sound_buf32[idx] = tmp | tmp << 16;
} while (++idx < dma_buf_len);
fill_dac(dma_buf_len, dac_rise);
size_t write_bytes;
_spk_i2s_write(i2s_port, sound_buf32, dma_buf_len * sizeof(int32_t), &write_bytes, portMAX_DELAY);
}
Expand Down
11 changes: 10 additions & 1 deletion src/utility/rtc/RX8130_Class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ namespace m5
public:
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x32;

RX8130_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C)
/// backup_charge: let the RX8130 charge VBAT from VDD (CHGEN). Only for a rechargeable
/// backup (supercapacitor, rechargeable cell); a primary cell such as a CR2032 must not be charged.
/// timer_irq: allow periodic timer IRQs; false still permits stop requests.
RX8130_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C, bool backup_charge = false, bool timer_irq = true)
: RTC_Base ( i2c_addr, freq, i2c )
, _backup_charge ( backup_charge )
, _timer_irq ( timer_irq )
{}

bool begin(I2C_Class* i2c = nullptr) override;
Expand All @@ -33,6 +38,10 @@ namespace m5
void disableIRQ(void) override;

bool getVoltLow(void) override;

private:
bool _backup_charge;
bool _timer_irq;
};
}

Expand Down
Loading
Loading