first commit
25
libraries/M5Unified/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
set(COMPONENT_ADD_INCLUDEDIRS
|
||||
src
|
||||
)
|
||||
file(GLOB SRCS
|
||||
src/*.cpp
|
||||
src/utility/*.cpp
|
||||
src/utility/imu/*.cpp
|
||||
)
|
||||
set(COMPONENT_SRCS ${SRCS})
|
||||
|
||||
if (IDF_VERSION_MAJOR GREATER_EQUAL 5)
|
||||
set(COMPONENT_REQUIRES M5GFX esp_adc driver)
|
||||
else()
|
||||
set(COMPONENT_REQUIRES M5GFX esp_adc_cal)
|
||||
endif()
|
||||
|
||||
### If you use arduino-esp32 components, please activate next comment line.
|
||||
# list(APPEND COMPONENT_REQUIRES arduino-esp32)
|
||||
|
||||
|
||||
message(STATUS "M5Unified use component = ${COMPONENT_REQUIRES}" )
|
||||
|
||||
|
||||
register_component()
|
||||
|
||||
21
libraries/M5Unified/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 M5Stack
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
417
libraries/M5Unified/README.md
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
# M5Unified
|
||||
### Arduino / ESP-IDF Library for M5Stack Series
|
||||
|
||||
## Usage
|
||||
This library provides access to the built-in hardware of M5Stack controllers. Library functionality is available through the `m5` namespace, and includes the following:
|
||||
|
||||
- LCD display access, fonts, and graphical primitives (in conjunction with [M5GFX](https://github.com/M5Stack/M5GFX))
|
||||
- Touchscreen and hardware button access
|
||||
- Speaker and microphone access
|
||||
- Power and battery charging control
|
||||
- Accelerometer, gyroscope, magnetometer access
|
||||
- Real-time clock and wakeup timer access
|
||||
|
||||
M5Unified requires M5GFX to be installed.
|
||||
|
||||
#### How To Use
|
||||
|
||||
`#include "M5Unified.h"`
|
||||
|
||||
#### Examples and Demonstrations
|
||||
|
||||
In the Arduino IDE, once the M5Unified library is installed through Library Manager, the following example programs are available under File > Examples > M5Unified > Basic.
|
||||
|
||||
- **Combined demonstration:** A demonstration exhibiting several functions in a single example is in [HowToUse](examples/Basic/HowToUse/HowToUse.ino)
|
||||
|
||||
##### Separate examples
|
||||
- **Displays:** A basic demonstration of printing text on each of the supported M5Stack displays is in [Displays](examples/Basic/Displays/Displays.ino).
|
||||
- **Buttons:** A demo of detecting and responding to button presses is in [Button](examples/Basic/Button/Button.ino).
|
||||
- **Touch:** A demo of detecting and reacting to touch events is in [Touch](examples/Basic/Touch/Touch.ino).
|
||||
- **Speaker:** An example of playing wave audio through built-in speakers and accessories that support audio is in [Speaker](examples/Basic/Speaker/Speaker.ino)
|
||||
- **Clock:** An example of connecting to an Internet time server over WiFi and setting the built-in clock is in [RTC](examples/Basic/Rtc/Rtc.ino).
|
||||
- **Microphone:** An example that records a short clip and plays it back through the speaker is in [Microphone](examples/Basic/Microphone/Microphone.ino).
|
||||
- **IMU:** An example demonstrating the accelerometer / gyro / magnetometer is in [IMU](examples/Basic/Imu/Imu.ino)
|
||||
- **Advanced** examples include playing audio over Bluetooth and streaming and decoding MP3 audio, and are in the [Advanced folder](examples/Advanced).
|
||||
|
||||
##### M5Stack hardware functionality supported by the ESP32 ecosystem
|
||||
The following functionality is built in to the ESP32 framework or available elsewhere, independent of any hardware included in the M5Stack products, and do not require
|
||||
or have explicit support in the M5Unified library. To use these functions, simply use existing ESP32 or Arduino supporting libraries. This list is provided for the benefit of newcomers who may be asking where the "M5Unified" support for a particular feature is found, before discovering that it's either already part of the basic ESP32 platform, or a device that is generic enough (e.g. I2C or SPI) to be fully supported with generic libraries found online, including in the Arduino library manager, that work with any platform.
|
||||
- Network access using WiFi (built into ESP32)
|
||||
- Micro SD card access (SD cards are generic SPI devices)
|
||||
- Infrared remote control transmitter
|
||||
- Color-changing LED (e.g. on Atom)
|
||||
|
||||
##### Desirable examples worthy of future contribution
|
||||
- **Power:** A demonstration of detecting the battery level and charging state, powering off, and entering deep sleep mode when power off is unavailable.
|
||||
- **Battery lifespan management:** An example/utility sketch that runs the battery down to an optimum storage percentage (~40%) before powering off as fully as possible, and as a separate function, charges it to a lifespan-preserving 75% maximum, on each M5Stack device where the power management hardware supports this.
|
||||
- **RTC wakeup:** A demonstration of using the realtime clock to set a wakeup timer to power-on, or bring the device out of sleep mode after a pre-determined number of seconds, on devices that support this.
|
||||
|
||||
## Supported frameworks
|
||||
- ESP-IDF
|
||||
- Arduino IDE
|
||||
|
||||
## Supported devices (ESP32)
|
||||
- M5Stack Core (BASIC / GRAY / GO / FIRE)
|
||||
- M5Stack Core2 / Core2 v1.1 / Tough
|
||||
- M5Stick C / CPlus / CPlus2
|
||||
- M5Stack CoreInk
|
||||
- M5Station
|
||||
- M5Paper
|
||||
- M5ATOM Lite / Matrix / ECHO / PSRAM / U
|
||||
- M5STAMP PICO
|
||||
|
||||
## Supported devices (ESP32-S3)
|
||||
- M5Stack CoreS3 / CoreS3SE
|
||||
- M5ATOMS3 / S3Lite / S3U / S3R
|
||||
- M5ATOMS3R / S3RExt / S3RCam
|
||||
- M5STAMPS3
|
||||
- M5Dial
|
||||
- M5DinMeter
|
||||
- M5Capsule
|
||||
- M5Cardputer
|
||||
- M5VAMeter
|
||||
- [M5PaperS3](https://github.com/m5stack/M5GFX/blob/master/docs/M5PaperS3.md)
|
||||
|
||||
## Supported devices (ESP32-C3)
|
||||
- M5STAMPC3 / C3U
|
||||
|
||||
## Supported devices (ESP32-C6)
|
||||
- M5NanoC6
|
||||
|
||||
## Supported external displays and video adapters
|
||||
- Unit LCD
|
||||
- Unit OLED
|
||||
- Unit Mini OLED
|
||||
- [Unit RCA](https://github.com/m5stack/M5GFX/blob/master/docs/UnitRCA.md) (without ESP32S3 series)
|
||||
- [Unit GLASS](https://github.com/m5stack/M5GFX/blob/master/docs/UnitGLASS.md)
|
||||
- Unit GLASS2
|
||||
- [ATOM Display](https://github.com/m5stack/M5GFX/blob/master/docs/ATOMDisplay.md) adapter (HDMI output for M5ATOM Lite / Matrix / PSRAM / S3 / S3Lite / S3R / S3RCam / S3RExt )
|
||||
- Module Display adapter (HDMI output for M5Stack Core / Core2 / Tough / CoreS3 / CoreS3SE )
|
||||
- Module RCA adapter (composite NTSC/PAL output for M5Stack Core / Core2 / Tough / (without ESP32S3 series))
|
||||
|
||||
## Supported external speakers and audio adapters
|
||||
- SPK HAT (with M5StickC / CPlus / M5Stack CoreInk)
|
||||
- SPK HAT2 (with M5StickCPlus)
|
||||
- ATOMIC SPK (with M5ATOM Lite / PSRAM / S3 / S3Lite / S3R / S3RCam / S3RExt )
|
||||
- ATOMIC ECHO BASE (with M5ATOM Lite / PSRAM / S3 / S3Lite / S3R / S3RCam / S3RExt )
|
||||
- Module Display adapter (Audio over HDMI for M5Stack Core / Core2 / Tough / CoreS3 / CoreS3SE )
|
||||
- Module RCA adapter (M5Stack Core / Core2 / Tough)
|
||||
|
||||
## Other supported external accessories
|
||||
- Unit RTC
|
||||
- Unit IMU
|
||||
|
||||
|
||||
# Hardware information
|
||||
|
||||
### ESP32 GPIO list
|
||||
| |M5Stack<BR>BASIC<BR>GRAY |M5Stack<BR>GO/FIRE |M5Stack<BR>Core2(AWS)<BR>Tough |M5Stick<BR>C/CPlus |M5Stick<BR>CPlus2 |M5Stack<BR>CoreInk |M5Paper |M5Station |M5ATOM<BR>Lite/Matrix<BR>ECHO/U<BR>PSRAM |M5STAMP<BR>PICO | |
|
||||
|:------------------:|:---------------------------------:|:---------------------------------:|:-------------------------------------:|:----------------------------:|:-------------------------:|:----------------------:|:---------------------:|:---------------------:|:---------------------------------------:|:------------------:|:------------------:|
|
||||
|GPIO 0<BR>`ADC2_CH1`|`M-Bus`<BR>IIS_MK |`M-Bus`<BR>IIS_MK |`M-Bus`<BR>**SPK_LRCK<BR>PDM_C**(Core2)|`HAT`<BR>`PAD`<BR>**PDM_C** |`HAT`<BR>**PDM_C** |**EPD_RST** | --- | --- | --- | |GPIO 0<BR>`ADC2_CH1`|
|
||||
|GPIO 1<BR>`USB_TX` |`M-Bus`<BR>**Serial** |`M-Bus`<BR>**Serial** |`M-Bus`<BR>**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |GPIO 1<BR>`USB_TX` |
|
||||
|GPIO 2<BR>`ADC2_CH2`|`M-Bus`<BR> |`M-Bus`<BR> |`M-Bus`<BR>**SPK_D** |`PAD`<BR>**Beep**(CPlus) |**Beep** |**Beep** |**PW_Hold** |**ReadEn** | --- | --- |GPIO 2<BR>`ADC2_CH2`|
|
||||
|GPIO 3<BR>`USB_RX` |`M-Bus`<BR>**Serial** |`M-Bus`<BR>**Serial** |`M-Bus`<BR>**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |**Serial** |GPIO 3<BR>`USB_RX` |
|
||||
|GPIO 4<BR>`ADC2_CH0`|**TF_CS** |**TF_CS** |**TF_CS** | --- |**PW_Hold** |**EPD_BUSY** |**TF_CS** |**RGB LED** | --- | --- |GPIO 4<BR>`ADC2_CH0`|
|
||||
|GPIO 5 |`M-Bus` |`M-Bus` |**LCD_CS** |**LCD_CS** |**LCD_CS** |**BTN_HAT** |**EXT_5V** |**LCD_CS** |`Bus`(P)<BR>**PDM_C**(U) | --- |GPIO 5 |
|
||||
|GPIO 9 | --- | --- | --- |**InfraRed** | --- |**EPD_CS** | --- | --- | --- | --- |GPIO 9 |
|
||||
|GPIO10 | --- | --- | --- |**LED** | --- |**LED** | --- | --- | --- | --- |GPIO10 |
|
||||
|GPIO12<BR>`ADC2_CH5`|`M-Bus`<BR>IIS_SK |`M-Bus`<BR>IIS_SK |**SPK_BCLK** | --- |**LCD_RST** |**PW_Hold** |**SPI_MOSI** |**USB_PW** |**InfraRed** | --- |GPIO12<BR>`ADC2_CH5`|
|
||||
|GPIO13<BR>`ADC2_CH4`|`M-Bus`<BR>IIS_WS |`M-Bus`<BR>IIS_WS |`M-Bus`<BR>RXD2 |**SPI_SCLK** |**SPI_SCLK** |`MI-Bus`<BR>RXD2 |**SPI_MISO** |`PORT.C1` | --- | --- |GPIO13<BR>`ADC2_CH4`|
|
||||
|GPIO14<BR>`ADC2_CH6`|**LCD_CS** |**LCD_CS** |`M-Bus`<BR>TXD2 | --- |**LCD_D/C** |`MI-Bus`<BR>TXD2 |**SPI_SCLK** |`PORT.C1` | --- | --- |GPIO14<BR>`ADC2_CH6`|
|
||||
|GPIO15<BR>`ADC2_CH3`|`M-Bus`<BR>IIS_OUT |`M-Bus`<BR>**RGB LED** |**LCD_D/C** |**SPI_MOSI** |**SPI_MOSI** |**EPD_D/C** |**EPD_CS** |**LCD_RST** | --- | --- |GPIO15<BR>`ADC2_CH3`|
|
||||
|GPIO16<BR>`PSRAM` |`M-Bus`<BR>RXD2 |`M-Bus`<BR>`PORT.C`<BR>RXD2 | --- | --- |--- | --- | --- |`PORT.C2`<BR>RXD2 | --- | --- |GPIO16<BR>`PSRAM` |
|
||||
|GPIO17<BR>`PSRAM` |`M-Bus`<BR>TXD2 |`M-Bus`<BR>`PORT.C`<BR>TXD2 | --- | --- |--- | --- | --- |`PORT.C2`<BR>TXD2 | --- | --- |GPIO17<BR>`PSRAM` |
|
||||
|GPIO18 |`M-Bus`<BR>**SPI_SCLK** |`M-Bus`<BR>**SPI_SCLK** |**SPI_SCLK** |**LCD_RST** |--- |`MI-Bus`<BR>**SPI_SCLK**|`PORT.C` |**SPI_SCLK** | --- | |GPIO18 |
|
||||
|GPIO19 |`M-Bus`<BR>**SPI_MISO** |`M-Bus`<BR>**SPI_MISO** |`M-Bus` | --- |**LED**<BR>**InfraRed** |**RTC_INT** |`PORT.C` |LCD_D/C |`Bus`<BR>**SPK_C**(ECHO)<BR>***PDM_D**(U)| |GPIO19 |
|
||||
|GPIO21 |`M-Bus`<BR>`PORT.A`<BR>**I2C0_SDA**|`M-Bus`<BR>`PORT.A`<BR>**I2C0_SDA**|**I2C1_SDA** |**I2C1_SDA** |**I2C1_SDA** |`MI-Bus`<BR>**I2C1_SDA**|**I2C1_SDA** |**I2C1_SDA** |`Bus`<BR>**I2C1_SCL** | |GPIO21 |
|
||||
|GPIO22 |`M-Bus`<BR>`PORT.A`<BR>**I2C0_SCL**|`M-Bus`<BR>`PORT.A`<BR>**I2C0_SCL**|**I2C1_SCL** |**I2C1_SCL** |**I2C1_SCL** |`MI-Bus`<BR>**I2C1_SCL**|**I2C1_SCL** |**I2C1_SCL** |`Bus`<BR>**SPK_D**(ECHO) | |GPIO22 |
|
||||
|GPIO23 |`M-Bus`<BR>**SPI_MOSI** |`M-Bus`<BR>**SPI_MOSI** |**SPI_MOSI** |**LCD_D/C** | --- |`MI-Bus`<BR>**SPI_MOSI**|**EPD_RST** |**SPI_MOSI** |`Bus`<BR>**PDM_D**(ECHO) | --- |GPIO23 |
|
||||
|GPIO25<BR>`DAC1` |`M-Bus`<BR>**SPK_DAC** |`M-Bus`<BR>**SPK_DAC** |`M-Bus`<BR>**RGB LED**(AWS) |`HAT`(CPlus)<BR>`PAD` |`HAT` |`MI-Bus`<BR>`HAT` |`PORT.A`<BR>I2C0_SDA |`PORT.B1` |`Bus`<BR>**I2C1_SDA** | |GPIO25<BR>`DAC1` |
|
||||
|GPIO26<BR>`DAC2` |`M-Bus` |`M-Bus`<BR>`PORT.B` |`M-Bus` |`HAT`<BR>`PAD` |`HAT` |`MI-Bus`<BR>`HAT` |`PORT.B` |`PORT.B2` |`PORT.A`<BR>**I2C0_SDA** | |GPIO26<BR>`DAC2` |
|
||||
|GPIO27<BR>`ADC2_CH7`|**LCD_D/C** |**LCD_D/C** |`M-Bus` |**AXP192 VBUSEN** |**LCD_BL** |**BTN_PWR** |**EPD_BUSY** |**IMU_INT** |**RGB LED** |**RGB LED** |GPIO27<BR>`ADC2_CH7`|
|
||||
|GPIO32<BR>`ADC1_CH4`|**LCD_BL** |**LCD_BL** |`M-Bus`<BR>`PORT.A`<BR>I2C0_SDA |`PORT.A`<BR>I2C0_SDA |`PORT.A`<BR>I2C0_SDA |`PORT.A`<BR>I2C0_SDA |`PORT.A`<BR>I2C0_SCL |`PORT.A`<BR>I2C0_SDA |`PORT.A`<BR>**I2C0_SCL** |`PORT.A`<BR>I2C0_SDA|GPIO32<BR>`ADC1_CH4`|
|
||||
|GPIO33<BR>`ADC1_CH5`|**LCD_RST** |**LCD_RST** |`M-Bus`<BR>`PORT.A`<BR>I2C0_SCL |`PORT.A`<BR>I2C0_SCL |`PORT.A`<BR>I2C0_SCL |`PORT.A`<BR>I2C0_SCL |`PORT.B` |`PORT.A`<BR>I2C0_SCL |`Bus`<BR>**PDM_C**(ECHO) |`PORT.A`<BR>I2C0_SCL|GPIO33<BR>`ADC1_CH5`|
|
||||
|GPIO34<BR>`ADC1_CH6`|`M-Bus`<BR>IIS_IN |`M-Bus`<BR>**MIC_ADC**<BR>IIS_IN |`M-Bus`<BR>**PDM_D**(Core2) |**PDM_D** |**PDM_D** |`MI-Bus`<BR>**SPI_MISO**| --- | USB Current? | | --- |GPIO34<BR>`ADC1_CH6`|
|
||||
|GPIO35<BR>`ADC1_CH7`|`M-Bus` |`M-Bus` |`M-Bus` |**RTC_INT** |**BTN_PWR** |**BAT_V** |**BAT_V** |`PORT.B1` | --- | --- |GPIO35<BR>`ADC1_CH7`|
|
||||
|GPIO36<BR>`ADC1_CH0`|`M-Bus` |`M-Bus`<BR>`PORT.B` |`M-Bus` |`HAT`<BR>`PAD` |`HAT` |`MI-Bus`<BR>`HAT` |**TP_INT** |`PORT.B2` | --- | --- |GPIO36<BR>`ADC1_CH0`|
|
||||
|GPIO37<BR>`ADC1_CH1`|**BTN_C** |**BTN_C** | --- |**BTN_A** |**BTN_A** |**SW_Up** |**SW_Up** |**BTN_A** | --- | --- |GPIO37<BR>`ADC1_CH1`|
|
||||
|GPIO38<BR>`ADC1_CH2`|**BTN_B** |**BTN_B** |`M-Bus`<BR>**SPI_MISO** |`PAD` |**BAT_V** |**SW_Press** |**SW_Press** |**BTN_B** | --- | --- |GPIO38<BR>`ADC1_CH2`|
|
||||
|GPIO39<BR>`ADC1_CH3`|**BTN_A** |**BTN_A** |**TP_INT** |**BTN_B** |**BTN_B** |**SW_Down** |**SW_Down** |**BTN_C** |**BTN** |**BTN** |GPIO39<BR>`ADC1_CH3`|
|
||||
| |M5Stack<BR>BASIC<BR>GRAY |M5Stack<BR>GO/FIRE |M5Stack<BR>Core2(AWS)<BR>Tough |M5Stick<BR>C/CPlus |M5Stick<BR>CPlus2 |M5Stack<BR>CoreInk |M5Paper |M5Station |M5ATOM<BR>Lite/Matrix<BR>ECHO/U<BR>PSRAM |M5STAMP<BR>PICO | |
|
||||
|
||||
|
||||
### ESP32C3 GPIO list
|
||||
| |M5Stamp<BR>C3 |M5Stamp<BR>C3U | |
|
||||
|:-------------:|:-----------------------:|:------------------------------:|:-------------:|
|
||||
|GPIO 0 |`PORT.A`<BR>**I2C0_SCL** |`PORT.A`<BR>**I2C_SCL** |GPIO 0 |
|
||||
|GPIO 1 |`PORT.A`<BR>**I2C0_SDA** |`PORT.A`<BR>**I2C_SDA** |GPIO 1 |
|
||||
|GPIO 2 |**RGB LED** |**RGB LED** |GPIO 2 |
|
||||
|GPIO 3 |**BTN_A** |`Bus` |GPIO 3 |
|
||||
|GPIO 4 |`Bus` |`Bus` |GPIO 4 |
|
||||
|GPIO 5 |`Bus` |`Bus` |GPIO 5 |
|
||||
|GPIO 6 |`Bus` |`Bus` |GPIO 6 |
|
||||
|GPIO 7 |`Bus` |`Bus` |GPIO 7 |
|
||||
|GPIO 8 |`Bus` |`Bus` |GPIO 8 |
|
||||
|GPIO 9 | --- |**BTN_A** |GPIO 9 |
|
||||
|GPIO10 |`Bus` |`Bus` |GPIO10 |
|
||||
|GPIO18<BR>`USB`|`PORT.U`<BR>**D-** |`USB`<BR>`PORT.U`<BR>**D-** |GPIO18<BR>`USB`|
|
||||
|GPIO19<BR>`USB`|`PORT.U`<BR>**D+** |`USB`<BR>`PORT.U`<BR>**D+** |GPIO19<BR>`USB`|
|
||||
|GPIO20 |`USB`<BR>**Serial** |`Bus`<BR> |GPIO20 |
|
||||
|GPIO21 |`USB`<BR>**Serial** |`Bus`<BR> |GPIO21 |
|
||||
| |M5Stamp<BR>C3 |M5Stamp<BR>C3U | |
|
||||
|
||||
|
||||
### ESP32S3 GPIO list
|
||||
| |M5Stack<BR>CoreS3<BR>CoreS3SE |M5ATOMS3R <BR>/ S3RCam |M5ATOMS3 <BR>/ S3Lite |M5ATOMS3U | M5STAMPS3 | M5Dial | M5Capsule | M5Cardputer | |
|
||||
|:-------------:|:-------------------------------:|:-----------------------:|:-----------------------:|:-----------------------:|:---------------------------------:|:------------------------:|:------------------------:|:------------------------:|:-------------:|
|
||||
|GPIO 0 |`M-Bus`<BR>**SPK_LRCK** |**I2Cx_SCL**<br>forIMU | --- | --- | `Bus`<BR>**BTN_A** | --- | --- | **BTN_A** |GPIO 0 |
|
||||
|GPIO 1 |`PORT.A`<BR>**I2C0_SCL** |`PORT.A`<BR>**I2C0_SCL** |`PORT.A`<BR>**I2C0_SCL** |`PORT.A`<BR>**I2C0_SCL** | `Bus` | `PORT.B` | --- | `PORT.A`<BR>**I2C0_SCL** |GPIO 1 |
|
||||
|GPIO 2 |`PORT.A`<BR>**I2C0_SDA** |`PORT.A`<BR>**I2C0_SDA** |`PORT.A`<BR>**I2C0_SDA** |`PORT.A`<BR>**I2C0_SDA** | `Bus` | `PORT.B` | **Beep** | `PORT.A`<BR>**I2C0_SDA** |GPIO 2 |
|
||||
|GPIO 3 |**LCD_CS** |**CAM_Y2** |vdd3v3 |vdd3v3 | `Bus` | **Beep** | --- | **KEY_MATRIX** |GPIO 3 |
|
||||
|GPIO 4 |**TF_CS** |**CAM_Y6** |**InfraRed** | --- | `Bus` | **LCD_RS** | **InfraRed** | **KEY_MATRIX** |GPIO 4 |
|
||||
|GPIO 5 |`M-Bus` |`Bus` |`Bus` | --- | `Bus` | **LCD_MOSI** | --- | **KEY_MATRIX** |GPIO 5 |
|
||||
|GPIO 6 |`M-Bus` |`Bus` |`Bus` | --- | `Bus` | **LCD_SCK** | **BAT_ADC** | **KEY_MATRIX** |GPIO 6 |
|
||||
|GPIO 7 |`M-Bus` |`Bus` |`Bus` | --- | `Bus` | **LCD_CS** | --- | **KEY_MATRIX** |GPIO 7 |
|
||||
|GPIO 8 |`M-Bus`<BR>`PORT.B` |`Bus` |`Bus` | --- | `Bus` | **LCD_RST** | **I2C1_SDA** | **KEY_MATRIX** |GPIO 8 |
|
||||
|GPIO 9 |`M-Bus`<BR>`PORT.B` |**I2Cy_SCL**<br>forCam | --- | --- | `Bus` | **LCD_BL** | --- | **KEY_MATRIX** |GPIO 9 |
|
||||
|GPIO10 |`M-Bus` |**CAM_VSYNC** | --- | --- | `Bus` | **RFID_INT** | **I2C1_SCL** | **BAT_ADC** |GPIO10 |
|
||||
|GPIO11 |**I2C1_SCL** |**CAM_Y8** | --- | --- | `Bus` | **I2C1_SDA** | **TF_CS** | **KEY_MATRIX** |GPIO11 |
|
||||
|GPIO12 |**I2C1_SDA** |**I2Cy_SDA**<br>forCam | --- |**InfraRed** | `Bus` | **I2C1_SCL** | **TF_MOSI** | **TF_CS** |GPIO12 |
|
||||
|GPIO13 |`M-Bus`<BR>**SPK_D** |**CAM_Y9** | --- | --- | `Bus`<BR>`PORT.A`<BR>**I2C0_SDA** | `PORT.A`<BR>**I2C0_SDA** | `PORT.A`<BR>**I2C0_SDA** | **KEY_MATRIX** |GPIO13 |
|
||||
|GPIO14 |`M-Bus`<BR>**MIC_IN** |**LCD_CS<BR>CAM_HREF** | --- |`Bus` | `Bus` | **TP_INT** | **TF_CLK** | **TF_MOSI** |GPIO14 |
|
||||
|GPIO15 |**CAM_D6** |**LCD_SCLK** |**LCD_CS** | --- | `Bus`<BR>`PORT.A`<BR>**I2C0_SCL** | `PORT.A`<BR>**I2C0_SCL** | `PORT.A`<BR>**I2C0_SCL** | **KEY_MATRIX** |GPIO15 |
|
||||
|GPIO16 |**CAM_D7** |**IMU_INT** |**LCD_BL** | --- | `FPC` | --- | --- | --- |GPIO16 |
|
||||
|GPIO17 |`M-Bus`<BR>`PORT.C` |**CAM_Y7** |**LCD_SCLK** |`Bus` | `FPC` | --- | --- | --- |GPIO17 |
|
||||
|GPIO18 |`M-Bus`<BR>`PORT.C` |**POWER_N** | --- | --- | `FPC` | --- | --- | --- |GPIO18 |
|
||||
|GPIO19<BR>`USB`|`USB`<BR>**D--** |`USB`<BR>**D--** |`USB`<BR>**D--** |`USB`<BR>**D--** | `USB`<BR>**D--** | `USB`<BR>**D--** | `USB`<BR>**D--** | `USB`<BR>**D--** |GPIO19<BR>`USB`|
|
||||
|GPIO20<BR>`USB`|`USB`<BR>**D++** |`USB`<BR>**D++** |`USB`<BR>**D++** |`USB`<BR>**D++** | `USB`<BR>**D++** | `USB`<BR>**D++** | `USB`<BR>**D++** | `USB`<BR>**D++** |GPIO20<BR>`USB`|
|
||||
|GPIO21 |**I2C_INT** |**LCD_MOSI<BR>CAM_XCLK** |**LCD_MOSI** | --- | **RGB LED** | **RGB LED** | **RGB LED** | **RGB_LED** |GPIO21 |
|
||||
|GPIO33 |**SPK_WCK** |OPI PSRAM |**LCD_DC** | --- | `FPC` | --- | --- | **LCD_RST** |GPIO33 |
|
||||
|GPIO34 |**SPK_BCK** |OPI PSRAM |**LCD_RST** | --- | `FPC` | --- | --- | **LCD_RS** |GPIO34 |
|
||||
|GPIO35 |`M-Bus`<BR>**SPI_MISO<BR>LCD DC**|OPI PSRAM |**RGB LED** |**RGB LED** | `FPC` | --- | --- | **LCD_DAT** |GPIO35 |
|
||||
|GPIO36 |`M-Bus`<BR>**SPI_SCLK** |OPI PSRAM | --- | --- | `FPC` | --- | --- | **LCD_SCK** |GPIO36 |
|
||||
|GPIO37 |`M-Bus`<BR>**SPI_MOSI** |OPI PSRAM | --- | --- | `FPC` | --- | --- | **LCD_CS** |GPIO37 |
|
||||
|GPIO38 |**CAM_HREF** |`Bus` |`Bus`<BR>**I2C1_SDA** |**PDM_DAT** | `FPC` | --- | --- | **LCD_BL** |GPIO38 |
|
||||
|GPIO39 |**CAM_D2** |`Bus` |`Bus`<BR>**I2C1_SCL** |**PDM_CLK** | `Bus` | --- | **TF_MISO** | **TF_MISO** |GPIO39 |
|
||||
|GPIO40 |**CAM_D3** |**CAM_PCLK** | --- |`Bus` | `Bus` | **ENCODER_B** | **MIC_CLK** | **TF_CLK** |GPIO40 |
|
||||
|GPIO41 |**CAM_D4** |**BTN_A** |**BTN_A** |**BTN_A** | `Bus` | **ENCODER_A** | **MIC_DAT** | **SPK_BCLK** |GPIO41 |
|
||||
|GPIO42 |**CAM_D5** |**LCD_D/D<BR>CAM_Y3** | --- |`Bus` | `Bus` | **BTN_A** | **BTN_A** | **SPK_SDATA** |GPIO42 |
|
||||
|GPIO43 |`M-Bus`<BR>**SerialTX** | --- | --- | --- | `Bus` | --- | --- | **I2S_LRCLK** |GPIO43 |
|
||||
|GPIO44 |`M-Bus`<BR>**SerialRX** | --- | --- | --- | `Bus` | --- | --- | **InfraRed** |GPIO44 |
|
||||
|GPIO45 |**CAM_PCLK** |**I2Cx_SDA**<br>forIMU | --- | --- | --- | --- | --- | --- |GPIO45 |
|
||||
|GPIO46 |**CAM_VSYNC** |**CAM_Y4** | --- | --- | `Bus` | **HOLD** | **HOLD** | **MIC_DAT** |GPIO46 |
|
||||
|GPIO47 |**CAM_D9** |**InfraRed** | --- | --- | --- | --- | --- | --- |GPIO47 |
|
||||
|GPIO48 |**CAM_D8** |**LCD_RST<BR>CAM_Y5** | --- | --- | --- | --- | --- | --- |GPIO48 |
|
||||
| |M5Stack<BR>CoreS3 |M5ATOMS3R <BR>/ S3RCam |M5ATOMS3 <BR>/ S3Lite |M5ATOMS3U | M5STAMPS3 | M5Dial | M5Capsule | M5Cardputer | |
|
||||
|
||||
### AXP192 power management IC connections
|
||||
| |M5Stack<BR>Core2 |M5Stack<BR>Tough |M5Stick<BR>C |M5Stick<BR>CPlus| M5Station | |
|
||||
|:------------:|:-----------------:|:-----------------:|:--------------:|:--------------:|:-----------:|:------------:|
|
||||
|GPIO0<br>LDO0 |BUS PW EN |BUS PW EN |MIC VCC |MIC VCC |PortA1.A2 EN |GPIO0<br>LDO0 |
|
||||
| GPIO1 |SYS LED |TP RST | --- | --- |PortB1 EN | GPIO1 |
|
||||
| GPIO2 |SPK EN |SPK EN | --- | --- |PortB2 EN | GPIO2 |
|
||||
| GPIO3 | --- | --- | --- | --- |PortC1 EN | GPIO3 |
|
||||
| GPIO4 |LCD RST<BR>TP RST |LCD RST | --- | --- |PortC2 EN | GPIO4 |
|
||||
| EXTEN |PORT 5V EN |PORT 5V EN |PORT 5V EN |PORT 5V EN |PORT 5V EN | EXTEN |
|
||||
| BACKUP |RTC BAT |RTC BAT |RTC BAT |RTC BAT | --- | BACKUP |
|
||||
| LDO1 |RTC VDD |RTC VDD |RTC VDD |RTC VDD |RTC VDD | LDO1 |
|
||||
| LDO2 |LCD PW<BR>Periph PW|LCD PW<BR>Periph PW|LCD BL |LCD BL | --- | LDO2 |
|
||||
| LDO3 |VIB MOTOR |LCD BL |LCD PW |LCD PW |LCD BL | LDO3 |
|
||||
| DCDC1 |ESP32 VDD |ESP32 VDD |ESP32 VDD |ESP32 VDD |ESP32 VDD | DCDC1 |
|
||||
| DCDC2 | --- | --- | --- | --- | --- | DCDC2 |
|
||||
| DCDC3 |LCD BL | --- | --- | --- | --- | DCDC3 |
|
||||
|
||||
### AXP2101 usage
|
||||
| |M5Stack<BR>Core2v1.1 |M5Stack<BR>CoreS3<BR>CoreS3SE| |
|
||||
|:---------:|:--------------------:|:-----------------:|:---------:|
|
||||
| ALDO1 | --- |VDD 1v8 | ALDO1 |
|
||||
| ALDO2 |LCD RST |VDDA 3v3 | ALDO2 |
|
||||
| ALDO3 |SPK EN |CAM 3v3 | ALDO3 |
|
||||
| ALDO4 |Periph PW<BR>TF,TP,LCD|TF 3v3 | ALDO4 |
|
||||
| BLDO1 |LCD BL |AVDD | BLDO1 |
|
||||
| BLDO2 |PORT 5V EN |DVDD | BLDO2 |
|
||||
| DLDO1/DC1 |VIB MOTOR |LCD BL | DLDO1/DC1 |
|
||||
| DLDO2/DC2 | --- | --- | DLDO2/DC2 |
|
||||
| BACKUP |RTC BAT |RTC BAT | BACKUP |
|
||||
|
||||
|
||||
### PinMap
|
||||
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TH></TH>
|
||||
<TH width="33%">M5Stack<BR>BASIC/GRAY<BR>GO/FIRE<BR>FACES II</TH>
|
||||
<TH width="33%">M5Stack<BR>Core2<BR>Core2AWS<BR>TOUGH</TH>
|
||||
<TH width="33%">M5Stack<BR>CoreS3<BR>CoreS3SE</TH>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD rowspan="2">Bus</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core_bus.svg" ><BR>M-Bus</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core2_bus.svg"><BR>M-Bus</TD>
|
||||
<TD><IMG src="docs/img/pin_def_cores3_bus.svg"><BR>M-Bus</TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD colspan="3">
|
||||
※ HPWR=not connected to the ESP32.
|
||||
Used by modules capable of supplying 12V power.
|
||||
</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TH></TH>
|
||||
<TH width="16%">M5Stack<BR>BASIC/GRAY<BR>GO/FIRE<BR>FACES II</TH>
|
||||
<TH width="16%">M5Stack<BR>Core2<BR>Core2AWS<BR>TOUGH</TH>
|
||||
<TH width="16%">M5Stack<BR>CoreS3</TH>
|
||||
<TH width="16%"> M5Paper </TH>
|
||||
<TH width="32%" colspan="2"> M5Station </TH>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortA</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core_porta.svg" title="G,V,21,22"><BR>PortA</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core2_porta.svg" title="G,V,32,33"><BR>PortA</TD>
|
||||
<TD><IMG src="docs/img/pin_def_cores3_porta.svg" title="G,V,2,1"><BR>PortA</TD>
|
||||
<TD><IMG src="docs/img/pin_def_paper_porta.svg" title="G,V,25,32"><BR>PortA</TD>
|
||||
<TD colspan="2"><IMG src="docs/img/pin_def_station_porta.svg" title="G,V,32,33"><BR>PortA</TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortB</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core_portb.svg" title="G,V,26,36"><BR>PortB</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core2_portb.svg" title="G,V,26,36"><BR>PortB</TD>
|
||||
<TD><IMG src="docs/img/pin_def_cores3_portb.svg" title="G,V,9,8"><BR>PortB</TD>
|
||||
<TD><IMG src="docs/img/pin_def_paper_portb.svg" title="G,V,26,33"><BR>PortB</TD>
|
||||
<TD><IMG src="docs/img/pin_def_station_portb1.svg" title="G,V,25,35"><BR>PortB1</TD>
|
||||
<TD><IMG src="docs/img/pin_def_station_portb2.svg" title="G,V,26,36"><BR>PortB2</TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortC</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core_portc.svg" title="G,V,17,16"><BR>PortC</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core2_portc.svg" title="G,V,14,13"><BR>PortC</TD>
|
||||
<TD><IMG src="docs/img/pin_def_cores3_portc.svg" title="G,V,17,18"><BR>PortC</TD>
|
||||
<TD><IMG src="docs/img/pin_def_paper_portc.svg" title="G,V,18,19"><BR>PortC</TD>
|
||||
<TD><IMG src="docs/img/pin_def_station_portc1.svg" title="G,V,14,13"><BR>PortC1</TD>
|
||||
<TD><IMG src="docs/img/pin_def_station_portc2.svg" title="G,V,17,16"><BR>PortC2</TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortD</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core_portd.svg" title="G,V,35,34"><BR>PortD</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core2_portd.svg" title="G,V,35,34"><BR>PortD</TD>
|
||||
<TD></TD>
|
||||
<TD></TD>
|
||||
<TD colspan="2"></TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortE</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core_porte.svg" title="G,V,13,5" ><BR>PortE</TD>
|
||||
<TD><IMG src="docs/img/pin_def_core2_porte.svg" title="G,V,19,27"><BR>PortE / 485<BR>TOUGH485:12V</TD>
|
||||
<TD></TD>
|
||||
<TD></TD>
|
||||
<TD colspan="2"></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TH></TH>
|
||||
<TH width="11%">M5Stick<BR>C</TH>
|
||||
<TH width="11%">M5Stick<BR>C Plus</TH>
|
||||
<TH width="18%">M5Stack<BR>CoreInk</TH>
|
||||
<TH width="18%">M5Stamp<BR>PICO</TH>
|
||||
<TH width="18%">M5Stamp<BR>C3</TH>
|
||||
<TH width="18%">M5Stamp<BR>C3U</TH>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortA</TD>
|
||||
<TD colspan="4"><IMG src="docs/img/pin_def_stickc_porta.svg" title="G,V,32,33"></TD>
|
||||
<TD></TD>
|
||||
<TD></TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>HAT</TD>
|
||||
<TD><IMG src="docs/img/pin_def_stickc_hat.svg" title="G,5Vout,26,36,0,BAT,3V,5Vin"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_stickcplus_hat.svg" title="G,5Vout,26,25/36,0,BAT,3V,5Vin"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_coreink_hat.svg" title="G,5Vout,26,36,25,BAT,3V,5Vin"></TD>
|
||||
<TD></TD>
|
||||
<TD></TD>
|
||||
<TD></TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>Bus</TD>
|
||||
<TD></TD>
|
||||
<TD></TD>
|
||||
<TD><IMG src="docs/img/pin_def_coreink_bus.svg"><BR>MI-Bus</TD>
|
||||
<TD><IMG src="docs/img/pin_def_stamppico_bus.svg"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_stampc3_bus.svg"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_stampc3u_bus.svg"></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TH></TH>
|
||||
<TH width="20%">ATOM<BR>Lite</TH>
|
||||
<TH width="20%">ATOM<BR>Matrix</TH>
|
||||
<TH width="20%">ATOM<BR>ECHO</TH>
|
||||
<TH width="20%">ATOM<BR>PSRAM</TH>
|
||||
<TH width="16%">ATOM<BR>U</TH>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortA</TD>
|
||||
<TD colspan="5"><IMG src="docs/img/pin_def_atom_porta.svg" title="G,V,26,32"></TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>Bus</TD>
|
||||
<TD><IMG src="docs/img/pin_def_atom_lite.svg"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_atom_matrix.svg"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_atom_echo.svg"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_atom_psram.svg"></TD>
|
||||
<TD><IMG src="docs/img/pin_def_atom_u.svg"></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TH></TH>
|
||||
<TH>ATOMS3<BR>/S3Lite</TH>
|
||||
<TH>NanoC6/Cardputer</TH>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>PortA</TD>
|
||||
<TD colspan="2"><IMG src="docs/img/pin_def_atom_s3_porta.svg" title="G,V,2,1"></TD>
|
||||
</TR>
|
||||
<TR align="center">
|
||||
<TD>Bus</TD>
|
||||
<TD><IMG src="docs/img/pin_def_atom_s3.svg"></TD>
|
||||
<TD></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
|
||||
|
||||
### SPI device
|
||||
|
||||
| |M5Stack<BR>BASIC<BR>GRAY<BR>GO/FIRE|M5Stack<BR>Core2<BR>Tough |M5Stick<BR>C |M5Stick<BR>CPlus |M5Stack<BR>CoreInk |M5Paper | |
|
||||
|:--------------:|:---------------------------------:|:-----------------------------:|:---------------------------:|:-----------------------------:|:--------------------------------:|:----------------------------:|:--------------:|
|
||||
| Display |`ILI9342C`<BR>320×240<BR>CS:G14 |`ILI9342C`<BR>320×240<BR>CS:G5|`ST7735S`<BR>80×160<BR>CS:G5|`ST7789V2`<BR>135×240<BR>CS:G5|`GDEW0154M09`<BR>200×200<BR>CS:G9|`IT8951`<BR>960×540<BR>CS:G15| Display |
|
||||
| TF Card |CS:4 |CS:4 | --- | --- | --- |CS:4 | TF Card |
|
||||
|
||||
|
||||
### I2C device
|
||||
|
||||
| |M5Stack<BR>BASIC/GRAY<BR>GO/FIRE |M5Stack<BR>Core2 |M5Stack<BR>Tough |M5Stack<BR>CoreS3 |M5Stick<BR>C<BR>CPlus|M5Stack<BR>CoreInk |M5Paper |ATOM<BR>Matrix |M5Station | |
|
||||
|:---------------------------:|:---------------------------------:|:--------------------:|:------------------:|:-----------------:|:-------------------:|:-----------------:|:-------------------:|:--------------:|:---------------------------:|:---------------------------:|
|
||||
|Touch<BR>Panel | --- |`FT6336U`<BR>38h |`CHSC6540`<BR>2Eh |`FT5xxx`<BR>38h | --- | --- |`GT911`<BR>14h or 5Dh| --- | --- |Touch<BR>Panel |
|
||||
|RTC | --- |`BM8563`<BR>51h |`BM8563`<BR>51h |`BM8563`<BR>51h |`BM8563`<BR>51h |`BM8563`<BR>51h |`BM8563`<BR>51h | --- |`BM8563`<BR>51h |RTC |
|
||||
|Power<BR>Manage |`IP5306`<BR>75h |`AXP192`<BR>34h |`AXP192`<BR>34h |`AXP2101`<BR>34h |`AXP192`<BR>34h | --- | --- | --- |`AXP192`<BR>34h |Power<BR>Manage |
|
||||
|IMU |`MPU6886`<BR>68h |`MPU6886`<BR>68h (Ext)| --- |`BMI270`<BR>69h |`MPU6886`<BR>68h | --- | --- |`MPU6886`<BR>68h|`MPU6886`<BR>68h (opt) |IMU |
|
||||
|IMU<BR>(old lot) |`SH200Q`<BR>6Ch | --- | --- | --- |`SH200Q`<BR>6Ch | --- | --- | --- | --- |IMU<BR>(old lot) |
|
||||
|ENV | --- | --- | --- |`LTR553ALS`<BR>23h | --- | --- |`SHT30`<BR>44h | --- | --- |ENV |
|
||||
|EEPROM | --- | --- | --- | --- | --- | --- |`FM24C02`<BR>50h | --- | --- |EEPROM |
|
||||
|Camera | --- | --- | --- |`GC0308`<BR>21h | --- | --- | --- | --- | --- |Camera |
|
||||
|Speaker | --- | --- | --- |`AW88298`<BR>36h | --- | --- | --- | --- | --- |Speaker |
|
||||
|Microphone | --- | --- | --- |`ES7210`<BR>40h | --- | --- | --- | --- | --- |Microphone |
|
||||
|GPIO Expander | --- | --- | --- |`AW9523B`<BR>58h | --- | --- | --- | --- | --- |GPIO Expander |
|
||||
|Current<BR>Voltage<BR>Monitor| --- | --- | --- | --- | --- | --- | --- | --- |`INA3221`<BR>40h/41h<BR>(opt)|Current<BR>Voltage<BR>Monitor|
|
||||
|
||||
|
||||
7
libraries/M5Unified/component.mk
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
COMPONENT_ADD_INCLUDEDIRS := src
|
||||
2
libraries/M5Unified/docs/img/pin_def_atom_echo.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="120" version="1.1" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="#c06000" height="24" stroke="lightgray" width="24" x="0" y="0" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="12.0">3V3</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="36.0">G22</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="24" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="36.0">I2SOUT</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="36.0">G21</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G19</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">I2S CLK</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="60.0">G25</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G23</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">G34/PDMin</text><rect fill="#e00000" height="24" stroke="lightgray" width="24" x="104" y="72" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="84.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="108.0">G33</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="96" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="108.0">LRCK</text><rect fill="black" height="24" stroke="lightgray" width="24" x="104" y="96" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="108.0">GND</text></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_lite.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="120" version="1.1" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="#c06000" height="24" stroke="lightgray" width="24" x="0" y="0" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="12.0">3V3</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="36.0">G22</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="36.0">G21</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G19</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="60.0">G25</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G23</text><rect fill="#005000" height="24" stroke="red" width="40" x="24" y="72" /><text fill="#FFFFFF" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">G34</text><rect fill="#e00000" height="24" stroke="lightgray" width="24" x="104" y="72" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="84.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="108.0">G33</text><rect fill="black" height="24" stroke="lightgray" width="24" x="104" y="96" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="108.0">GND</text></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_matrix.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="120" version="1.1" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="#c06000" height="24" stroke="lightgray" width="24" x="0" y="0" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="12.0">3V3</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="36.0">G22</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="36.0">G21</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="64" y="24" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="84.0" y="36.0">SCL</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G19</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="60.0">G25</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="64" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="84.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G23</text><rect fill="#005000" height="24" stroke="red" width="40" x="24" y="72" /><text fill="#FFFFFF" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">G34</text><rect fill="#e00000" height="24" stroke="lightgray" width="24" x="104" y="72" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="84.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="108.0">G33</text><rect fill="black" height="24" stroke="lightgray" width="24" x="104" y="96" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="108.0">GND</text></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G26</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G32</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_psram.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="120" version="1.1" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="#c06000" height="24" stroke="lightgray" width="24" x="0" y="0" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="12.0">3V3</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="36.0">G22</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="36.0">G21</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G19</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="60.0">G25</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G5</text><rect fill="#e00000" height="24" stroke="lightgray" width="24" x="104" y="72" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="84.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="108.0">G33</text><rect fill="black" height="24" stroke="lightgray" width="24" x="104" y="96" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="108.0">GND</text></svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_s3.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="120" version="1.1" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="#c06000" height="24" stroke="lightgray" width="24" x="0" y="0" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="12.0">3V3</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="36.0">G5</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="24" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="36.0">G39</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="64" y="24" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="84.0" y="36.0">SCL</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G6</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="104" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="60.0">G38</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="64" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="84.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G7</text><rect fill="#e00000" height="24" stroke="lightgray" width="24" x="104" y="72" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="84.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="108.0">G8</text><rect fill="black" height="24" stroke="lightgray" width="24" x="104" y="96" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="116.0" y="108.0">GND</text></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_s3_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G2</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G1</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_atom_u.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="144" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="60.0">G25</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="84.0">G33</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="108.0">G22</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="120" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="132.0">G21</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core2_bus.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
2
libraries/M5Unified/docs/img/pin_def_core2_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G32</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G33</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core2_portb.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G26</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">DAC</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G36</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">ADC</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core2_portc.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G14</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">TXD2</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G13</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">RXD2</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core2_portd.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="48" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G35</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">ADC</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G34</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">PDM IN</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core2_porte.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G19</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">GPIO</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G27</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">GPIO</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core_bus.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
2
libraries/M5Unified/docs/img/pin_def_core_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G21</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G22</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core_portb.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G26</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">DAC</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G36</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">ADC</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core_portc.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G17</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">TXD2</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G16</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">RXD2</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core_portd.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="48" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G35</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">ADC</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G34</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">I2S IN</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_core_porte.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G13</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">I2S WS</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G5</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">GPIO</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_coreink_bus.svg
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_coreink_hat.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="192" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5Vout</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="60.0">G26</text><rect fill="#005000" height="24" stroke="red" width="64" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="84.0">G36</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="108.0">G25</text><rect fill="#c0c000" height="24" stroke="lightgray" width="64" x="0" y="120" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="132.0">BAT</text><rect fill="#c06000" height="24" stroke="lightgray" width="64" x="0" y="144" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="156.0">3V3</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="168" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="180.0">5Vin</text></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
2
libraries/M5Unified/docs/img/pin_def_cores3_bus.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
2
libraries/M5Unified/docs/img/pin_def_cores3_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G2</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">PA SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G1</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">PA SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_cores3_portb.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G9</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">PB OUT</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G8</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">PB IN</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_cores3_portc.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G17</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">PC TX</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G18</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">PC RX</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_paper_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G25</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">DAC</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G32</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">GPIO</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_paper_portb.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G26</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">DAC</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G33</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">GPIO</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_paper_portc.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G18</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">GPIO</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G19</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">GPIO</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_stampc3_bus.svg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
2
libraries/M5Unified/docs/img/pin_def_stampc3u_bus.svg
Normal file
|
After Width: | Height: | Size: 6 KiB |
2
libraries/M5Unified/docs/img/pin_def_stamppico_bus.svg
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
2
libraries/M5Unified/docs/img/pin_def_station_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G32</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G33</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_station_portb1.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G25</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">DAC</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G35</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">ADC</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_station_portb2.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G26</text><rect fill="#ff8080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">DAC</text><rect fill="#005000" height="24" stroke="red" width="24" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G36</text><rect fill="#ffc0c0" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">ADC</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_station_portc1.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G14</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">TXD2</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G13</text><rect fill="#20a020" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">RXD2</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_station_portc2.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G17</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">GPIO</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G16</text><rect fill="#c08080" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#000000" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">GPIO</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_stickc_hat.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="192" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5Vout</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="60.0">G26</text><rect fill="#005000" height="24" stroke="red" width="64" x="0" y="72" /><text fill="#FFFFFF" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="84.0">G36</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="108.0">G0</text><rect fill="#c0c000" height="24" stroke="lightgray" width="64" x="0" y="120" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="132.0">BAT</text><rect fill="#c06000" height="24" stroke="lightgray" width="64" x="0" y="144" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="156.0">3V3</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="168" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="180.0">5Vin</text></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
2
libraries/M5Unified/docs/img/pin_def_stickc_porta.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="96" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5V</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="60.0">G32</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="48" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="60.0">SDA</text><rect fill="#ffffff" height="24" stroke="lightgray" width="24" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="12.0" y="84.0">G33</text><rect fill="#ff1010" height="24" stroke="lightgray" width="40" x="24" y="72" /><text fill="#ffffff" style="font-size:50%; text-anchor:middle; dominant-baseline:central" x="44.0" y="84.0">SCL</text></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
2
libraries/M5Unified/docs/img/pin_def_stickcplus_hat.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="192" version="1.1" width="64" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs /><rect fill="black" height="24" stroke="lightgray" width="64" x="0" y="0" /><text fill="white" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="12.0">GND</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="24" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="36.0">5Vout</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="48" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="60.0">G26</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="72" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="84.0">G25/G36</text><rect fill="#ffffff" height="24" stroke="lightgray" width="64" x="0" y="96" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="108.0">G0</text><rect fill="#c0c000" height="24" stroke="lightgray" width="64" x="0" y="120" /><text fill="#000000" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="132.0">BAT</text><rect fill="#c06000" height="24" stroke="lightgray" width="64" x="0" y="144" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="156.0">3V3</text><rect fill="#e00000" height="24" stroke="lightgray" width="64" x="0" y="168" /><text fill="#ffffff" style="font-size:70%; text-anchor:middle; dominant-baseline:central" x="32.0" y="180.0">5Vin</text></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,677 @@
|
|||
|
||||
#include <M5UnitLCD.h>
|
||||
#include <M5UnitOLED.h>
|
||||
#include <M5Unified.h>
|
||||
|
||||
/// need ESP32-A2DP library. ( URL : https://github.com/pschatzmann/ESP32-A2DP/ )
|
||||
#include <BluetoothA2DPSink.h>
|
||||
|
||||
/// set M5Speaker virtual channel (0-7)
|
||||
static constexpr uint8_t m5spk_virtual_channel = 0;
|
||||
|
||||
/// set ESP32-A2DP device name
|
||||
static constexpr char bt_device_name[] = "ESP32";
|
||||
|
||||
|
||||
class BluetoothA2DPSink_M5Speaker : public BluetoothA2DPSink
|
||||
{
|
||||
public:
|
||||
BluetoothA2DPSink_M5Speaker(m5::Speaker_Class* m5sound, uint8_t virtual_channel = 0)
|
||||
: BluetoothA2DPSink()
|
||||
{
|
||||
is_i2s_output = false; // I2S control by BluetoothA2DPSink is not required.
|
||||
}
|
||||
|
||||
// get rawdata buffer for FFT.
|
||||
const int16_t* getBuffer(void) const { return _tri_buf[_export_index]; }
|
||||
|
||||
const char* getMetaData(size_t id, bool clear_flg = true) { if (clear_flg) { _meta_bits &= ~(1<<id); } return (id < metatext_num) ? _meta_text[id] : nullptr; }
|
||||
|
||||
uint8_t getMetaUpdateInfo(void) const { return _meta_bits; }
|
||||
void clearMetaUpdateInfo(void) { _meta_bits = 0; }
|
||||
|
||||
void clear(void)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (_tri_buf[i]) { memset(_tri_buf[i], 0, _tri_buf_size[i]); }
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t metatext_size = 128;
|
||||
static constexpr size_t metatext_num = 3;
|
||||
|
||||
protected:
|
||||
int16_t* _tri_buf[3] = { nullptr, nullptr, nullptr };
|
||||
size_t _tri_buf_size[3] = { 0, 0, 0 };
|
||||
size_t _tri_index = 0;
|
||||
size_t _export_index = 0;
|
||||
char _meta_text[metatext_num][metatext_size];
|
||||
uint8_t _meta_bits = 0;
|
||||
size_t _sample_rate = 48000;
|
||||
|
||||
void clearMetaData(void)
|
||||
{
|
||||
for (int i = 0; i < metatext_num; ++i)
|
||||
{
|
||||
_meta_text[i][0] = 0;
|
||||
}
|
||||
_meta_bits = (1<<metatext_num)-1;
|
||||
}
|
||||
|
||||
void av_hdl_a2d_evt(uint16_t event, void *p_param) override
|
||||
{
|
||||
esp_a2d_cb_param_t* a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
|
||||
switch (event) {
|
||||
case ESP_A2D_CONNECTION_STATE_EVT:
|
||||
if (ESP_A2D_CONNECTION_STATE_CONNECTED == a2d->conn_stat.state)
|
||||
{ // 接続
|
||||
|
||||
}
|
||||
else
|
||||
if (ESP_A2D_CONNECTION_STATE_DISCONNECTED == a2d->conn_stat.state)
|
||||
{ // 切断
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_A2D_AUDIO_STATE_EVT:
|
||||
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state)
|
||||
{ // 再生
|
||||
|
||||
} else
|
||||
if ( ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND == a2d->audio_stat.state
|
||||
|| ESP_A2D_AUDIO_STATE_STOPPED == a2d->audio_stat.state )
|
||||
{ // 停止
|
||||
clearMetaData();
|
||||
clear();
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_A2D_AUDIO_CFG_EVT:
|
||||
{
|
||||
esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
size_t tmp = a2d->audio_cfg.mcc.cie.sbc[0];
|
||||
size_t rate = 16000;
|
||||
if ( tmp & (1 << 6)) { rate = 32000; }
|
||||
else if (tmp & (1 << 5)) { rate = 44100; }
|
||||
else if (tmp & (1 << 4)) { rate = 48000; }
|
||||
_sample_rate = rate;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothA2DPSink::av_hdl_a2d_evt(event, p_param);
|
||||
}
|
||||
|
||||
void av_hdl_avrc_evt(uint16_t event, void *p_param) override
|
||||
{
|
||||
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param);
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT:
|
||||
for (size_t i = 0; i < metatext_num; ++i)
|
||||
{
|
||||
if (0 == (rc->meta_rsp.attr_id & (1 << i))) { continue; }
|
||||
strncpy(_meta_text[i], (char*)(rc->meta_rsp.attr_text), metatext_size);
|
||||
_meta_bits |= rc->meta_rsp.attr_id;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
|
||||
break;
|
||||
|
||||
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BluetoothA2DPSink::av_hdl_avrc_evt(event, p_param);
|
||||
}
|
||||
|
||||
int16_t* get_next_buf(const uint8_t* src_data, uint32_t len)
|
||||
{
|
||||
size_t tri = _tri_index < 2 ? _tri_index + 1 : 0;
|
||||
if (_tri_buf_size[tri] < len)
|
||||
{
|
||||
_tri_buf_size[tri] = len;
|
||||
if (_tri_buf[tri] != nullptr) { heap_caps_free(_tri_buf[tri]); }
|
||||
auto tmp = (int16_t*)heap_caps_malloc(len, MALLOC_CAP_8BIT);
|
||||
_tri_buf[tri] = tmp;
|
||||
if (tmp == nullptr)
|
||||
{
|
||||
_tri_buf_size[tri] = 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
memcpy(_tri_buf[tri], src_data, len);
|
||||
_tri_index = tri;
|
||||
return _tri_buf[tri];
|
||||
}
|
||||
|
||||
void audio_data_callback(const uint8_t *data, uint32_t length) override
|
||||
{
|
||||
// Reduce memory requirements by dividing the received data into the first and second halves.
|
||||
length >>= 1;
|
||||
M5.Speaker.playRaw(get_next_buf( data , length), length >> 1, _sample_rate, true, 1, m5spk_virtual_channel);
|
||||
M5.Speaker.playRaw(get_next_buf(&data[length], length), length >> 1, _sample_rate, true, 1, m5spk_virtual_channel);
|
||||
_export_index = _tri_index;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define FFT_SIZE 256
|
||||
class fft_t
|
||||
{
|
||||
float _wr[FFT_SIZE + 1];
|
||||
float _wi[FFT_SIZE + 1];
|
||||
float _fr[FFT_SIZE + 1];
|
||||
float _fi[FFT_SIZE + 1];
|
||||
uint16_t _br[FFT_SIZE + 1];
|
||||
size_t _ie;
|
||||
|
||||
public:
|
||||
fft_t(void)
|
||||
{
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592653
|
||||
#endif
|
||||
_ie = logf( (float)FFT_SIZE ) / log(2.0) + 0.5;
|
||||
static constexpr float omega = 2.0f * M_PI / FFT_SIZE;
|
||||
static constexpr int s4 = FFT_SIZE / 4;
|
||||
static constexpr int s2 = FFT_SIZE / 2;
|
||||
for ( int i = 1 ; i < s4 ; ++i)
|
||||
{
|
||||
float f = cosf(omega * i);
|
||||
_wi[s4 + i] = f;
|
||||
_wi[s4 - i] = f;
|
||||
_wr[ i] = f;
|
||||
_wr[s2 - i] = -f;
|
||||
}
|
||||
_wi[s4] = _wr[0] = 1;
|
||||
|
||||
size_t je = 1;
|
||||
_br[0] = 0;
|
||||
_br[1] = FFT_SIZE / 2;
|
||||
for ( size_t i = 0 ; i < _ie - 1 ; ++i )
|
||||
{
|
||||
_br[ je << 1 ] = _br[ je ] >> 1;
|
||||
je = je << 1;
|
||||
for ( size_t j = 1 ; j < je ; ++j )
|
||||
{
|
||||
_br[je + j] = _br[je] + _br[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exec(const int16_t* in)
|
||||
{
|
||||
memset(_fi, 0, sizeof(_fi));
|
||||
for ( size_t j = 0 ; j < FFT_SIZE / 2 ; ++j )
|
||||
{
|
||||
float basej = 0.25 * (1.0-_wr[j]);
|
||||
size_t r = FFT_SIZE - j - 1;
|
||||
|
||||
/// perform han window and stereo to mono convert.
|
||||
_fr[_br[j]] = basej * (in[j * 2] + in[j * 2 + 1]);
|
||||
_fr[_br[r]] = basej * (in[r * 2] + in[r * 2 + 1]);
|
||||
}
|
||||
|
||||
size_t s = 1;
|
||||
size_t i = 0;
|
||||
do
|
||||
{
|
||||
size_t ke = s;
|
||||
s <<= 1;
|
||||
size_t je = FFT_SIZE / s;
|
||||
size_t j = 0;
|
||||
do
|
||||
{
|
||||
size_t k = 0;
|
||||
do
|
||||
{
|
||||
size_t l = s * j + k;
|
||||
size_t m = ke * (2 * j + 1) + k;
|
||||
size_t p = je * k;
|
||||
float Wxmr = _fr[m] * _wr[p] + _fi[m] * _wi[p];
|
||||
float Wxmi = _fi[m] * _wr[p] - _fr[m] * _wi[p];
|
||||
_fr[m] = _fr[l] - Wxmr;
|
||||
_fi[m] = _fi[l] - Wxmi;
|
||||
_fr[l] += Wxmr;
|
||||
_fi[l] += Wxmi;
|
||||
} while ( ++k < ke) ;
|
||||
} while ( ++j < je );
|
||||
} while ( ++i < _ie );
|
||||
}
|
||||
|
||||
uint32_t get(size_t index)
|
||||
{
|
||||
return (index < FFT_SIZE / 2) ? (uint32_t)sqrtf(_fr[ index ] * _fr[ index ] + _fi[ index ] * _fi[ index ]) : 0u;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr size_t WAVE_SIZE = 320;
|
||||
static BluetoothA2DPSink_M5Speaker a2dp_sink = { &M5.Speaker, m5spk_virtual_channel };
|
||||
static fft_t fft;
|
||||
static bool fft_enabled = false;
|
||||
static bool wave_enabled = false;
|
||||
static uint16_t prev_y[(FFT_SIZE / 2)+1];
|
||||
static uint16_t peak_y[(FFT_SIZE / 2)+1];
|
||||
static int16_t wave_y[WAVE_SIZE];
|
||||
static int16_t wave_h[WAVE_SIZE];
|
||||
static int16_t raw_data[WAVE_SIZE * 2];
|
||||
static int header_height = 0;
|
||||
|
||||
|
||||
uint32_t bgcolor(LGFX_Device* gfx, int y)
|
||||
{
|
||||
auto h = gfx->height();
|
||||
auto dh = h - header_height;
|
||||
int v = ((h - y)<<5) / dh;
|
||||
if (dh > 44)
|
||||
{
|
||||
int v2 = ((h - y - 1)<<5) / dh;
|
||||
if ((v >> 2) != (v2 >> 2))
|
||||
{
|
||||
return 0x666666u;
|
||||
}
|
||||
}
|
||||
return gfx->color888(v + 2, v, v + 6);
|
||||
}
|
||||
|
||||
void gfxSetup(LGFX_Device* gfx)
|
||||
{
|
||||
if (gfx == nullptr) { return; }
|
||||
if (gfx->width() < gfx->height())
|
||||
{
|
||||
gfx->setRotation(gfx->getRotation()^1);
|
||||
}
|
||||
gfx->setFont(&fonts::lgfxJapanGothic_12);
|
||||
gfx->setEpdMode(epd_mode_t::epd_fastest);
|
||||
gfx->setCursor(0, 8);
|
||||
gfx->print("BT A2DP : ");
|
||||
gfx->println(bt_device_name);
|
||||
gfx->setTextWrap(false);
|
||||
gfx->fillRect(0, 6, gfx->width(), 2, TFT_BLACK);
|
||||
|
||||
header_height = (gfx->height() > 80) ? 45 : 21;
|
||||
fft_enabled = !gfx->isEPD();
|
||||
if (fft_enabled)
|
||||
{
|
||||
wave_enabled = (gfx->getBoard() != m5gfx::board_M5UnitLCD);
|
||||
|
||||
for (int y = header_height; y < gfx->height(); ++y)
|
||||
{
|
||||
gfx->drawFastHLine(0, y, gfx->width(), bgcolor(gfx, y));
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < (FFT_SIZE/2)+1; ++x)
|
||||
{
|
||||
prev_y[x] = INT16_MAX;
|
||||
peak_y[x] = INT16_MAX;
|
||||
}
|
||||
for (int x = 0; x < WAVE_SIZE; ++x)
|
||||
{
|
||||
wave_y[x] = gfx->height();
|
||||
wave_h[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gfxLoop(LGFX_Device* gfx)
|
||||
{
|
||||
if (gfx == nullptr) { return; }
|
||||
if (header_height > 32)
|
||||
{
|
||||
auto bits = a2dp_sink.getMetaUpdateInfo();
|
||||
if (bits)
|
||||
{
|
||||
gfx->startWrite();
|
||||
for (int id = 0; id < a2dp_sink.metatext_num; ++id)
|
||||
{
|
||||
if (0 == (bits & (1<<id))) { continue; }
|
||||
size_t y = id * 12;
|
||||
if (y+12 >= header_height) { continue; }
|
||||
gfx->setCursor(4, 8 + y);
|
||||
gfx->fillRect(0, 8 + y, gfx->width(), 12, gfx->getBaseColor());
|
||||
gfx->print(a2dp_sink.getMetaData(id));
|
||||
gfx->print(" "); // Garbage data removal when UTF8 characters are broken in the middle.
|
||||
}
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static int title_x;
|
||||
static int title_id;
|
||||
static int wait = INT16_MAX;
|
||||
|
||||
if (a2dp_sink.getMetaUpdateInfo())
|
||||
{
|
||||
gfx->fillRect(0, 8, gfx->width(), 12, TFT_BLACK);
|
||||
a2dp_sink.clearMetaUpdateInfo();
|
||||
title_x = 4;
|
||||
title_id = 0;
|
||||
wait = 0;
|
||||
}
|
||||
|
||||
if (--wait < 0)
|
||||
{
|
||||
int tx = title_x;
|
||||
int tid = title_id;
|
||||
wait = 3;
|
||||
gfx->startWrite();
|
||||
uint_fast8_t no_data_bits = 0;
|
||||
do
|
||||
{
|
||||
if (tx == 4) { wait = 255; }
|
||||
gfx->setCursor(tx, 8);
|
||||
const char* meta = a2dp_sink.getMetaData(tid, false);
|
||||
if (meta[0] != 0)
|
||||
{
|
||||
gfx->print(meta);
|
||||
gfx->print(" / ");
|
||||
tx = gfx->getCursorX();
|
||||
if (++tid == a2dp_sink.metatext_num) { tid = 0; }
|
||||
if (tx <= 4)
|
||||
{
|
||||
title_x = tx;
|
||||
title_id = tid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((no_data_bits |= 1 << tid) == ((1 << a2dp_sink.metatext_num) - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (++tid == a2dp_sink.metatext_num) { tid = 0; }
|
||||
}
|
||||
} while (tx < gfx->width());
|
||||
--title_x;
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
if (!gfx->displayBusy())
|
||||
{ // draw volume bar
|
||||
static int px;
|
||||
uint8_t v = M5.Speaker.getVolume();
|
||||
int x = v * (gfx->width()) >> 8;
|
||||
if (px != x)
|
||||
{
|
||||
gfx->fillRect(x, 6, px - x, 2, px < x ? 0xAAFFAAu : 0u);
|
||||
gfx->display();
|
||||
px = x;
|
||||
}
|
||||
}
|
||||
|
||||
if (fft_enabled && !gfx->displayBusy())
|
||||
{
|
||||
static int prev_x[2];
|
||||
static int peak_x[2];
|
||||
static bool prev_conn;
|
||||
bool connected = a2dp_sink.is_connected();
|
||||
if (prev_conn != connected)
|
||||
{
|
||||
prev_conn = connected;
|
||||
if (!connected)
|
||||
{
|
||||
a2dp_sink.clear();
|
||||
}
|
||||
}
|
||||
|
||||
auto buf = a2dp_sink.getBuffer();
|
||||
if (buf)
|
||||
{
|
||||
memcpy(raw_data, buf, WAVE_SIZE * 2 * sizeof(int16_t)); // stereo data copy
|
||||
gfx->startWrite();
|
||||
|
||||
// draw stereo level meter
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
int32_t level = 0;
|
||||
for (size_t j = i; j < 640; j += 32)
|
||||
{
|
||||
uint32_t lv = abs(raw_data[j]);
|
||||
if (level < lv) { level = lv; }
|
||||
}
|
||||
|
||||
int32_t x = (level * gfx->width()) / INT16_MAX;
|
||||
int32_t px = prev_x[i];
|
||||
if (px != x)
|
||||
{
|
||||
gfx->fillRect(x, i * 3, px - x, 2, px < x ? 0xFF9900u : 0x330000u);
|
||||
prev_x[i] = x;
|
||||
}
|
||||
px = peak_x[i];
|
||||
if (px > x)
|
||||
{
|
||||
gfx->writeFastVLine(px, i * 3, 2, TFT_BLACK);
|
||||
px--;
|
||||
}
|
||||
else
|
||||
{
|
||||
px = x;
|
||||
}
|
||||
if (peak_x[i] != px)
|
||||
{
|
||||
peak_x[i] = px;
|
||||
gfx->writeFastVLine(px, i * 3, 2, TFT_WHITE);
|
||||
}
|
||||
}
|
||||
gfx->display();
|
||||
|
||||
// draw FFT level meter
|
||||
fft.exec(raw_data);
|
||||
size_t bw = gfx->width() / 60;
|
||||
if (bw < 3) { bw = 3; }
|
||||
int32_t dsp_height = gfx->height();
|
||||
int32_t fft_height = dsp_height - header_height - 1;
|
||||
size_t xe = gfx->width() / bw;
|
||||
if (xe > (FFT_SIZE/2)) { xe = (FFT_SIZE/2); }
|
||||
int32_t wave_next = ((header_height + dsp_height) >> 1) + (((256 - (raw_data[0] + raw_data[1])) * fft_height) >> 17);
|
||||
|
||||
uint32_t bar_color[2] = { 0x000033u, 0x99AAFFu };
|
||||
|
||||
for (size_t bx = 0; bx <= xe; ++bx)
|
||||
{
|
||||
size_t x = bx * bw;
|
||||
if ((x & 7) == 0) { gfx->display(); taskYIELD(); }
|
||||
int32_t f = fft.get(bx);
|
||||
int32_t y = (f * fft_height) >> 18;
|
||||
if (y > fft_height) { y = fft_height; }
|
||||
y = dsp_height - y;
|
||||
int32_t py = prev_y[bx];
|
||||
if (y != py)
|
||||
{
|
||||
gfx->fillRect(x, y, bw - 1, py - y, bar_color[(y < py)]);
|
||||
prev_y[bx] = y;
|
||||
}
|
||||
py = peak_y[bx] + 1;
|
||||
if (py < y)
|
||||
{
|
||||
gfx->writeFastHLine(x, py - 1, bw - 1, bgcolor(gfx, py - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
py = y - 1;
|
||||
}
|
||||
if (peak_y[bx] != py)
|
||||
{
|
||||
peak_y[bx] = py;
|
||||
gfx->writeFastHLine(x, py, bw - 1, TFT_WHITE);
|
||||
}
|
||||
|
||||
|
||||
if (wave_enabled)
|
||||
{
|
||||
for (size_t bi = 0; bi < bw; ++bi)
|
||||
{
|
||||
size_t i = x + bi;
|
||||
if (i >= gfx->width() || i >= WAVE_SIZE) { break; }
|
||||
y = wave_y[i];
|
||||
int32_t h = wave_h[i];
|
||||
bool use_bg = (bi+1 == bw);
|
||||
if (h>0)
|
||||
{ /// erase previous wave.
|
||||
gfx->setAddrWindow(i, y, 1, h);
|
||||
h += y;
|
||||
do
|
||||
{
|
||||
uint32_t bg = (use_bg || y < peak_y[bx]) ? bgcolor(gfx, y)
|
||||
: (y == peak_y[bx]) ? 0xFFFFFFu
|
||||
: bar_color[(y >= prev_y[bx])];
|
||||
gfx->writeColor(bg, 1);
|
||||
} while (++y < h);
|
||||
}
|
||||
size_t i2 = i << 1;
|
||||
int32_t y1 = wave_next;
|
||||
wave_next = ((header_height + dsp_height) >> 1) + (((256 - (raw_data[i2] + raw_data[i2 + 1])) * fft_height) >> 17);
|
||||
int32_t y2 = wave_next;
|
||||
if (y1 > y2)
|
||||
{
|
||||
int32_t tmp = y1;
|
||||
y1 = y2;
|
||||
y2 = tmp;
|
||||
}
|
||||
y = y1;
|
||||
h = y2 + 1 - y;
|
||||
wave_y[i] = y;
|
||||
wave_h[i] = h;
|
||||
if (h>0)
|
||||
{ /// draw new wave.
|
||||
gfx->setAddrWindow(i, y, 1, h);
|
||||
h += y;
|
||||
do
|
||||
{
|
||||
uint32_t bg = (y < prev_y[bx]) ? 0xFFCC33u : 0xFFFFFFu;
|
||||
gfx->writeColor(bg, 1);
|
||||
} while (++y < h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// If you want to play sound from ModuleDisplay, write this
|
||||
// cfg.external_speaker.module_display = true;
|
||||
|
||||
// If you want to play sound from ModuleRCA, write this
|
||||
// cfg.external_speaker.module_rca = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker, write this
|
||||
cfg.external_speaker.hat_spk = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker2, write this
|
||||
// cfg.external_speaker.hat_spk2 = true;
|
||||
|
||||
// If you want to play sound from ATOMIC Speaker, write this
|
||||
cfg.external_speaker.atomic_spk = true;
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
|
||||
{ /// custom setting
|
||||
auto spk_cfg = M5.Speaker.config();
|
||||
/// Increasing the sample_rate will improve the sound quality instead of increasing the CPU load.
|
||||
spk_cfg.sample_rate = 96000; // default:64000 (64kHz) e.g. 48000 , 50000 , 80000 , 96000 , 100000 , 128000 , 144000 , 192000 , 200000
|
||||
spk_cfg.task_pinned_core = APP_CPU_NUM;
|
||||
// spk_cfg.task_priority = configMAX_PRIORITIES - 2;
|
||||
spk_cfg.dma_buf_count = 20;
|
||||
// spk_cfg.dma_buf_len = 512;
|
||||
M5.Speaker.config(spk_cfg);
|
||||
}
|
||||
|
||||
|
||||
M5.Speaker.begin();
|
||||
|
||||
a2dp_sink.start(bt_device_name, false);
|
||||
|
||||
gfxSetup(&M5.Display);
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
gfxLoop(&M5.Display);
|
||||
|
||||
{
|
||||
static int prev_frame;
|
||||
int frame;
|
||||
do
|
||||
{
|
||||
vTaskDelay(1);
|
||||
} while (prev_frame == (frame = millis() >> 3)); /// 8 msec cycle wait
|
||||
prev_frame = frame;
|
||||
}
|
||||
|
||||
M5.update();
|
||||
if (M5.BtnA.wasPressed())
|
||||
{
|
||||
M5.Speaker.tone(440, 50);
|
||||
}
|
||||
if (M5.BtnA.wasDecideClickCount())
|
||||
{
|
||||
switch (M5.BtnA.getClickCount())
|
||||
{
|
||||
case 1:
|
||||
M5.Speaker.tone(1000, 100);
|
||||
a2dp_sink.next();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
M5.Speaker.tone(800, 100);
|
||||
a2dp_sink.previous();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (M5.BtnA.isHolding() || M5.BtnB.isPressed() || M5.BtnC.isPressed())
|
||||
{
|
||||
size_t v = M5.Speaker.getVolume();
|
||||
int add = (M5.BtnB.isPressed()) ? -1 : 1;
|
||||
if (M5.BtnA.isHolding())
|
||||
{
|
||||
add = M5.BtnA.getClickCount() ? -1 : 1;
|
||||
}
|
||||
v += add;
|
||||
if (v <= 255)
|
||||
{
|
||||
M5.Speaker.setVolume(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
#include <SD.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <math.h>
|
||||
|
||||
/// need ESP8266Audio library. ( URL : https://github.com/earlephilhower/ESP8266Audio/ )
|
||||
#include <AudioOutput.h>
|
||||
#include <AudioFileSourceSD.h>
|
||||
#include <AudioFileSourceID3.h>
|
||||
#include <AudioGeneratorMP3.h>
|
||||
|
||||
#include <M5UnitLCD.h>
|
||||
#include <M5UnitOLED.h>
|
||||
#include <M5Unified.h>
|
||||
|
||||
/// set M5Speaker virtual channel (0-7)
|
||||
static constexpr uint8_t m5spk_virtual_channel = 0;
|
||||
|
||||
/// set your mp3 filename
|
||||
static constexpr const char* filename[] =
|
||||
{
|
||||
"/mp3/file01.mp3",
|
||||
"/mp3/file02.mp3",
|
||||
"/mp3/file03.mp3",
|
||||
"/mp3/file04.mp3",
|
||||
};
|
||||
static constexpr const size_t filecount = sizeof(filename) / sizeof(filename[0]);
|
||||
|
||||
class AudioOutputM5Speaker : public AudioOutput
|
||||
{
|
||||
public:
|
||||
AudioOutputM5Speaker(m5::Speaker_Class* m5sound, uint8_t virtual_sound_channel = 0)
|
||||
{
|
||||
_m5sound = m5sound;
|
||||
_virtual_ch = virtual_sound_channel;
|
||||
}
|
||||
virtual ~AudioOutputM5Speaker(void) {};
|
||||
virtual bool begin(void) override { return true; }
|
||||
virtual bool ConsumeSample(int16_t sample[2]) override
|
||||
{
|
||||
if (_tri_buffer_index < tri_buf_size)
|
||||
{
|
||||
_tri_buffer[_tri_index][_tri_buffer_index ] = sample[0];
|
||||
_tri_buffer[_tri_index][_tri_buffer_index+1] = sample[1];
|
||||
_tri_buffer_index += 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
flush();
|
||||
return false;
|
||||
}
|
||||
virtual void flush(void) override
|
||||
{
|
||||
if (_tri_buffer_index)
|
||||
{
|
||||
_m5sound->playRaw(_tri_buffer[_tri_index], _tri_buffer_index, hertz, true, 1, _virtual_ch);
|
||||
_tri_index = _tri_index < 2 ? _tri_index + 1 : 0;
|
||||
_tri_buffer_index = 0;
|
||||
}
|
||||
}
|
||||
virtual bool stop(void) override
|
||||
{
|
||||
flush();
|
||||
_m5sound->stop(_virtual_ch);
|
||||
return true;
|
||||
}
|
||||
|
||||
const int16_t* getBuffer(void) const { return _tri_buffer[(_tri_index + 2) % 3]; }
|
||||
|
||||
protected:
|
||||
m5::Speaker_Class* _m5sound;
|
||||
uint8_t _virtual_ch;
|
||||
static constexpr size_t tri_buf_size = 1536;
|
||||
int16_t _tri_buffer[3][tri_buf_size];
|
||||
size_t _tri_buffer_index = 0;
|
||||
size_t _tri_index = 0;
|
||||
};
|
||||
|
||||
|
||||
#define FFT_SIZE 256
|
||||
class fft_t
|
||||
{
|
||||
float _wr[FFT_SIZE + 1];
|
||||
float _wi[FFT_SIZE + 1];
|
||||
float _fr[FFT_SIZE + 1];
|
||||
float _fi[FFT_SIZE + 1];
|
||||
uint16_t _br[FFT_SIZE + 1];
|
||||
size_t _ie;
|
||||
|
||||
public:
|
||||
fft_t(void)
|
||||
{
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592653
|
||||
#endif
|
||||
_ie = logf( (float)FFT_SIZE ) / log(2.0) + 0.5;
|
||||
static constexpr float omega = 2.0f * M_PI / FFT_SIZE;
|
||||
static constexpr int s4 = FFT_SIZE / 4;
|
||||
static constexpr int s2 = FFT_SIZE / 2;
|
||||
for ( int i = 1 ; i < s4 ; ++i)
|
||||
{
|
||||
float f = cosf(omega * i);
|
||||
_wi[s4 + i] = f;
|
||||
_wi[s4 - i] = f;
|
||||
_wr[ i] = f;
|
||||
_wr[s2 - i] = -f;
|
||||
}
|
||||
_wi[s4] = _wr[0] = 1;
|
||||
|
||||
size_t je = 1;
|
||||
_br[0] = 0;
|
||||
_br[1] = FFT_SIZE / 2;
|
||||
for ( size_t i = 0 ; i < _ie - 1 ; ++i )
|
||||
{
|
||||
_br[ je << 1 ] = _br[ je ] >> 1;
|
||||
je = je << 1;
|
||||
for ( size_t j = 1 ; j < je ; ++j )
|
||||
{
|
||||
_br[je + j] = _br[je] + _br[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exec(const int16_t* in)
|
||||
{
|
||||
memset(_fi, 0, sizeof(_fi));
|
||||
for ( size_t j = 0 ; j < FFT_SIZE / 2 ; ++j )
|
||||
{
|
||||
float basej = 0.25 * (1.0-_wr[j]);
|
||||
size_t r = FFT_SIZE - j - 1;
|
||||
|
||||
/// perform han window and stereo to mono convert.
|
||||
_fr[_br[j]] = basej * (in[j * 2] + in[j * 2 + 1]);
|
||||
_fr[_br[r]] = basej * (in[r * 2] + in[r * 2 + 1]);
|
||||
}
|
||||
|
||||
size_t s = 1;
|
||||
size_t i = 0;
|
||||
do
|
||||
{
|
||||
size_t ke = s;
|
||||
s <<= 1;
|
||||
size_t je = FFT_SIZE / s;
|
||||
size_t j = 0;
|
||||
do
|
||||
{
|
||||
size_t k = 0;
|
||||
do
|
||||
{
|
||||
size_t l = s * j + k;
|
||||
size_t m = ke * (2 * j + 1) + k;
|
||||
size_t p = je * k;
|
||||
float Wxmr = _fr[m] * _wr[p] + _fi[m] * _wi[p];
|
||||
float Wxmi = _fi[m] * _wr[p] - _fr[m] * _wi[p];
|
||||
_fr[m] = _fr[l] - Wxmr;
|
||||
_fi[m] = _fi[l] - Wxmi;
|
||||
_fr[l] += Wxmr;
|
||||
_fi[l] += Wxmi;
|
||||
} while ( ++k < ke) ;
|
||||
} while ( ++j < je );
|
||||
} while ( ++i < _ie );
|
||||
}
|
||||
|
||||
uint32_t get(size_t index)
|
||||
{
|
||||
return (index < FFT_SIZE / 2) ? (uint32_t)sqrtf(_fr[ index ] * _fr[ index ] + _fi[ index ] * _fi[ index ]) : 0u;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr size_t WAVE_SIZE = 320;
|
||||
static AudioFileSourceSD file;
|
||||
static AudioOutputM5Speaker out(&M5.Speaker, m5spk_virtual_channel);
|
||||
static AudioGeneratorMP3 mp3;
|
||||
static AudioFileSourceID3* id3 = nullptr;
|
||||
static fft_t fft;
|
||||
static bool fft_enabled = false;
|
||||
static bool wave_enabled = false;
|
||||
static uint16_t prev_y[(FFT_SIZE / 2)+1];
|
||||
static uint16_t peak_y[(FFT_SIZE / 2)+1];
|
||||
static int16_t wave_y[WAVE_SIZE];
|
||||
static int16_t wave_h[WAVE_SIZE];
|
||||
static int16_t raw_data[WAVE_SIZE * 2];
|
||||
static int header_height = 0;
|
||||
static size_t fileindex = 0;
|
||||
|
||||
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
|
||||
{
|
||||
(void)cbData;
|
||||
if (string[0] == 0) { return; }
|
||||
if (strcmp(type, "eof") == 0)
|
||||
{
|
||||
M5.Display.display();
|
||||
return;
|
||||
}
|
||||
int y = M5.Display.getCursorY();
|
||||
if (y+1 >= header_height) { return; }
|
||||
M5.Display.fillRect(0, y, M5.Display.width(), 12, M5.Display.getBaseColor());
|
||||
M5.Display.printf("%s: %s", type, string);
|
||||
M5.Display.setCursor(0, y+12);
|
||||
}
|
||||
|
||||
void stop(void)
|
||||
{
|
||||
if (id3 == nullptr) return;
|
||||
out.stop();
|
||||
mp3.stop();
|
||||
id3->RegisterMetadataCB(nullptr, nullptr);
|
||||
id3->close();
|
||||
file.close();
|
||||
delete id3;
|
||||
id3 = nullptr;
|
||||
}
|
||||
|
||||
void play(const char* fname)
|
||||
{
|
||||
if (id3 != nullptr) { stop(); }
|
||||
M5.Display.setCursor(0, 8);
|
||||
file.open(fname);
|
||||
id3 = new AudioFileSourceID3(&file);
|
||||
id3->RegisterMetadataCB(MDCallback, (void*)"ID3TAG");
|
||||
id3->open(fname);
|
||||
mp3.begin(id3, &out);
|
||||
}
|
||||
|
||||
uint32_t bgcolor(LGFX_Device* gfx, int y)
|
||||
{
|
||||
auto h = gfx->height();
|
||||
auto dh = h - header_height;
|
||||
int v = ((h - y)<<5) / dh;
|
||||
if (dh > 44)
|
||||
{
|
||||
int v2 = ((h - y - 1)<<5) / dh;
|
||||
if ((v >> 2) != (v2 >> 2))
|
||||
{
|
||||
return 0x666666u;
|
||||
}
|
||||
}
|
||||
return gfx->color888(v + 2, v, v + 6);
|
||||
}
|
||||
|
||||
void gfxSetup(LGFX_Device* gfx)
|
||||
{
|
||||
if (gfx == nullptr) { return; }
|
||||
if (gfx->width() < gfx->height())
|
||||
{
|
||||
gfx->setRotation(gfx->getRotation()^1);
|
||||
}
|
||||
gfx->setFont(&fonts::lgfxJapanGothic_12);
|
||||
gfx->setEpdMode(epd_mode_t::epd_fastest);
|
||||
gfx->setCursor(0, 8);
|
||||
gfx->println("MP3 player");
|
||||
gfx->setTextWrap(false);
|
||||
gfx->fillRect(0, 6, gfx->width(), 2, TFT_BLACK);
|
||||
|
||||
header_height = 45;
|
||||
fft_enabled = !gfx->isEPD();
|
||||
if (fft_enabled)
|
||||
{
|
||||
wave_enabled = (gfx->getBoard() != m5gfx::board_M5UnitLCD);
|
||||
|
||||
for (int y = header_height; y < gfx->height(); ++y)
|
||||
{
|
||||
gfx->drawFastHLine(0, y, gfx->width(), bgcolor(gfx, y));
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < (FFT_SIZE/2)+1; ++x)
|
||||
{
|
||||
prev_y[x] = INT16_MAX;
|
||||
peak_y[x] = INT16_MAX;
|
||||
}
|
||||
for (int x = 0; x < WAVE_SIZE; ++x)
|
||||
{
|
||||
wave_y[x] = gfx->height();
|
||||
wave_h[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gfxLoop(LGFX_Device* gfx)
|
||||
{
|
||||
if (gfx == nullptr) { return; }
|
||||
|
||||
if (!gfx->displayBusy())
|
||||
{ // draw volume bar
|
||||
static int px;
|
||||
uint8_t v = M5.Speaker.getVolume();
|
||||
int x = v * (gfx->width()) >> 8;
|
||||
if (px != x)
|
||||
{
|
||||
gfx->fillRect(x, 6, px - x, 2, px < x ? 0xAAFFAAu : 0u);
|
||||
gfx->display();
|
||||
px = x;
|
||||
}
|
||||
}
|
||||
|
||||
if (fft_enabled && !gfx->displayBusy() && M5.Speaker.isPlaying(m5spk_virtual_channel) > 1)
|
||||
{
|
||||
static int prev_x[2];
|
||||
static int peak_x[2];
|
||||
|
||||
auto buf = out.getBuffer();
|
||||
if (buf)
|
||||
{
|
||||
memcpy(raw_data, buf, WAVE_SIZE * 2 * sizeof(int16_t)); // stereo data copy
|
||||
gfx->startWrite();
|
||||
|
||||
// draw stereo level meter
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
int32_t level = 0;
|
||||
for (size_t j = i; j < 640; j += 32)
|
||||
{
|
||||
uint32_t lv = abs(raw_data[j]);
|
||||
if (level < lv) { level = lv; }
|
||||
}
|
||||
|
||||
int32_t x = (level * gfx->width()) / INT16_MAX;
|
||||
int32_t px = prev_x[i];
|
||||
if (px != x)
|
||||
{
|
||||
gfx->fillRect(x, i * 3, px - x, 2, px < x ? 0xFF9900u : 0x330000u);
|
||||
prev_x[i] = x;
|
||||
}
|
||||
px = peak_x[i];
|
||||
if (px > x)
|
||||
{
|
||||
gfx->writeFastVLine(px, i * 3, 2, TFT_BLACK);
|
||||
px--;
|
||||
}
|
||||
else
|
||||
{
|
||||
px = x;
|
||||
}
|
||||
if (peak_x[i] != px)
|
||||
{
|
||||
peak_x[i] = px;
|
||||
gfx->writeFastVLine(px, i * 3, 2, TFT_WHITE);
|
||||
}
|
||||
}
|
||||
gfx->display();
|
||||
|
||||
// draw FFT level meter
|
||||
fft.exec(raw_data);
|
||||
size_t bw = gfx->width() / 60;
|
||||
if (bw < 3) { bw = 3; }
|
||||
int32_t dsp_height = gfx->height();
|
||||
int32_t fft_height = dsp_height - header_height - 1;
|
||||
size_t xe = gfx->width() / bw;
|
||||
if (xe > (FFT_SIZE/2)) { xe = (FFT_SIZE/2); }
|
||||
int32_t wave_next = ((header_height + dsp_height) >> 1) + (((256 - (raw_data[0] + raw_data[1])) * fft_height) >> 17);
|
||||
|
||||
uint32_t bar_color[2] = { 0x000033u, 0x99AAFFu };
|
||||
|
||||
for (size_t bx = 0; bx <= xe; ++bx)
|
||||
{
|
||||
size_t x = bx * bw;
|
||||
if ((x & 7) == 0) { gfx->display(); taskYIELD(); }
|
||||
int32_t f = fft.get(bx);
|
||||
int32_t y = (f * fft_height) >> 18;
|
||||
if (y > fft_height) { y = fft_height; }
|
||||
y = dsp_height - y;
|
||||
int32_t py = prev_y[bx];
|
||||
if (y != py)
|
||||
{
|
||||
gfx->fillRect(x, y, bw - 1, py - y, bar_color[(y < py)]);
|
||||
prev_y[bx] = y;
|
||||
}
|
||||
py = peak_y[bx] + 1;
|
||||
if (py < y)
|
||||
{
|
||||
gfx->writeFastHLine(x, py - 1, bw - 1, bgcolor(gfx, py - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
py = y - 1;
|
||||
}
|
||||
if (peak_y[bx] != py)
|
||||
{
|
||||
peak_y[bx] = py;
|
||||
gfx->writeFastHLine(x, py, bw - 1, TFT_WHITE);
|
||||
}
|
||||
|
||||
|
||||
if (wave_enabled)
|
||||
{
|
||||
for (size_t bi = 0; bi < bw; ++bi)
|
||||
{
|
||||
size_t i = x + bi;
|
||||
if (i >= gfx->width() || i >= WAVE_SIZE) { break; }
|
||||
y = wave_y[i];
|
||||
int32_t h = wave_h[i];
|
||||
bool use_bg = (bi+1 == bw);
|
||||
if (h>0)
|
||||
{ /// erase previous wave.
|
||||
gfx->setAddrWindow(i, y, 1, h);
|
||||
h += y;
|
||||
do
|
||||
{
|
||||
uint32_t bg = (use_bg || y < peak_y[bx]) ? bgcolor(gfx, y)
|
||||
: (y == peak_y[bx]) ? 0xFFFFFFu
|
||||
: bar_color[(y >= prev_y[bx])];
|
||||
gfx->writeColor(bg, 1);
|
||||
} while (++y < h);
|
||||
}
|
||||
size_t i2 = i << 1;
|
||||
int32_t y1 = wave_next;
|
||||
wave_next = ((header_height + dsp_height) >> 1) + (((256 - (raw_data[i2] + raw_data[i2 + 1])) * fft_height) >> 17);
|
||||
int32_t y2 = wave_next;
|
||||
if (y1 > y2)
|
||||
{
|
||||
int32_t tmp = y1;
|
||||
y1 = y2;
|
||||
y2 = tmp;
|
||||
}
|
||||
y = y1;
|
||||
h = y2 + 1 - y;
|
||||
wave_y[i] = y;
|
||||
wave_h[i] = h;
|
||||
if (h>0)
|
||||
{ /// draw new wave.
|
||||
gfx->setAddrWindow(i, y, 1, h);
|
||||
h += y;
|
||||
do
|
||||
{
|
||||
uint32_t bg = (y < prev_y[bx]) ? 0xFFCC33u : 0xFFFFFFu;
|
||||
gfx->writeColor(bg, 1);
|
||||
} while (++y < h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// If you want to play sound from ModuleDisplay, write this
|
||||
// cfg.external_speaker.module_display = true;
|
||||
|
||||
// If you want to play sound from ModuleRCA, write this
|
||||
// cfg.external_speaker.module_rca = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker, write this
|
||||
cfg.external_speaker.hat_spk = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker2, write this
|
||||
// cfg.external_speaker.hat_spk2 = true;
|
||||
|
||||
// If you want to play sound from ATOMIC Speaker, write this
|
||||
cfg.external_speaker.atomic_spk = true;
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
|
||||
{ /// custom setting
|
||||
auto spk_cfg = M5.Speaker.config();
|
||||
/// Increasing the sample_rate will improve the sound quality instead of increasing the CPU load.
|
||||
spk_cfg.sample_rate = 96000; // default:64000 (64kHz) e.g. 48000 , 50000 , 80000 , 96000 , 100000 , 128000 , 144000 , 192000 , 200000
|
||||
M5.Speaker.config(spk_cfg);
|
||||
}
|
||||
|
||||
|
||||
M5.Speaker.begin();
|
||||
|
||||
while (false == SD.begin(GPIO_NUM_4, SPI, 25000000))
|
||||
{
|
||||
M5.delay(500);
|
||||
}
|
||||
|
||||
gfxSetup(&M5.Display);
|
||||
|
||||
play(filename[fileindex]);
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
gfxLoop(&M5.Display);
|
||||
|
||||
if (mp3.isRunning())
|
||||
{
|
||||
if (!mp3.loop()) { mp3.stop(); }
|
||||
}
|
||||
else
|
||||
{
|
||||
M5.delay(1);
|
||||
}
|
||||
|
||||
M5.update();
|
||||
if (M5.BtnA.wasClicked())
|
||||
{
|
||||
M5.Speaker.tone(1000, 100);
|
||||
stop();
|
||||
if (++fileindex >= filecount) { fileindex = 0; }
|
||||
play(filename[fileindex]);
|
||||
}
|
||||
else
|
||||
if (M5.BtnA.isHolding() || M5.BtnB.isPressed() || M5.BtnC.isPressed())
|
||||
{
|
||||
size_t v = M5.Speaker.getVolume();
|
||||
if (M5.BtnB.isPressed()) { --v; } else { ++v; }
|
||||
if (v <= 255 || M5.BtnA.isHolding())
|
||||
{
|
||||
M5.Speaker.setVolume(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
777
libraries/M5Unified/examples/Advanced/Mic_FFT/Mic_FFT.ino
Normal file
|
|
@ -0,0 +1,777 @@
|
|||
// External displays can be enabled if necessary
|
||||
// #include <M5ModuleDisplay.h>
|
||||
// #include <M5AtomDisplay.h>
|
||||
// #include <M5UnitGLASS2.h>
|
||||
// #include <M5UnitOLED.h>
|
||||
// #include <M5UnitLCD.h>
|
||||
|
||||
#include <M5Unified.h>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
static void* memory_alloc(size_t size)
|
||||
{
|
||||
return heap_caps_malloc(size, MALLOC_CAP_8BIT);
|
||||
}
|
||||
|
||||
static void memory_free(void* ptr)
|
||||
{
|
||||
heap_caps_free(ptr);
|
||||
}
|
||||
|
||||
struct wav_data_t {
|
||||
int16_t* wav = nullptr;
|
||||
size_t length = 0;
|
||||
size_t latest_index = 0;
|
||||
|
||||
size_t searchEdge(size_t offset, size_t search_length) const {
|
||||
int mem_position = latest_index + offset;
|
||||
if (mem_position >= length) { mem_position -= length; }
|
||||
int mem_difference = 0;
|
||||
int position = - 1;
|
||||
uint32_t counter[2] = { 0,0 };
|
||||
bool prev_sign = false;
|
||||
|
||||
for (size_t i = 0; i < search_length; ++i) {
|
||||
size_t idx = latest_index + i;
|
||||
if (idx >= length) { idx -= length; }
|
||||
int value = wav[idx];
|
||||
bool sign = value < 0;
|
||||
if (prev_sign != sign) {
|
||||
prev_sign = sign;
|
||||
if (sign) { // When changing from positive to negative
|
||||
if (position >= 0) {
|
||||
int diff = counter[0] + counter[1];
|
||||
if (mem_difference < diff) {
|
||||
mem_difference = diff;
|
||||
mem_position = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
counter[sign] = 0;
|
||||
if (i >= offset) {
|
||||
int pidx = (idx ? idx : length) - 1;
|
||||
int cv = abs(wav[idx]);
|
||||
int pv = abs(wav[pidx]);
|
||||
position = (cv < pv) ? idx : pidx;
|
||||
}
|
||||
}
|
||||
uint32_t v = value * value;
|
||||
counter[sign] += v >> 7;
|
||||
}
|
||||
mem_position -= offset;
|
||||
if (mem_position < 0) { mem_position += length; }
|
||||
return mem_position;
|
||||
}
|
||||
};
|
||||
|
||||
struct fft_data_t {
|
||||
float *fdata = nullptr;
|
||||
size_t length = 0;
|
||||
size_t sample_rate;
|
||||
|
||||
wav_data_t *wav_data = nullptr;
|
||||
uint8_t fft_size_bits = 0;
|
||||
|
||||
float getDataByPixel(uint16_t x, uint16_t width) const {
|
||||
if (length <= width) {
|
||||
int index = x * length / width;
|
||||
if (index >= length) { index = length - 1; }
|
||||
return fdata[index];
|
||||
}
|
||||
int index0 = x * length / width;
|
||||
int index1 = (x + 1) * length / width;
|
||||
if (index0 >= length) { index0 = length - 1; }
|
||||
if (index1 >= length) { index1 = length - 1; }
|
||||
float value = 0;
|
||||
for (int i = index0; i < index1; ++i) {
|
||||
if (value < fdata[i]) {
|
||||
value = fdata[i];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
class fft_function_t {
|
||||
public:
|
||||
bool setup(uint8_t max_fft_size_bits)
|
||||
{
|
||||
close();
|
||||
int FFT_SIZE = 1 << max_fft_size_bits;
|
||||
int need_size = 1+(FFT_SIZE * 3 / 4) * sizeof(float) + (FFT_SIZE * sizeof(uint16_t));
|
||||
_work_area = memory_alloc(need_size);
|
||||
if (_work_area == nullptr) {
|
||||
return false;
|
||||
}
|
||||
_max_fft_size_bits = max_fft_size_bits;
|
||||
_initialized_fft_size_bits = 0;
|
||||
|
||||
fi = (float*)memory_alloc(sizeof(float) * FFT_SIZE + 1);
|
||||
fr = (float*)memory_alloc(sizeof(float) * FFT_SIZE + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void close(void)
|
||||
{
|
||||
if (_work_area) {
|
||||
memory_free(_work_area);
|
||||
_work_area = nullptr;
|
||||
}
|
||||
_max_fft_size_bits = 0;
|
||||
_initialized_fft_size_bits = 0;
|
||||
}
|
||||
|
||||
__attribute((optimize("-O3")))
|
||||
bool update(fft_data_t* fft_data)
|
||||
{
|
||||
if (_work_area == nullptr || fft_data == nullptr || fft_data->fdata == nullptr || fft_data->fft_size_bits == 0) {
|
||||
return false;
|
||||
}
|
||||
if (_initialized_fft_size_bits != fft_data->fft_size_bits) {
|
||||
if (!_init(fft_data->fft_size_bits)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int FFT_SIZE = 1 << fft_data->fft_size_bits;
|
||||
|
||||
uint16_t *br = (uint16_t*)_work_area;
|
||||
{
|
||||
auto src = fft_data->wav_data->wav;
|
||||
for (int i = 0; i < FFT_SIZE; ++i)
|
||||
{
|
||||
float lv = src[i];
|
||||
fr[br[i]] = lv;
|
||||
}
|
||||
memset(fi, 0, sizeof(float) * FFT_SIZE);
|
||||
}
|
||||
int s4 = FFT_SIZE / 4;
|
||||
|
||||
float *wi = (float*) (&br[FFT_SIZE]);
|
||||
size_t s = 1;
|
||||
size_t i = 0;
|
||||
size_t je = FFT_SIZE;
|
||||
do
|
||||
{
|
||||
size_t ke = s;
|
||||
s <<= 1;
|
||||
je >>= 1;
|
||||
size_t j = 0;
|
||||
do
|
||||
{
|
||||
size_t k = 0;
|
||||
size_t m = ke * ((j << 1) + 1);
|
||||
size_t l = s * j;
|
||||
auto frm_p = &fr[m];
|
||||
auto fim_p = &fi[m];
|
||||
auto frl_p = &fr[l];
|
||||
auto fil_p = &fi[l];
|
||||
auto wi_p = &wi[0];
|
||||
auto wr_p = &wi[s4];
|
||||
do
|
||||
{
|
||||
// size_t p = je * k;
|
||||
float wi = *wi_p;
|
||||
float wr = *wr_p;
|
||||
float frm = *frm_p;
|
||||
float fim = *fim_p;
|
||||
float Wxmr = frm * wr + fim * wi;
|
||||
float Wxmi = fim * wr - frm * wi;
|
||||
float frl = *frl_p;
|
||||
float fil = *fil_p;
|
||||
*frm_p++ = frl - Wxmr;
|
||||
*frl_p++ = frl + Wxmr;
|
||||
*fim_p++ = fil - Wxmi;
|
||||
*fil_p++ = fil + Wxmi;
|
||||
wi_p += je;
|
||||
wr_p += je;
|
||||
} while ( ++k < ke);
|
||||
} while ( ++j < je );
|
||||
} while ( ++i < _initialized_fft_size_bits );
|
||||
|
||||
|
||||
int loop_end = fft_data->length;
|
||||
if (loop_end > FFT_SIZE) {
|
||||
loop_end = FFT_SIZE;
|
||||
}
|
||||
|
||||
float max_value = 0.0f;
|
||||
for (uint32_t i = 0; i < loop_end; ++i) {
|
||||
float vr = fr[i];
|
||||
float vi = fi[i];
|
||||
float v = sqrtf(vr * vr + vi * vi);
|
||||
fft_data->fdata[i] = v;
|
||||
if (max_value < v) { max_value = v; }
|
||||
}
|
||||
|
||||
// peak level adjust
|
||||
float k = (65536.0f) / max_value;
|
||||
if (k > 0.03125f) { k = 0.03125f; } else
|
||||
{
|
||||
for (uint32_t i = 0; i < loop_end; ++i) {
|
||||
float tmp = fft_data->fdata[i];
|
||||
tmp *= k;
|
||||
fft_data->fdata[i] = tmp;
|
||||
}
|
||||
}
|
||||
//*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
bool _init(uint8_t size_bits)
|
||||
{
|
||||
if (_max_fft_size_bits < size_bits) { return false; }
|
||||
_initialized_fft_size_bits = size_bits;
|
||||
|
||||
uint16_t *br = (uint16_t*)_work_area;
|
||||
|
||||
int FFT_SIZE = 1 << size_bits;
|
||||
float *wi = (float*) (&br[FFT_SIZE]);
|
||||
|
||||
size_t je = 1;
|
||||
br[0] = 0;
|
||||
br[1] = FFT_SIZE >> 1;
|
||||
for ( size_t i = 0 ; i < size_bits - 1 ; ++i )
|
||||
{
|
||||
br[ je << 1 ] = br[ je ] >> 1;
|
||||
je = je << 1;
|
||||
for ( size_t j = 1 ; j < je ; ++j )
|
||||
{
|
||||
br[je + j] = br[je] + br[j];
|
||||
}
|
||||
}
|
||||
|
||||
float omega = 2.0f * M_PI / FFT_SIZE;
|
||||
int s2 = FFT_SIZE >> 1;
|
||||
int s4 = FFT_SIZE >> 2;
|
||||
wi[0] = 0;
|
||||
wi[s2] = 0;
|
||||
wi[s4] = 1;
|
||||
for ( int i = 1 ; i < s4 ; ++i)
|
||||
{
|
||||
float f = cosf(omega * i);
|
||||
wi[s4 + i] = f;
|
||||
wi[s4 - i] = f;
|
||||
wi[s4+s2 - i] = -f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
float* fi;
|
||||
float* fr;
|
||||
void* _work_area = nullptr;
|
||||
uint8_t _max_fft_size_bits = 0;
|
||||
uint8_t _initialized_fft_size_bits;
|
||||
};
|
||||
|
||||
|
||||
struct rect_t
|
||||
{
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
};
|
||||
|
||||
|
||||
class wav_drawer_t
|
||||
{
|
||||
LGFX_Device* _gfx = nullptr;
|
||||
int16_t* prev_y = nullptr;
|
||||
int16_t* prev_h = nullptr;
|
||||
rect_t draw_rect = {0, 0, 0, 0};
|
||||
uint32_t bg_color = 0x000000u;
|
||||
uint32_t fg_color = 0xFFFFFFu;
|
||||
uint32_t line_color = 0x303030u;
|
||||
|
||||
public:
|
||||
bool setup(LGFX_Device* gfx, const rect_t& rect)
|
||||
{
|
||||
if (gfx == nullptr) { return false; }
|
||||
_gfx = gfx;
|
||||
draw_rect = rect;
|
||||
gfx->fillRect(rect.x, rect.y, rect.w, rect.h, bg_color);
|
||||
gfx->drawFastVLine(rect.x + (rect.w >> 1), rect.y, rect.h, line_color);
|
||||
int width = rect.w;
|
||||
prev_y = (int16_t*)memory_alloc(width * sizeof(int16_t));
|
||||
prev_h = (int16_t*)memory_alloc(width * sizeof(int16_t));
|
||||
memset(prev_y, 0, width * sizeof(int16_t));
|
||||
memset(prev_h, 0, width * sizeof(int16_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update(const wav_data_t& wav_data)
|
||||
{
|
||||
auto gfx = _gfx;
|
||||
|
||||
int32_t width = draw_rect.w;
|
||||
int32_t height = draw_rect.h;
|
||||
|
||||
int wav_count = wav_data.length;
|
||||
int wav_index = wav_data.searchEdge(draw_rect.w >> 1, wav_count / 3);
|
||||
auto wav = wav_data.wav;
|
||||
|
||||
int32_t max_value = 1;
|
||||
auto wi = wav_index;
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
int32_t tmp = abs(wav[wi]);
|
||||
if (max_value < tmp) { max_value = tmp; }
|
||||
if (++wi >= wav_count) { wi = 0; }
|
||||
}
|
||||
int new_k = (draw_rect.h << 15) / max_value;
|
||||
if (new_k > 65536)
|
||||
{ new_k = 65536; }
|
||||
static int k;
|
||||
if (k > new_k) { k = new_k; }
|
||||
else {
|
||||
k = (k * 127 + new_k) >> 7;
|
||||
}
|
||||
int32_t value1 = (32768-wav[wav_index] * k) >> 16;
|
||||
int32_t value2 = value1;
|
||||
int32_t base_y = draw_rect.y + (height >> 1);
|
||||
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
if (++wav_index >= wav_count) { wav_index = 0; }
|
||||
int32_t x = i + draw_rect.x;
|
||||
int32_t y = prev_y[i];
|
||||
int32_t h = prev_h[i];
|
||||
|
||||
gfx->setColor(i == (width >> 1) ? line_color : bg_color);
|
||||
gfx->drawFastVLine(x, base_y + y, h);
|
||||
|
||||
int32_t value0 = value1;
|
||||
value1 = value2;
|
||||
value2 = (32768-wav[wav_index] * k) >> 16;
|
||||
int32_t value_01 = (value0 + value1) >> 1;
|
||||
int32_t value_12 = (value1 + value2) >> 1;
|
||||
|
||||
int32_t y_min = (value_01 < value_12) ? value_01 : value_12;
|
||||
if (y_min > value1) { y_min = value1; }
|
||||
|
||||
int32_t y_max = (value_01 > value_12) ? value_01 : value_12;
|
||||
if (y_max < value1) { y_max = value1; }
|
||||
|
||||
y = y_min;
|
||||
h = y_max + 1 - y;
|
||||
prev_y[i] = y;
|
||||
prev_h[i] = h;
|
||||
gfx->drawPixel(x, base_y, line_color);
|
||||
/// draw new wave.
|
||||
gfx->drawFastVLine(x, base_y + y, h, fg_color);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class fft_drawer_t
|
||||
{
|
||||
LGFX_Device* _gfx = nullptr;
|
||||
|
||||
uint16_t* prev_y = nullptr;
|
||||
uint16_t* prev_h = nullptr;
|
||||
rect_t draw_rect = {0, 0, 0, 0};
|
||||
uint32_t bg_color = 0x000066u;
|
||||
uint32_t fg_color = 0x00FF00u;
|
||||
|
||||
public:
|
||||
bool setup(LGFX_Device* gfx, const rect_t& rect)
|
||||
{
|
||||
if (gfx == nullptr) { return false; }
|
||||
_gfx = gfx;
|
||||
draw_rect = rect;
|
||||
gfx->fillRect(rect.x, rect.y, rect.w, rect.h, bg_color);
|
||||
int width = rect.w;
|
||||
|
||||
prev_y = (uint16_t*)memory_alloc(width * sizeof(int16_t));
|
||||
prev_h = (uint16_t*)memory_alloc(width * sizeof(int16_t));
|
||||
memset(prev_y, 0, width * sizeof(int16_t));
|
||||
memset(prev_h, 0, width * sizeof(int16_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update(const fft_data_t& fft_data)
|
||||
{
|
||||
auto gfx = _gfx;
|
||||
|
||||
int32_t width = draw_rect.w;
|
||||
int32_t height = draw_rect.h - 1;
|
||||
|
||||
int32_t value1 = height - (((int32_t)(fft_data.getDataByPixel(0, width) * height)) >> 16);
|
||||
if (value1 < 0) { value1 = 0; }
|
||||
int32_t value2 = value1;
|
||||
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
int32_t x = i + draw_rect.x;
|
||||
int32_t y = prev_y[i];
|
||||
int32_t h = prev_h[i];
|
||||
|
||||
gfx->drawFastVLine(x, draw_rect.y + y, h, bg_color);
|
||||
|
||||
int32_t value0 = value1;
|
||||
value1 = value2;
|
||||
value2 = height - (((int32_t)(fft_data.getDataByPixel(i+1, width) * height)) >> 16);
|
||||
if (value2 < 0) { value2 = 0; }
|
||||
|
||||
int32_t value_01 = (value0 + value1) >> 1;
|
||||
int32_t value_12 = (value1 + value2) >> 1;
|
||||
|
||||
int32_t y_min = (value_01 < value_12) ? value_01 : value_12;
|
||||
if (y_min > value1) { y_min = value1; }
|
||||
|
||||
int32_t y_max = (value_01 > value_12) ? value_01 : value_12;
|
||||
if (y_max < value1) { y_max = value1; }
|
||||
|
||||
y = y_min;
|
||||
h = y_max + 1 - y;
|
||||
prev_y[i] = y;
|
||||
prev_h[i] = h;
|
||||
|
||||
gfx->drawFastVLine(x, draw_rect.y + y, h, fg_color);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class fft_peak_t
|
||||
{
|
||||
LGFX_Device* _gfx = nullptr;
|
||||
|
||||
rect_t draw_rect = {0, 0, 0, 0};
|
||||
uint32_t bg_color = 0x000000u;
|
||||
uint32_t fg_color = 0x00FFFFu;
|
||||
std::set<uint16_t> peak_index_set;
|
||||
uint16_t prev_peak_index = UINT16_MAX;
|
||||
char text_buf[10] = {0,};
|
||||
char prev_text[10] = {0,};
|
||||
uint8_t step = 0;
|
||||
public:
|
||||
bool setup(LGFX_Device* gfx, const rect_t& rect)
|
||||
{
|
||||
if (gfx == nullptr) { return false; }
|
||||
_gfx = gfx;
|
||||
draw_rect = rect;
|
||||
gfx->fillRect(rect.x, rect.y, rect.w, rect.h, bg_color);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update(const fft_data_t& fft_data)
|
||||
{
|
||||
auto gfx = _gfx;
|
||||
|
||||
auto fdata = fft_data.fdata;
|
||||
|
||||
int32_t loop_end = fft_data.length;
|
||||
|
||||
auto boarder_value = (uint16_t*)memory_alloc(sizeof(uint16_t) * loop_end);
|
||||
memset(boarder_value, 0, sizeof(uint16_t) * loop_end);
|
||||
{
|
||||
float thresh = 0;
|
||||
for (int i = 0; i < loop_end; ++i)
|
||||
{
|
||||
float value = fdata[i];
|
||||
if (thresh < value) {
|
||||
thresh = value;
|
||||
}
|
||||
boarder_value[i] = thresh;
|
||||
thresh = thresh * 0.9f;
|
||||
}
|
||||
for (int i = loop_end - 1; i >= 0; --i)
|
||||
{
|
||||
int value = fdata[i];
|
||||
if (thresh < value) {
|
||||
thresh = value;
|
||||
}
|
||||
if (boarder_value[i] < thresh) {
|
||||
boarder_value[i] = thresh;
|
||||
}
|
||||
thresh = thresh * 0.9f;
|
||||
}
|
||||
}
|
||||
static constexpr const size_t peak_index_set_size = 8;
|
||||
std::multimap<float, uint16_t> peak_map;
|
||||
for (int i = 0; i < loop_end; ++i)
|
||||
{
|
||||
float value = fdata[i];
|
||||
if (value >= boarder_value[i]) {
|
||||
peak_map.insert(std::make_pair(value, i));
|
||||
if (peak_map.size() > peak_index_set_size) {
|
||||
peak_map.erase(peak_map.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
memory_free(boarder_value);
|
||||
if (peak_map.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
auto c = text_buf[step];
|
||||
if (prev_text[step] != c) {
|
||||
prev_text[step] = c;
|
||||
gfx->drawChar(c, draw_rect.x + (step * draw_rect.w >> 3), draw_rect.y);
|
||||
break;
|
||||
}
|
||||
if (++step >= 8)
|
||||
{
|
||||
step = 0;
|
||||
uint16_t peak_index = peak_map.rbegin()->second;
|
||||
if (prev_peak_index != peak_index) {
|
||||
prev_peak_index = peak_index;
|
||||
gfx->setTextDatum(m5gfx::datum_t::top_right);
|
||||
float freq = (float)(fft_data.sample_rate * peak_index) / (float)(1<<fft_data.fft_size_bits);
|
||||
snprintf(text_buf, sizeof(text_buf), "%8.1f", freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width = draw_rect.w;
|
||||
int icon_size = peak_index_set_size;
|
||||
for (auto peak_index : peak_index_set)
|
||||
{
|
||||
int x = draw_rect.x + (peak_index * width / fft_data.length);
|
||||
int y = draw_rect.y + draw_rect.h - icon_size - 1;
|
||||
gfx->fillRect(x - icon_size, y, icon_size * 2 + 1, icon_size+1, bg_color);
|
||||
}
|
||||
peak_index_set.clear();
|
||||
|
||||
for (auto it = peak_map.rbegin(); it != peak_map.rend(); ++it)
|
||||
{
|
||||
int peak_index = it->second;
|
||||
int x = draw_rect.x + (peak_index * width / fft_data.length);
|
||||
int y = draw_rect.y + draw_rect.h - 1;
|
||||
gfx->drawTriangle(x, y, x - icon_size, y - icon_size, x + icon_size, y - icon_size, fg_color);
|
||||
peak_index_set.insert(peak_index);
|
||||
icon_size -= 2;
|
||||
if (icon_size <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class fft_history_t
|
||||
{
|
||||
LGFX_Device* _gfx = nullptr;
|
||||
|
||||
uint8_t* color_map = nullptr;
|
||||
rect_t draw_rect = {0, 0, 0, 0};
|
||||
uint32_t bg_color = 0x000033u;
|
||||
uint32_t fg_color = 0xFFFF00u;
|
||||
int step = 0;
|
||||
public:
|
||||
bool setup(LGFX_Device* gfx, const rect_t& rect)
|
||||
{
|
||||
if (gfx == nullptr) { return false; }
|
||||
_gfx = gfx;
|
||||
draw_rect = rect;
|
||||
int width = rect.w;
|
||||
int height = rect.h;
|
||||
|
||||
color_map = (uint8_t*)memory_alloc(width * height * sizeof(uint8_t));
|
||||
memset(color_map, 0, width * height * sizeof(uint8_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update(const fft_data_t& fft_data)
|
||||
{
|
||||
int32_t width = draw_rect.w;
|
||||
int32_t height = draw_rect.h;
|
||||
|
||||
if (--step < 0) {
|
||||
step = 7;
|
||||
memmove(&color_map[width], color_map, width * (height - 1));
|
||||
memset(color_map, 0, width);
|
||||
}
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
int v = fft_data.getDataByPixel(i, width) / 256.0f;
|
||||
if (v > 255) { v = 255; }
|
||||
color_map[i] = color_map[i] > v ? color_map[i] : v;
|
||||
}
|
||||
int y0 = ( step * draw_rect.h) >> 3;
|
||||
int y1 = ((step + 1) * draw_rect.h) >> 3;
|
||||
_gfx->pushGrayscaleImage(draw_rect.x, draw_rect.y + y0, draw_rect.w, y1 - y0, &color_map[y0*width], m5gfx::color_depth_t::grayscale_8bit, fg_color, bg_color);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// The higher the sample rate, the higher the frequency results obtained by FFT.
|
||||
// If limited to the audible range, 24kHz to 48kHz is sufficient.
|
||||
static constexpr const size_t SAMPLE_RATE = 24000;
|
||||
// static constexpr const size_t SAMPLE_RATE = 96000;
|
||||
|
||||
// The larger the FFT_BITS, the higher the accuracy of the FFT, but the processing load also increases
|
||||
static constexpr const size_t FFT_BITS = 10;
|
||||
|
||||
// WAVE_BLOCK_SIZE is the size of one processing when capturing data from I2S.
|
||||
// If it is too large, the loop cycle will be slow and the frequency of drawing updates will decrease.
|
||||
// If it is too small, recoding interruptions will occur.
|
||||
// For example, if the sample rate is 96kHz and the block size is 384, data will be captured every 4ms.
|
||||
// Therefore, the drawing process, FFT process, and other loop iterations must be completed within 4ms.
|
||||
static constexpr const size_t WAVE_BLOCK_SIZE = 256;
|
||||
|
||||
static constexpr const size_t FFT_SIZE = 1u << FFT_BITS;
|
||||
static constexpr const size_t WAVE_BLOCK_COUNT = 3 + FFT_SIZE / WAVE_BLOCK_SIZE;
|
||||
static constexpr const size_t WAVE_TOTAL_SIZE = WAVE_BLOCK_SIZE * WAVE_BLOCK_COUNT;
|
||||
static fft_function_t fft_function;
|
||||
static fft_data_t fft_data;
|
||||
static wav_data_t wav_data;
|
||||
static wav_drawer_t wav_drawer;
|
||||
static fft_drawer_t fft_drawer;
|
||||
static fft_peak_t fft_peak;
|
||||
static fft_history_t fft_history;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
#if defined ( __M5GFX_M5MODULEDISPLAY__ )
|
||||
cfg.module_display.logical_width = 320;
|
||||
cfg.module_display.logical_height = 180;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5ATOMDISPLAY__ )
|
||||
cfg.atom_display.logical_width = 320;
|
||||
cfg.atom_display.logical_height = 180;
|
||||
#endif
|
||||
|
||||
// use for ATOMIC ECHO BASE
|
||||
cfg.external_speaker.atomic_echo = true;
|
||||
|
||||
M5.begin(cfg);
|
||||
M5.setPrimaryDisplayType({m5gfx::board_M5ModuleDisplay, m5gfx::board_M5AtomDisplay});
|
||||
M5.Speaker.tone(440, 100);
|
||||
M5.delay(100);
|
||||
{
|
||||
auto cfg = M5.Mic.config();
|
||||
cfg.dma_buf_count = 3;
|
||||
cfg.dma_buf_len = WAVE_BLOCK_SIZE;
|
||||
cfg.over_sampling = 1;
|
||||
cfg.noise_filter_level = 0;
|
||||
cfg.sample_rate = SAMPLE_RATE;
|
||||
cfg.magnification = cfg.use_adc ? 16 : 1;
|
||||
|
||||
/*
|
||||
{ // use for Unit PDM ( Port A )
|
||||
cfg.pin_data_in = M5.getPin(m5::pin_name_t::port_a_pin2);
|
||||
cfg.pin_ws = M5.getPin(m5::pin_name_t::port_a_pin1);
|
||||
cfg.pin_bck = -1;
|
||||
cfg.pin_mck = -1;
|
||||
cfg.use_adc = false;
|
||||
cfg.stereo = false;
|
||||
cfg.i2s_port = i2s_port_t::I2S_NUM_0;
|
||||
}
|
||||
//*/
|
||||
M5.Mic.config(cfg);
|
||||
}
|
||||
|
||||
if (!M5.Mic.isEnabled()) {
|
||||
M5.Display.printf("microphone is not available.");
|
||||
M5_LOGE("microphone is not available.");
|
||||
for (;;) {
|
||||
M5.delay(256);
|
||||
}
|
||||
}
|
||||
M5.Speaker.end();
|
||||
M5.Mic.begin();
|
||||
|
||||
M5.Display.startWrite();
|
||||
|
||||
int16_t w = M5.Display.width();
|
||||
int16_t h = M5.Display.height() >> 2;
|
||||
int16_t y = 0;
|
||||
|
||||
rect_t rect_fft_peak = {0, y, w, h};
|
||||
y += h;
|
||||
rect_t rect_fft_drawer = {0, y, w, h};
|
||||
y += h;
|
||||
rect_t rect_fft_history = {0, y, w, h};
|
||||
y += h;
|
||||
rect_t rect_wav_drawer = {0, y, w, h};
|
||||
|
||||
fft_function.setup(FFT_BITS);
|
||||
fft_peak.setup(&M5.Display, rect_fft_peak);
|
||||
fft_drawer.setup(&M5.Display, rect_fft_drawer);
|
||||
fft_history.setup(&M5.Display, rect_fft_history);
|
||||
wav_drawer.setup(&M5.Display, rect_wav_drawer);
|
||||
|
||||
M5.Display.setTextSize(w / 64.0f, h / 16.0f);
|
||||
M5.Display.setFont(&fonts::AsciiFont8x16);
|
||||
M5.Display.setEpdMode(epd_mode_t::epd_fastest);
|
||||
|
||||
fft_data.fft_size_bits = FFT_BITS;
|
||||
fft_data.sample_rate = SAMPLE_RATE;
|
||||
fft_data.wav_data = &wav_data;
|
||||
fft_data.length = (1 << (fft_data.fft_size_bits - 1)) + 1;
|
||||
fft_data.fdata = (typeof(fft_data.fdata))memory_alloc(fft_data.length * sizeof(fft_data.fdata[0]));
|
||||
|
||||
wav_data.length = WAVE_TOTAL_SIZE;
|
||||
wav_data.wav = (typeof(wav_data.wav))memory_alloc(WAVE_TOTAL_SIZE * sizeof(wav_data.wav[0]));
|
||||
memset(wav_data.wav, 0 , WAVE_TOTAL_SIZE * sizeof(int16_t));
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
static int step = -1;
|
||||
|
||||
if (M5.Mic.isEnabled())
|
||||
{
|
||||
int wav_idx = wav_data.latest_index;
|
||||
while (M5.Mic.isRecording() < 2) {
|
||||
M5.Mic.record(&(wav_data.wav[wav_idx]), WAVE_BLOCK_SIZE, SAMPLE_RATE, false);
|
||||
wav_idx += WAVE_BLOCK_SIZE;
|
||||
if (wav_idx >= WAVE_TOTAL_SIZE) { wav_idx = 0; }
|
||||
wav_data.latest_index = wav_idx;
|
||||
};
|
||||
|
||||
{
|
||||
switch (++step) {
|
||||
default:
|
||||
step = 0;
|
||||
M5.Display.display();
|
||||
fft_function.update(&fft_data);
|
||||
break;
|
||||
case 1:
|
||||
wav_drawer.update(wav_data);
|
||||
break;
|
||||
case 2:
|
||||
fft_drawer.update(fft_data);
|
||||
break;
|
||||
case 3:
|
||||
fft_history.update(fft_data);
|
||||
break;
|
||||
case 4:
|
||||
fft_peak.update(fft_data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for ESP-IDF
|
||||
extern "C" {
|
||||
__attribute__((weak))
|
||||
void app_main()
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
#include <M5UnitLCD.h>
|
||||
#include <M5UnitOLED.h>
|
||||
#include <M5Unified.h>
|
||||
|
||||
/// need AquesTalk library. ( URL : https://www.a-quest.com/ )
|
||||
#include <aquestalk.h>
|
||||
|
||||
/// set M5Speaker virtual channel (0-7)
|
||||
static constexpr uint8_t m5spk_virtual_channel = 0;
|
||||
|
||||
static constexpr uint8_t LEN_FRAME = 32;
|
||||
|
||||
static uint32_t workbuf[AQ_SIZE_WORKBUF];
|
||||
static TaskHandle_t task_handle = nullptr;
|
||||
volatile bool is_talking = false;
|
||||
|
||||
static void talk_task(void*)
|
||||
{
|
||||
int16_t wav[3][LEN_FRAME];
|
||||
int tri_index = 0;
|
||||
for (;;)
|
||||
{
|
||||
ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); // wait notify
|
||||
while (is_talking)
|
||||
{
|
||||
uint16_t len;
|
||||
if (CAqTkPicoF_SyntheFrame(wav[tri_index], &len)) { is_talking = false; break; }
|
||||
M5.Speaker.playRaw(wav[tri_index], len, 8000, false, 1, m5spk_virtual_channel, false);
|
||||
tri_index = tri_index < 2 ? tri_index + 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 音声再生の終了を待機する;
|
||||
static void waitAquesTalk(void)
|
||||
{
|
||||
while (is_talking) { vTaskDelay(1); }
|
||||
}
|
||||
|
||||
/// 音声再生を停止する;
|
||||
static void stopAquesTalk(void)
|
||||
{
|
||||
if (is_talking) { is_talking = false; vTaskDelay(1); }
|
||||
}
|
||||
|
||||
/// 音声再生を開始する。(再生中の場合は中断して新たな音声再生を開始する) ;
|
||||
static void playAquesTalk(const char *koe)
|
||||
{
|
||||
stopAquesTalk();
|
||||
|
||||
M5.Display.printf("Play:%s\n", koe);
|
||||
|
||||
int iret = CAqTkPicoF_SetKoe((const uint8_t*)koe, 100, 0xFFu);
|
||||
if (iret) { M5.Display.println("ERR:CAqTkPicoF_SetKoe"); }
|
||||
|
||||
is_talking = true;
|
||||
xTaskNotifyGive(task_handle);
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// If you want to play sound from ModuleDisplay, write this
|
||||
// cfg.external_speaker.module_display = true;
|
||||
|
||||
// If you want to play sound from ModuleRCA, write this
|
||||
// cfg.external_speaker.module_rca = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker, write this
|
||||
cfg.external_speaker.hat_spk = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker2, write this
|
||||
// cfg.external_speaker.hat_spk2 = true;
|
||||
|
||||
// If you want to play sound from ATOMIC Speaker, write this
|
||||
cfg.external_speaker.atomic_spk = true;
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
xTaskCreateUniversal(talk_task, "talk_task", 4096, nullptr, 1, &task_handle, APP_CPU_NUM);
|
||||
/*
|
||||
/// Increasing the sample_rate will improve the sound quality instead of increasing the CPU load.
|
||||
auto spk_cfg = M5.Speaker.config();
|
||||
spk_cfg.sample_rate = 96000; // default:64000 (64kHz) e.g. 48000 , 50000 , 80000 , 96000 , 100000 , 128000 , 144000 , 192000 , 200000
|
||||
M5.Speaker.config(spk_cfg);
|
||||
//*/
|
||||
M5.Speaker.setVolume(128);
|
||||
|
||||
M5.Display.setEpdMode(epd_mode_t::epd_fastest);
|
||||
M5.Display.setTextWrap(true);
|
||||
|
||||
int iret = CAqTkPicoF_Init(workbuf, LEN_FRAME, "XXX-XXX-XXX");
|
||||
if (iret) {
|
||||
M5.Display.println("ERR:CAqTkPicoF_Init");
|
||||
}
|
||||
|
||||
playAquesTalk("akue_suto'-_ku/kido-shima'_shita.");
|
||||
waitAquesTalk();
|
||||
playAquesTalk("botanno/o_shitekudasa'i.");
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.update();
|
||||
|
||||
if ( M5.BtnA.wasClicked()) { playAquesTalk("kuri'kku"); }
|
||||
else if (M5.BtnA.wasHold()) { playAquesTalk("ho'-rudo"); }
|
||||
else if (M5.BtnA.wasReleased()) { playAquesTalk("riri'-su"); }
|
||||
else if (M5.BtnB.wasReleased()) { playAquesTalk("korewa;te'_sutode_su."); }
|
||||
else if (M5.BtnC.wasReleased()) { playAquesTalk("yukkuri_siteittene?"); }
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreateUniversal(loopTask, "loopTask", 8192, NULL, 1, NULL, APP_CPU_NUM);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
#include <SD.h>
|
||||
#include <M5Unified.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
static constexpr const gpio_num_t SDCARD_CSPIN = GPIO_NUM_4;
|
||||
|
||||
static constexpr const char* files[] = {
|
||||
"/file1.wav",
|
||||
"/file2.wav",
|
||||
"/file3.wav",
|
||||
};
|
||||
|
||||
static constexpr const size_t buf_num = 3;
|
||||
static constexpr const size_t buf_size = 1024;
|
||||
static uint8_t wav_data[buf_num][buf_size];
|
||||
|
||||
struct __attribute__((packed)) wav_header_t
|
||||
{
|
||||
char RIFF[4];
|
||||
uint32_t chunk_size;
|
||||
char WAVEfmt[8];
|
||||
uint32_t fmt_chunk_size;
|
||||
uint16_t audiofmt;
|
||||
uint16_t channel;
|
||||
uint32_t sample_rate;
|
||||
uint32_t byte_per_sec;
|
||||
uint16_t block_size;
|
||||
uint16_t bit_per_sample;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) sub_chunk_t
|
||||
{
|
||||
char identifier[4];
|
||||
uint32_t chunk_size;
|
||||
uint8_t data[1];
|
||||
};
|
||||
|
||||
static bool playSdWav(const char* filename)
|
||||
{
|
||||
auto file = SD.open(filename);
|
||||
|
||||
if (!file) { return false; }
|
||||
|
||||
wav_header_t wav_header;
|
||||
file.read((uint8_t*)&wav_header, sizeof(wav_header_t));
|
||||
|
||||
ESP_LOGD("wav", "RIFF : %.4s" , wav_header.RIFF );
|
||||
ESP_LOGD("wav", "chunk_size : %d" , wav_header.chunk_size );
|
||||
ESP_LOGD("wav", "WAVEfmt : %.8s" , wav_header.WAVEfmt );
|
||||
ESP_LOGD("wav", "fmt_chunk_size : %d" , wav_header.fmt_chunk_size);
|
||||
ESP_LOGD("wav", "audiofmt : %d" , wav_header.audiofmt );
|
||||
ESP_LOGD("wav", "channel : %d" , wav_header.channel );
|
||||
ESP_LOGD("wav", "sample_rate : %d" , wav_header.sample_rate );
|
||||
ESP_LOGD("wav", "byte_per_sec : %d" , wav_header.byte_per_sec );
|
||||
ESP_LOGD("wav", "block_size : %d" , wav_header.block_size );
|
||||
ESP_LOGD("wav", "bit_per_sample : %d" , wav_header.bit_per_sample);
|
||||
|
||||
if ( memcmp(wav_header.RIFF, "RIFF", 4)
|
||||
|| memcmp(wav_header.WAVEfmt, "WAVEfmt ", 8)
|
||||
|| wav_header.audiofmt != 1
|
||||
|| wav_header.bit_per_sample < 8
|
||||
|| wav_header.bit_per_sample > 16
|
||||
|| wav_header.channel == 0
|
||||
|| wav_header.channel > 2
|
||||
)
|
||||
{
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
file.seek(offsetof(wav_header_t, audiofmt) + wav_header.fmt_chunk_size);
|
||||
sub_chunk_t sub_chunk;
|
||||
|
||||
file.read((uint8_t*)&sub_chunk, 8);
|
||||
|
||||
ESP_LOGD("wav", "sub id : %.4s" , sub_chunk.identifier);
|
||||
ESP_LOGD("wav", "sub chunk_size : %d" , sub_chunk.chunk_size);
|
||||
|
||||
while(memcmp(sub_chunk.identifier, "data", 4))
|
||||
{
|
||||
if (!file.seek(sub_chunk.chunk_size, SeekMode::SeekCur)) { break; }
|
||||
file.read((uint8_t*)&sub_chunk, 8);
|
||||
|
||||
ESP_LOGD("wav", "sub id : %.4s" , sub_chunk.identifier);
|
||||
ESP_LOGD("wav", "sub chunk_size : %d" , sub_chunk.chunk_size);
|
||||
}
|
||||
|
||||
if (memcmp(sub_chunk.identifier, "data", 4))
|
||||
{
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t data_len = sub_chunk.chunk_size;
|
||||
bool flg_16bit = (wav_header.bit_per_sample >> 4);
|
||||
|
||||
size_t idx = 0;
|
||||
while (data_len > 0) {
|
||||
size_t len = data_len < buf_size ? data_len : buf_size;
|
||||
len = file.read(wav_data[idx], len);
|
||||
data_len -= len;
|
||||
|
||||
if (flg_16bit) {
|
||||
M5.Speaker.playRaw((const int16_t*)wav_data[idx], len >> 1, wav_header.sample_rate, wav_header.channel > 1, 1, 0);
|
||||
} else {
|
||||
M5.Speaker.playRaw((const uint8_t*)wav_data[idx], len, wav_header.sample_rate, wav_header.channel > 1, 1, 0);
|
||||
}
|
||||
idx = idx < (buf_num - 1) ? idx + 1 : 0;
|
||||
}
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
M5.begin();
|
||||
|
||||
SD.begin(SDCARD_CSPIN, SPI, 25000000);
|
||||
|
||||
// M5.Speaker.setVolume(32);
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
for (auto filename : files) {
|
||||
playSdWav(filename);
|
||||
M5.delay(500);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,681 @@
|
|||
#define WIFI_SSID "SET YOUR WIFI SSID"
|
||||
#define WIFI_PASS "SET YOUR WIFI PASS"
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <math.h>
|
||||
|
||||
/// need ESP8266Audio library. ( URL : https://github.com/earlephilhower/ESP8266Audio/ )
|
||||
#include <AudioOutput.h>
|
||||
#include <AudioFileSourceICYStream.h>
|
||||
#include <AudioFileSource.h>
|
||||
#include <AudioFileSourceBuffer.h>
|
||||
#include <AudioGeneratorMP3.h>
|
||||
|
||||
#include <M5UnitLCD.h>
|
||||
#include <M5UnitOLED.h>
|
||||
#include <M5Unified.h>
|
||||
|
||||
/// set M5Speaker virtual channel (0-7)
|
||||
static constexpr uint8_t m5spk_virtual_channel = 0;
|
||||
|
||||
/// set web radio station url
|
||||
static constexpr const char* station_list[][2] =
|
||||
{
|
||||
{"thejazzstream" , "http://wbgo.streamguys.net/thejazzstream"},
|
||||
{"181-beatles_128k" , "http://listen.181fm.com/181-beatles_128k.mp3"},
|
||||
{"illstreet-128-mp3" , "http://ice1.somafm.com/illstreet-128-mp3"},
|
||||
{"bootliquor-128-mp3", "http://ice1.somafm.com/bootliquor-128-mp3"},
|
||||
{"dronezone-128-mp3" , "http://ice1.somafm.com/dronezone-128-mp3"},
|
||||
{"Lite Favorites" , "http://naxos.cdnstream.com:80/1255_128"},
|
||||
{"Classic FM" , "http://media-ice.musicradio.com:80/ClassicFMMP3"},
|
||||
{"MAXXED Out" , "http://149.56.195.94:8015/steam"},
|
||||
{"Asia Dream" , "http://igor.torontocast.com:1025/;.-mp3"},
|
||||
};
|
||||
static constexpr const size_t stations = sizeof(station_list) / sizeof(station_list[0]);
|
||||
|
||||
class AudioOutputM5Speaker : public AudioOutput
|
||||
{
|
||||
public:
|
||||
AudioOutputM5Speaker(m5::Speaker_Class* m5sound, uint8_t virtual_sound_channel = 0)
|
||||
{
|
||||
_m5sound = m5sound;
|
||||
_virtual_ch = virtual_sound_channel;
|
||||
}
|
||||
virtual ~AudioOutputM5Speaker(void) {};
|
||||
virtual bool begin(void) override { return true; }
|
||||
virtual bool ConsumeSample(int16_t sample[2]) override
|
||||
{
|
||||
if (_tri_buffer_index < tri_buf_size)
|
||||
{
|
||||
_tri_buffer[_tri_index][_tri_buffer_index ] = sample[0];
|
||||
_tri_buffer[_tri_index][_tri_buffer_index+1] = sample[1];
|
||||
_tri_buffer_index += 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
flush();
|
||||
return false;
|
||||
}
|
||||
virtual void flush(void) override
|
||||
{
|
||||
if (_tri_buffer_index)
|
||||
{
|
||||
_m5sound->playRaw(_tri_buffer[_tri_index], _tri_buffer_index, hertz, true, 1, _virtual_ch);
|
||||
_tri_index = _tri_index < 2 ? _tri_index + 1 : 0;
|
||||
_tri_buffer_index = 0;
|
||||
++_update_count;
|
||||
}
|
||||
}
|
||||
virtual bool stop(void) override
|
||||
{
|
||||
flush();
|
||||
_m5sound->stop(_virtual_ch);
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
memset(_tri_buffer[i], 0, tri_buf_size * sizeof(int16_t));
|
||||
}
|
||||
++_update_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
const int16_t* getBuffer(void) const { return _tri_buffer[(_tri_index + 2) % 3]; }
|
||||
const uint32_t getUpdateCount(void) const { return _update_count; }
|
||||
|
||||
protected:
|
||||
m5::Speaker_Class* _m5sound;
|
||||
uint8_t _virtual_ch;
|
||||
static constexpr size_t tri_buf_size = 640;
|
||||
int16_t _tri_buffer[3][tri_buf_size];
|
||||
size_t _tri_buffer_index = 0;
|
||||
size_t _tri_index = 0;
|
||||
size_t _update_count = 0;
|
||||
};
|
||||
|
||||
|
||||
#define FFT_SIZE 256
|
||||
class fft_t
|
||||
{
|
||||
float _wr[FFT_SIZE + 1];
|
||||
float _wi[FFT_SIZE + 1];
|
||||
float _fr[FFT_SIZE + 1];
|
||||
float _fi[FFT_SIZE + 1];
|
||||
uint16_t _br[FFT_SIZE + 1];
|
||||
size_t _ie;
|
||||
|
||||
public:
|
||||
fft_t(void)
|
||||
{
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592653
|
||||
#endif
|
||||
_ie = logf( (float)FFT_SIZE ) / log(2.0) + 0.5;
|
||||
static constexpr float omega = 2.0f * M_PI / FFT_SIZE;
|
||||
static constexpr int s4 = FFT_SIZE / 4;
|
||||
static constexpr int s2 = FFT_SIZE / 2;
|
||||
for ( int i = 1 ; i < s4 ; ++i)
|
||||
{
|
||||
float f = cosf(omega * i);
|
||||
_wi[s4 + i] = f;
|
||||
_wi[s4 - i] = f;
|
||||
_wr[ i] = f;
|
||||
_wr[s2 - i] = -f;
|
||||
}
|
||||
_wi[s4] = _wr[0] = 1;
|
||||
|
||||
size_t je = 1;
|
||||
_br[0] = 0;
|
||||
_br[1] = FFT_SIZE / 2;
|
||||
for ( size_t i = 0 ; i < _ie - 1 ; ++i )
|
||||
{
|
||||
_br[ je << 1 ] = _br[ je ] >> 1;
|
||||
je = je << 1;
|
||||
for ( size_t j = 1 ; j < je ; ++j )
|
||||
{
|
||||
_br[je + j] = _br[je] + _br[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exec(const int16_t* in)
|
||||
{
|
||||
memset(_fi, 0, sizeof(_fi));
|
||||
for ( size_t j = 0 ; j < FFT_SIZE / 2 ; ++j )
|
||||
{
|
||||
float basej = 0.25 * (1.0-_wr[j]);
|
||||
size_t r = FFT_SIZE - j - 1;
|
||||
|
||||
/// perform han window and stereo to mono convert.
|
||||
_fr[_br[j]] = basej * (in[j * 2] + in[j * 2 + 1]);
|
||||
_fr[_br[r]] = basej * (in[r * 2] + in[r * 2 + 1]);
|
||||
}
|
||||
|
||||
size_t s = 1;
|
||||
size_t i = 0;
|
||||
do
|
||||
{
|
||||
size_t ke = s;
|
||||
s <<= 1;
|
||||
size_t je = FFT_SIZE / s;
|
||||
size_t j = 0;
|
||||
do
|
||||
{
|
||||
size_t k = 0;
|
||||
do
|
||||
{
|
||||
size_t l = s * j + k;
|
||||
size_t m = ke * (2 * j + 1) + k;
|
||||
size_t p = je * k;
|
||||
float Wxmr = _fr[m] * _wr[p] + _fi[m] * _wi[p];
|
||||
float Wxmi = _fi[m] * _wr[p] - _fr[m] * _wi[p];
|
||||
_fr[m] = _fr[l] - Wxmr;
|
||||
_fi[m] = _fi[l] - Wxmi;
|
||||
_fr[l] += Wxmr;
|
||||
_fi[l] += Wxmi;
|
||||
} while ( ++k < ke) ;
|
||||
} while ( ++j < je );
|
||||
} while ( ++i < _ie );
|
||||
}
|
||||
|
||||
uint32_t get(size_t index)
|
||||
{
|
||||
return (index < FFT_SIZE / 2) ? (uint32_t)sqrtf(_fr[ index ] * _fr[ index ] + _fi[ index ] * _fi[ index ]) : 0u;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr const int preallocateBufferSize = 5 * 1024;
|
||||
static constexpr const int preallocateCodecSize = 29192; // MP3 codec max mem needed
|
||||
static void* preallocateBuffer = nullptr;
|
||||
static void* preallocateCodec = nullptr;
|
||||
static constexpr size_t WAVE_SIZE = 320;
|
||||
static AudioOutputM5Speaker out(&M5.Speaker, m5spk_virtual_channel);
|
||||
static AudioGenerator *decoder = nullptr;
|
||||
static AudioFileSourceICYStream *file = nullptr;
|
||||
static AudioFileSourceBuffer *buff = nullptr;
|
||||
static fft_t fft;
|
||||
static bool fft_enabled = false;
|
||||
static bool wave_enabled = false;
|
||||
static uint16_t prev_y[(FFT_SIZE / 2)+1];
|
||||
static uint16_t peak_y[(FFT_SIZE / 2)+1];
|
||||
static int16_t wave_y[WAVE_SIZE];
|
||||
static int16_t wave_h[WAVE_SIZE];
|
||||
static int16_t raw_data[WAVE_SIZE * 2];
|
||||
static int header_height = 0;
|
||||
static size_t station_index = 0;
|
||||
static char stream_title[128] = { 0 };
|
||||
static const char* meta_text[2] = { nullptr, stream_title };
|
||||
static const size_t meta_text_num = sizeof(meta_text) / sizeof(meta_text[0]);
|
||||
static uint8_t meta_mod_bits = 0;
|
||||
static volatile size_t playindex = ~0u;
|
||||
|
||||
static void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
|
||||
{
|
||||
(void)cbData;
|
||||
if ((strcmp(type, "StreamTitle") == 0) && (strcmp(stream_title, string) != 0))
|
||||
{
|
||||
strncpy(stream_title, string, sizeof(stream_title));
|
||||
meta_mod_bits |= 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void stop(void)
|
||||
{
|
||||
if (decoder) {
|
||||
decoder->stop();
|
||||
delete decoder;
|
||||
decoder = nullptr;
|
||||
}
|
||||
|
||||
if (buff) {
|
||||
buff->close();
|
||||
delete buff;
|
||||
buff = nullptr;
|
||||
}
|
||||
if (file) {
|
||||
file->close();
|
||||
delete file;
|
||||
file = nullptr;
|
||||
}
|
||||
out.stop();
|
||||
}
|
||||
|
||||
static void play(size_t index)
|
||||
{
|
||||
playindex = index;
|
||||
}
|
||||
|
||||
static void decodeTask(void*)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
M5.delay(1);
|
||||
if (playindex != ~0u)
|
||||
{
|
||||
auto index = playindex;
|
||||
playindex = ~0u;
|
||||
stop();
|
||||
meta_text[0] = station_list[index][0];
|
||||
stream_title[0] = 0;
|
||||
meta_mod_bits = 3;
|
||||
file = new AudioFileSourceICYStream(station_list[index][1]);
|
||||
file->RegisterMetadataCB(MDCallback, (void*)"ICY");
|
||||
buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize);
|
||||
decoder = new AudioGeneratorMP3(preallocateCodec, preallocateCodecSize);
|
||||
decoder->begin(buff, &out);
|
||||
}
|
||||
if (decoder && decoder->isRunning())
|
||||
{
|
||||
if (!decoder->loop()) { decoder->stop(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t bgcolor(LGFX_Device* gfx, int y)
|
||||
{
|
||||
auto h = gfx->height();
|
||||
auto dh = h - header_height;
|
||||
int v = ((h - y)<<5) / dh;
|
||||
if (dh > 44)
|
||||
{
|
||||
int v2 = ((h - y - 1)<<5) / dh;
|
||||
if ((v >> 2) != (v2 >> 2))
|
||||
{
|
||||
return 0x666666u;
|
||||
}
|
||||
}
|
||||
return gfx->color888(v + 2, v, v + 6);
|
||||
}
|
||||
|
||||
static void gfxSetup(LGFX_Device* gfx)
|
||||
{
|
||||
if (gfx == nullptr) { return; }
|
||||
if (gfx->width() < gfx->height())
|
||||
{
|
||||
gfx->setRotation(gfx->getRotation()^1);
|
||||
}
|
||||
gfx->setFont(&fonts::lgfxJapanGothic_12);
|
||||
gfx->setEpdMode(epd_mode_t::epd_fastest);
|
||||
gfx->setTextWrap(false);
|
||||
gfx->setCursor(0, 8);
|
||||
gfx->println("WebRadio player");
|
||||
gfx->fillRect(0, 6, gfx->width(), 2, TFT_BLACK);
|
||||
|
||||
header_height = (gfx->height() > 80) ? 33 : 21;
|
||||
fft_enabled = !gfx->isEPD();
|
||||
if (fft_enabled)
|
||||
{
|
||||
wave_enabled = (gfx->getBoard() != m5gfx::board_M5UnitLCD);
|
||||
|
||||
for (int y = header_height; y < gfx->height(); ++y)
|
||||
{
|
||||
gfx->drawFastHLine(0, y, gfx->width(), bgcolor(gfx, y));
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < (FFT_SIZE/2)+1; ++x)
|
||||
{
|
||||
prev_y[x] = INT16_MAX;
|
||||
peak_y[x] = INT16_MAX;
|
||||
}
|
||||
for (int x = 0; x < WAVE_SIZE; ++x)
|
||||
{
|
||||
wave_y[x] = gfx->height();
|
||||
wave_h[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gfxLoop(LGFX_Device* gfx)
|
||||
{
|
||||
if (gfx == nullptr) { return; }
|
||||
if (header_height > 32)
|
||||
{
|
||||
if (meta_mod_bits)
|
||||
{
|
||||
gfx->startWrite();
|
||||
for (int id = 0; id < meta_text_num; ++id)
|
||||
{
|
||||
if (0 == (meta_mod_bits & (1<<id))) { continue; }
|
||||
meta_mod_bits &= ~(1<<id);
|
||||
size_t y = id * 12;
|
||||
if (y+12 >= header_height) { continue; }
|
||||
gfx->setCursor(4, 8 + y);
|
||||
gfx->fillRect(0, 8 + y, gfx->width(), 12, gfx->getBaseColor());
|
||||
gfx->print(meta_text[id]);
|
||||
gfx->print(" "); // Garbage data removal when UTF8 characters are broken in the middle.
|
||||
}
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static int title_x;
|
||||
static int title_id;
|
||||
static int wait = INT16_MAX;
|
||||
|
||||
if (meta_mod_bits)
|
||||
{
|
||||
if (meta_mod_bits & 1)
|
||||
{
|
||||
title_x = 4;
|
||||
title_id = 0;
|
||||
gfx->fillRect(0, 8, gfx->width(), 12, gfx->getBaseColor());
|
||||
}
|
||||
meta_mod_bits = 0;
|
||||
wait = 0;
|
||||
}
|
||||
|
||||
if (--wait < 0)
|
||||
{
|
||||
int tx = title_x;
|
||||
int tid = title_id;
|
||||
wait = 3;
|
||||
gfx->startWrite();
|
||||
uint_fast8_t no_data_bits = 0;
|
||||
do
|
||||
{
|
||||
if (tx == 4) { wait = 255; }
|
||||
gfx->setCursor(tx, 8);
|
||||
const char* meta = meta_text[tid];
|
||||
if (meta[0] != 0)
|
||||
{
|
||||
gfx->print(meta);
|
||||
gfx->print(" / ");
|
||||
tx = gfx->getCursorX();
|
||||
if (++tid == meta_text_num) { tid = 0; }
|
||||
if (tx <= 4)
|
||||
{
|
||||
title_x = tx;
|
||||
title_id = tid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((no_data_bits |= 1 << tid) == ((1 << meta_text_num) - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (++tid == meta_text_num) { tid = 0; }
|
||||
}
|
||||
} while (tx < gfx->width());
|
||||
--title_x;
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
|
||||
if (fft_enabled)
|
||||
{
|
||||
static int prev_x[2];
|
||||
static int peak_x[2];
|
||||
|
||||
auto buf = out.getBuffer();
|
||||
if (buf)
|
||||
{
|
||||
memcpy(raw_data, buf, WAVE_SIZE * 2 * sizeof(int16_t)); // stereo data copy
|
||||
gfx->startWrite();
|
||||
|
||||
// draw stereo level meter
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
int32_t level = 0;
|
||||
for (size_t j = i; j < 640; j += 32)
|
||||
{
|
||||
uint32_t lv = abs(raw_data[j]);
|
||||
if (level < lv) { level = lv; }
|
||||
}
|
||||
|
||||
int32_t x = (level * gfx->width()) / INT16_MAX;
|
||||
int32_t px = prev_x[i];
|
||||
if (px != x)
|
||||
{
|
||||
gfx->fillRect(x, i * 3, px - x, 2, px < x ? 0xFF9900u : 0x330000u);
|
||||
prev_x[i] = x;
|
||||
}
|
||||
px = peak_x[i];
|
||||
if (px > x)
|
||||
{
|
||||
gfx->writeFastVLine(px, i * 3, 2, TFT_BLACK);
|
||||
px--;
|
||||
}
|
||||
else
|
||||
{
|
||||
px = x;
|
||||
}
|
||||
if (peak_x[i] != px)
|
||||
{
|
||||
peak_x[i] = px;
|
||||
gfx->writeFastVLine(px, i * 3, 2, TFT_WHITE);
|
||||
}
|
||||
}
|
||||
gfx->display();
|
||||
|
||||
// draw FFT level meter
|
||||
fft.exec(raw_data);
|
||||
size_t bw = gfx->width() / 60;
|
||||
if (bw < 3) { bw = 3; }
|
||||
int32_t dsp_height = gfx->height();
|
||||
int32_t fft_height = dsp_height - header_height - 1;
|
||||
size_t xe = gfx->width() / bw;
|
||||
if (xe > (FFT_SIZE/2)) { xe = (FFT_SIZE/2); }
|
||||
int32_t wave_next = ((header_height + dsp_height) >> 1) + (((256 - (raw_data[0] + raw_data[1])) * fft_height) >> 17);
|
||||
|
||||
uint32_t bar_color[2] = { 0x000033u, 0x99AAFFu };
|
||||
|
||||
for (size_t bx = 0; bx <= xe; ++bx)
|
||||
{
|
||||
size_t x = bx * bw;
|
||||
if ((x & 7) == 0) { gfx->display(); taskYIELD(); }
|
||||
int32_t f = fft.get(bx);
|
||||
int32_t y = (f * fft_height) >> 18;
|
||||
if (y > fft_height) { y = fft_height; }
|
||||
y = dsp_height - y;
|
||||
int32_t py = prev_y[bx];
|
||||
if (y != py)
|
||||
{
|
||||
gfx->fillRect(x, y, bw - 1, py - y, bar_color[(y < py)]);
|
||||
prev_y[bx] = y;
|
||||
}
|
||||
py = peak_y[bx] + 1;
|
||||
if (py < y)
|
||||
{
|
||||
gfx->writeFastHLine(x, py - 1, bw - 1, bgcolor(gfx, py - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
py = y - 1;
|
||||
}
|
||||
if (peak_y[bx] != py)
|
||||
{
|
||||
peak_y[bx] = py;
|
||||
gfx->writeFastHLine(x, py, bw - 1, TFT_WHITE);
|
||||
}
|
||||
|
||||
|
||||
if (wave_enabled)
|
||||
{
|
||||
for (size_t bi = 0; bi < bw; ++bi)
|
||||
{
|
||||
size_t i = x + bi;
|
||||
if (i >= gfx->width() || i >= WAVE_SIZE) { break; }
|
||||
y = wave_y[i];
|
||||
int32_t h = wave_h[i];
|
||||
bool use_bg = (bi+1 == bw);
|
||||
if (h>0)
|
||||
{ /// erase previous wave.
|
||||
gfx->setAddrWindow(i, y, 1, h);
|
||||
h += y;
|
||||
do
|
||||
{
|
||||
uint32_t bg = (use_bg || y < peak_y[bx]) ? bgcolor(gfx, y)
|
||||
: (y == peak_y[bx]) ? 0xFFFFFFu
|
||||
: bar_color[(y >= prev_y[bx])];
|
||||
gfx->writeColor(bg, 1);
|
||||
} while (++y < h);
|
||||
}
|
||||
size_t i2 = i << 1;
|
||||
int32_t y1 = wave_next;
|
||||
wave_next = ((header_height + dsp_height) >> 1) + (((256 - (raw_data[i2] + raw_data[i2 + 1])) * fft_height) >> 17);
|
||||
int32_t y2 = wave_next;
|
||||
if (y1 > y2)
|
||||
{
|
||||
int32_t tmp = y1;
|
||||
y1 = y2;
|
||||
y2 = tmp;
|
||||
}
|
||||
y = y1;
|
||||
h = y2 + 1 - y;
|
||||
wave_y[i] = y;
|
||||
wave_h[i] = h;
|
||||
if (h>0)
|
||||
{ /// draw new wave.
|
||||
gfx->setAddrWindow(i, y, 1, h);
|
||||
h += y;
|
||||
do
|
||||
{
|
||||
uint32_t bg = (y < prev_y[bx]) ? 0xFFCC33u : 0xFFFFFFu;
|
||||
gfx->writeColor(bg, 1);
|
||||
} while (++y < h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gfx->display();
|
||||
gfx->endWrite();
|
||||
}
|
||||
}
|
||||
|
||||
if (!gfx->displayBusy())
|
||||
{ // draw volume bar
|
||||
static int px;
|
||||
uint8_t v = M5.Speaker.getVolume();
|
||||
int x = v * (gfx->width()) >> 8;
|
||||
if (px != x)
|
||||
{
|
||||
gfx->fillRect(x, 6, px - x, 2, px < x ? 0xAAFFAAu : 0u);
|
||||
gfx->display();
|
||||
px = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// If you want to play sound from ModuleDisplay, write this
|
||||
// cfg.external_speaker.module_display = true;
|
||||
|
||||
// If you want to play sound from ModuleRCA, write this
|
||||
// cfg.external_speaker.module_rca = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker, write this
|
||||
cfg.external_speaker.hat_spk = true;
|
||||
|
||||
// If you want to play sound from HAT Speaker2, write this
|
||||
// cfg.external_speaker.hat_spk2 = true;
|
||||
|
||||
// If you want to play sound from ATOMIC Speaker, write this
|
||||
cfg.external_speaker.atomic_spk = true;
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
preallocateBuffer = malloc(preallocateBufferSize);
|
||||
preallocateCodec = malloc(preallocateCodecSize);
|
||||
if (!preallocateBuffer || !preallocateCodec) {
|
||||
M5.Display.printf("FATAL ERROR: Unable to preallocate %d bytes for app\n", preallocateBufferSize + preallocateCodecSize);
|
||||
for (;;) { M5.delay(1000); }
|
||||
}
|
||||
|
||||
{ /// custom setting
|
||||
auto spk_cfg = M5.Speaker.config();
|
||||
/// Increasing the sample_rate will improve the sound quality instead of increasing the CPU load.
|
||||
spk_cfg.sample_rate = 48000; // default:64000 (64kHz) e.g. 48000 , 50000 , 80000 , 96000 , 100000 , 128000 , 144000 , 192000 , 200000
|
||||
spk_cfg.task_pinned_core = APP_CPU_NUM;
|
||||
M5.Speaker.config(spk_cfg);
|
||||
}
|
||||
|
||||
|
||||
M5.Speaker.begin();
|
||||
|
||||
M5.Display.println("Connecting to WiFi");
|
||||
WiFi.disconnect();
|
||||
WiFi.softAPdisconnect(true);
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
#if defined ( WIFI_SSID ) && defined ( WIFI_PASS )
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
#else
|
||||
WiFi.begin();
|
||||
#endif
|
||||
|
||||
// Try forever
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
M5.Display.print(".");
|
||||
M5.delay(100);
|
||||
}
|
||||
M5.Display.clear();
|
||||
|
||||
gfxSetup(&M5.Display);
|
||||
|
||||
play(station_index);
|
||||
|
||||
xTaskCreatePinnedToCore(decodeTask, "decodeTask", 4096, nullptr, 1, nullptr, PRO_CPU_NUM);
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
gfxLoop(&M5.Display);
|
||||
|
||||
{
|
||||
static int prev_frame;
|
||||
int frame;
|
||||
do
|
||||
{
|
||||
M5.delay(1);
|
||||
} while (prev_frame == (frame = millis() >> 3)); /// 8 msec cycle wait
|
||||
prev_frame = frame;
|
||||
}
|
||||
|
||||
M5.update();
|
||||
auto td = M5.Touch.getDetail();
|
||||
if (M5.BtnA.wasPressed() || td.wasPressed())
|
||||
{
|
||||
M5.Speaker.tone(440, 50);
|
||||
}
|
||||
int cc = M5.BtnA.getClickCount();
|
||||
if (cc == 0) cc = td.getClickCount();
|
||||
if (M5.BtnA.wasDecideClickCount() || td.wasClicked())
|
||||
{
|
||||
switch (cc)
|
||||
{
|
||||
case 1:
|
||||
M5.Speaker.tone(1000, 100);
|
||||
if (++station_index >= stations) { station_index = 0; }
|
||||
play(station_index);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
M5.Speaker.tone(800, 100);
|
||||
if (station_index == 0) { station_index = stations; }
|
||||
play(--station_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (M5.BtnA.isHolding() || M5.BtnB.isPressed() || M5.BtnC.isPressed() || td.isHolding())
|
||||
{
|
||||
size_t v = M5.Speaker.getVolume();
|
||||
int add = (M5.BtnB.isPressed()) ? -1 : 1;
|
||||
if (M5.BtnA.isHolding() || td.isHolding())
|
||||
{
|
||||
add = (M5.BtnA.getClickCount() || td.getClickCount()) ? -1 : 1;
|
||||
}
|
||||
v += add;
|
||||
if (v <= 255)
|
||||
{
|
||||
M5.Speaker.setVolume(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
#include <M5Unified.h>
|
||||
|
||||
static bool got_notif_flag = false;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
M5.begin();
|
||||
M5.Power.Axp2101.disableIRQ(AXP2101_IRQ_ALL);
|
||||
M5.Power.Axp2101.clearIRQStatuses();
|
||||
M5.Power.Axp2101.enableIRQ(
|
||||
AXP2101_IRQ_BAT_CHG_UNDER_TEMP | AXP2101_IRQ_BAT_CHG_OVER_TEMP | // Battery temp in charging
|
||||
AXP2101_IRQ_VBUS_INSERT | AXP2101_IRQ_VBUS_REMOVE // Usb insert/remove
|
||||
);
|
||||
|
||||
M5.Display.setTextSize(3);
|
||||
}
|
||||
|
||||
void check_irq_statuses()
|
||||
{
|
||||
M5.Power.Axp2101.getIRQStatuses();
|
||||
|
||||
if(M5.Power.Axp2101.isBatChargerUnderTemperatureIrq())
|
||||
{
|
||||
M5.Display.drawString("BatUnderTempCharge", 50, 120);
|
||||
got_notif_flag = true;
|
||||
}
|
||||
if(M5.Power.Axp2101.isBatChargerOverTemperatureIrq())
|
||||
{
|
||||
M5.Display.drawString("BatOverTempCharge", 50, 120);
|
||||
got_notif_flag = true;
|
||||
}
|
||||
if(M5.Power.Axp2101.isVbusInsertIrq())
|
||||
{
|
||||
M5.Display.drawString("Usb inserted", 50, 120);
|
||||
got_notif_flag = true;
|
||||
}
|
||||
if(M5.Power.Axp2101.isVbusRemoveIrq())
|
||||
{
|
||||
M5.Display.drawString("Usb removed", 50, 120);
|
||||
got_notif_flag = true;
|
||||
}
|
||||
|
||||
M5.Power.Axp2101.clearIRQStatuses();
|
||||
}
|
||||
|
||||
void refresh_display()
|
||||
{
|
||||
static unsigned long started_time = 0;
|
||||
if(got_notif_flag == false) { return; }
|
||||
|
||||
unsigned long now_time = millis();
|
||||
if(started_time == 0)
|
||||
{
|
||||
started_time = now_time;
|
||||
}
|
||||
else if(now_time - started_time > 500)
|
||||
{
|
||||
started_time = 0;
|
||||
M5.Display.fillScreen(BLACK);
|
||||
got_notif_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.update();
|
||||
check_irq_statuses();
|
||||
refresh_display();
|
||||
vTaskDelay(50);
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
124
libraries/M5Unified/examples/Basic/Button/Button.ino
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include <M5Unified.h>
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
M5.begin();
|
||||
|
||||
/// For models with EPD : refresh control
|
||||
M5.Display.setEpdMode(epd_mode_t::epd_fastest); // fastest but very-low quality.
|
||||
|
||||
if (M5.Display.width() < M5.Display.height())
|
||||
{ /// Landscape mode.
|
||||
M5.Display.setRotation(M5.Display.getRotation() ^ 1);
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.delay(1);
|
||||
|
||||
M5.update();
|
||||
//------------------- Button test
|
||||
/*
|
||||
/// List of available buttons:
|
||||
M5Stack BASIC/GRAY/GO/FIRE: BtnA,BtnB,BtnC
|
||||
M5Stack Core2: BtnA,BtnB,BtnC,BtnPWR
|
||||
M5Stick C/CPlus: BtnA,BtnB, BtnPWR
|
||||
M5Stick CoreInk: BtnA,BtnB,BtnC,BtnPWR,BtnEXT
|
||||
M5Paper: BtnA,BtnB,BtnC
|
||||
M5Station: BtnA,BtnB,BtnC,BtnPWR
|
||||
M5Tough: BtnPWR
|
||||
M5Atom M5AtomU: BtnA
|
||||
M5Stamp Pico/C3/C3U: BtnA
|
||||
*/
|
||||
|
||||
static constexpr const int colors[] = { TFT_WHITE, TFT_CYAN, TFT_RED, TFT_YELLOW, TFT_BLUE, TFT_GREEN };
|
||||
static constexpr const char* const names[] = { "none", "wasHold", "wasClicked", "wasPressed", "wasReleased", "wasDeciedCount" };
|
||||
|
||||
int w = M5.Display.width() / 5;
|
||||
int h = M5.Display.height();
|
||||
M5.Display.startWrite();
|
||||
|
||||
/// BtnPWR: "wasClicked"/"wasHold" can be use.
|
||||
/// BtnPWR of CoreInk: "isPressed"/"wasPressed"/"isReleased"/"wasReleased"/"wasClicked"/"wasHold"/"isHolding" can be use.
|
||||
int state = M5.BtnPWR.wasHold() ? 1
|
||||
: M5.BtnPWR.wasClicked() ? 2
|
||||
: M5.BtnPWR.wasPressed() ? 3
|
||||
: M5.BtnPWR.wasReleased() ? 4
|
||||
: M5.BtnPWR.wasDecideClickCount() ? 5
|
||||
: 0;
|
||||
|
||||
if (state)
|
||||
{
|
||||
M5_LOGI("BtnPWR:%s count:%d", names[state], M5.BtnPWR.getClickCount());
|
||||
M5.Display.fillRect(w*0, 0, w-1, h, colors[state]);
|
||||
}
|
||||
|
||||
/// BtnA,BtnB,BtnC,BtnEXT: "isPressed"/"wasPressed"/"isReleased"/"wasReleased"/"wasClicked"/"wasHold"/"isHolding" can be use.
|
||||
state = M5.BtnA.wasHold() ? 1
|
||||
: M5.BtnA.wasClicked() ? 2
|
||||
: M5.BtnA.wasPressed() ? 3
|
||||
: M5.BtnA.wasReleased() ? 4
|
||||
: M5.BtnA.wasDecideClickCount() ? 5
|
||||
: 0;
|
||||
if (state)
|
||||
{
|
||||
M5_LOGI("BtnA:%s count:%d", names[state], M5.BtnA.getClickCount());
|
||||
M5.Display.fillRect(w*1, 0, w-1, h, colors[state]);
|
||||
}
|
||||
|
||||
state = M5.BtnB.wasHold() ? 1
|
||||
: M5.BtnB.wasClicked() ? 2
|
||||
: M5.BtnB.wasPressed() ? 3
|
||||
: M5.BtnB.wasReleased() ? 4
|
||||
: M5.BtnB.wasDecideClickCount() ? 5
|
||||
: 0;
|
||||
if (state)
|
||||
{
|
||||
M5_LOGI("BtnB:%s count:%d", names[state], M5.BtnB.getClickCount());
|
||||
M5.Display.fillRect(w*2, 0, w-1, h, colors[state]);
|
||||
}
|
||||
|
||||
state = M5.BtnC.wasHold() ? 1
|
||||
: M5.BtnC.wasClicked() ? 2
|
||||
: M5.BtnC.wasPressed() ? 3
|
||||
: M5.BtnC.wasReleased() ? 4
|
||||
: M5.BtnC.wasDecideClickCount() ? 5
|
||||
: 0;
|
||||
if (state)
|
||||
{
|
||||
M5_LOGI("BtnC:%s count:%d", names[state], M5.BtnC.getClickCount());
|
||||
M5.Display.fillRect(w*3, 0, w-1, h, colors[state]);
|
||||
}
|
||||
|
||||
state = M5.BtnEXT.wasHold() ? 1
|
||||
: M5.BtnEXT.wasClicked() ? 2
|
||||
: M5.BtnEXT.wasPressed() ? 3
|
||||
: M5.BtnEXT.wasReleased() ? 4
|
||||
: M5.BtnEXT.wasDecideClickCount() ? 5
|
||||
: 0;
|
||||
if (state)
|
||||
{
|
||||
M5_LOGI("BtnEXT:%s count:%d", names[state], M5.BtnEXT.getClickCount());
|
||||
M5.Display.fillRect(w*4, 0, w-1, h, colors[state]);
|
||||
}
|
||||
M5.Display.endWrite();
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO ) && defined ( ESP_PLATFORM )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
291
libraries/M5Unified/examples/Basic/Displays/Displays.ino
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
|
||||
#if defined ( ARDUINO )
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// If you use SD card, write this.
|
||||
#include <SD.h>
|
||||
|
||||
// If you use SPIFFS, write this.
|
||||
#include <SPIFFS.h>
|
||||
|
||||
#endif
|
||||
|
||||
// * The filesystem header must be included before the display library.
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// If you use ATOM Display, write this.
|
||||
#include <M5AtomDisplay.h>
|
||||
|
||||
// If you use Module Display, write this.
|
||||
#include <M5ModuleDisplay.h>
|
||||
|
||||
// If you use Module RCA, write this.
|
||||
#include <M5ModuleRCA.h>
|
||||
|
||||
// If you use Unit GLASS, write this.
|
||||
#include <M5UnitGLASS.h>
|
||||
|
||||
// If you use Unit GLASS2, write this.
|
||||
#include <M5UnitGLASS2.h>
|
||||
|
||||
// If you use Unit OLED, write this.
|
||||
#include <M5UnitOLED.h>
|
||||
|
||||
// If you use Unit Mini OLED, write this.
|
||||
#include <M5UnitMiniOLED.h>
|
||||
|
||||
// If you use Unit LCD, write this.
|
||||
#include <M5UnitLCD.h>
|
||||
|
||||
// If you use UnitRCA (for Video output), write this.
|
||||
#include <M5UnitRCA.h>
|
||||
|
||||
// * The display header must be included before the M5Unified library.
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// Include this to enable the M5 global instance.
|
||||
#include <M5Unified.h>
|
||||
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// external display setting. (Pre-include required)
|
||||
cfg.external_display.module_display = true; // default=true. use ModuleDisplay
|
||||
cfg.external_display.atom_display = true; // default=true. use AtomDisplay
|
||||
cfg.external_display.unit_glass = false; // default=true. use UnitGLASS
|
||||
cfg.external_display.unit_glass2 = false; // default=true. use UnitGLASS2
|
||||
cfg.external_display.unit_oled = false; // default=true. use UnitOLED
|
||||
cfg.external_display.unit_mini_oled = false; // default=true. use UnitMiniOLED
|
||||
cfg.external_display.unit_lcd = false; // default=true. use UnitLCD
|
||||
cfg.external_display.unit_rca = false; // default=true. use UnitRCA VideoOutput
|
||||
cfg.external_display.module_rca = false; // default=true. use ModuleRCA VideoOutput
|
||||
|
||||
/*
|
||||
※ Unit OLED, Unit Mini OLED, Unit GLASS2 cannot be distinguished at runtime and may be misidentified as each other.
|
||||
|
||||
※ Display with auto-detection
|
||||
- module_display
|
||||
- atom_display
|
||||
- unit_glass
|
||||
- unit_glass2
|
||||
- unit_oled
|
||||
- unit_mini_oled
|
||||
- unit_lcd
|
||||
|
||||
※ Displays that cannot be auto-detected
|
||||
- module_rca
|
||||
- unit_rca
|
||||
|
||||
※ Note that if you enable a display that cannot be auto-detected,
|
||||
it will operate as if it were connected, even if it is not actually connected.
|
||||
When RCA is enabled, it consumes a lot of memory to allocate the frame buffer.
|
||||
//*/
|
||||
|
||||
|
||||
// Set individual parameters for external displays.
|
||||
// (※ Use only the items you wish to change. Basically, it can be omitted.)
|
||||
#if defined ( __M5GFX_M5ATOMDISPLAY__ ) // setting for ATOM Display.
|
||||
// cfg.atom_display.logical_width = 1280;
|
||||
// cfg.atom_display.logical_height = 720;
|
||||
// cfg.atom_display.output_width = 1280;
|
||||
// cfg.atom_display.output_height = 720;
|
||||
// cfg.atom_display.refresh_rate = 60;
|
||||
// cfg.atom_display.scale_w = 1;
|
||||
// cfg.atom_display.scale_h = 1;
|
||||
// cfg.atom_display.pixel_clock = 74250000;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5MODULEDISPLAY__ ) // setting for Module Display.
|
||||
// cfg.module_display.logical_width = 1280;
|
||||
// cfg.module_display.logical_height = 720;
|
||||
// cfg.module_display.output_width = 1280;
|
||||
// cfg.module_display.output_height = 720;
|
||||
// cfg.module_display.refresh_rate = 60;
|
||||
// cfg.module_display.scale_w = 1;
|
||||
// cfg.module_display.scale_h = 1;
|
||||
// cfg.module_display.pixel_clock = 74250000;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5MODULERCA__ ) // setting for Module RCA.
|
||||
// cfg.module_rca.logical_width = 216;
|
||||
// cfg.module_rca.logical_height = 144;
|
||||
// cfg.module_rca.output_width = 216;
|
||||
// cfg.module_rca.output_height = 144;
|
||||
// cfg.module_rca.signal_type = M5ModuleRCA::signal_type_t::PAL; // NTSC / NTSC_J / PAL_M / PAL_N
|
||||
// cfg.module_rca.use_psram = M5ModuleRCA::use_psram_t::psram_use; // psram_no_use / psram_half_use
|
||||
// cfg.module_rca.pin_dac = GPIO_NUM_26;
|
||||
// cfg.module_rca.output_level = 128;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITRCA__ ) // setting for Unit RCA.
|
||||
// cfg.unit_rca.logical_width = 216;
|
||||
// cfg.unit_rca.logical_height = 144;
|
||||
// cfg.unit_rca.output_width = 216;
|
||||
// cfg.unit_rca.output_height = 144;
|
||||
// cfg.unit_rca.signal_type = M5UnitRCA::signal_type_t::PAL; // NTSC / NTSC_J / PAL_M / PAL_N
|
||||
// cfg.unit_rca.use_psram = M5UnitRCA::use_psram_t::psram_use; // psram_no_use / psram_half_use
|
||||
// cfg.unit_rca.pin_dac = GPIO_NUM_26;
|
||||
// cfg.unit_rca.output_level = 128;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITGLASS__ ) // setting for Unit GLASS.
|
||||
// cfg.unit_glass.pin_sda = GPIO_NUM_21;
|
||||
// cfg.unit_glass.pin_scl = GPIO_NUM_22;
|
||||
// cfg.unit_glass.i2c_addr = 0x3D;
|
||||
// cfg.unit_glass.i2c_freq = 400000;
|
||||
// cfg.unit_glass.i2c_port = I2C_NUM_0;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITGLASS2__ ) // setting for Unit GLASS2.
|
||||
// cfg.unit_glass2.pin_sda = GPIO_NUM_21;
|
||||
// cfg.unit_glass2.pin_scl = GPIO_NUM_22;
|
||||
// cfg.unit_glass2.i2c_addr = 0x3C;
|
||||
// cfg.unit_glass2.i2c_freq = 400000;
|
||||
// cfg.unit_glass2.i2c_port = I2C_NUM_0;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITOLED__ ) // setting for Unit OLED.
|
||||
// cfg.unit_oled.pin_sda = GPIO_NUM_21;
|
||||
// cfg.unit_oled.pin_scl = GPIO_NUM_22;
|
||||
// cfg.unit_oled.i2c_addr = 0x3C;
|
||||
// cfg.unit_oled.i2c_freq = 400000;
|
||||
// cfg.unit_oled.i2c_port = I2C_NUM_0;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITMINIOLED__ ) // setting for Unit Mini OLED.
|
||||
// cfg.unit_mini_oled.pin_sda = GPIO_NUM_21;
|
||||
// cfg.unit_mini_oled.pin_scl = GPIO_NUM_22;
|
||||
// cfg.unit_mini_oled.i2c_addr = 0x3C;
|
||||
// cfg.unit_mini_oled.i2c_freq = 400000;
|
||||
// cfg.unit_mini_oled.i2c_port = I2C_NUM_0;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITLCD__ ) // setting for Unit LCD.
|
||||
// cfg.unit_lcd.pin_sda = GPIO_NUM_21;
|
||||
// cfg.unit_lcd.pin_scl = GPIO_NUM_22;
|
||||
// cfg.unit_lcd.i2c_addr = 0x3E;
|
||||
// cfg.unit_lcd.i2c_freq = 400000;
|
||||
// cfg.unit_lcd.i2c_port = I2C_NUM_0;
|
||||
#endif
|
||||
|
||||
|
||||
// begin M5Unified.
|
||||
M5.begin(cfg);
|
||||
|
||||
// Get the number of available displays
|
||||
int display_count = M5.getDisplayCount();
|
||||
|
||||
for (int i = 0; i < display_count; ++i) {
|
||||
// All displays are available in M5.Displays.
|
||||
// ※ Note that the order of which displays are numbered is the order in which they are detected, so the order may change.
|
||||
|
||||
int textsize = M5.Displays(i).height() / 60;
|
||||
if (textsize == 0) { textsize = 1; }
|
||||
M5.Displays(i).setTextSize(textsize);
|
||||
M5.Displays(i).printf("No.%d\n", i);
|
||||
}
|
||||
|
||||
|
||||
// If an external display is to be used as the main display, it can be listed in order of priority.
|
||||
M5.setPrimaryDisplayType( {
|
||||
m5::board_t::board_M5ModuleDisplay,
|
||||
m5::board_t::board_M5AtomDisplay,
|
||||
// m5::board_t::board_M5ModuleRCA,
|
||||
// m5::board_t::board_M5UnitGLASS,
|
||||
// m5::board_t::board_M5UnitGLASS2,
|
||||
// m5::board_t::board_M5UnitMiniOLED,
|
||||
// m5::board_t::board_M5UnitOLED,
|
||||
// m5::board_t::board_M5UnitLCD,
|
||||
// m5::board_t::board_M5UnitRCA,
|
||||
} );
|
||||
|
||||
|
||||
// The primary display can be used with M5.Display.
|
||||
M5.Display.print("primary display\n");
|
||||
|
||||
|
||||
// Examine the indexes of a given type of display
|
||||
int index_module_display = M5.getDisplayIndex(m5::board_t::board_M5ModuleDisplay);
|
||||
int index_atom_display = M5.getDisplayIndex(m5::board_t::board_M5AtomDisplay);
|
||||
int index_module_rca = M5.getDisplayIndex(m5::board_t::board_M5ModuleRCA);
|
||||
int index_unit_glass = M5.getDisplayIndex(m5::board_t::board_M5UnitGLASS);
|
||||
int index_unit_glass2 = M5.getDisplayIndex(m5::board_t::board_M5UnitGLASS2);
|
||||
int index_unit_oled = M5.getDisplayIndex(m5::board_t::board_M5UnitOLED);
|
||||
int index_unit_mini_oled = M5.getDisplayIndex(m5::board_t::board_M5UnitMiniOLED);
|
||||
int index_unit_lcd = M5.getDisplayIndex(m5::board_t::board_M5UnitLCD);
|
||||
int index_unit_rca = M5.getDisplayIndex(m5::board_t::board_M5UnitRCA);
|
||||
|
||||
if (index_module_display >= 0) {
|
||||
M5.Displays(index_module_display).print("This is Module Display\n");
|
||||
}
|
||||
if (index_atom_display >= 0) {
|
||||
M5.Displays(index_atom_display).print("This is Atom Display\n");
|
||||
}
|
||||
if (index_module_rca >= 0) {
|
||||
M5.Displays(index_module_rca).print("This is Module RCA\n");
|
||||
}
|
||||
if (index_unit_glass >= 0) {
|
||||
M5.Displays(index_unit_glass).print("This is Unit GLASS\n");
|
||||
}
|
||||
if (index_unit_glass2 >= 0) {
|
||||
M5.Displays(index_unit_glass2).print("This is Unit GLASS2\n");
|
||||
}
|
||||
if (index_unit_oled >= 0) {
|
||||
M5.Displays(index_unit_oled).print("This is Unit OLED\n");
|
||||
}
|
||||
if (index_unit_mini_oled >= 0) {
|
||||
M5.Displays(index_unit_mini_oled ).print("This is Unit Mini OLED\n");
|
||||
}
|
||||
if (index_unit_lcd >= 0) {
|
||||
M5.Displays(index_unit_lcd).print("This is Unit LCD\n");
|
||||
}
|
||||
if (index_unit_rca >= 0) {
|
||||
M5.Displays(index_unit_rca).print("This is Unit RCA\n");
|
||||
}
|
||||
M5.delay(5000);
|
||||
}
|
||||
|
||||
|
||||
// When creating a function for drawing, it can be used universally by accepting a LovyanGFX type as an argument.
|
||||
void draw_function(LovyanGFX* gfx)
|
||||
{
|
||||
int x = rand() % gfx->width();
|
||||
int y = rand() % gfx->height();
|
||||
int r = (gfx->width() >> 4) + 2;
|
||||
uint16_t c = rand();
|
||||
gfx->fillRect(x-r, y-r, r*2, r*2, c);
|
||||
}
|
||||
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.delay(1);
|
||||
|
||||
for (int i = 0; i < M5.getDisplayCount(); ++i) {
|
||||
int x = rand() % M5.Displays(i).width();
|
||||
int y = rand() % M5.Displays(i).height();
|
||||
int r = (M5.Displays(i).width() >> 4) + 2;
|
||||
uint16_t c = rand();
|
||||
M5.Displays(i).fillCircle(x, y, r, c);
|
||||
}
|
||||
|
||||
for (int i = 0; i < M5.getDisplayCount(); ++i) {
|
||||
draw_function(&M5.Displays(i));
|
||||
}
|
||||
}
|
||||
|
||||
// for ESP-IDF compat
|
||||
#if !defined ( ARDUINO ) && defined ( ESP_PLATFORM )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
3574
libraries/M5Unified/examples/Basic/HowToUse/HowToUse.ino
Normal file
299
libraries/M5Unified/examples/Basic/Imu/Imu.ino
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
|
||||
// If you use Unit OLED, write this.
|
||||
// #include <M5UnitOLED.h>
|
||||
|
||||
// If you use Unit LCD, write this.
|
||||
// #include <M5UnitLCD.h>
|
||||
|
||||
|
||||
// Include this to enable the M5 global instance.
|
||||
#include <M5Unified.h>
|
||||
|
||||
// Strength of the calibration operation;
|
||||
// 0: disables calibration.
|
||||
// 1 is weakest and 255 is strongest.
|
||||
static constexpr const uint8_t calib_value = 64;
|
||||
|
||||
|
||||
// This sample code performs calibration by clicking on a button or screen.
|
||||
// After 10 seconds of calibration, the results are stored in NVS.
|
||||
// The saved calibration values are loaded at the next startup.
|
||||
//
|
||||
// === How to calibration ===
|
||||
// ※ Calibration method for Accelerometer
|
||||
// Change the direction of the main unit by 90 degrees
|
||||
// and hold it still for 2 seconds. Repeat multiple times.
|
||||
// It is recommended that as many surfaces as possible be on the bottom.
|
||||
//
|
||||
// ※ Calibration method for Gyro
|
||||
// Simply place the unit on a quiet desk and hold it still.
|
||||
// It is recommended that this be done after the accelerometer calibration.
|
||||
//
|
||||
// ※ Calibration method for geomagnetic sensors
|
||||
// Rotate the main unit slowly in multiple directions.
|
||||
// It is recommended that as many surfaces as possible be oriented to the north.
|
||||
//
|
||||
// Values for extremely large attitude changes are ignored.
|
||||
// During calibration, it is desirable to move the device as gently as possible.
|
||||
|
||||
struct rect_t
|
||||
{
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t w;
|
||||
int32_t h;
|
||||
};
|
||||
|
||||
static constexpr const uint32_t color_tbl[18] =
|
||||
{
|
||||
0xFF0000u, 0xCCCC00u, 0xCC00FFu,
|
||||
0xFFCC00u, 0x00FF00u, 0x0088FFu,
|
||||
0xFF00CCu, 0x00FFCCu, 0x0000FFu,
|
||||
0xFF0000u, 0xCCCC00u, 0xCC00FFu,
|
||||
0xFFCC00u, 0x00FF00u, 0x0088FFu,
|
||||
0xFF00CCu, 0x00FFCCu, 0x0000FFu,
|
||||
};
|
||||
static constexpr const float coefficient_tbl[3] = { 0.5f, (1.0f / 256.0f), (1.0f / 1024.0f) };
|
||||
|
||||
static auto &dsp = (M5.Display);
|
||||
static rect_t rect_graph_area;
|
||||
static rect_t rect_text_area;
|
||||
|
||||
static uint8_t calib_countdown = 0;
|
||||
|
||||
static int prev_xpos[18];
|
||||
|
||||
void drawBar(int32_t ox, int32_t oy, int32_t nx, int32_t px, int32_t h, uint32_t color)
|
||||
{
|
||||
uint32_t bgcolor = (color >> 3) & 0x1F1F1Fu;
|
||||
if (px && ((nx < 0) != (px < 0)))
|
||||
{
|
||||
dsp.fillRect(ox, oy, px, h, bgcolor);
|
||||
px = 0;
|
||||
}
|
||||
if (px != nx)
|
||||
{
|
||||
if ((nx > px) != (nx < 0))
|
||||
{
|
||||
bgcolor = color;
|
||||
}
|
||||
dsp.setColor(bgcolor);
|
||||
dsp.fillRect(nx + ox, oy, px - nx, h);
|
||||
}
|
||||
}
|
||||
|
||||
void drawGraph(const rect_t& r, const m5::imu_data_t& data)
|
||||
{
|
||||
float aw = (128 * r.w) >> 1;
|
||||
float gw = (128 * r.w) / 256.0f;
|
||||
float mw = (128 * r.w) / 1024.0f;
|
||||
int ox = (r.x + r.w)>>1;
|
||||
int oy = r.y;
|
||||
int h = (r.h / 18) * (calib_countdown ? 1 : 2);
|
||||
int bar_count = 9 * (calib_countdown ? 2 : 1);
|
||||
|
||||
dsp.startWrite();
|
||||
for (int index = 0; index < bar_count; ++index)
|
||||
{
|
||||
float xval;
|
||||
if (index < 9)
|
||||
{
|
||||
auto coe = coefficient_tbl[index / 3] * r.w;
|
||||
xval = data.value[index] * coe;
|
||||
}
|
||||
else
|
||||
{
|
||||
xval = M5.Imu.getOffsetData(index - 9) * (1.0f / (1 << 19));
|
||||
}
|
||||
|
||||
// for Linear scale graph.
|
||||
float tmp = xval;
|
||||
|
||||
// The smaller the value, the larger the amount of change in the graph.
|
||||
// float tmp = sqrtf(fabsf(xval * 128)) * (signbit(xval) ? -1 : 1);
|
||||
|
||||
int nx = tmp;
|
||||
int px = prev_xpos[index];
|
||||
if (nx != px)
|
||||
prev_xpos[index] = nx;
|
||||
drawBar(ox, oy + h * index, nx, px, h - 1, color_tbl[index]);
|
||||
}
|
||||
dsp.endWrite();
|
||||
}
|
||||
|
||||
void updateCalibration(uint32_t c, bool clear = false)
|
||||
{
|
||||
calib_countdown = c;
|
||||
|
||||
if (c == 0) {
|
||||
clear = true;
|
||||
}
|
||||
|
||||
if (clear)
|
||||
{
|
||||
memset(prev_xpos, 0, sizeof(prev_xpos));
|
||||
dsp.fillScreen(TFT_BLACK);
|
||||
|
||||
if (c)
|
||||
{ // Start calibration.
|
||||
M5.Imu.setCalibration(calib_value, calib_value, calib_value);
|
||||
// ※ The actual calibration operation is performed each time during M5.Imu.update.
|
||||
//
|
||||
// There are three arguments, which can be specified in the order of Accelerometer, gyro, and geomagnetic.
|
||||
// If you want to calibrate only the Accelerometer, do the following.
|
||||
// M5.Imu.setCalibration(100, 0, 0);
|
||||
//
|
||||
// If you want to calibrate only the gyro, do the following.
|
||||
// M5.Imu.setCalibration(0, 100, 0);
|
||||
//
|
||||
// If you want to calibrate only the geomagnetism, do the following.
|
||||
// M5.Imu.setCalibration(0, 0, 100);
|
||||
}
|
||||
else
|
||||
{ // Stop calibration. (Continue calibration only for the geomagnetic sensor)
|
||||
M5.Imu.setCalibration(0, 0, calib_value);
|
||||
|
||||
// If you want to stop all calibration, write this.
|
||||
// M5.Imu.setCalibration(0, 0, 0);
|
||||
|
||||
// save calibration values.
|
||||
M5.Imu.saveOffsetToNVS();
|
||||
}
|
||||
}
|
||||
|
||||
auto backcolor = (c == 0) ? TFT_BLACK : TFT_BLUE;
|
||||
dsp.fillRect(rect_text_area.x, rect_text_area.y, rect_text_area.w, rect_text_area.h, backcolor);
|
||||
|
||||
if (c)
|
||||
{
|
||||
dsp.setCursor(rect_text_area.x + 2, rect_text_area.y + 1);
|
||||
dsp.setTextColor(TFT_WHITE, TFT_BLUE);
|
||||
dsp.printf("Countdown:%d ", c);
|
||||
}
|
||||
}
|
||||
|
||||
void startCalibration(void)
|
||||
{
|
||||
updateCalibration(10, true);
|
||||
}
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// If you want to use external IMU, write this
|
||||
//cfg.external_imu = true;
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
const char* name;
|
||||
auto imu_type = M5.Imu.getType();
|
||||
switch (imu_type)
|
||||
{
|
||||
case m5::imu_none: name = "not found"; break;
|
||||
case m5::imu_sh200q: name = "sh200q"; break;
|
||||
case m5::imu_mpu6050: name = "mpu6050"; break;
|
||||
case m5::imu_mpu6886: name = "mpu6886"; break;
|
||||
case m5::imu_mpu9250: name = "mpu9250"; break;
|
||||
case m5::imu_bmi270: name = "bmi270"; break;
|
||||
default: name = "unknown"; break;
|
||||
};
|
||||
M5_LOGI("imu:%s", name);
|
||||
M5.Display.printf("imu:%s", name);
|
||||
|
||||
if (imu_type == m5::imu_none)
|
||||
{
|
||||
for (;;) { delay(1); }
|
||||
}
|
||||
|
||||
int32_t w = dsp.width();
|
||||
int32_t h = dsp.height();
|
||||
if (w < h)
|
||||
{
|
||||
dsp.setRotation(dsp.getRotation() ^ 1);
|
||||
w = dsp.width();
|
||||
h = dsp.height();
|
||||
}
|
||||
int32_t graph_area_h = ((h - 8) / 18) * 18;
|
||||
int32_t text_area_h = h - graph_area_h;
|
||||
float fontsize = text_area_h / 8;
|
||||
dsp.setTextSize(fontsize);
|
||||
|
||||
rect_graph_area = { 0, 0, w, graph_area_h };
|
||||
rect_text_area = {0, graph_area_h, w, text_area_h };
|
||||
|
||||
|
||||
// Read calibration values from NVS.
|
||||
if (!M5.Imu.loadOffsetFromNVS())
|
||||
{
|
||||
startCalibration();
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
static uint32_t frame_count = 0;
|
||||
static uint32_t prev_sec = 0;
|
||||
|
||||
// To update the IMU value, use M5.Imu.update.
|
||||
// If a new value is obtained, the return value is non-zero.
|
||||
auto imu_update = M5.Imu.update();
|
||||
if (imu_update)
|
||||
{
|
||||
// Obtain data on the current value of the IMU.
|
||||
auto data = M5.Imu.getImuData();
|
||||
drawGraph(rect_graph_area, data);
|
||||
/*
|
||||
// The data obtained by getImuData can be used as follows.
|
||||
data.accel.x; // accel x-axis value.
|
||||
data.accel.y; // accel y-axis value.
|
||||
data.accel.z; // accel z-axis value.
|
||||
data.accel.value; // accel 3values array [0]=x / [1]=y / [2]=z.
|
||||
|
||||
data.gyro.x; // gyro x-axis value.
|
||||
data.gyro.y; // gyro y-axis value.
|
||||
data.gyro.z; // gyro z-axis value.
|
||||
data.gyro.value; // gyro 3values array [0]=x / [1]=y / [2]=z.
|
||||
|
||||
data.mag.x; // mag x-axis value.
|
||||
data.mag.y; // mag y-axis value.
|
||||
data.mag.z; // mag z-axis value.
|
||||
data.mag.value; // mag 3values array [0]=x / [1]=y / [2]=z.
|
||||
|
||||
data.value; // all sensor 9values array [0~2]=accel / [3~5]=gyro / [6~8]=mag
|
||||
|
||||
M5_LOGV("ax:%f ay:%f az:%f", data.accel.x, data.accel.y, data.accel.z);
|
||||
M5_LOGV("gx:%f gy:%f gz:%f", data.gyro.x , data.gyro.y , data.gyro.z );
|
||||
M5_LOGV("mx:%f my:%f mz:%f", data.mag.x , data.mag.y , data.mag.z );
|
||||
//*/
|
||||
++frame_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
M5.update();
|
||||
|
||||
// Calibration is initiated when a button or screen is clicked.
|
||||
if (M5.BtnA.wasClicked() || M5.BtnPWR.wasClicked() || M5.Touch.getDetail().wasClicked())
|
||||
{
|
||||
startCalibration();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t sec = millis() / 1000;
|
||||
if (prev_sec != sec)
|
||||
{
|
||||
prev_sec = sec;
|
||||
M5_LOGI("sec:%d frame:%d", sec, frame_count);
|
||||
frame_count = 0;
|
||||
|
||||
if (calib_countdown)
|
||||
{
|
||||
updateCalibration(calib_countdown - 1);
|
||||
}
|
||||
|
||||
if ((sec & 7) == 0)
|
||||
{ // prevent WDT.
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
libraries/M5Unified/examples/Basic/LogOutput/LogOutput.ino
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#include <M5Unified.h>
|
||||
|
||||
|
||||
void user_made_log_callback(esp_log_level_t, bool, const char*);
|
||||
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
/// Example of ESP32 standard log output macro usage.
|
||||
/// ESP_LOGx series outputs to USB-connected PC terminal.
|
||||
/// You can set the output level with `Core Debug Level` in ArduinoIDE.
|
||||
/// ※ If the `Core Debug Level` is set to `None`, nothing is output.
|
||||
#if defined ( ESP_PLATFORM )
|
||||
ESP_LOGE("TAG", "using ESP_LOGE error log."); // Critical errors, software module can not recover on its own
|
||||
ESP_LOGW("TAG", "using ESP_LOGW warn log."); // Error conditions from which recovery measures have been taken
|
||||
ESP_LOGI("TAG", "using ESP_LOGI info log."); // Information messages which describe normal flow of events
|
||||
ESP_LOGD("TAG", "using ESP_LOGD debug log."); // Extra information which is not necessary for normal use (values, pointers, sizes, etc).
|
||||
ESP_LOGV("TAG", "using ESP_LOGV verbose log."); // Bigger chunks of debugging information, or frequent messages which can potentially flood the output.
|
||||
#endif
|
||||
|
||||
M5.begin();
|
||||
|
||||
/// If you want to output logs to the display, write this.
|
||||
M5.setLogDisplayIndex(0);
|
||||
|
||||
/// use wrapping from bottom edge to top edge.
|
||||
M5.Display.setTextWrap(true, true);
|
||||
/// use scrolling.
|
||||
// M5.Display.setTextScroll(true);
|
||||
|
||||
/// Example of M5Unified log output class usage.
|
||||
/// Unlike ESP_LOGx, the M5.Log series can output to serial, display, and user callback function in a single line of code.
|
||||
|
||||
|
||||
/// You can set Log levels for each output destination.
|
||||
/// ESP_LOG_ERROR / ESP_LOG_WARN / ESP_LOG_INFO / ESP_LOG_DEBUG / ESP_LOG_VERBOSE
|
||||
M5.Log.setLogLevel(m5::log_target_serial, ESP_LOG_VERBOSE);
|
||||
M5.Log.setLogLevel(m5::log_target_display, ESP_LOG_DEBUG);
|
||||
M5.Log.setLogLevel(m5::log_target_callback, ESP_LOG_INFO);
|
||||
|
||||
/// Set up user-specific callback functions.
|
||||
M5.Log.setCallback(user_made_log_callback);
|
||||
|
||||
/// You can color the log or not.
|
||||
M5.Log.setEnableColor(m5::log_target_serial, true);
|
||||
M5.Log.setEnableColor(m5::log_target_display, true);
|
||||
M5.Log.setEnableColor(m5::log_target_callback, true);
|
||||
|
||||
/// You can set the text to be added to the end of the log for each output destination.
|
||||
/// ( default value : "\n" )
|
||||
M5.Log.setSuffix(m5::log_target_serial, "\n");
|
||||
M5.Log.setSuffix(m5::log_target_display, "\n");
|
||||
M5.Log.setSuffix(m5::log_target_callback, "");
|
||||
|
||||
/// `M5.Log()` can be used to output a simple log
|
||||
M5.Log(ESP_LOG_ERROR , "M5.Log error log"); /// ERROR level output
|
||||
M5.Log(ESP_LOG_WARN , "M5.Log warn log"); /// WARN level output
|
||||
M5.Log(ESP_LOG_INFO , "M5.Log info log"); /// INFO level output
|
||||
M5.Log(ESP_LOG_DEBUG , "M5.Log debug log"); /// DEBUG level output
|
||||
M5.Log(ESP_LOG_VERBOSE , "M5.Log verbose log"); /// VERBOSE level output
|
||||
|
||||
/// `M5_LOGx` macro can be used to output a log containing the source file name, line number, and function name.
|
||||
M5_LOGE("M5_LOGE error log"); /// ERROR level output with source info
|
||||
M5_LOGW("M5_LOGW warn log"); /// WARN level output with source info
|
||||
M5_LOGI("M5_LOGI info log"); /// INFO level output with source info
|
||||
M5_LOGD("M5_LOGD debug log"); /// DEBUG level output with source info
|
||||
M5_LOGV("M5_LOGV verbose log"); /// VERBOSE level output with source info
|
||||
|
||||
/// `M5.Log.printf()` is output without log level and without suffix and is output to all serial, display, and callback.
|
||||
M5.Log.printf("M5.Log.printf non level output\n");
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.delay(1);
|
||||
M5.update();
|
||||
|
||||
if (M5.BtnPWR.wasClicked()) { M5_LOGE("BtnP %d click", M5.BtnPWR.getClickCount()); }
|
||||
if (M5.BtnA .wasClicked()) { M5_LOGW("BtnA %d click", M5.BtnA .getClickCount()); }
|
||||
if (M5.BtnB .wasClicked()) { M5_LOGI("BtnB %d click", M5.BtnB .getClickCount()); }
|
||||
if (M5.BtnC .wasClicked()) { M5_LOGD("BtnC %d click", M5.BtnC .getClickCount()); }
|
||||
|
||||
static uint32_t counter = 0;
|
||||
if ((++counter & 0x3FF) == 0)
|
||||
{
|
||||
static int prev_y;
|
||||
int cursor_y = M5.Display.getCursorY();
|
||||
if (prev_y > cursor_y)
|
||||
{
|
||||
M5.Display.clear();
|
||||
}
|
||||
prev_y = cursor_y;
|
||||
M5_LOGV("count:%d", counter >> 10);
|
||||
}
|
||||
}
|
||||
|
||||
void user_made_log_callback(esp_log_level_t log_level, bool use_color, const char* log_text)
|
||||
{
|
||||
// You can also create your own callback function to output log contents to a file,WiFi,and more other destination
|
||||
|
||||
#if defined ( ARDUINO )
|
||||
/*
|
||||
if (SD.begin(GPIO_NUM_4, SPI, 25000000))
|
||||
{
|
||||
auto file = SD.open("/logfile.txt", FILE_APPEND);
|
||||
file.print(log_text);
|
||||
file.close();
|
||||
SD.end();
|
||||
}
|
||||
//*/
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// for ESP-IDF
|
||||
#if !defined ( ARDUINO ) && defined ( ESP_PLATFORM )
|
||||
extern "C"
|
||||
{
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
145
libraries/M5Unified/examples/Basic/Microphone/Microphone.ino
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#include <M5UnitLCD.h>
|
||||
#include <M5UnitOLED.h>
|
||||
#include <M5Unified.h>
|
||||
|
||||
static constexpr const size_t record_number = 256;
|
||||
static constexpr const size_t record_length = 200;
|
||||
static constexpr const size_t record_size = record_number * record_length;
|
||||
static constexpr const size_t record_samplerate = 16000;
|
||||
static int16_t prev_y[record_length];
|
||||
static int16_t prev_h[record_length];
|
||||
static size_t rec_record_idx = 2;
|
||||
static size_t draw_record_idx = 0;
|
||||
static int16_t *rec_data;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
// cfg.external_speaker.hat_spk = true; /// use external speaker (HAT SPK)
|
||||
// cfg.external_speaker.hat_spk2 = true; /// use external speaker (HAT SPK2)
|
||||
// cfg.external_speaker.atomic_spk = true; /// use external speaker (ATOMIC SPK)
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
M5.Display.startWrite();
|
||||
|
||||
if (M5.Display.width() > M5.Display.height())
|
||||
{
|
||||
M5.Display.setRotation(M5.Display.getRotation()^1);
|
||||
}
|
||||
|
||||
M5.Display.setCursor(0, 0);
|
||||
M5.Display.print("REC");
|
||||
rec_data = (typeof(rec_data))heap_caps_malloc(record_size * sizeof(int16_t), MALLOC_CAP_8BIT);
|
||||
memset(rec_data, 0 , record_size * sizeof(int16_t));
|
||||
M5.Speaker.setVolume(255);
|
||||
|
||||
/// Since the microphone and speaker cannot be used at the same time, turn off the speaker here.
|
||||
M5.Speaker.end();
|
||||
M5.Mic.begin();
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.update();
|
||||
|
||||
if (M5.Mic.isEnabled())
|
||||
{
|
||||
static constexpr int shift = 6;
|
||||
auto data = &rec_data[rec_record_idx * record_length];
|
||||
if (M5.Mic.record(data, record_length, record_samplerate))
|
||||
{
|
||||
data = &rec_data[draw_record_idx * record_length];
|
||||
|
||||
int32_t w = M5.Display.width();
|
||||
if (w > record_length - 1) { w = record_length - 1; }
|
||||
for (int32_t x = 0; x < w; ++x)
|
||||
{
|
||||
M5.Display.writeFastVLine(x, prev_y[x], prev_h[x], TFT_BLACK);
|
||||
int32_t y1 = (data[x ] >> shift);
|
||||
int32_t y2 = (data[x + 1] >> shift);
|
||||
if (y1 > y2)
|
||||
{
|
||||
int32_t tmp = y1;
|
||||
y1 = y2;
|
||||
y2 = tmp;
|
||||
}
|
||||
int32_t y = (M5.Display.height() >> 1) + y1;
|
||||
int32_t h = (M5.Display.height() >> 1) + y2 + 1 - y;
|
||||
prev_y[x] = y;
|
||||
prev_h[x] = h;
|
||||
M5.Display.writeFastVLine(x, y, h, TFT_WHITE);
|
||||
}
|
||||
M5.Display.display();
|
||||
|
||||
if (++draw_record_idx >= record_number) { draw_record_idx = 0; }
|
||||
if (++rec_record_idx >= record_number) { rec_record_idx = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
if (M5.BtnA.wasHold() || M5.BtnB.wasClicked())
|
||||
{
|
||||
auto cfg = M5.Mic.config();
|
||||
cfg.noise_filter_level = (cfg.noise_filter_level + 8) & 255;
|
||||
M5.Mic.config(cfg);
|
||||
M5.Display.setCursor(32,0);
|
||||
M5.Display.printf("nf:%03d", cfg.noise_filter_level);
|
||||
}
|
||||
else
|
||||
if (M5.BtnA.wasClicked() || (M5.Touch.getCount() && M5.Touch.getDetail(0).wasClicked()))
|
||||
{
|
||||
if (M5.Speaker.isEnabled())
|
||||
{
|
||||
M5.Display.clear();
|
||||
while (M5.Mic.isRecording()) { M5.delay(1); }
|
||||
|
||||
/// Since the microphone and speaker cannot be used at the same time, turn off the microphone here.
|
||||
M5.Mic.end();
|
||||
M5.Speaker.begin();
|
||||
|
||||
M5.Display.setCursor(0,0);
|
||||
M5.Display.print("PLAY");
|
||||
int start_pos = rec_record_idx * record_length;
|
||||
if (start_pos < record_size)
|
||||
{
|
||||
M5.Speaker.playRaw(&rec_data[start_pos], record_size - start_pos, record_samplerate, false, 1, 0);
|
||||
}
|
||||
if (start_pos > 0)
|
||||
{
|
||||
M5.Speaker.playRaw(rec_data, start_pos, record_samplerate, false, 1, 0);
|
||||
}
|
||||
do
|
||||
{
|
||||
M5.delay(1);
|
||||
M5.update();
|
||||
} while (M5.Speaker.isPlaying());
|
||||
|
||||
/// Since the microphone and speaker cannot be used at the same time, turn off the speaker here.
|
||||
M5.Speaker.end();
|
||||
M5.Mic.begin();
|
||||
|
||||
M5.Display.clear();
|
||||
M5.Display.setCursor(0,0);
|
||||
M5.Display.print("REC");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
155
libraries/M5Unified/examples/Basic/Rtc/Rtc.ino
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#if defined ( ARDUINO )
|
||||
|
||||
#define WIFI_SSID "YOUR WIFI SSID NAME"
|
||||
#define WIFI_PASSWORD "YOUR WIFI PASSWORD"
|
||||
#define NTP_TIMEZONE "JST-9"
|
||||
#define NTP_SERVER1 "0.pool.ntp.org"
|
||||
#define NTP_SERVER2 "1.pool.ntp.org"
|
||||
#define NTP_SERVER3 "2.pool.ntp.org"
|
||||
|
||||
#include <WiFi.h>
|
||||
|
||||
// Different versions of the framework have different SNTP header file names and availability.
|
||||
#if __has_include (<esp_sntp.h>)
|
||||
#include <esp_sntp.h>
|
||||
#define SNTP_ENABLED 1
|
||||
#elif __has_include (<sntp.h>)
|
||||
#include <sntp.h>
|
||||
#define SNTP_ENABLED 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef SNTP_ENABLED
|
||||
#define SNTP_ENABLED 0
|
||||
#endif
|
||||
|
||||
#include <M5Unified.h>
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
auto cfg = M5.config();
|
||||
|
||||
cfg.external_rtc = true; // default=false. use Unit RTC.
|
||||
|
||||
M5.begin(cfg);
|
||||
|
||||
M5.Display.setEpdMode(m5gfx::epd_fastest);
|
||||
M5.setLogDisplayIndex(0);
|
||||
|
||||
if (!M5.Rtc.isEnabled())
|
||||
{
|
||||
M5.Log.println("RTC not found.");
|
||||
for (;;) { M5.delay(500); }
|
||||
}
|
||||
|
||||
M5.Log.println("RTC found.");
|
||||
|
||||
// It is recommended to set UTC for the RTC and ESP32 internal clocks.
|
||||
|
||||
|
||||
/* /// setup RTC ( direct setting )
|
||||
// YYYY MM DD hh mm ss
|
||||
M5.Rtc.setDateTime( { { 2021, 12, 31 }, { 12, 34, 56 } } );
|
||||
//*/
|
||||
|
||||
|
||||
/// setup RTC ( NTP auto setting )
|
||||
configTzTime(NTP_TIMEZONE, NTP_SERVER1, NTP_SERVER2, NTP_SERVER3);
|
||||
#ifdef WiFi_h
|
||||
M5.Log.print("WiFi:");
|
||||
WiFi.begin( WIFI_SSID, WIFI_PASSWORD );
|
||||
|
||||
for (int i = 20; i && WiFi.status() != WL_CONNECTED; --i)
|
||||
{
|
||||
M5.Log.print(".");
|
||||
M5.delay(500);
|
||||
}
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
M5.Log.println("\r\nWiFi Connected.");
|
||||
M5.Log.print("NTP:");
|
||||
#if SNTP_ENABLED
|
||||
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED)
|
||||
{
|
||||
M5.Log.print(".");
|
||||
M5.delay(1000);
|
||||
}
|
||||
#else
|
||||
M5.delay(1600);
|
||||
struct tm timeInfo;
|
||||
while (!getLocalTime(&timeInfo, 1000))
|
||||
{
|
||||
M5.Log.print('.');
|
||||
};
|
||||
#endif
|
||||
M5.Log.println("\r\nNTP Connected.");
|
||||
|
||||
time_t t = time(nullptr)+1; // Advance one second.
|
||||
while (t > time(nullptr)); /// Synchronization in seconds
|
||||
M5.Rtc.setDateTime( gmtime( &t ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
M5.Log.println("\r\nWiFi none...");
|
||||
}
|
||||
#endif
|
||||
|
||||
M5.Display.clear();
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
static constexpr const char* const wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
|
||||
|
||||
M5.delay(500);
|
||||
|
||||
auto dt = M5.Rtc.getDateTime();
|
||||
M5.Display.setCursor(0,0);
|
||||
M5.Log.printf("RTC UTC :%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n"
|
||||
, dt.date.year
|
||||
, dt.date.month
|
||||
, dt.date.date
|
||||
, wd[dt.date.weekDay]
|
||||
, dt.time.hours
|
||||
, dt.time.minutes
|
||||
, dt.time.seconds
|
||||
);
|
||||
|
||||
/// ESP32 internal timer
|
||||
auto t = time(nullptr);
|
||||
{
|
||||
auto tm = gmtime(&t); // for UTC.
|
||||
M5.Display.setCursor(0,20);
|
||||
M5.Log.printf("ESP32 UTC :%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n",
|
||||
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
|
||||
wd[tm->tm_wday],
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
|
||||
{
|
||||
auto tm = localtime(&t); // for local timezone.
|
||||
M5.Display.setCursor(0,40);
|
||||
M5.Log.printf("ESP32 %s:%04d/%02d/%02d (%s) %02d:%02d:%02d\r\n", NTP_TIMEZONE,
|
||||
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
|
||||
wd[tm->tm_wday],
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
17724
libraries/M5Unified/examples/Basic/Speaker/Speaker.ino
Normal file
164
libraries/M5Unified/examples/Basic/Touch/DragDrop/DragDrop.ino
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#include <M5Unified.h>
|
||||
|
||||
struct box_t
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
std::uint16_t color;
|
||||
int touch_id = -1;
|
||||
|
||||
void clear(void)
|
||||
{
|
||||
M5.Display.setColor(M5.Display.getBaseColor());
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
M5.Display.drawRect(x+i, y+i, w-i*2, h-i*2);
|
||||
}
|
||||
}
|
||||
void draw(void)
|
||||
{
|
||||
M5.Display.setColor(color);
|
||||
int ie = touch_id < 0 ? 4 : 8;
|
||||
for (int i = 0; i < ie; ++i)
|
||||
{
|
||||
M5.Display.drawRect(x+i, y+i, w-i*2, h-i*2);
|
||||
}
|
||||
}
|
||||
bool contain(int x, int y)
|
||||
{
|
||||
return this->x <= x && x < (this->x + this->w)
|
||||
&& this->y <= y && y < (this->y + this->h);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr std::size_t box_count = 4;
|
||||
static box_t box_list[box_count];
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
M5.begin();
|
||||
|
||||
for (std::size_t i = 0; i < box_count; ++i)
|
||||
{
|
||||
box_list[i].w = 100;
|
||||
box_list[i].h = 100;
|
||||
}
|
||||
box_list[0].x = 0;
|
||||
box_list[0].y = 0;
|
||||
box_list[1].x = M5.Display.width() - box_list[1].w;
|
||||
box_list[1].y = 0;
|
||||
box_list[2].x = 0;
|
||||
box_list[2].y = M5.Display.height() - box_list[2].h;
|
||||
box_list[3].x = M5.Display.width() - box_list[3].w;
|
||||
box_list[3].y = M5.Display.height() - box_list[3].h;
|
||||
box_list[0].color = TFT_RED;
|
||||
box_list[1].color = TFT_BLUE;
|
||||
box_list[2].color = TFT_GREEN;
|
||||
box_list[3].color = TFT_YELLOW;
|
||||
|
||||
M5.Display.setEpdMode(epd_mode_t::epd_fastest);
|
||||
M5.Display.startWrite();
|
||||
M5.Display.setTextSize(2);
|
||||
for (std::size_t i = 0; i < box_count; ++i)
|
||||
{
|
||||
box_list[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.delay(1);
|
||||
|
||||
M5.update();
|
||||
|
||||
auto count = M5.Touch.getCount();
|
||||
if (!count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static m5::touch_state_t prev_state;
|
||||
auto t = M5.Touch.getDetail();
|
||||
if (prev_state != t.state)
|
||||
{
|
||||
prev_state = t.state;
|
||||
static constexpr const char* state_name[16] =
|
||||
{ "none"
|
||||
, "touch"
|
||||
, "touch_end"
|
||||
, "touch_begin"
|
||||
, "___"
|
||||
, "hold"
|
||||
, "hold_end"
|
||||
, "hold_begin"
|
||||
, "___"
|
||||
, "flick"
|
||||
, "flick_end"
|
||||
, "flick_begin"
|
||||
, "___"
|
||||
, "drag"
|
||||
, "drag_end"
|
||||
, "drag_begin"
|
||||
};
|
||||
M5_LOGI("%s", state_name[t.state]);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
auto t = M5.Touch.getDetail(i);
|
||||
|
||||
for (std::size_t j = 0; j < box_count; ++j)
|
||||
{
|
||||
if (t.wasHold())
|
||||
{
|
||||
if (box_list[j].contain(t.x, t.y))
|
||||
{
|
||||
box_list[j].touch_id = t.id;
|
||||
}
|
||||
}
|
||||
|
||||
M5.Display.waitDisplay();
|
||||
if (box_list[j].touch_id == t.id)
|
||||
{
|
||||
if (t.wasReleased())
|
||||
{
|
||||
box_list[j].touch_id = -1;
|
||||
box_list[j].clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dx = t.deltaX();
|
||||
auto dy = t.deltaY();
|
||||
if (dx || dy)
|
||||
{
|
||||
box_list[j].clear();
|
||||
box_list[j].x += dx;
|
||||
box_list[j].y += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
box_list[j].draw();
|
||||
}
|
||||
}
|
||||
M5.Display.display();
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO ) && defined ( ESP_PLATFORM )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
228
libraries/M5Unified/examples/Basic/Touch/SliderUI/SliderUI.ino
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#include <M5Unified.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct rect_t
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
bool contain(int x, int y)
|
||||
{
|
||||
return this->x <= x && x < (this->x + this->w)
|
||||
&& this->y <= y && y < (this->y + this->h);
|
||||
}
|
||||
bool contain(m5::Touch_Class::point_t &p) {
|
||||
return contain(p.x, p.y);
|
||||
}
|
||||
};
|
||||
|
||||
class slider_t
|
||||
{
|
||||
public:
|
||||
void setup(const rect_t &r, int min_value, int max_value, int value, uint16_t frame_color = 0xFFFF, uint16_t back_color = 0, uint16_t thumb_color = 0xFFE0) {
|
||||
setupPosition(r);
|
||||
setupValue(min_value, max_value, value);
|
||||
setupColor(frame_color, back_color, thumb_color);
|
||||
}
|
||||
|
||||
void setupPosition(const rect_t &r) {
|
||||
_rect = r;
|
||||
_vertical = r.h > r.w;
|
||||
}
|
||||
|
||||
void setupValue(int min_value, int max_value, int value)
|
||||
{
|
||||
_min_value = min_value;
|
||||
_max_value = max_value;
|
||||
_current_value = value;
|
||||
_control_value = value;
|
||||
}
|
||||
|
||||
void setupColor(uint16_t frame_color, uint16_t back_color, uint16_t thumb_color)
|
||||
{
|
||||
_frame_color = frame_color;
|
||||
_back_color = back_color;
|
||||
_thumb_color = thumb_color;
|
||||
}
|
||||
|
||||
void draw(LovyanGFX* gfx = &M5.Display)
|
||||
{
|
||||
gfx->startWrite();
|
||||
draw_frame(gfx);
|
||||
int pos = calc_pos(_current_value);
|
||||
draw_thumb(pos, _thumb_color, gfx);
|
||||
gfx->endWrite();
|
||||
}
|
||||
|
||||
bool update(m5::touch_detail_t &td, LovyanGFX* gfx = &M5.Display)
|
||||
{
|
||||
_was_changed = false;
|
||||
if (!_rect.contain(td.base)) return false;
|
||||
|
||||
if (td.wasReleased()) {
|
||||
_current_value = _control_value;
|
||||
}
|
||||
|
||||
if (td.isPressed()) {
|
||||
int rw = _vertical ? _rect.h : _rect.w;
|
||||
int tmp = (_current_value - _min_value) * rw / (_max_value - _min_value);
|
||||
tmp += _vertical ? td.distanceY() : td.distanceX();
|
||||
int v = _min_value + (_max_value - _min_value) * tmp / rw;
|
||||
_was_changed = value_update(v, gfx);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getValue(void) const { return _control_value; }
|
||||
bool wasChanged(void) const { return _was_changed; }
|
||||
|
||||
protected:
|
||||
rect_t _rect;
|
||||
|
||||
int _min_value;
|
||||
int _max_value;
|
||||
int _current_value;
|
||||
int _control_value;
|
||||
|
||||
uint16_t _frame_color = 0xFFFF;
|
||||
uint16_t _back_color = 0;
|
||||
uint16_t _thumb_color = 0xFFE0;
|
||||
bool _vertical = false;
|
||||
bool _was_changed = false;
|
||||
|
||||
bool value_update(int new_value, LovyanGFX* gfx = &M5.Display)
|
||||
{
|
||||
int min_v = _min_value;
|
||||
int max_v = _max_value;
|
||||
if (min_v > max_v) {
|
||||
min_v = _max_value;
|
||||
max_v = _min_value;
|
||||
}
|
||||
new_value = new_value < min_v ? min_v : new_value > max_v ? max_v : new_value;
|
||||
|
||||
if (_control_value == new_value) {
|
||||
return false;
|
||||
}
|
||||
int prev_pos = calc_pos(_control_value);
|
||||
int new_pos = calc_pos(new_value);
|
||||
_control_value = new_value;
|
||||
|
||||
if (prev_pos != new_pos) {
|
||||
gfx->startWrite();
|
||||
draw_thumb(prev_pos, _back_color, gfx);
|
||||
draw_thumb(new_pos, _thumb_color, gfx);
|
||||
gfx->endWrite();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int calc_pos(int value)
|
||||
{
|
||||
int diff = _max_value - _min_value;
|
||||
if (diff == 0) return 0;
|
||||
value -= _min_value;
|
||||
int w = _vertical ? (_rect.h - _rect.w) : (_rect.w - _rect.h);
|
||||
return (w * value) / diff;
|
||||
}
|
||||
|
||||
void draw_frame(LovyanGFX* gfx = &M5.Display)
|
||||
{
|
||||
gfx->fillRoundRect(_rect.x+1, _rect.y+1, _rect.w-2, _rect.h-2, 2, _back_color);
|
||||
gfx->drawRoundRect(_rect.x, _rect.y, _rect.w, _rect.h, 3, _frame_color);
|
||||
}
|
||||
|
||||
void draw_thumb(int pos, uint16_t color, LovyanGFX* gfx = &M5.Display)
|
||||
{
|
||||
int rx = _rect.x + 1;
|
||||
int ry = _rect.y + 1;
|
||||
int rw;
|
||||
if (_vertical) {
|
||||
ry += pos;
|
||||
rw = _rect.w;
|
||||
} else {
|
||||
rx += pos;
|
||||
rw = _rect.h;
|
||||
}
|
||||
rw -= 2;
|
||||
gfx->fillRoundRect(rx, ry, rw, rw, 3, color);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static constexpr std::size_t slider_count = 4;
|
||||
static slider_t slider_list[slider_count];
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
M5.begin();
|
||||
|
||||
int dw = M5.Display.width();
|
||||
int dh = M5.Display.height();
|
||||
|
||||
slider_list[0].setup({ 20, dh/2 -20, dw-40, 40 }, 5, 25, 15, TFT_WHITE, TFT_BLACK, TFT_LIGHTGRAY);
|
||||
slider_list[1].setup({ dw*1/4-20, 0, 40, dh/2 -40 }, 255, 0, 127, TFT_WHITE, TFT_BLACK, TFT_RED);
|
||||
slider_list[2].setup({ dw*2/4-20, 0, 40, dh/2 -40 }, 255, 0, 127, TFT_WHITE, TFT_BLACK, TFT_GREEN);
|
||||
slider_list[3].setup({ dw*3/4-20, 0, 40, dh/2 -40 }, 255, 0, 127, TFT_WHITE, TFT_BLACK, TFT_BLUE);
|
||||
|
||||
M5.Display.setEpdMode(epd_mode_t::epd_fastest);
|
||||
M5.Display.setTextSize(2);
|
||||
for (std::size_t i = 0; i < slider_count; ++i)
|
||||
{
|
||||
slider_list[i].draw();
|
||||
}
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.delay(16);
|
||||
|
||||
M5.update();
|
||||
|
||||
if (0 == M5.Touch.getCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
auto t = M5.Touch.getDetail();
|
||||
for (std::size_t i = 0; i < slider_count; ++i)
|
||||
{
|
||||
if (slider_list[i].update(t)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
int w = slider_list[0].getValue();
|
||||
int r = slider_list[1].getValue();
|
||||
int g = slider_list[2].getValue();
|
||||
int b = slider_list[3].getValue();
|
||||
uint32_t rgb = r << 16 | g << 8 | b;
|
||||
if (slider_list[0].wasChanged())
|
||||
{
|
||||
M5.Display.fillRect(M5.Display.width()/2-25, M5.Display.height()*3/4-25, 51,51, TFT_BLACK);
|
||||
}
|
||||
M5.Display.fillCircle(M5.Display.width()/2, M5.Display.height()*3/4, w, rgb);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined ( ARDUINO ) && defined ( ESP_PLATFORM )
|
||||
extern "C" {
|
||||
void loopTask(void*)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
3
libraries/M5Unified/examples/PlatformIO_SDL/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
# Check here for setup instructions.
|
||||
## [Steps to run M5GFX on a PC.](https://github.com/m5stack/M5GFX/blob/master/examples/PlatformIO_SDL/README.md)
|
||||
66
libraries/M5Unified/examples/PlatformIO_SDL/platformio.ini
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = native
|
||||
|
||||
[env]
|
||||
lib_extra_dirs=../../../
|
||||
|
||||
[env:native]
|
||||
platform = native
|
||||
build_type = debug
|
||||
build_flags = -O0 -xc++ -std=c++14 -lSDL2
|
||||
-I"/usr/local/include/SDL2" ; for intel mac homebrew SDL2
|
||||
-L"/usr/local/lib" ; for intel mac homebrew SDL2
|
||||
-I"${sysenv.HOMEBREW_PREFIX}/include/SDL2" ; for arm mac homebrew SDL2
|
||||
-L"${sysenv.HOMEBREW_PREFIX}/lib" ; for arm mac homebrew SDL2
|
||||
|
||||
[env:native_StickCPlus]
|
||||
extends = native
|
||||
platform = native
|
||||
build_flags = ${env:native.build_flags}
|
||||
-DM5GFX_SCALE=2
|
||||
-DM5GFX_ROTATION=0
|
||||
-DM5GFX_BOARD=board_M5StickCPlus
|
||||
|
||||
[env:native_Paper]
|
||||
extends = native
|
||||
platform = native
|
||||
build_flags = ${env:native.build_flags}
|
||||
-DM5GFX_ROTATION=0
|
||||
-DM5GFX_BOARD=board_M5Paper
|
||||
|
||||
[esp32_base]
|
||||
build_type = debug
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
upload_speed = 1500000
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32_arduino]
|
||||
extends = esp32_base
|
||||
framework = arduino
|
||||
|
||||
[env:esp32c3_arduino]
|
||||
extends = esp32_base
|
||||
framework = arduino
|
||||
board = esp32-c3-devkitm-1
|
||||
|
||||
[env:esp32s3_arduino]
|
||||
extends = esp32_base
|
||||
framework = arduino
|
||||
board = esp32-s3-devkitc-1
|
||||
|
||||
[env:esp32_idf]
|
||||
extends = esp32_base
|
||||
framework = espidf
|
||||
|
||||
25
libraries/M5Unified/examples/PlatformIO_SDL/src/sdl_main.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include <M5GFX.h>
|
||||
#if defined ( SDL_h_ )
|
||||
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
|
||||
__attribute__((weak))
|
||||
int user_func(bool* running)
|
||||
{
|
||||
setup();
|
||||
do
|
||||
{
|
||||
loop();
|
||||
} while (*running);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// The second argument is effective for step execution with breakpoints.
|
||||
// You can specify the time in milliseconds to perform slow execution that ensures screen updates.
|
||||
return lgfx::Panel_sdl::main(user_func, 128);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#include <M5Unified.h>
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
/// You may output logs to standard output.
|
||||
M5_LOGE("this is error LOG");
|
||||
M5_LOGW("this is warning LOG");
|
||||
M5_LOGI("this is info LOG");
|
||||
M5_LOGD("this is debug LOG");
|
||||
M5_LOGV("this is verbose LOG");
|
||||
|
||||
M5.begin();
|
||||
M5.Speaker.tone(2000, 100, 0, false);
|
||||
M5.Speaker.tone(1000, 100, 0, false);
|
||||
|
||||
M5.Display.printf("Please push cursor keys.\nButtonA == Left key\nButtonB == Down key\nButtonC == Right key\nButtonPWR == Up key\n");
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
M5.delay(8);
|
||||
M5.update();
|
||||
auto td = M5.Touch.getDetail();
|
||||
if (td.isPressed()) {
|
||||
M5.Display.fillCircle(td.x, td.y, 64, rand());
|
||||
int32_t tone = 880 + td.x + td.y;
|
||||
if (tone > 0) {
|
||||
M5.Speaker.tone(tone, 50, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (M5.BtnPWR.wasClicked()) {
|
||||
M5.Speaker.tone(4000, 200);
|
||||
M5_LOGD("BtnPWR Clicked");
|
||||
}
|
||||
if (M5.BtnA.wasClicked()) {
|
||||
M5.Speaker.tone(2000, 200);
|
||||
M5_LOGI("BtnA Clicked");
|
||||
}
|
||||
if (M5.BtnB.wasClicked()) {
|
||||
M5.Speaker.tone(1000, 200);
|
||||
M5_LOGW("BtnB Clicked");
|
||||
}
|
||||
if (M5.BtnC.wasClicked()) {
|
||||
M5.Speaker.tone(500, 200);
|
||||
M5_LOGE("BtnC Clicked");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined ( ESP_PLATFORM ) && !defined ( ARDUINO )
|
||||
extern "C" {
|
||||
int app_main(int, char**)
|
||||
{
|
||||
setup();
|
||||
for (;;) {
|
||||
loop();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
23
libraries/M5Unified/library.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "M5Unified",
|
||||
"description": "Library for M5Stack/Core2/Tough/CoreS3/CoreS3SE, M5StickC/C-Plus/C-Plus2, M5CoreInk, M5Paper, M5ATOM, M5STAMP, M5Station, M5Dial, M5DinMeter, M5Capsule, M5Cardputer, M5VAMeter",
|
||||
"keywords": "M5Unified",
|
||||
"authors": {
|
||||
"name": "M5Stack, lovyan03",
|
||||
"url": "http://www.m5stack.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/m5stack/M5Unified.git"
|
||||
},
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "M5GFX",
|
||||
"version": ">=0.2.8"
|
||||
}
|
||||
],
|
||||
"version": "0.2.7",
|
||||
"frameworks": ["arduino", "espidf", "*"],
|
||||
"platforms": ["espressif32", "native"],
|
||||
"headers": "M5Unified.h"
|
||||
}
|
||||
11
libraries/M5Unified/library.properties
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
name=M5Unified
|
||||
version=0.2.7
|
||||
author=M5Stack
|
||||
maintainer=M5Stack
|
||||
sentence=Library for M5Stack/Core2/Tough/CoreS3/CoreS3SE, M5StickC/C-Plus/C-Plus2, M5CoreInk, M5Paper, M5ATOM, M5STAMP, M5Station, M5Dial, M5DinMeter, M5Capsule, M5Cardputer, M5VAMeter
|
||||
paragraph=M5Stack, M5Stack Core2, M5Stack CoreInk, M5StickC, M5StickC-Plus, M5Paper, M5Tough, M5ATOM, M5STAMP, M5Station, See more on http://M5Stack.com
|
||||
category=Display
|
||||
url=https://github.com/m5stack/M5Unified.git
|
||||
architectures=esp32
|
||||
includes=M5Unified.h
|
||||
depends=M5GFX
|
||||
2122
libraries/M5Unified/src/M5Unified.cpp
Normal file
11
libraries/M5Unified/src/M5Unified.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "M5Unified.hpp"
|
||||
|
||||
#else
|
||||
|
||||
#error M5Unified requires a C++ compiler, please change file extension to .cc or .cpp
|
||||
|
||||
#endif
|
||||
588
libraries/M5Unified/src/M5Unified.hpp
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5UNIFIED_HPP__
|
||||
#define __M5UNIFIED_HPP__
|
||||
|
||||
#include "utility/m5unified_common.h"
|
||||
|
||||
#if __has_include(<sdkconfig.h>)
|
||||
#include <sdkconfig.h>
|
||||
#endif
|
||||
|
||||
// If you want to use a set of functions to handle SD/SPIFFS/HTTP,
|
||||
// please include <SD.h>,<SPIFFS.h>,<HTTPClient.h> before <M5GFX.h>
|
||||
// #include <SD.h>
|
||||
// #include <SPIFFS.h>
|
||||
// #include <HTTPClient.h>
|
||||
|
||||
#include <M5GFX.h>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
using board_t = m5gfx::board_t;
|
||||
using touch_point_t = m5gfx::touch_point_t;
|
||||
|
||||
enum pin_name_t
|
||||
{
|
||||
in_i2c_scl,
|
||||
in_i2c_sda,
|
||||
port_a_pin1, port_a_scl = port_a_pin1, ex_i2c_scl = port_a_pin1,
|
||||
port_a_pin2, port_a_sda = port_a_pin2, ex_i2c_sda = port_a_pin2,
|
||||
port_b_pin1, port_b_in = port_b_pin1,
|
||||
port_b_pin2, port_b_out = port_b_pin2,
|
||||
port_c_pin1, port_c_rxd = port_c_pin1,
|
||||
port_c_pin2, port_c_txd = port_c_pin2,
|
||||
port_d_pin1, port_d_rxd = port_d_pin1, port_b2_pin1 = port_d_pin1, // b2,c2 for M5Station
|
||||
port_d_pin2, port_d_txd = port_d_pin2, port_b2_pin2 = port_d_pin2,
|
||||
port_e_pin1, port_e_rxd = port_e_pin1, port_c2_pin1 = port_e_pin1,
|
||||
port_e_pin2, port_e_txd = port_e_pin2, port_c2_pin2 = port_e_pin2,
|
||||
sd_spi_sclk,
|
||||
sd_spi_copi, sd_spi_mosi = sd_spi_copi,
|
||||
sd_spi_cipo, sd_spi_miso = sd_spi_cipo,
|
||||
sd_spi_cs, sd_spi_ss = sd_spi_cs,
|
||||
rgb_led,
|
||||
power_hold,
|
||||
mbus_pin1, mbus_pin2, mbus_pin3, mbus_pin4, mbus_pin5,
|
||||
mbus_pin6, mbus_pin7, mbus_pin8, mbus_pin9, mbus_pin10,
|
||||
mbus_pin11, mbus_pin12, mbus_pin13, mbus_pin14, mbus_pin15,
|
||||
mbus_pin16, mbus_pin17, mbus_pin18, mbus_pin19, mbus_pin20,
|
||||
mbus_pin21, mbus_pin22, mbus_pin23, mbus_pin24, mbus_pin25,
|
||||
mbus_pin26, mbus_pin27, mbus_pin28, mbus_pin29, mbus_pin30,
|
||||
pin_name_max,
|
||||
};
|
||||
};
|
||||
|
||||
#include "gitTagVersion.h"
|
||||
#include "utility/RTC8563_Class.hpp"
|
||||
#include "utility/AXP192_Class.hpp"
|
||||
#include "utility/IP5306_Class.hpp"
|
||||
#include "utility/Button_Class.hpp"
|
||||
#include "utility/Power_Class.hpp"
|
||||
#include "utility/Speaker_Class.hpp"
|
||||
#include "utility/Mic_Class.hpp"
|
||||
#include "utility/Touch_Class.hpp"
|
||||
#include "utility/Log_Class.hpp"
|
||||
#include "utility/IMU_Class.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
using touch_detail_t = Touch_Class::touch_detail_t;
|
||||
|
||||
class M5Unified
|
||||
{
|
||||
public:
|
||||
struct config_t
|
||||
{
|
||||
#if defined ( ARDUINO )
|
||||
|
||||
/// use "Serial" begin. (0=disabled / Usually 115200 is used.)
|
||||
/// When this value is not 0, Serial.begin is executed.
|
||||
uint32_t serial_baudrate = 0;
|
||||
|
||||
#endif
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t module_display : 1;
|
||||
uint8_t module_rca : 1;
|
||||
uint8_t hat_spk : 1;
|
||||
uint8_t atomic_spk : 1;
|
||||
uint8_t hat_spk2 : 1;
|
||||
uint8_t atomic_echo : 1;
|
||||
uint8_t reserve : 2;
|
||||
} external_speaker;
|
||||
uint8_t external_speaker_value = 0x00;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint16_t module_display : 1;
|
||||
uint16_t atom_display : 1;
|
||||
uint16_t unit_oled : 1;
|
||||
uint16_t unit_mini_oled : 1;
|
||||
uint16_t unit_lcd : 1;
|
||||
uint16_t unit_glass : 1;
|
||||
uint16_t unit_glass2 : 1;
|
||||
uint16_t unit_rca : 1;
|
||||
uint16_t module_rca : 1;
|
||||
uint16_t reserve : 7;
|
||||
} external_display;
|
||||
uint16_t external_display_value = 0xFFFF;
|
||||
};
|
||||
|
||||
/// Clear the screen when startup.
|
||||
bool clear_display = true;
|
||||
|
||||
/// 5V output to external port.
|
||||
bool output_power = true;
|
||||
|
||||
/// use PMIC(AXP192) pek for M5.BtnPWR.
|
||||
bool pmic_button = true;
|
||||
|
||||
/// use internal IMU.
|
||||
bool internal_imu = true;
|
||||
|
||||
/// use internal RTC.
|
||||
bool internal_rtc = true;
|
||||
|
||||
/// use the microphone.
|
||||
bool internal_mic = true;
|
||||
|
||||
/// use the speaker.
|
||||
bool internal_spk = true;
|
||||
|
||||
/// use Unit Accel & Gyro.
|
||||
bool external_imu = false;
|
||||
|
||||
/// use Unit RTC.
|
||||
bool external_rtc = false;
|
||||
|
||||
/// Turn off the IRQ bit of the RTC at startup.
|
||||
bool disable_rtc_irq = true;
|
||||
|
||||
/// system LED brightness (0=off / 255=max) (※ not RGBcolorLED)
|
||||
uint8_t led_brightness = 0;
|
||||
|
||||
/// If auto-detection fails, the board will operate as the board configured here.
|
||||
board_t fallback_board
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32S3)
|
||||
= board_t::board_M5AtomS3Lite;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C3)
|
||||
= board_t::board_M5StampC3;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32P4)
|
||||
= board_t::board_M5Tab5;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32) || !defined (CONFIG_IDF_TARGET)
|
||||
= board_t::board_M5AtomLite;
|
||||
#else
|
||||
= board_t::board_unknown;
|
||||
#endif
|
||||
|
||||
union
|
||||
{
|
||||
uint8_t external_spk = 0;
|
||||
[[deprecated("Change to external_speaker")]]
|
||||
struct
|
||||
{
|
||||
uint8_t enabled : 1;
|
||||
uint8_t omit_atomic_spk : 1;
|
||||
uint8_t omit_spk_hat : 1;
|
||||
uint8_t reserve : 5;
|
||||
} external_spk_detail;
|
||||
};
|
||||
|
||||
#if defined ( __M5GFX_M5ATOMDISPLAY__ )
|
||||
M5AtomDisplay::config_t atom_display;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5MODULEDISPLAY__ )
|
||||
M5ModuleDisplay::config_t module_display;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5MODULERCA__ )
|
||||
M5ModuleRCA::config_t module_rca;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITGLASS__ )
|
||||
M5UnitGLASS::config_t unit_glass;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITGLASS2__ )
|
||||
M5UnitGLASS2::config_t unit_glass2;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITOLED__ )
|
||||
M5UnitOLED::config_t unit_oled;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITMINIOLED__ )
|
||||
M5UnitMiniOLED::config_t unit_mini_oled;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITLCD__ )
|
||||
M5UnitLCD::config_t unit_lcd;
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITRCA__ )
|
||||
M5UnitRCA::config_t unit_rca;
|
||||
#endif
|
||||
};
|
||||
|
||||
M5GFX Display; // setPrimaryされたディスプレイのインスタンス
|
||||
M5GFX &Lcd = Display;
|
||||
|
||||
IMU_Class Imu;
|
||||
Log_Class Log;
|
||||
Power_Class Power;
|
||||
RTC8563_Class Rtc;
|
||||
Touch_Class Touch;
|
||||
|
||||
/*
|
||||
/// List of available buttons:
|
||||
M5Stack BASIC/GRAY/GO/FIRE: BtnA,BtnB,BtnC
|
||||
M5Stack Core2: BtnA,BtnB,BtnC,BtnPWR
|
||||
M5Stick C/CPlus: BtnA,BtnB, BtnPWR
|
||||
M5Stick CoreInk: BtnA,BtnB,BtnC,BtnPWR,BtnEXT
|
||||
M5Paper: BtnA,BtnB,BtnC
|
||||
M5Station: BtnA,BtnB,BtnC,BtnPWR
|
||||
M5Tough: BtnPWR
|
||||
M5ATOM: BtnA
|
||||
*/
|
||||
Button_Class &BtnA = _buttons[0];
|
||||
Button_Class &BtnB = _buttons[1];
|
||||
Button_Class &BtnC = _buttons[2];
|
||||
Button_Class &BtnEXT = _buttons[3]; // CoreInk top button
|
||||
Button_Class &BtnPWR = _buttons[4]; // CoreInk power button / AXP192 power button
|
||||
|
||||
/// for internal I2C device
|
||||
I2C_Class& In_I2C = m5::In_I2C;
|
||||
|
||||
/// for external I2C device (Port.A)
|
||||
I2C_Class& Ex_I2C = m5::Ex_I2C;
|
||||
|
||||
Speaker_Class Speaker;
|
||||
|
||||
Mic_Class Mic;
|
||||
|
||||
static int8_t getPin(pin_name_t name) { return _get_pin_table[name]; }
|
||||
|
||||
Button_Class& getButton(size_t index) { return _buttons[index]; }
|
||||
|
||||
Button_Class& Buttons(size_t index) { return getButton(index); }
|
||||
|
||||
M5GFX& getDisplay(size_t index);
|
||||
|
||||
M5GFX& Displays(size_t index) { return getDisplay(index); }
|
||||
|
||||
std::size_t getDisplayCount(void) const { return this->_displays.size(); }
|
||||
|
||||
std::size_t addDisplay(M5GFX& dsp);
|
||||
|
||||
// Get the display index of the type matching the argument.
|
||||
// Returns -1 if not found.
|
||||
int32_t getDisplayIndex(m5gfx::board_t board);
|
||||
|
||||
int32_t getDisplayIndex(std::initializer_list<m5gfx::board_t> board_list);
|
||||
|
||||
// Designates the display of the specified index as PrimaryDisplay.
|
||||
bool setPrimaryDisplay(std::size_t index);
|
||||
|
||||
// Find a display that matches the specified display type and designate it as PrimaryDisplay.
|
||||
// Multiple display types can be specified in the initializer list.
|
||||
bool setPrimaryDisplayType(std::initializer_list<m5gfx::board_t> board_list);
|
||||
|
||||
// Find a display that matches the specified display type and designate it as PrimaryDisplay.
|
||||
bool setPrimaryDisplayType(m5gfx::board_t board) { return setPrimaryDisplayType( { board } ); };
|
||||
|
||||
/// Set the display to show logs.
|
||||
void setLogDisplayIndex(size_t index);
|
||||
|
||||
void setLogDisplayType(std::initializer_list<m5gfx::board_t> board_list);
|
||||
|
||||
void setLogDisplayType(m5gfx::board_t board) { setLogDisplayType( { board } ); };
|
||||
|
||||
/// milli seconds at the time the update was called
|
||||
std::uint32_t getUpdateMsec(void) const { return _updateMsec; }
|
||||
|
||||
static inline config_t config(void)
|
||||
{
|
||||
return config_t();
|
||||
}
|
||||
|
||||
static inline void delay(uint32_t msec)
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
vTaskDelay( msec / portTICK_PERIOD_MS );
|
||||
#else
|
||||
SDL_Delay(msec);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t millis(void)
|
||||
{
|
||||
return m5gfx::millis();
|
||||
}
|
||||
|
||||
static inline uint32_t micros(void)
|
||||
{
|
||||
return m5gfx::micros();
|
||||
}
|
||||
|
||||
/// get the board type of the runtime environment.
|
||||
/// @return board type
|
||||
board_t getBoard(void) const { return _board; }
|
||||
|
||||
/// To call this function in a loop function.
|
||||
virtual void update(void);
|
||||
|
||||
/// Perform initialization process at startup.
|
||||
void begin(void)
|
||||
{
|
||||
config_t cfg;
|
||||
begin(cfg);
|
||||
}
|
||||
|
||||
/// Perform initialization process at startup.
|
||||
virtual void begin(config_t cfg)
|
||||
{
|
||||
// Allow begin execution only once.
|
||||
if (_board != m5gfx::board_t::board_unknown) { return; }
|
||||
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32S3 )
|
||||
// Power Hold pin for Capsule/Dial/DinMeter
|
||||
m5gfx::gpio_hi(GPIO_NUM_46);
|
||||
m5gfx::pinMode(GPIO_NUM_46, m5gfx::pin_mode_t::output);
|
||||
#endif
|
||||
|
||||
auto brightness = Display.getBrightness();
|
||||
Display.setBrightness(0);
|
||||
bool res = Display.init_without_reset(cfg.clear_display);
|
||||
auto board = _check_boardtype(Display.getBoard());
|
||||
if (board == board_t::board_unknown) { board = cfg.fallback_board; }
|
||||
_board = board;
|
||||
_setup_pinmap(board);
|
||||
_setup_i2c(board);
|
||||
if (res && getDisplayCount() == 0) {
|
||||
addDisplay(Display);
|
||||
}
|
||||
|
||||
#if defined ( __M5GFX_M5ATOMDISPLAY__ )
|
||||
if (cfg.external_display.atom_display) {
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32S3)
|
||||
if (_board == board_t::board_M5AtomS3 || _board == board_t::board_M5AtomS3Lite || _board == board_t::board_M5AtomS3R || _board == board_t::board_M5AtomS3RCam || _board == board_t::board_M5AtomS3RExt)
|
||||
#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
if (_board == board_t::board_M5AtomLite || _board == board_t::board_M5AtomMatrix || _board == board_t::board_M5AtomEcho || _board == board_t::board_M5AtomPsram)
|
||||
#else
|
||||
if (false)
|
||||
#endif
|
||||
{
|
||||
M5AtomDisplay dsp(cfg.atom_display);
|
||||
if (dsp.init_without_reset()) {
|
||||
addDisplay(dsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_begin(cfg);
|
||||
|
||||
|
||||
// Module Display / Unit OLED / Unit LCD is determined after _begin (because it must be after external power supply)
|
||||
#if defined ( __M5GFX_M5MODULEDISPLAY__ )
|
||||
if (cfg.external_display.module_display) {
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32P4)
|
||||
if (_board == board_t::board_M5Tab5)
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32S3)
|
||||
if (_board == board_t::board_M5StackCoreS3 || _board == board_t::board_M5StackCoreS3SE)
|
||||
#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
if (_board == board_t::board_M5Stack || _board == board_t::board_M5StackCore2 || _board == board_t::board_M5Tough)
|
||||
#else
|
||||
if (false)
|
||||
#endif
|
||||
{
|
||||
M5ModuleDisplay dsp(cfg.module_display);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Speaker selection is performed after the Module Display has been determined.
|
||||
_begin_spk(cfg);
|
||||
|
||||
update();
|
||||
|
||||
bool port_a_used = _begin_rtc_imu(cfg);
|
||||
(void)port_a_used;
|
||||
|
||||
if (cfg.external_display_value)
|
||||
{
|
||||
#if defined ( __M5GFX_M5UNITOLED__ )
|
||||
if (cfg.external_display.unit_oled)
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
if (cfg.unit_oled.pin_sda >= GPIO_NUM_MAX) { cfg.unit_oled.pin_sda = (uint8_t)Ex_I2C.getSDA(); }
|
||||
if (cfg.unit_oled.pin_scl >= GPIO_NUM_MAX) { cfg.unit_oled.pin_scl = (uint8_t)Ex_I2C.getSCL(); }
|
||||
if (cfg.unit_oled.i2c_port < 0) { cfg.unit_oled.i2c_port = (int8_t)Ex_I2C.getPort(); }
|
||||
#endif
|
||||
|
||||
M5UnitOLED dsp(cfg.unit_oled);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
port_a_used = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined ( __M5GFX_M5UNITMINIOLED__ )
|
||||
if (cfg.external_display.unit_mini_oled)
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
if (cfg.unit_mini_oled.pin_sda >= GPIO_NUM_MAX) { cfg.unit_mini_oled.pin_sda = (uint8_t)Ex_I2C.getSDA(); }
|
||||
if (cfg.unit_mini_oled.pin_scl >= GPIO_NUM_MAX) { cfg.unit_mini_oled.pin_scl = (uint8_t)Ex_I2C.getSCL(); }
|
||||
if (cfg.unit_mini_oled.i2c_port < 0) { cfg.unit_mini_oled.i2c_port = (int8_t)Ex_I2C.getPort(); }
|
||||
#endif
|
||||
|
||||
M5UnitMiniOLED dsp(cfg.unit_mini_oled);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
port_a_used = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined ( __M5GFX_M5UNITGLASS__ )
|
||||
if (cfg.external_display.unit_glass)
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
if (cfg.unit_glass.pin_sda >= GPIO_NUM_MAX) { cfg.unit_glass.pin_sda = (uint8_t)Ex_I2C.getSDA(); }
|
||||
if (cfg.unit_glass.pin_scl >= GPIO_NUM_MAX) { cfg.unit_glass.pin_scl = (uint8_t)Ex_I2C.getSCL(); }
|
||||
if (cfg.unit_glass.i2c_port < 0) { cfg.unit_glass.i2c_port = (int8_t)Ex_I2C.getPort(); }
|
||||
#endif
|
||||
|
||||
M5UnitGLASS dsp(cfg.unit_glass);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
port_a_used = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined ( __M5GFX_M5UNITGLASS2__ )
|
||||
if (cfg.external_display.unit_glass2)
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
if (cfg.unit_glass2.pin_sda >= GPIO_NUM_MAX) { cfg.unit_glass2.pin_sda = (uint8_t)Ex_I2C.getSDA(); }
|
||||
if (cfg.unit_glass2.pin_scl >= GPIO_NUM_MAX) { cfg.unit_glass2.pin_scl = (uint8_t)Ex_I2C.getSCL(); }
|
||||
if (cfg.unit_glass2.i2c_port < 0) { cfg.unit_glass2.i2c_port = (int8_t)Ex_I2C.getPort(); }
|
||||
#endif
|
||||
|
||||
M5UnitGLASS2 dsp(cfg.unit_glass2);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
port_a_used = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined ( __M5GFX_M5UNITLCD__ )
|
||||
if (cfg.external_display.unit_lcd)
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
if (cfg.unit_lcd.pin_sda >= GPIO_NUM_MAX) { cfg.unit_lcd.pin_sda = (uint8_t)Ex_I2C.getSDA(); }
|
||||
if (cfg.unit_lcd.pin_scl >= GPIO_NUM_MAX) { cfg.unit_lcd.pin_scl = (uint8_t)Ex_I2C.getSCL(); }
|
||||
if (cfg.unit_lcd.i2c_port < 0) { cfg.unit_lcd.i2c_port = (int8_t)Ex_I2C.getPort(); }
|
||||
#endif
|
||||
|
||||
M5UnitLCD dsp(cfg.unit_lcd);
|
||||
int retry = 8;
|
||||
do {
|
||||
m5gfx::delay(32);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
port_a_used = true;
|
||||
break;
|
||||
}
|
||||
} while (--retry);
|
||||
}
|
||||
#endif
|
||||
|
||||
// RCA is not available on ESP32S3
|
||||
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
#if defined ( __M5GFX_M5MODULERCA__ ) || defined ( __M5GFX_M5UNITRCA__ )
|
||||
{
|
||||
bool unit_rca = cfg.external_display.unit_rca;
|
||||
(void)unit_rca;
|
||||
#if defined ( __M5GFX_M5MODULERCA__ )
|
||||
if (cfg.external_display.module_rca)
|
||||
{
|
||||
if (board == board_t::board_M5Stack
|
||||
|| board == board_t::board_M5StackCore2
|
||||
|| board == board_t::board_M5Tough
|
||||
) {
|
||||
// When ModuleRCA is used, UnitRCA is not used.
|
||||
unit_rca = false;
|
||||
M5ModuleRCA dsp(cfg.module_rca);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined ( __M5GFX_M5UNITRCA__ )
|
||||
if (unit_rca)
|
||||
{
|
||||
if ( board == board_t::board_M5Stack
|
||||
|| board == board_t::board_M5StackCore2
|
||||
|| board == board_t::board_M5Paper
|
||||
|| board == board_t::board_M5Tough
|
||||
|| board == board_t::board_M5Station
|
||||
|| (!port_a_used && ( // ATOM does not allow video output via UnitRCA when PortA is used.
|
||||
board == board_t::board_M5AtomLite
|
||||
|| board == board_t::board_M5AtomMatrix
|
||||
|| board == board_t::board_M5AtomEcho
|
||||
|| board == board_t::board_M5AtomPsram
|
||||
|| board == board_t::board_M5AtomU
|
||||
)))
|
||||
{
|
||||
M5UnitRCA dsp(cfg.unit_rca);
|
||||
if (dsp.init()) {
|
||||
addDisplay(dsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
if (Display.getBoard() != board_t::board_unknown)
|
||||
{
|
||||
Display.setBrightness(brightness);
|
||||
}
|
||||
}
|
||||
|
||||
void setTouchButtonHeightByRatio(uint8_t ratio);
|
||||
void setTouchButtonHeight(uint16_t pixel) { _touch_button_height = pixel; }
|
||||
uint16_t getTouchButtonHeight(void) const { return _touch_button_height; }
|
||||
|
||||
private:
|
||||
static constexpr std::size_t BTNPWR_MIN_UPDATE_MSEC = 4;
|
||||
|
||||
Button_Class _buttons[5];
|
||||
|
||||
std::vector<M5GFX> _displays; // 登録された全ディスプレイのインスタンス
|
||||
std::uint32_t _updateMsec = 0;
|
||||
std::uint16_t _touch_button_height = 0;
|
||||
m5gfx::board_t _board = m5gfx::board_t::board_unknown;
|
||||
|
||||
std::uint8_t _primary_display_index = -1;
|
||||
bool use_pmic_button = false;
|
||||
bool use_hat_spk = false;
|
||||
|
||||
void _begin(const config_t& cfg);
|
||||
void _begin_spk(config_t& cfg);
|
||||
bool _begin_rtc_imu(const config_t& cfg);
|
||||
|
||||
board_t _check_boardtype(board_t);
|
||||
void _setup_i2c(board_t);
|
||||
|
||||
static void _setup_pinmap(board_t);
|
||||
static bool _speaker_enabled_cb_core2(void* args, bool enabled);
|
||||
static bool _speaker_enabled_cb_cores3(void* args, bool enabled);
|
||||
static bool _speaker_enabled_cb_hat_spk(void* args, bool enabled);
|
||||
static bool _speaker_enabled_cb_atomic_echo(void* args, bool enabled);
|
||||
static bool _speaker_enabled_cb_tab5(void* args, bool enabled);
|
||||
static bool _microphone_enabled_cb_stickc(void* args, bool enabled);
|
||||
static bool _microphone_enabled_cb_cores3(void* args, bool enabled);
|
||||
static bool _microphone_enabled_cb_atomic_echo(void* args, bool enabled);
|
||||
static bool _microphone_enabled_cb_tab5(void* args, bool enabled);
|
||||
|
||||
static int8_t _get_pin_table[pin_name_max];
|
||||
};
|
||||
}
|
||||
|
||||
extern m5::M5Unified M5;
|
||||
|
||||
#endif
|
||||
4
libraries/M5Unified/src/gitTagVersion.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#define M5UNIFIED_VERSION_MAJOR 0
|
||||
#define M5UNIFIED_VERSION_MINOR 2
|
||||
#define M5UNIFIED_VERSION_PATCH 7
|
||||
#define M5UNIFIED_VERSION F( M5UNIFIED_VERSION_MAJOR "." M5UNIFIED_VERSION_MINOR "." M5UNIFIED_VERSION_PATCH )
|
||||
363
libraries/M5Unified/src/utility/AXP192_Class.cpp
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "AXP192_Class.hpp"
|
||||
|
||||
#if __has_include(<esp_log.h>)
|
||||
#include <esp_log.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
/*
|
||||
DCDC1 : 0.7-3.5V, 25mV/step 1200mA
|
||||
DCDC2 : 0.7-2.275V,25mV/step 1600mA
|
||||
DCDC3 : 0.7-3.5V, 25mV/step 700mA
|
||||
|
||||
LDOio0: 1.8-3.3V, 100mV/step 50mA
|
||||
LDO1 : 30mA always on
|
||||
LDO2 : 1.8-3.3V, 100mV/step 200mA
|
||||
LDO3 : 1.8-3.3V, 100mV/step 200mA
|
||||
*/
|
||||
bool AXP192_Class::begin(void)
|
||||
{
|
||||
std::uint8_t val;
|
||||
_init = readRegister(0x03, &val, 1);
|
||||
if (_init)
|
||||
{
|
||||
_init = (val == 0x03);
|
||||
#if defined (ESP_LOGV)
|
||||
// ESP_LOGV("AXP192", "reg03h:%02x : init:%d", val, _init);
|
||||
#endif
|
||||
}
|
||||
return _init;
|
||||
}
|
||||
|
||||
/// @param num 0=DCDC1 / 1=DCDC2 / 2=DCDC3
|
||||
void AXP192_Class::_set_DCDC(std::uint8_t num, int voltage)
|
||||
{
|
||||
static constexpr uint8_t reg12bit_tbl[] = { 0x01, 0x10, 0x02 };
|
||||
static constexpr uint8_t volt_reg_tbl[] = { 0x26, 0x23, 0x27 };
|
||||
static constexpr uint8_t volt_max_tbl[] = { 0x7F, 0x3F, 0x7F };
|
||||
|
||||
voltage -= 700;
|
||||
std::uint_fast8_t val = (voltage < 0) ? 0 : std::min(voltage / 25, (int)volt_max_tbl[num]);
|
||||
writeRegister8(volt_reg_tbl[num], val);
|
||||
if (voltage < 0)
|
||||
{
|
||||
bitOff(0x12, reg12bit_tbl[num]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOn(0x12, reg12bit_tbl[num]);
|
||||
}
|
||||
}
|
||||
|
||||
/// @param num 0:LDOio0 ; 2:LDO2 ; 3=LDO3
|
||||
void AXP192_Class::_set_LDO(std::uint8_t num, int voltage)
|
||||
{
|
||||
if (num > 3 || num == 1) return;
|
||||
std::uint8_t reg_volt = (num == 0) ? 0x91 : 0x28;
|
||||
voltage -= 1800;
|
||||
/// convert voltage to value
|
||||
std::uint_fast8_t val = (voltage < 0) ? 0 : std::min(voltage / 100, 0x0F);
|
||||
std::uint_fast8_t now = readRegister8(reg_volt);
|
||||
if (num == 3)
|
||||
{ /// LDO3
|
||||
now = (now & 0xF0) + val;
|
||||
}
|
||||
else
|
||||
{ /// LDOio0 , LDO2
|
||||
now = (now & 0x0F) | (val << 4);
|
||||
}
|
||||
writeRegister8(reg_volt, now);
|
||||
|
||||
if (num)
|
||||
{ // LDO2 , LDO3
|
||||
std::uint_fast8_t reg12bit = 1 << num;
|
||||
if (voltage < 0)
|
||||
{
|
||||
bitOff(0x12, reg12bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOn(0x12, reg12bit);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // LDOio0
|
||||
writeRegister8(0x90, (voltage < 0) ? 0x07 : 0x02 ); /// floating or LDO
|
||||
}
|
||||
}
|
||||
|
||||
/// @param num 0=LDO2 / 1=LDO3
|
||||
void AXP192_Class::_set_LDO2_LDO3(std::uint8_t num, int voltage)
|
||||
{
|
||||
voltage -= 1800;
|
||||
std::uint_fast8_t val = (voltage < 0) ? 0 : std::min(voltage / 100, 0x0F);
|
||||
std::uint_fast8_t now = readRegister8(0x28);
|
||||
if (num == 1)
|
||||
{ /// LDO3
|
||||
now = (now & 0xF0) + val;
|
||||
}
|
||||
else
|
||||
{ /// LDO2
|
||||
now = (now & 0x0F) | (val << 4);
|
||||
}
|
||||
writeRegister8(0x28, now);
|
||||
|
||||
std::uint_fast8_t reg12bit = 1 << (num + 2);
|
||||
if (voltage < 0)
|
||||
{
|
||||
bitOff(0x12, reg12bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOn(0x12, reg12bit);
|
||||
}
|
||||
}
|
||||
|
||||
/// @param num 0=GPIO0 / 1=GPIO1 / 2=GPIO2
|
||||
void AXP192_Class::_set_GPIO0_2(std::uint8_t num, bool state)
|
||||
{
|
||||
static constexpr uint8_t reg[] = { 0x90, 0x92, 0x93 };
|
||||
writeRegister8(reg[num], state ? 0x06 : 0x05); // floating or LOW
|
||||
}
|
||||
|
||||
/// @param num 0=GPIO3 / 1=GPIO4
|
||||
void AXP192_Class::_set_GPIO3_4(std::uint8_t num, bool state)
|
||||
{
|
||||
uint32_t bit = num ? 2 : 1;
|
||||
if (state)
|
||||
{
|
||||
bitOn(0x96, bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOff(0x96, bit);
|
||||
}
|
||||
uint_fast8_t mask = num ? ~0x0C : ~0x03;
|
||||
uint_fast8_t reg0x95 = readRegister8(0x95) & mask;
|
||||
writeRegister8(0x95, reg0x95 | (num ? 0x84 : 0x81)); // set GPIO mode
|
||||
}
|
||||
|
||||
void AXP192_Class::setBatteryCharge(bool enable)
|
||||
{
|
||||
std::uint8_t val = 0;
|
||||
if (readRegister(0x33, &val, 1))
|
||||
{
|
||||
writeRegister8(0x33, (val & 0x7F) + (enable ? 0x80 : 0x00));
|
||||
}
|
||||
}
|
||||
|
||||
void AXP192_Class::setChargeCurrent(std::uint16_t max_mA)
|
||||
{
|
||||
max_mA /= 10;
|
||||
if (max_mA > 132) { max_mA = 132; }
|
||||
static constexpr std::uint8_t table[] = { 19, 28, 36, 45, 55, 63, 70, 78, 88, 96, 100, 108, 116, 124, 132, 255 };
|
||||
|
||||
size_t i = 0;
|
||||
while (table[i] <= max_mA) { ++i; }
|
||||
|
||||
std::uint8_t val = 0;
|
||||
if (readRegister(0x33, &val, 1))
|
||||
{
|
||||
writeRegister8(0x33, (val & 0xF0) + i);
|
||||
}
|
||||
}
|
||||
|
||||
void AXP192_Class::setChargeVoltage(std::uint16_t max_mV)
|
||||
{
|
||||
max_mV = (max_mV / 10) - 410;
|
||||
if (max_mV > 436 - 410) { max_mV = 436 - 410; }
|
||||
static constexpr std::uint8_t table[] =
|
||||
{ 415 - 410 /// 4150mV
|
||||
, 420 - 410 /// 4200mV
|
||||
, 436 - 410 /// 4360mV
|
||||
, 255
|
||||
};
|
||||
size_t i = 0;
|
||||
while (table[i] <= max_mV) { ++i; }
|
||||
|
||||
std::uint8_t val = 0;
|
||||
if (readRegister(0x33, &val, 1))
|
||||
{
|
||||
writeRegister8(0x33, (val & 0x9F) + (i << 5));
|
||||
}
|
||||
}
|
||||
|
||||
std::int8_t AXP192_Class::getBatteryLevel(void)
|
||||
{
|
||||
std::uint8_t buf[4];
|
||||
if (!readRegister(0x78, buf, 4)) { return -1; }
|
||||
|
||||
std::uint_fast16_t voltage = (buf[0] << 4) + buf[1];
|
||||
std::uint_fast16_t current = (buf[2] << 5) + buf[3];
|
||||
|
||||
std::int_fast16_t res = (voltage > 3150) ? (( voltage - 3075 ) * 0.16f )
|
||||
: (voltage > 2690) ? (( voltage - 2690 ) * 0.027f )
|
||||
: 0;
|
||||
if (current > 16) { res -= 16; }
|
||||
|
||||
return (res < 100) ? res : 100;
|
||||
}
|
||||
|
||||
bool AXP192_Class::isCharging(void)
|
||||
{
|
||||
return readRegister8(0x00) & 0x04;
|
||||
}
|
||||
|
||||
void AXP192_Class::powerOff(void)
|
||||
{
|
||||
bitOn(0x32, 0x80);
|
||||
}
|
||||
|
||||
void AXP192_Class::setAdcState(bool enable)
|
||||
{
|
||||
writeRegister8(0x82, enable ? 0xff : 0x00);
|
||||
}
|
||||
|
||||
void AXP192_Class::setAdcRate( std::uint8_t rate )
|
||||
{
|
||||
std::uint_fast8_t buf = readRegister8(0x84);
|
||||
writeRegister8(0x84, (buf & ~(0xc0)) | (rate & 0xc0));
|
||||
}
|
||||
|
||||
void AXP192_Class::setEXTEN(bool enable)
|
||||
{
|
||||
static constexpr std::uint8_t add = 0x12;
|
||||
static constexpr std::uint8_t bit = 1 << 6;
|
||||
if (enable)
|
||||
{
|
||||
bitOn(add, bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOff(add, bit);
|
||||
}
|
||||
}
|
||||
|
||||
bool AXP192_Class::getEXTEN(void)
|
||||
{
|
||||
return readRegister8(0x12) & (1 << 6);
|
||||
}
|
||||
|
||||
void AXP192_Class::setBACKUP(bool enable)
|
||||
{
|
||||
static constexpr std::uint8_t add = 0x35;
|
||||
static constexpr std::uint8_t bit = 1 << 7;
|
||||
if (enable)
|
||||
{ // Enable
|
||||
bitOn(add, bit);
|
||||
}
|
||||
else
|
||||
{ // Disable
|
||||
bitOff(add, bit);
|
||||
}
|
||||
}
|
||||
|
||||
bool AXP192_Class::isACIN(void)
|
||||
{
|
||||
return readRegister8(0x00) & 0x80;
|
||||
}
|
||||
bool AXP192_Class::isVBUS(void)
|
||||
{
|
||||
return readRegister8(0x00) & 0x20;
|
||||
}
|
||||
|
||||
bool AXP192_Class::getBatState(void)
|
||||
{
|
||||
return readRegister8(0x01) & 0x20;
|
||||
}
|
||||
|
||||
std::uint8_t AXP192_Class::getPekPress(void)
|
||||
{
|
||||
std::uint8_t val = readRegister8(0x46) & 0x03;
|
||||
if (val) { writeRegister8(0x46, val); }
|
||||
return val;
|
||||
}
|
||||
|
||||
float AXP192_Class::getACINVoltage(void)
|
||||
{
|
||||
return readRegister12(0x56) * (1.7f / 1000.0f);
|
||||
}
|
||||
|
||||
float AXP192_Class::getACINCurrent(void)
|
||||
{
|
||||
return readRegister12(0x58) * 0.625f;
|
||||
}
|
||||
|
||||
float AXP192_Class::getVBUSVoltage(void)
|
||||
{
|
||||
return readRegister12(0x5a) * (1.7f / 1000.0f);
|
||||
}
|
||||
|
||||
float AXP192_Class::getVBUSCurrent(void)
|
||||
{
|
||||
return readRegister12(0x5c) * 0.375f;
|
||||
}
|
||||
|
||||
float AXP192_Class::getInternalTemperature(void)
|
||||
{
|
||||
return readRegister12(0x5e) * 0.1f -144.7f;
|
||||
}
|
||||
|
||||
float AXP192_Class::getBatteryPower(void)
|
||||
{
|
||||
return readRegister24(0x70) * (1.1f * 0.5f / 1000.0f);
|
||||
}
|
||||
|
||||
float AXP192_Class::getBatteryVoltage(void)
|
||||
{
|
||||
return readRegister12(0x78) * (1.1f / 1000.0f);
|
||||
}
|
||||
|
||||
float AXP192_Class::getBatteryChargeCurrent(void)
|
||||
{
|
||||
return readRegister13(0x7a) * 0.5f;
|
||||
}
|
||||
|
||||
float AXP192_Class::getBatteryDischargeCurrent(void)
|
||||
{
|
||||
return readRegister13(0x7c) * 0.5f;
|
||||
}
|
||||
|
||||
float AXP192_Class::getAPSVoltage(void)
|
||||
{
|
||||
return readRegister12(0x7e) * (1.4f / 1000.0f);
|
||||
}
|
||||
|
||||
|
||||
std::size_t AXP192_Class::readRegister12(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return buf[0] << 4 | buf[1];
|
||||
}
|
||||
std::size_t AXP192_Class::readRegister13(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return buf[0] << 5 | buf[1];
|
||||
}
|
||||
std::size_t AXP192_Class::readRegister16(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return buf[0] << 8 | buf[1];
|
||||
}
|
||||
std::size_t AXP192_Class::readRegister24(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[3] = {0};
|
||||
readRegister(addr, buf, 3);
|
||||
return buf[0] << 16 | buf[1] << 8 | buf[2];
|
||||
}
|
||||
std::size_t AXP192_Class::readRegister32(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[4] = {0};
|
||||
readRegister(addr, buf, 4);
|
||||
return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
|
||||
}
|
||||
}
|
||||
109
libraries/M5Unified/src/utility/AXP192_Class.hpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_AXP192_CLASS_H__
|
||||
#define __M5_AXP192_CLASS_H__
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class AXP192_Class : public I2C_Device
|
||||
{
|
||||
public:
|
||||
|
||||
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x34;
|
||||
|
||||
AXP192_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C)
|
||||
: I2C_Device ( i2c_addr, freq, i2c )
|
||||
{}
|
||||
|
||||
bool begin(void);
|
||||
|
||||
/// Get the remaining battery power.
|
||||
/// @return 0-100 level
|
||||
std::int8_t getBatteryLevel(void);
|
||||
|
||||
/// set battery charge enable.
|
||||
/// @param enable true=enable / false=disable
|
||||
void setBatteryCharge(bool enable);
|
||||
|
||||
/// set battery charge current
|
||||
/// @param max_mA milli ampere. (100 - 1320).
|
||||
void setChargeCurrent(std::uint16_t max_mA);
|
||||
|
||||
/// set battery charge voltage
|
||||
/// @param max_mV milli volt. (4100 - 4360).
|
||||
void setChargeVoltage(std::uint16_t max_mV);
|
||||
|
||||
/// Get whether the battery is currently charging or not.
|
||||
bool isCharging(void);
|
||||
|
||||
inline void setDCDC1(int voltage) { _set_DCDC(0, voltage); }
|
||||
inline void setDCDC2(int voltage) { _set_DCDC(1, voltage); }
|
||||
inline void setDCDC3(int voltage) { _set_DCDC(2, voltage); }
|
||||
|
||||
/// set LDOio0 voltage
|
||||
/// @param voltage milli volt. (0 - 3300).
|
||||
inline void setLDO0(int voltage) { _set_LDO(0, voltage); }
|
||||
|
||||
/// set LDO2 voltage
|
||||
/// @param voltage milli volt. (0 - 3300).
|
||||
inline void setLDO2(int voltage) { _set_LDO(2, voltage); }
|
||||
|
||||
/// set LDO3 voltage
|
||||
/// @param voltage milli volt. (0 - 3300).
|
||||
inline void setLDO3(int voltage) { _set_LDO(3, voltage); }
|
||||
|
||||
inline void setGPIO(uint8_t gpio_num, bool state) { if (gpio_num < 3) { _set_GPIO0_2(gpio_num, state); } else { _set_GPIO3_4(gpio_num - 3, state); } }
|
||||
inline void setGPIO0(bool state) { _set_GPIO0_2(0, state); }
|
||||
inline void setGPIO1(bool state) { _set_GPIO0_2(1, state); }
|
||||
inline void setGPIO2(bool state) { _set_GPIO0_2(2, state); }
|
||||
inline void setGPIO3(bool state) { _set_GPIO3_4(0, state); }
|
||||
inline void setGPIO4(bool state) { _set_GPIO3_4(1, state); }
|
||||
|
||||
void powerOff(void);
|
||||
|
||||
void setAdcState(bool enable);
|
||||
void setAdcRate( std::uint8_t rate );
|
||||
|
||||
void setEXTEN(bool enable);
|
||||
void setBACKUP(bool enable);
|
||||
|
||||
bool isACIN(void);
|
||||
bool isVBUS(void);
|
||||
bool getBatState(void);
|
||||
bool getEXTEN(void);
|
||||
|
||||
float getBatteryVoltage(void);
|
||||
float getBatteryDischargeCurrent(void);
|
||||
float getBatteryChargeCurrent(void);
|
||||
float getBatteryPower(void);
|
||||
float getACINVoltage(void);
|
||||
float getACINCurrent(void);
|
||||
float getVBUSVoltage(void);
|
||||
float getVBUSCurrent(void);
|
||||
float getAPSVoltage(void);
|
||||
float getInternalTemperature(void);
|
||||
|
||||
std::uint8_t getPekPress(void);
|
||||
|
||||
[[deprecated("use getACINVoltage()")]]
|
||||
inline float getACINVolatge(void) { return getACINVoltage(); }
|
||||
|
||||
private:
|
||||
std::size_t readRegister12(std::uint8_t addr);
|
||||
std::size_t readRegister13(std::uint8_t addr);
|
||||
std::size_t readRegister16(std::uint8_t addr);
|
||||
std::size_t readRegister24(std::uint8_t addr);
|
||||
std::size_t readRegister32(std::uint8_t addr);
|
||||
|
||||
void _set_DCDC(std::uint8_t num, int voltage);
|
||||
void _set_LDO(std::uint8_t num, int voltage);
|
||||
void _set_LDO2_LDO3(std::uint8_t num, int voltage);
|
||||
void _set_GPIO0_2(std::uint8_t num, bool state);
|
||||
void _set_GPIO3_4(std::uint8_t num, bool state);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
584
libraries/M5Unified/src/utility/AXP2101_Class.cpp
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "AXP2101_Class.hpp"
|
||||
|
||||
#if __has_include(<esp_log.h>)
|
||||
#include <esp_log.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define IS_BIT_SET(val,mask) (((val)&(mask)) == (mask))
|
||||
|
||||
namespace m5
|
||||
{
|
||||
/*
|
||||
DCDC1 : 1.5-3.4V, 2000mA
|
||||
DCDC2 : 0.5-1.2V,1.22-1.54V, 2000mA
|
||||
DCDC3 : 0.5-1.2V,1.22-1.54V, 1.6-3.4V, 2000mA
|
||||
DCDC4 : 0.5-1.2V, 1.22-1.84V, 1500mA
|
||||
DCDC5 : 1.2V , 1.4-3.7V, 1000mA
|
||||
|
||||
RTCLDO1/2 : 1.8V/2.5V/3V/3.3V, 30mA
|
||||
|
||||
ALDO1~4 : 0.5-3.5V, 100mV/step 300mA
|
||||
*/
|
||||
bool AXP2101_Class::begin(void)
|
||||
{
|
||||
std::uint8_t val;
|
||||
_init = readRegister(0x03, &val, 1);
|
||||
if (_init)
|
||||
{
|
||||
_init = (val == 0x4A);
|
||||
#if defined (ESP_LOGV)
|
||||
// ESP_LOGV("AXP2101", "reg03h:%02x : init:%d", val, _init);
|
||||
#endif
|
||||
}
|
||||
return _init;
|
||||
}
|
||||
|
||||
// 0=ALDO1 ~ 3=ALDO4 / 4=BLDO1 / 5=BLDO2
|
||||
void AXP2101_Class::_set_LDO(std::uint8_t num, int voltage)
|
||||
{
|
||||
if (num > 5) return;
|
||||
std::uint8_t reg_volt = num + 0x92;
|
||||
voltage -= 500;
|
||||
/// convert voltage to value
|
||||
std::uint_fast8_t val = (voltage < 0) ? 0 : std::min(voltage / 100, 0x1E);
|
||||
writeRegister8(reg_volt, val);
|
||||
|
||||
std::uint_fast8_t reg90bit = 1 << num;
|
||||
if (voltage < 0)
|
||||
{
|
||||
bitOff(0x90, reg90bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOn(0x90, reg90bit);
|
||||
}
|
||||
}
|
||||
|
||||
void AXP2101_Class::_set_DLDO(std::uint8_t num, int voltage)
|
||||
{
|
||||
if (num > 1) return;
|
||||
|
||||
std::uint8_t reg_volt = num + 0x99;
|
||||
voltage -= 500;
|
||||
/// convert voltage to value
|
||||
std::uint_fast8_t val = (voltage < 0) ? 0 : std::min(voltage / (num ? 50 : 100), num ? 0x13 : 0x1C);
|
||||
writeRegister8(reg_volt, val);
|
||||
|
||||
uint8_t reg = 0x90 + num;
|
||||
uint8_t bit = num ? 0x01 : 0x80;
|
||||
if (voltage < 0)
|
||||
{
|
||||
bitOff(reg, bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOn(reg, bit);
|
||||
}
|
||||
}
|
||||
|
||||
bool AXP2101_Class::_get_LDOEn(std::uint8_t num)
|
||||
{
|
||||
bool res = false;
|
||||
if (num <= 5) {
|
||||
std::uint_fast8_t reg90bit = 1 << num;
|
||||
res = readRegister8(0x90) & reg90bit;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void AXP2101_Class::setBatteryCharge(bool enable)
|
||||
{
|
||||
std::uint8_t val = 0;
|
||||
if (readRegister(0x18, &val, 1))
|
||||
{
|
||||
writeRegister8(0x18, (val & 0xFD) | (enable << 1));
|
||||
}
|
||||
}
|
||||
|
||||
void AXP2101_Class::setPreChargeCurrent(std::uint16_t max_mA)
|
||||
{
|
||||
static constexpr std::uint8_t table[] = { 0, 25, 50, 75, 100, 125, 150, 175, 200, 255 };
|
||||
if (max_mA > 200) { max_mA = 200; }
|
||||
|
||||
size_t i = 0;
|
||||
while (table[i] <= max_mA) { ++i; }
|
||||
i -= 1;
|
||||
writeRegister8(0x61, i);
|
||||
}
|
||||
|
||||
void AXP2101_Class::setChargeCurrent(std::uint16_t max_mA)
|
||||
{
|
||||
max_mA /= 5;
|
||||
if (max_mA > 1000/5) { max_mA = 1000/5; }
|
||||
static constexpr std::uint8_t table[] = { 125 / 5, 150 / 5, 175 / 5, 200 / 5, 300 / 5, 400 / 5, 500 / 5, 600 / 5, 700 / 5, 800 / 5, 900 / 5, 1000 / 5, 255 };
|
||||
|
||||
size_t i = 0;
|
||||
while (table[i] <= max_mA) { ++i; }
|
||||
i += 4;
|
||||
writeRegister8(0x62, i);
|
||||
}
|
||||
|
||||
void AXP2101_Class::setChargeVoltage(std::uint16_t max_mV)
|
||||
{
|
||||
max_mV = (max_mV / 10) - 400;
|
||||
if (max_mV > 460 - 400) { max_mV = 460 - 400; }
|
||||
static constexpr std::uint8_t table[] =
|
||||
{ 410 - 400 /// 4100mV
|
||||
, 420 - 400 /// 4200mV
|
||||
, 435 - 400 /// 4350mV
|
||||
, 440 - 400 /// 4400mV
|
||||
, 460 - 400 /// 4600mV
|
||||
, 255
|
||||
};
|
||||
size_t i = 0;
|
||||
while (table[i] <= max_mV) { ++i; }
|
||||
|
||||
if (++i >= 0b110) { i = 0; }
|
||||
writeRegister8(0x64, i);
|
||||
}
|
||||
|
||||
std::int8_t AXP2101_Class::getBatteryLevel(void)
|
||||
{
|
||||
std::int8_t res = readRegister8(0xA4);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// @return -1:discharge / 0:standby / 1:charge
|
||||
int AXP2101_Class::getChargeStatus(void)
|
||||
{
|
||||
uint32_t val = (readRegister8(0x01) >> 5) & 0b11;
|
||||
// 0b01:charge / 0b10:dischage / 0b00:stanby
|
||||
return (val == 1) ? 1 : ((val == 2) ? -1 : 0);
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isCharging(void)
|
||||
{
|
||||
return (readRegister8(0x01) & 0b01100000) == 0b00100000;
|
||||
}
|
||||
|
||||
void AXP2101_Class::powerOff(void)
|
||||
{
|
||||
bitOn(0x10, 0x01);
|
||||
}
|
||||
|
||||
//enable all ADC channel control or set default values
|
||||
void AXP2101_Class::setAdcState(bool enable)
|
||||
{
|
||||
writeRegister8(0x30, enable == true ? 0b111111 : 0b11);
|
||||
}
|
||||
|
||||
void AXP2101_Class::setAdcRate( std::uint8_t rate )
|
||||
{
|
||||
}
|
||||
|
||||
void AXP2101_Class::setBACKUP(bool enable)
|
||||
{
|
||||
/*
|
||||
static constexpr std::uint8_t add = 0x35;
|
||||
static constexpr std::uint8_t bit = 1 << 7;
|
||||
if (enable)
|
||||
{ // Enable
|
||||
bitOn(add, bit);
|
||||
}
|
||||
else
|
||||
{ // Disable
|
||||
bitOff(add, bit);
|
||||
}
|
||||
//*/
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isACIN(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool AXP2101_Class::isVBUS(void)
|
||||
{ // VBUS good indication
|
||||
return readRegister8(0x00) & 0x20;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::getBatState(void)
|
||||
{ // Battery present state
|
||||
return readRegister8(0x00) & 0x08;
|
||||
}
|
||||
|
||||
std::uint8_t AXP2101_Class::getPekPress(void)
|
||||
{
|
||||
std::uint8_t val = readRegister8(0x49) & 0x0C;
|
||||
if (val) { writeRegister8(0x49, val); }
|
||||
return val >> 2;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getACINVoltage(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getACINCurrent(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getVBUSVoltage(void)
|
||||
{
|
||||
if (isVBUS() == false) { return 0.0f; }
|
||||
|
||||
float vBus = readRegister14(0x38);
|
||||
if (vBus >= 16375) { return 0.0f; }
|
||||
|
||||
return vBus / 1000.0f;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getVBUSCurrent(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getTSVoltage(void)
|
||||
{
|
||||
float volt = readRegister14(0x36);
|
||||
if (volt >= 16375) { return 0.0f; }
|
||||
|
||||
return volt / 2000.0f;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getInternalTemperature(void)
|
||||
{
|
||||
return 22 + ((7274 - readRegister16(0x3C)) / 20);
|
||||
}
|
||||
|
||||
float AXP2101_Class::getBatteryPower(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getBatteryVoltage(void)
|
||||
{
|
||||
return readRegister14(0x34) / 1000.0f;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getBatteryChargeCurrent(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getBatteryDischargeCurrent(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float AXP2101_Class::getAPSVoltage(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::enableIRQ(std::uint64_t registerEn)
|
||||
{
|
||||
return setIRQEnRegister(registerEn, true);
|
||||
}
|
||||
|
||||
bool AXP2101_Class::disableIRQ(std::uint64_t registerEn)
|
||||
{
|
||||
return setIRQEnRegister(registerEn, false);
|
||||
}
|
||||
|
||||
bool AXP2101_Class::setIRQEnRegister(std::uint64_t registerEn, bool enable)
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t data = 0, value = 0;
|
||||
if (registerEn & 0x0000FF)
|
||||
{
|
||||
value = registerEn & 0xFF;
|
||||
data = readRegister8(AXP2101_IRQEN0);
|
||||
intRegister[0] = enable ? (data | value) : (data & (~value));
|
||||
res |= writeRegister8(AXP2101_IRQEN0, intRegister[0]);
|
||||
}
|
||||
if (registerEn & 0x00FF00)
|
||||
{
|
||||
value = registerEn >> 8;
|
||||
data = readRegister8(AXP2101_IRQEN1);
|
||||
intRegister[1] = enable ? (data | value) : (data & (~value));
|
||||
res |= writeRegister8(AXP2101_IRQEN1, intRegister[1]);
|
||||
}
|
||||
if (registerEn & 0xFF0000)
|
||||
{
|
||||
value = registerEn >> 16;
|
||||
data = readRegister8(AXP2101_IRQEN2);
|
||||
intRegister[2] = enable ? (data | value) : (data & (~value));
|
||||
res |= writeRegister8(AXP2101_IRQEN2, intRegister[2]);
|
||||
}
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
std::uint64_t AXP2101_Class::getIRQStatuses(void)
|
||||
{
|
||||
statusRegister[0] = readRegister8(AXP2101_IRQSTAT0);
|
||||
statusRegister[1] = readRegister8(AXP2101_IRQSTAT1);
|
||||
statusRegister[2] = readRegister8(AXP2101_IRQSTAT2);
|
||||
return (uint32_t)(statusRegister[0] << 16) | (uint32_t)(statusRegister[1] << 8) | (uint32_t)(statusRegister[2]);
|
||||
}
|
||||
|
||||
void AXP2101_Class::clearIRQStatuses()
|
||||
{
|
||||
for (int i = 0; i < AXP2101_IRQSTAT_CNT; i++)
|
||||
{
|
||||
writeRegister8(AXP2101_IRQSTAT0 + i, 0xFF);
|
||||
statusRegister[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isDropWarningLevel2Irq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_WARNING_LEVEL2;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isDropWarningLevel1Irq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_WARNING_LEVEL1;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isGaugeWdtTimeoutIrq()
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_GAUGE_WDT_TIMEOUT;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatChargerOverTemperatureIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_CHG_OVER_TEMP;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatChargerUnderTemperatureIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_CHG_UNDER_TEMP;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatWorkOverTemperatureIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_OVER_TEMP;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatWorkUnderTemperatureIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_UNDER_TEMP;
|
||||
if (intRegister[0] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[0], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isVbusInsertIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_VBUS_INSERT >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isVbusRemoveIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_VBUS_REMOVE >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatInsertIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_INSERT >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatRemoveIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_REMOVE >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isPekeyShortPressIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_PKEY_SHORT_PRESS >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isPekeyLongPressIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_PKEY_LONG_PRESS >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isPekeyNegativeIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_PKEY_NEGATIVE_EDGE >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isPekeyPositiveIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_PKEY_POSITIVE_EDGE >> 8;
|
||||
if (intRegister[1] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[1], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isWdtExpireIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_WDT_EXPIRE >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isLdoOverCurrentIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_LDO_OVER_CURR >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatfetOverCurrentIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BATFET_OVER_CURR >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatChagerDoneIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_CHG_DONE >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatChagerStartIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_CHG_START >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatDieOverTemperatureIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_DIE_OVER_TEMP >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isChagerOverTimeoutIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_CHAGER_TIMER >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AXP2101_Class::isBatOverVoltageIrq(void)
|
||||
{
|
||||
uint8_t mask = AXP2101_IRQ_BAT_OVER_VOLTAGE >> 16;
|
||||
if (intRegister[2] & mask)
|
||||
{
|
||||
return IS_BIT_SET(statusRegister[2], mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t AXP2101_Class::readRegister12(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return (buf[0] & 0x0F) << 8 | buf[1];
|
||||
}
|
||||
std::size_t AXP2101_Class::readRegister14(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return (buf[0] & 0x3F) << 8 | buf[1];
|
||||
}
|
||||
std::size_t AXP2101_Class::readRegister16(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return buf[0] << 8 | buf[1];
|
||||
}
|
||||
|
||||
}
|
||||
188
libraries/M5Unified/src/utility/AXP2101_Class.hpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_AXP2101_CLASS_H__
|
||||
#define __M5_AXP2101_CLASS_H__
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
//IRQ ENABLE REGISTER
|
||||
#define AXP2101_IRQEN0 0x40
|
||||
#define AXP2101_IRQEN1 0x41
|
||||
#define AXP2101_IRQEN2 0x42
|
||||
|
||||
//IRQ STATUS REGISTER
|
||||
#define AXP2101_IRQSTAT0 0x48
|
||||
#define AXP2101_IRQSTAT1 0x49
|
||||
#define AXP2101_IRQSTAT2 0x4A
|
||||
#define AXP2101_IRQSTAT_CNT 3
|
||||
|
||||
|
||||
namespace m5
|
||||
{
|
||||
typedef enum {
|
||||
AXP2101_IRQ_BAT_UNDER_TEMP = 1 << 0, // Battery Under Temperature in Work mode IRQ(bwut_irq)
|
||||
AXP2101_IRQ_BAT_OVER_TEMP = 1 << 1, // Battery Over Temperature in Work mode IRQ(bwot_irq)
|
||||
AXP2101_IRQ_BAT_CHG_UNDER_TEMP = 1 << 2, // Battery Under Temperature in Charge mode IRQ(bcut_irq)
|
||||
AXP2101_IRQ_BAT_CHG_OVER_TEMP = 1 << 3, // Battery Over Temperature in Charge mode IRQ(bcot_irq)
|
||||
AXP2101_IRQ_GAUGE_NEW_SOC = 1 << 4, // Gauge New SOC IRQ(lowsoc_irq)
|
||||
AXP2101_IRQ_GAUGE_WDT_TIMEOUT = 1 << 5, // Gauge Watchdog Timeout IRQ(gwdt_irq)
|
||||
AXP2101_IRQ_WARNING_LEVEL1 = 1 << 6, // SOC drop to Warning Level1 IRQ(socwl1_irq)
|
||||
AXP2101_IRQ_WARNING_LEVEL2 = 1 << 7, // SOC drop to Warning Level2 IRQ(socwl2_irq)
|
||||
|
||||
// IRQ2 REG 41H
|
||||
AXP2101_IRQ_PKEY_POSITIVE_EDGE = 1 << 8, // POWERON Positive Edge IRQ(ponpe_irq_en)
|
||||
AXP2101_IRQ_PKEY_NEGATIVE_EDGE = 1 << 9, // POWERON Negative Edge IRQ(ponne_irq_en)
|
||||
AXP2101_IRQ_PKEY_LONG_PRESS = 1 << 10, // POWERON Long PRESS IRQ(ponlp_irq)
|
||||
AXP2101_IRQ_PKEY_SHORT_PRESS = 1 << 11, // POWERON Short PRESS IRQ(ponsp_irq_en)
|
||||
AXP2101_IRQ_BAT_REMOVE = 1 << 12, // Battery Remove IRQ(bremove_irq)
|
||||
AXP2101_IRQ_BAT_INSERT = 1 << 13, // Battery Insert IRQ(binsert_irq)
|
||||
AXP2101_IRQ_VBUS_REMOVE = 1 << 14, // VBUS Remove IRQ(vremove_irq)
|
||||
AXP2101_IRQ_VBUS_INSERT = 1 << 15, // VBUS Insert IRQ(vinsert_irq)
|
||||
|
||||
// IRQ3 REG 42H
|
||||
AXP2101_IRQ_BAT_OVER_VOLTAGE = 1 << 16, // Battery Over Voltage Protection IRQ(bovp_irq)
|
||||
AXP2101_IRQ_CHAGER_TIMER = 1 << 17, // Charger Safety Timer1/2 expire IRQ(chgte_irq)
|
||||
AXP2101_IRQ_DIE_OVER_TEMP = 1 << 18, // DIE Over Temperature level1 IRQ(dotl1_irq)
|
||||
AXP2101_IRQ_BAT_CHG_START = 1 << 19, // Charger start IRQ(chgst_irq)
|
||||
AXP2101_IRQ_BAT_CHG_DONE = 1 << 20, // Battery charge done IRQ(chgdn_irq)
|
||||
AXP2101_IRQ_BATFET_OVER_CURR = 1 << 21, // BATFET Over Current Protection IRQ(bocp_irq)
|
||||
AXP2101_IRQ_LDO_OVER_CURR = 1 << 22, // LDO Over Current IRQ(ldooc_irq)
|
||||
AXP2101_IRQ_WDT_EXPIRE = 1 << 23, // Watchdog Expire IRQ(wdexp_irq)
|
||||
|
||||
// ALL IRQ
|
||||
AXP2101_IRQ_ALL = (0xFFFFFFFFUL)
|
||||
} axp2101_irq_t;
|
||||
class AXP2101_Class : public I2C_Device
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t AXP2101_EFUS_OP_CFG = 0xF0;
|
||||
static constexpr uint8_t AXP2101_EFREQ_CTRL = 0xF1;
|
||||
static constexpr uint8_t AXP2101_TWI_ADDR_EXT = 0xFF;
|
||||
|
||||
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x34;
|
||||
|
||||
AXP2101_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C)
|
||||
: I2C_Device ( i2c_addr, freq, i2c )
|
||||
{}
|
||||
|
||||
bool begin(void);
|
||||
|
||||
/// Get the remaining battery power.
|
||||
/// @return 0-100 level
|
||||
std::int8_t getBatteryLevel(void);
|
||||
|
||||
/// set battery charge enable.
|
||||
/// @param enable true=enable / false=disable
|
||||
void setBatteryCharge(bool enable);
|
||||
|
||||
/// set battery precharge current
|
||||
/// @param max_mA milli ampere. (0 - 200).
|
||||
void setPreChargeCurrent(std::uint16_t max_mA);
|
||||
|
||||
/// set battery charge current
|
||||
/// @param max_mA milli ampere. (100 - 1320).
|
||||
void setChargeCurrent(std::uint16_t max_mA);
|
||||
|
||||
/// set battery charge voltage
|
||||
/// @param max_mV milli volt. (4100 - 4360).
|
||||
void setChargeVoltage(std::uint16_t max_mV);
|
||||
|
||||
/// @return -1:discharge / 0:standby / 1:charge
|
||||
int getChargeStatus(void);
|
||||
|
||||
/// Get whether the battery is currently charging or not.
|
||||
bool isCharging(void);
|
||||
|
||||
|
||||
inline void setALDO1(int voltage) { _set_LDO(0, voltage); }
|
||||
inline void setALDO2(int voltage) { _set_LDO(1, voltage); }
|
||||
inline void setALDO3(int voltage) { _set_LDO(2, voltage); }
|
||||
inline void setALDO4(int voltage) { _set_LDO(3, voltage); }
|
||||
inline void setBLDO1(int voltage) { _set_LDO(4, voltage); }
|
||||
inline void setBLDO2(int voltage) { _set_LDO(5, voltage); }
|
||||
inline void setDLDO1(int voltage) { _set_DLDO(0, voltage); }
|
||||
inline void setDLDO2(int voltage) { _set_DLDO(1, voltage); }
|
||||
|
||||
inline bool getALDO1Enabled(void) { return _get_LDOEn(0); }
|
||||
inline bool getALDO2Enabled(void) { return _get_LDOEn(1); }
|
||||
inline bool getALDO3Enabled(void) { return _get_LDOEn(2); }
|
||||
inline bool getALDO4Enabled(void) { return _get_LDOEn(3); }
|
||||
inline bool getBLDO1Enabled(void) { return _get_LDOEn(4); }
|
||||
inline bool getBLDO2Enabled(void) { return _get_LDOEn(5); }
|
||||
|
||||
void powerOff(void);
|
||||
|
||||
void setAdcState(bool enable);
|
||||
void setAdcRate( std::uint8_t rate );
|
||||
|
||||
void setBACKUP(bool enable);
|
||||
|
||||
bool isACIN(void);
|
||||
bool isVBUS(void);
|
||||
bool getBatState(void);
|
||||
|
||||
float getBatteryVoltage(void);
|
||||
float getBatteryDischargeCurrent(void);
|
||||
float getBatteryChargeCurrent(void);
|
||||
float getBatteryPower(void);
|
||||
float getACINVoltage(void);
|
||||
float getACINCurrent(void);
|
||||
float getVBUSVoltage(void);
|
||||
float getVBUSCurrent(void);
|
||||
float getTSVoltage(void);
|
||||
float getAPSVoltage(void);
|
||||
float getInternalTemperature(void);
|
||||
|
||||
/// @return 0:none / 1:Long press / 2:Short press / 3:both
|
||||
std::uint8_t getPekPress(void);
|
||||
|
||||
bool enableIRQ(std::uint64_t registerEn);
|
||||
bool disableIRQ(std::uint64_t registerEn);
|
||||
std::uint64_t getIRQStatuses(void);
|
||||
void clearIRQStatuses();
|
||||
//IRQ STATUS 0
|
||||
bool isDropWarningLevel2Irq(void);
|
||||
bool isDropWarningLevel1Irq(void);
|
||||
bool isGaugeWdtTimeoutIrq();
|
||||
bool isBatChargerUnderTemperatureIrq(void);
|
||||
bool isBatChargerOverTemperatureIrq(void);
|
||||
bool isBatWorkOverTemperatureIrq(void);
|
||||
bool isBatWorkUnderTemperatureIrq(void);
|
||||
//IRQ STATUS 1
|
||||
bool isVbusInsertIrq(void);
|
||||
bool isVbusRemoveIrq(void);
|
||||
bool isBatInsertIrq(void);
|
||||
bool isBatRemoveIrq(void);
|
||||
bool isPekeyShortPressIrq(void);
|
||||
bool isPekeyLongPressIrq(void);
|
||||
bool isPekeyNegativeIrq(void);
|
||||
bool isPekeyPositiveIrq(void);
|
||||
//IRQ STATUS 2
|
||||
bool isWdtExpireIrq(void);
|
||||
bool isLdoOverCurrentIrq(void);
|
||||
bool isBatfetOverCurrentIrq(void);
|
||||
bool isBatChagerDoneIrq(void);
|
||||
bool isBatChagerStartIrq(void);
|
||||
bool isBatDieOverTemperatureIrq(void);
|
||||
bool isChagerOverTimeoutIrq(void);
|
||||
bool isBatOverVoltageIrq(void);
|
||||
|
||||
|
||||
private:
|
||||
std::uint8_t statusRegister[AXP2101_IRQSTAT_CNT];
|
||||
std::uint8_t intRegister[AXP2101_IRQSTAT_CNT];
|
||||
|
||||
std::size_t readRegister12(std::uint8_t addr);
|
||||
std::size_t readRegister14(std::uint8_t addr);
|
||||
std::size_t readRegister16(std::uint8_t addr);
|
||||
|
||||
void _set_LDO(std::uint8_t num, int voltage);
|
||||
void _set_DLDO(std::uint8_t num, int voltage);
|
||||
bool _get_LDOEn(std::uint8_t num);
|
||||
|
||||
bool setIRQEnRegister(std::uint64_t registerEn, bool enable);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
84
libraries/M5Unified/src/utility/Button_Class.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "Button_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
void Button_Class::setState(std::uint32_t msec, button_state_t state)
|
||||
{
|
||||
if (_currentState == state_decide_click_count)
|
||||
{
|
||||
_clickCount = 0;
|
||||
}
|
||||
|
||||
_lastMsec = msec;
|
||||
bool flg_timeout = (msec - _lastClicked > _msecHold);
|
||||
switch (state)
|
||||
{
|
||||
case state_nochange:
|
||||
if (flg_timeout && !_press && _clickCount)
|
||||
{
|
||||
if (_oldPress == 0 && _currentState == state_nochange)
|
||||
{
|
||||
state = state_decide_click_count;
|
||||
}
|
||||
else { _clickCount = 0; }
|
||||
}
|
||||
break;
|
||||
|
||||
case state_clicked:
|
||||
++_clickCount;
|
||||
_lastClicked = msec;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_currentState = state;
|
||||
}
|
||||
|
||||
void Button_Class::setRawState(std::uint32_t msec, bool press)
|
||||
{
|
||||
button_state_t state = button_state_t::state_nochange;
|
||||
bool disable_db = (msec - _lastMsec) > _msecDebounce;
|
||||
auto oldPress = _press;
|
||||
_oldPress = oldPress;
|
||||
if (_raw_press != press)
|
||||
{
|
||||
_raw_press = press;
|
||||
_lastRawChange = msec;
|
||||
}
|
||||
if (disable_db || msec - _lastRawChange >= _msecDebounce)
|
||||
{
|
||||
if (press != (0 != oldPress))
|
||||
{
|
||||
_lastChange = msec;
|
||||
}
|
||||
|
||||
if (press)
|
||||
{
|
||||
std::uint32_t holdPeriod = msec - _lastChange;
|
||||
_lastHoldPeriod = holdPeriod;
|
||||
if (!oldPress)
|
||||
{
|
||||
_press = 1;
|
||||
} else
|
||||
if (oldPress == 1 && (holdPeriod >= _msecHold))
|
||||
{
|
||||
_press = 2;
|
||||
state = button_state_t::state_hold;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_press = 0;
|
||||
if (oldPress == 1)
|
||||
{
|
||||
state = button_state_t::state_clicked;
|
||||
}
|
||||
}
|
||||
}
|
||||
setState(msec, state);
|
||||
}
|
||||
}
|
||||
86
libraries/M5Unified/src/utility/Button_Class.hpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_BUTTON_CLASS_H__
|
||||
#define __M5_BUTTON_CLASS_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class Button_Class
|
||||
{
|
||||
public:
|
||||
enum button_state_t : std::uint8_t
|
||||
{ state_nochange
|
||||
, state_clicked
|
||||
, state_hold
|
||||
, state_decide_click_count
|
||||
};
|
||||
|
||||
/// Returns true when the button is pressed briefly and released.
|
||||
bool wasClicked(void) const { return _currentState == state_clicked; }
|
||||
|
||||
/// Returns true when the button has been held pressed for a while.
|
||||
bool wasHold(void) const { return _currentState == state_hold; }
|
||||
|
||||
/// Returns true when some time has passed since the button was single clicked.
|
||||
bool wasSingleClicked(void) const { return _currentState == state_decide_click_count && _clickCount == 1; }
|
||||
|
||||
/// Returns true when some time has passed since the button was double clicked.
|
||||
bool wasDoubleClicked(void) const { return _currentState == state_decide_click_count && _clickCount == 2; }
|
||||
|
||||
/// Returns true when some time has passed since the button was multiple clicked.
|
||||
bool wasDecideClickCount(void) const { return _currentState == state_decide_click_count; }
|
||||
|
||||
[[deprecated("use wasDecideClickCount()")]]
|
||||
bool wasDeciedClickCount(void) const { return wasDecideClickCount(); }
|
||||
|
||||
std::uint8_t getClickCount(void) const { return _clickCount; }
|
||||
|
||||
/// Returns true if the button is currently held pressed.
|
||||
bool isHolding(void) const { return _press == 2; }
|
||||
bool wasChangePressed(void) const { return ((bool)_press) != ((bool)_oldPress); }
|
||||
|
||||
bool isPressed(void) const { return _press; }
|
||||
bool isReleased(void) const { return !_press; }
|
||||
bool wasPressed(void) const { return !_oldPress && _press; }
|
||||
bool wasReleased(void) const { return _oldPress && !_press; }
|
||||
bool wasReleasedAfterHold(void) const { return !_press && _oldPress == 2; }
|
||||
bool wasReleaseFor(std::uint32_t ms) const { return _oldPress && !_press && _lastHoldPeriod >= ms; }
|
||||
|
||||
[[deprecated("use wasReleaseFor()")]]
|
||||
bool wasReleasefor(std::uint32_t ms) const { return wasReleaseFor(ms); }
|
||||
bool pressedFor(std::uint32_t ms) const { return (_press && _lastMsec - _lastChange >= ms); }
|
||||
bool releasedFor(std::uint32_t ms) const { return (!_press && _lastMsec - _lastChange >= ms); }
|
||||
|
||||
void setDebounceThresh(std::uint32_t msec) { _msecDebounce = msec; }
|
||||
void setHoldThresh(std::uint32_t msec) { _msecHold = msec; }
|
||||
|
||||
void setRawState(std::uint32_t msec, bool press);
|
||||
void setState(std::uint32_t msec, button_state_t state);
|
||||
button_state_t getState(void) const { return _currentState; }
|
||||
std::uint32_t lastChange(void) const { return _lastChange; }
|
||||
|
||||
std::uint32_t getDebounceThresh(void) const { return _msecDebounce; }
|
||||
std::uint32_t getHoldThresh(void) const { return _msecHold; }
|
||||
|
||||
std::uint32_t getUpdateMsec(void) const { return _lastMsec; }
|
||||
private:
|
||||
std::uint32_t _lastMsec = 0;
|
||||
std::uint32_t _lastChange = 0;
|
||||
std::uint32_t _lastRawChange = 0;
|
||||
std::uint32_t _lastClicked = 0;
|
||||
std::uint16_t _msecDebounce = 10;
|
||||
std::uint16_t _msecHold = 500;
|
||||
std::uint16_t _lastHoldPeriod = 0;
|
||||
button_state_t _currentState = state_nochange; // 0:nochange 1:click 2:hold
|
||||
bool _raw_press = false;
|
||||
std::uint8_t _press = 0; // 0:release 1:click 2:holding
|
||||
std::uint8_t _oldPress = 0;
|
||||
std::uint8_t _clickCount = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
125
libraries/M5Unified/src/utility/I2C_Class.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
#include <M5GFX.h>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
I2C_Class In_I2C;
|
||||
I2C_Class Ex_I2C;
|
||||
|
||||
void I2C_Class::setPort(i2c_port_t port_num, int sda, int scl)
|
||||
{
|
||||
_port_num = port_num;
|
||||
_pin_sda = sda;
|
||||
_pin_scl = scl;
|
||||
m5gfx::i2c::setPins(port_num, sda, scl).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::begin(i2c_port_t port_num, int sda, int scl)
|
||||
{
|
||||
setPort(port_num, sda, scl);
|
||||
return begin();
|
||||
}
|
||||
|
||||
bool I2C_Class::begin(void)
|
||||
{
|
||||
return m5gfx::i2c::init(_port_num).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::release(void) const
|
||||
{
|
||||
return m5gfx::i2c::release(_port_num).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::start(std::uint8_t address, bool read, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::beginTransaction(_port_num, address, freq, read).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::restart(std::uint8_t address, bool read, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::restart(_port_num, address, freq, read).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::stop(void) const
|
||||
{
|
||||
return m5gfx::i2c::endTransaction(_port_num).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::write(std::uint8_t data) const
|
||||
{
|
||||
return m5gfx::i2c::writeBytes(_port_num, &data, 1).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::write(const std::uint8_t* __restrict__ data, std::size_t length) const
|
||||
{
|
||||
return m5gfx::i2c::writeBytes(_port_num, data, length).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::read(std::uint8_t* __restrict__ result, std::size_t length, bool last_nack) const
|
||||
{
|
||||
return m5gfx::i2c::readBytes(_port_num, result, length, last_nack).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::writeRegister(std::uint8_t address, std::uint8_t reg, const std::uint8_t* __restrict__ data, std::size_t length, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::beginTransaction(_port_num, address, freq, false).has_value()
|
||||
&& m5gfx::i2c::writeBytes(_port_num, ®, 1).has_value()
|
||||
&& m5gfx::i2c::writeBytes(_port_num, data, length).has_value()
|
||||
&& m5gfx::i2c::endTransaction(_port_num).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::readRegister(std::uint8_t address, std::uint8_t reg, std::uint8_t* __restrict__ result, std::size_t length, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::readRegister(_port_num, address, reg, result, length, freq).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::writeRegister8(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::writeRegister8(_port_num, address, reg, data, 0, freq).has_value();
|
||||
}
|
||||
|
||||
std::uint8_t I2C_Class::readRegister8(std::uint8_t address, std::uint8_t reg, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::readRegister8(_port_num, address, reg, freq).value_or(0);
|
||||
}
|
||||
|
||||
bool I2C_Class::bitOn(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::bitOn(_port_num, address, reg, data, freq).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::bitOff(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const
|
||||
{
|
||||
return m5gfx::i2c::bitOff(_port_num, address, reg, data, freq).has_value();
|
||||
}
|
||||
|
||||
bool I2C_Class::scanID(uint8_t addr) const
|
||||
{
|
||||
return start(addr, false, 400000) && stop();
|
||||
}
|
||||
|
||||
void I2C_Class::scanID(bool* __restrict__ result) const
|
||||
{
|
||||
// ESP32S3ではアドレス0~7をスキャン対象に含めると動作が停止する
|
||||
for (int i = 8; i < 0x78; i++)
|
||||
{
|
||||
result[i] = start(i, false, 400000) && stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool I2C_Device::writeRegister8Array(const std::uint8_t* reg_data_array, std::size_t length) const
|
||||
{
|
||||
for (size_t i = 0; i < length; i+=2)
|
||||
{
|
||||
if (!_i2c->writeRegister8(_addr, reg_data_array[i], reg_data_array[i+1], _freq))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
211
libraries/M5Unified/src/utility/I2C_Class.hpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_I2C_CLASS_H__
|
||||
#define __M5_I2C_CLASS_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
#if __has_include ( <driver/i2c.h> )
|
||||
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class I2C_Class
|
||||
{
|
||||
public:
|
||||
/// setup I2C port parameters. (No begin)
|
||||
/// @param port_num I2C number. (I2C_NUM_0 or I2C_NUM_1).
|
||||
/// @param pin_sda SDA pin number.
|
||||
/// @param pin_scl SCL pin number.
|
||||
void setPort(i2c_port_t port_num, int pin_sda, int pin_scl);
|
||||
|
||||
/// setup and begin I2C peripheral. (No communication is performed.)
|
||||
/// @param port_num I2C number. (I2C_NUM_0 or I2C_NUM_1).
|
||||
/// @param pin_sda SDA pin number.
|
||||
/// @param pin_scl SCL pin number.
|
||||
/// @return success(true) or failed(false).
|
||||
bool begin(i2c_port_t port_num, int pin_sda, int pin_scl);
|
||||
|
||||
/// begin I2C peripheral. (No communication is performed.)
|
||||
/// @return success(true) or failed(false).
|
||||
bool begin(void);
|
||||
|
||||
/// release I2C peripheral.
|
||||
/// @return success(true) or failed(false).
|
||||
bool release(void) const;
|
||||
|
||||
/// Sends the I2C start condition and the address of the slave.
|
||||
/// @param address slave addr.
|
||||
/// @param read bit of read flag. true=read / false=write.
|
||||
/// @return success(true) or failed(false).
|
||||
bool start(std::uint8_t address, bool read, std::uint32_t freq) const;
|
||||
|
||||
/// Sends the I2C repeated start condition and the address of the slave.
|
||||
/// @param address slave addr.
|
||||
/// @param read bit of read flag. true=read / false=write.
|
||||
/// @return success(true) or failed(false).
|
||||
bool restart(std::uint8_t address, bool read, std::uint32_t freq) const;
|
||||
|
||||
/// Sends the I2C stop condition.
|
||||
/// If an ACK error occurs, return false.
|
||||
/// @return success(true) or failed(false).
|
||||
bool stop(void) const;
|
||||
|
||||
/// Send 1 byte of data.
|
||||
/// @param data write data.
|
||||
/// @return success(true) or failed(false).
|
||||
bool write(std::uint8_t data) const;
|
||||
|
||||
/// Send multiple bytes of data.
|
||||
/// @param[in] data write data array.
|
||||
/// @param length data array length.
|
||||
/// @return success(true) or failed(false).
|
||||
bool write(const std::uint8_t* data, std::size_t length) const;
|
||||
|
||||
/// Receive multiple bytes of data.
|
||||
/// @param[out] result read data array.
|
||||
/// @param length data array length.
|
||||
/// @return success(true) or failed(false).
|
||||
bool read(std::uint8_t* result, std::size_t length, bool last_nack = false) const;
|
||||
|
||||
//----------
|
||||
|
||||
/// Write multiple bytes value to the register. Performs a series of communications from START to STOP.
|
||||
/// @param address slave addr.
|
||||
/// @param reg register number.
|
||||
/// @param[in] data write data array.
|
||||
/// @param length data array length.
|
||||
/// @return success(true) or failed(false).
|
||||
bool writeRegister(std::uint8_t address, std::uint8_t reg, const std::uint8_t* data, std::size_t length, std::uint32_t freq) const;
|
||||
|
||||
/// Read multiple bytes value from the register. Performs a series of communications from START to STOP.
|
||||
/// @param address slave addr.
|
||||
/// @param reg register number.
|
||||
/// @param[out] result read data array.
|
||||
/// @param length data array length.
|
||||
/// @return success(true) or failed(false).
|
||||
bool readRegister(std::uint8_t address, std::uint8_t reg, std::uint8_t* result, std::size_t length, std::uint32_t freq) const;
|
||||
|
||||
/// Write a 1-byte value to the register. Performs a series of communications from START to STOP.
|
||||
/// @param address slave addr.
|
||||
/// @param reg register number.
|
||||
/// @param data write data.
|
||||
/// @return success(true) or failed(false).
|
||||
bool writeRegister8(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const;
|
||||
|
||||
/// Read a 1-byte value from the register. Performs a series of communications from START to STOP.
|
||||
/// @param address slave addr.
|
||||
/// @param reg register number.
|
||||
/// @return read value.
|
||||
std::uint8_t readRegister8(std::uint8_t address, std::uint8_t reg, std::uint32_t freq) const;
|
||||
|
||||
/// Write a 1-byte value to the register by bit add operation. Performs a series of communications from START to STOP.
|
||||
/// @param address slave addr.
|
||||
/// @param reg register number.
|
||||
/// @param data add bit data.
|
||||
/// @return success(true) or failed(false).
|
||||
bool bitOn(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const;
|
||||
|
||||
/// Write a 1-byte value to the register by bit erase operation. Performs a series of communications from START to STOP.
|
||||
/// @param address slave addr.
|
||||
/// @param reg register number.
|
||||
/// @param data erase bit data.
|
||||
/// @return success(true) or failed(false).
|
||||
bool bitOff(std::uint8_t address, std::uint8_t reg, std::uint8_t data, std::uint32_t freq) const;
|
||||
|
||||
/// execute I2C scan. (for 7bit address)
|
||||
/// @param[out] result data array needs 120 Bytes.
|
||||
void scanID(bool* result) const;
|
||||
|
||||
bool scanID(uint8_t addr) const;
|
||||
|
||||
i2c_port_t getPort(void) const { return _port_num; }
|
||||
int8_t getSDA(void) const { return _pin_sda; }
|
||||
int8_t getSCL(void) const { return _pin_scl; }
|
||||
|
||||
bool isEnabled(void) const { return _port_num >= 0; }
|
||||
|
||||
private:
|
||||
i2c_port_t _port_num = (i2c_port_t)-1;
|
||||
int8_t _pin_sda = -1;
|
||||
int8_t _pin_scl = -1;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// for internal I2C device
|
||||
extern I2C_Class In_I2C;
|
||||
|
||||
/// for external I2C device
|
||||
extern I2C_Class Ex_I2C;
|
||||
|
||||
|
||||
|
||||
class I2C_Device
|
||||
{
|
||||
public:
|
||||
I2C_Device(std::uint8_t i2c_addr, std::uint32_t freq, I2C_Class* i2c = &In_I2C)
|
||||
: _i2c { i2c }
|
||||
, _freq { freq }
|
||||
, _addr { i2c_addr }
|
||||
, _init { false }
|
||||
{}
|
||||
|
||||
void setPort(I2C_Class* i2c) { _i2c = i2c; }
|
||||
|
||||
void setClock(std::uint32_t freq) { _freq = freq; }
|
||||
|
||||
void setAddress(std::uint8_t i2c_addr) { _addr = i2c_addr; }
|
||||
|
||||
std::uint8_t getAddress(void) const { return _addr; }
|
||||
|
||||
bool writeRegister8(std::uint8_t reg, std::uint8_t data) const
|
||||
{
|
||||
return _i2c->writeRegister8(_addr, reg, data, _freq);
|
||||
}
|
||||
|
||||
std::uint8_t readRegister8(std::uint8_t reg) const
|
||||
{
|
||||
return _i2c->readRegister8(_addr, reg, _freq);
|
||||
}
|
||||
|
||||
bool writeRegister8Array(const std::uint8_t* reg_data_array, std::size_t length) const;
|
||||
|
||||
bool writeRegister(std::uint8_t reg, const std::uint8_t* data, std::size_t length) const
|
||||
{
|
||||
return _i2c->writeRegister(_addr, reg, data, length, _freq);
|
||||
}
|
||||
|
||||
bool readRegister(std::uint8_t reg, std::uint8_t* result, std::size_t length) const
|
||||
{
|
||||
return _i2c->readRegister(_addr, reg, result, length, _freq);
|
||||
}
|
||||
|
||||
bool bitOn(std::uint8_t reg, std::uint8_t bit) const
|
||||
{
|
||||
return _i2c->bitOn(_addr, reg, bit, _freq);
|
||||
}
|
||||
|
||||
bool bitOff(std::uint8_t reg, std::uint8_t bit) const
|
||||
{
|
||||
return _i2c->bitOff(_addr, reg, bit, _freq);
|
||||
}
|
||||
|
||||
bool isEnabled(void) const { return _init; }
|
||||
|
||||
protected:
|
||||
I2C_Class *_i2c;
|
||||
std::uint32_t _freq;
|
||||
std::uint8_t _addr;
|
||||
bool _init;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
597
libraries/M5Unified/src/utility/IMU_Class.cpp
Normal file
|
|
@ -0,0 +1,597 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "../M5Unified.hpp"
|
||||
#include "IMU_Class.hpp"
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#if defined(ESP_PLATFORM)
|
||||
|
||||
#include <sdkconfig.h>
|
||||
#include <nvs.h>
|
||||
|
||||
#include "imu/MPU6886_Class.hpp"
|
||||
#include "imu/SH200Q_Class.hpp"
|
||||
#include "imu/BMI270_Class.hpp"
|
||||
#include "imu/BMM150_Class.hpp"
|
||||
#include "imu/AK8963_Class.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
static constexpr char LIBRARY_NAME[] = "M5Unified";
|
||||
|
||||
namespace m5
|
||||
{
|
||||
static constexpr const char* nvs_key_names[9] = { "ax", "ay", "az", "gx", "gy", "gz", "mx", "my", "mz" };
|
||||
|
||||
bool IMU_Class::begin(I2C_Class* i2c, m5::board_t board)
|
||||
{
|
||||
#if defined(M5UNIFIED_PC_BUILD)
|
||||
(void)i2c;
|
||||
(void)board;
|
||||
#else
|
||||
if (i2c)
|
||||
{
|
||||
i2c->begin();
|
||||
}
|
||||
|
||||
_imu = imu_t::imu_none;
|
||||
_has_sensor_mask = sensor_mask_none;
|
||||
|
||||
{
|
||||
auto mpu6886 = new MPU6886_Class();
|
||||
auto res = mpu6886->begin(i2c);
|
||||
if (!res) { delete mpu6886; }
|
||||
else
|
||||
{
|
||||
_imu_instance[0].reset(mpu6886);
|
||||
switch (mpu6886->whoAmI())
|
||||
{
|
||||
case MPU6886_Class::DEV_ID_MPU6050:
|
||||
_imu = imu_t::imu_mpu6050;
|
||||
break;
|
||||
case MPU6886_Class::DEV_ID_MPU6886:
|
||||
_imu = imu_t::imu_mpu6886;
|
||||
break;
|
||||
case MPU6886_Class::DEV_ID_MPU9250:
|
||||
_imu = imu_t::imu_mpu9250;
|
||||
break;
|
||||
default:
|
||||
_imu = imu_t::imu_unknown;
|
||||
break;
|
||||
}
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32) || !defined ( CONFIG_IDF_TARGET )
|
||||
if (board == m5::board_t::board_M5AtomMatrix)
|
||||
{ // ATOM Matrix's IMU is oriented differently, so change the setting.
|
||||
_internal_axisorder_fixed[sensor_index_accel] = (internal_axisorder_t)(axis_invert_x | axis_invert_z); // X軸,Z軸反転
|
||||
_internal_axisorder_fixed[sensor_index_gyro ] = (internal_axisorder_t)(axis_invert_x | axis_invert_z); // X軸,Z軸反転
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (_imu == imu_t::imu_none)
|
||||
{
|
||||
auto bmi2 = new BMI270_Class();
|
||||
if (!bmi2->begin(i2c)) {
|
||||
bmi2->setAddress(bmi2->getAddress() == 0x68 ? 0x69 : 0x68);
|
||||
if (!bmi2->begin(i2c)) {
|
||||
delete bmi2;
|
||||
bmi2 = nullptr;
|
||||
}
|
||||
}
|
||||
if (bmi2 != nullptr)
|
||||
{
|
||||
_imu_instance[0].reset(bmi2);
|
||||
_imu = imu_t::imu_bmi270;
|
||||
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32S3 )
|
||||
if (board == m5::board_t::board_M5StackCoreS3 && bmi2->getAddress() == 0x69)
|
||||
{ // CoreS3 では、地磁気のY軸Z軸をそれぞれ反転する
|
||||
_internal_axisorder_fixed[sensor_index_mag] = (internal_axisorder_t)(axis_invert_y | axis_invert_z); // Y軸,Z軸反転
|
||||
} else
|
||||
if (board == m5::board_t::board_M5AtomS3R || board == m5::board_t::board_M5AtomS3RCam || board == m5::board_t::board_M5AtomS3RExt)
|
||||
{ // AtomS3Rシリーズ では、ジャイロと加速度のY軸とX軸を入れ替え、Y軸を反転するほか、地磁気のX軸とZ軸をそれぞれ反転する
|
||||
_internal_axisorder_fixed[sensor_index_accel] = (internal_axisorder_t)(axis_order_yxz | axis_invert_y);
|
||||
_internal_axisorder_fixed[sensor_index_gyro ] = (internal_axisorder_t)(axis_order_yxz | axis_invert_y);
|
||||
_internal_axisorder_fixed[sensor_index_mag ] = (internal_axisorder_t)(axis_invert_x | axis_invert_z); // X軸,Z軸反転
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (_imu == imu_t::imu_none)
|
||||
{
|
||||
auto sh200q = new SH200Q_Class();
|
||||
if (!sh200q->begin(i2c)) { delete sh200q; }
|
||||
else
|
||||
{
|
||||
_imu_instance[0].reset(sh200q);
|
||||
_imu = imu_t::imu_sh200q;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto bmm150 = new BMM150_Class();
|
||||
if (!bmm150->begin(i2c)) {
|
||||
delete bmm150;
|
||||
}
|
||||
else
|
||||
{
|
||||
_imu_instance[1].reset(bmm150);
|
||||
if (board == m5::board_t::board_M5Stack)
|
||||
{ // M5Stack MPU6886 + BMM150構成では、地磁気のX軸とZ軸をそれぞれ反転する
|
||||
// M5Stack SH200Q + BMM150構成での動作は未確認。(過去に一時期製造されている)
|
||||
_internal_axisorder_fixed[sensor_index_mag] = (internal_axisorder_t)(axis_invert_x | axis_invert_z); // X軸,Z軸反転
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto ak8963 = new AK8963_Class();
|
||||
if (!ak8963->begin(i2c)) {
|
||||
delete ak8963;
|
||||
}
|
||||
else
|
||||
{
|
||||
_imu_instance[1].reset(ak8963);
|
||||
if (_imu == imu_t::imu_mpu9250)
|
||||
{ // MPU9250内蔵AK8963は地磁気のX軸とY軸を取り換え、Z軸の向きを反転する
|
||||
_internal_axisorder_fixed[sensor_index_mag ] = (internal_axisorder_t)(axis_order_yxz | axis_invert_z); // Y軸X軸を入替, Z軸反転
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (_imu == imu_t::imu_none)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&_offset_data, 0, sizeof(imu_offset_data_t));
|
||||
_update_convert_param();
|
||||
_update_axis_order();
|
||||
|
||||
if (!loadOffsetFromNVS())
|
||||
{
|
||||
setCalibration(255, 255, 255);
|
||||
update();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
int32_t d = _raw_data.sensor[i].value[j] << 16;
|
||||
_offset_data.sensor[i].prev_value[j] = d;
|
||||
_offset_data.sensor[i].avg_value[j] = d;
|
||||
}
|
||||
_offset_data.sensor[i].stillness = 255;
|
||||
_offset_data.sensor[i].calibration();
|
||||
}
|
||||
}
|
||||
setCalibration(0, 0, 0);
|
||||
|
||||
// debug
|
||||
// for(int i=0;i<3;++i){for(int j=0;j<3;++j){_offset_data.sensor[i].value[j] = 65536*512;}}
|
||||
// for(int i=0;i<3;++i){for(int j=0;j<3;++j){_offset_data.sensor[i].value[j] = 0;}}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IMU_Class::sleep(void)
|
||||
{
|
||||
bool res = false;
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
if (_imu_instance[i].get())
|
||||
{
|
||||
res = _imu_instance[i]->sleep() || res;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void IMU_Class::setClock(std::uint32_t freq)
|
||||
{
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
if (_imu_instance[i].get())
|
||||
{
|
||||
_imu_instance[i]->setClock(freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMU_Class::_update_convert_param(void)
|
||||
{
|
||||
if (_imu_instance[0]) { _imu_instance[0]->getConvertParam(&_convert_param); }
|
||||
if (_imu_instance[1]) { _imu_instance[1]->getConvertParam(&_convert_param); }
|
||||
|
||||
// 加速度は 1.0G ± 0.000488f の範囲に収まるよう調整
|
||||
_offset_data.accel.radius = 1.0f / _convert_param.accel_res;
|
||||
_offset_data.accel.tolerance = (1.0f / 2048.0f) / _convert_param.accel_res;
|
||||
_offset_data.accel.noise_level = 0.0625f / _convert_param.accel_res;
|
||||
_offset_data.accel.average_shifter = 1;
|
||||
|
||||
// ジャイロは 誤差 ±2.0度/sec の範囲に収まるよう調整
|
||||
_offset_data.gyro.radius = 0;
|
||||
_offset_data.gyro.tolerance = 0.0f / _convert_param.gyro_res;
|
||||
_offset_data.gyro.noise_level = 2.0f / _convert_param.gyro_res;
|
||||
_offset_data.gyro.average_shifter = 6;
|
||||
|
||||
// 地磁気は…パラメータ模索中…
|
||||
_offset_data.mag.radius = 384.0f / _convert_param.mag_res;
|
||||
_offset_data.mag.tolerance = 64.0f / _convert_param.mag_res;
|
||||
_offset_data.mag.noise_level = 96.0f / _convert_param.mag_res;
|
||||
_offset_data.mag.average_shifter = 1;
|
||||
}
|
||||
|
||||
void IMU_Class::setCalibration(uint8_t accel, uint8_t gyro, uint8_t mag)
|
||||
{
|
||||
_offset_data.accel.strength = accel;
|
||||
_offset_data.gyro.strength = gyro;
|
||||
_offset_data.mag.strength = mag;
|
||||
_calibration_flg = (sensor_mask_t)((accel ? sensor_mask_accel : 0)
|
||||
| (gyro ? sensor_mask_gyro : 0)
|
||||
| (mag ? sensor_mask_mag : 0));
|
||||
}
|
||||
|
||||
bool IMU_Class::setAxisOrder(axis_t axis0, axis_t axis1, axis_t axis2)
|
||||
{ // データの取り出し順序を指定する
|
||||
uint_fast8_t result;
|
||||
switch ((axis0 >> 1) + ((axis1 >> 1) << 2) + ((axis2 >> 1) << 4))
|
||||
{
|
||||
case 0 << 0 | 1 << 2 | 2 << 4: result = axis_order_xyz; break;
|
||||
case 0 << 0 | 2 << 2 | 1 << 4: result = axis_order_xzy; break;
|
||||
case 1 << 0 | 0 << 2 | 2 << 4: result = axis_order_yxz; break;
|
||||
case 1 << 0 | 2 << 2 | 0 << 4: result = axis_order_yzx; break;
|
||||
case 2 << 0 | 0 << 2 | 1 << 4: result = axis_order_zxy; break;
|
||||
case 2 << 0 | 1 << 2 | 0 << 4: result = axis_order_zyx; break;
|
||||
default: return false;
|
||||
}
|
||||
result += (axis0 & 1) + ((axis1 & 1) << 1) + ((axis2 & 1) << 2);
|
||||
_internal_axisorder_user = (internal_axisorder_t)result;
|
||||
_update_axis_order();
|
||||
return true;
|
||||
}
|
||||
|
||||
void IMU_Class::_update_axis_order(void)
|
||||
{ // システムの軸設定とユーザの軸設定を反映したデータの取り出し順序を求める
|
||||
static constexpr const uint8_t internal_axisorder_table[6] =
|
||||
{//X idx | Y idx | Z idx
|
||||
0 << 0 | 1 << 2 | 2 << 4, // axis_order_xyz
|
||||
0 << 0 | 2 << 2 | 1 << 4, // axis_order_xzy
|
||||
1 << 0 | 0 << 2 | 2 << 4, // axis_order_yxz
|
||||
1 << 0 | 2 << 2 | 0 << 4, // axis_order_yzx
|
||||
2 << 0 | 0 << 2 | 1 << 4, // axis_order_zxy
|
||||
2 << 0 | 1 << 2 | 0 << 4, // axis_order_zyx
|
||||
};
|
||||
|
||||
std::uint32_t result = 0;
|
||||
std::uint32_t bitshift = 0;
|
||||
auto axis_order_user = _internal_axisorder_user;
|
||||
auto order_user_tbl = internal_axisorder_table[axis_order_user >> axis_order_shift];
|
||||
for (std::size_t sensor = 0; sensor < 3; ++sensor)
|
||||
{
|
||||
auto axis_order_fixed = _internal_axisorder_fixed[sensor];
|
||||
auto order_fixed_tbl = internal_axisorder_table[axis_order_fixed >> axis_order_shift];
|
||||
for (std::size_t j = 0; j < 3; ++j, bitshift += 3)
|
||||
{ // ユーザが要求している軸順と反転指定を求める
|
||||
std::uint_fast8_t axis_user_index = (order_user_tbl >> (j << 1)) & 3;
|
||||
bool invert_user = axis_order_user & (1 << j);
|
||||
|
||||
// システムで設定された軸順テーブルを、ユーザーが要求している軸順で取得する
|
||||
std::uint_fast8_t axis_fixed_index = (order_fixed_tbl >> (axis_user_index << 1)) & 3;
|
||||
bool invert_fixed = axis_order_fixed & (1 << axis_user_index);
|
||||
|
||||
// 3ビットでx=0b000,y=b010,z=0b100 | 反転フラグ0b001 を表現する。
|
||||
// これを3軸×3センサの9個分を並べて保持する。合計3bit×9の27ビットが使用される。
|
||||
result |= ((axis_fixed_index << 1) + (invert_user != invert_fixed ? 1 : 0)) << bitshift;
|
||||
// printf("user_index:%d invert_user:%d fixed_index:%d invert_fixed:%d res:%08x\n", axis_user_index, invert_user, axis_fixed_index, invert_fixed, result);
|
||||
}
|
||||
}
|
||||
_axis_order_3bit_x9 = result;
|
||||
}
|
||||
|
||||
// 指定された2軸を元に、残りの1軸を求める (右手系)
|
||||
static IMU_Class::axis_t getAxis2(IMU_Class::axis_t axis0, IMU_Class::axis_t axis1)
|
||||
{
|
||||
std::uint_fast8_t axis2 = (1 << (axis0 >> 1) | 1 << (axis1 >> 1)) ^ 0b111;
|
||||
axis2 &= 0b110;
|
||||
std::uint_fast8_t ax0 = axis2 ? (axis2 - 2) : 4;
|
||||
axis2 += (bool)((axis0 & 0b110) == ax0) == (bool)((axis0 & 1) == (axis1 & 1));
|
||||
return (IMU_Class::axis_t)axis2;
|
||||
}
|
||||
|
||||
bool IMU_Class::setAxisOrderRightHanded(axis_t axis0, axis_t axis1)
|
||||
{
|
||||
return setAxisOrder(axis0, axis1, getAxis2(axis0, axis1));
|
||||
}
|
||||
|
||||
bool IMU_Class::setAxisOrderLeftHanded(axis_t axis0, axis_t axis1)
|
||||
{
|
||||
// 右手系で求めた軸に反転フラグを設定し、左手系とする。
|
||||
auto axis2 = getAxis2(axis0, axis1) ^ 1;
|
||||
return setAxisOrder(axis0, axis1, (axis_t)axis2);
|
||||
}
|
||||
|
||||
bool IMU_Class::saveOffsetToNVS(void)
|
||||
{ // NVSへオフセット値を保存する
|
||||
#if !defined(M5UNIFIED_PC_BUILD)
|
||||
std::uint32_t nvs_handle = 0;
|
||||
if (ESP_OK != nvs_open(LIBRARY_NAME, NVS_READWRITE, &nvs_handle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < 3; ++j, ++index)
|
||||
{
|
||||
int32_t val = _offset_data.sensor[i].value[j];
|
||||
nvs_set_i32(nvs_handle, nvs_key_names[index], val);
|
||||
M5_LOGV("%s:%d", nvs_key_names[index], val);
|
||||
}
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IMU_Class::loadOffsetFromNVS(void)
|
||||
{ // NVSからオフセット値を読み込む
|
||||
#if !defined(M5UNIFIED_PC_BUILD)
|
||||
std::uint32_t nvs_handle = 0;
|
||||
if (ESP_OK != nvs_open(LIBRARY_NAME, NVS_READONLY, &nvs_handle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < 3; ++j, ++index)
|
||||
{
|
||||
int32_t val = 0;
|
||||
nvs_get_i32(nvs_handle, nvs_key_names[index], &val);
|
||||
_offset_data.sensor[i].value[j] = val;
|
||||
M5_LOGD("%s:%d", nvs_key_names[index], val);
|
||||
}
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void IMU_Class::clearOffsetData(void)
|
||||
{
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < 3; ++j, ++index)
|
||||
{
|
||||
_offset_data.sensor[i].value[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMU_Class::setOffsetData(size_t index, int32_t value)
|
||||
{
|
||||
if (index < 9) {
|
||||
_offset_data.sensor[index / 3].value[index % 3] = value;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t IMU_Class::getOffsetData(size_t index)
|
||||
{
|
||||
return index < 9 ? _offset_data.sensor[index / 3].value[index % 3] : 0;
|
||||
}
|
||||
|
||||
int16_t IMU_Class::getRawData(size_t index)
|
||||
{
|
||||
return index < 9 ? _raw_data.value[index] : 0;
|
||||
}
|
||||
|
||||
IMU_Class::sensor_mask_t IMU_Class::update(void)
|
||||
{
|
||||
sensor_mask_t res = (sensor_mask_t)0;
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
if (_imu_instance[i].get())
|
||||
{
|
||||
uint_fast8_t t = _imu_instance[i]->getImuRawData(&_raw_data);
|
||||
if (t)
|
||||
{
|
||||
res = (sensor_mask_t)(res | t);
|
||||
}
|
||||
if (i == 0)
|
||||
{
|
||||
_latest_micros = m5gfx::micros();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res)
|
||||
{
|
||||
std::uint_fast8_t mask_flg = _calibration_flg & res;
|
||||
// キャリブレーション処理
|
||||
for (size_t i = 0; mask_flg && i < 3; ++i, mask_flg >>= 1)
|
||||
{
|
||||
// if ((mask_flg & 1) && (16 < _offset_data.sensor[i].updateStillness(_raw_data.sensor[i])))
|
||||
if (mask_flg & 1)
|
||||
{
|
||||
auto st = _offset_data.sensor[i].updateStillness(_raw_data.sensor[i]);
|
||||
if (16 < st)
|
||||
{
|
||||
_offset_data.sensor[i].calibration();
|
||||
// if (i == 1) { printf("Ok %d\n" ,st); }
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// if (i == 1) { printf(" NG %d\n", st); }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void IMU_Class::getImuData(imu_data_t* data)
|
||||
{
|
||||
data->usec = _latest_micros;
|
||||
auto &raw = _raw_data;
|
||||
// data->temp = raw.temp * _convert_param.temp_res + _convert_param.temp_offset;
|
||||
auto &offset = _offset_data;
|
||||
auto order = _axis_order_3bit_x9;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
float resolution = _convert_param.value[i] * (1.0f / 65536.0f);
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
auto axis_index = (order >> 1) & 3;
|
||||
int32_t value = (raw.sensor[i].value[axis_index] << 16) - offset.sensor[i].value[axis_index];
|
||||
if (order & 1) { value = -value; }
|
||||
data->sensor[i].value[j] = resolution * value;
|
||||
order >>= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IMU_Class::getAccel(float *x, float *y, float *z)
|
||||
{
|
||||
bool res = true;
|
||||
uint32_t us = m5gfx::micros();
|
||||
if (us - _latest_micros > 256)
|
||||
{
|
||||
res = update();
|
||||
}
|
||||
imu_data_t data;
|
||||
getImuData(&data);
|
||||
*x = data.accel.x;
|
||||
*y = data.accel.y;
|
||||
*z = data.accel.z;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool IMU_Class::getGyro(float *x, float *y, float *z)
|
||||
{
|
||||
bool res = true;
|
||||
uint32_t us = m5gfx::micros();
|
||||
if (us - _latest_micros > 256)
|
||||
{
|
||||
res = update();
|
||||
}
|
||||
imu_data_t data;
|
||||
getImuData(&data);
|
||||
*x = data.gyro.x;
|
||||
*y = data.gyro.y;
|
||||
*z = data.gyro.z;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool IMU_Class::getMag(float* x, float* y, float* z)
|
||||
{
|
||||
bool res = true;
|
||||
uint32_t us = m5gfx::micros();
|
||||
if (us - _latest_micros > 256)
|
||||
{
|
||||
res = update();
|
||||
}
|
||||
imu_data_t data;
|
||||
getImuData(&data);
|
||||
*x = data.mag.x;
|
||||
*y = data.mag.y;
|
||||
*z = data.mag.z;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool IMU_Class::getTemp(float *t)
|
||||
{
|
||||
int16_t temp;
|
||||
if (_imu_instance[0].get() && _imu_instance[0]->getTempAdc(&temp))
|
||||
{
|
||||
_raw_data.temp = temp;
|
||||
*t = temp * _convert_param.temp_res + _convert_param.temp_offset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IMU_Class::setINTPinActiveLogic(bool level)
|
||||
{
|
||||
return _imu_instance[0].get() && _imu_instance[0]->setINTPinActiveLogic(level);
|
||||
}
|
||||
|
||||
|
||||
std::uint_fast8_t IMU_Class::offset_point_t::updateStillness(const IMU_Base::point3d_i16_t& dst)
|
||||
{ // 前回の座標情報との差が許容範囲以内か調べる
|
||||
int32_t res = stillness + 1;
|
||||
|
||||
int32_t maxdiff = 0;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
int32_t d = dst.value[i] << 16;
|
||||
int32_t pv = prev_value[i];
|
||||
int32_t diff = d - pv;
|
||||
prev_value[i] = d;
|
||||
maxdiff = std::max(maxdiff, abs(diff));
|
||||
|
||||
int32_t av = avg_value[i];
|
||||
diff = d - av;
|
||||
maxdiff = std::max(maxdiff, abs(diff));
|
||||
|
||||
diff = (diff + (1 << (average_shifter - 1))) >> average_shifter;
|
||||
avg_value[i] = av + diff;
|
||||
// maxdiff = std::max(maxdiff, abs(diff) << (average_shifter));
|
||||
}
|
||||
|
||||
int32_t rt = noise_level << 8;
|
||||
maxdiff >>= 8;
|
||||
if (rt > maxdiff)
|
||||
{
|
||||
maxdiff = rt - (maxdiff + 1);
|
||||
maxdiff = (maxdiff << 8) / rt;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxdiff = 0;
|
||||
}
|
||||
res = std::min(res, maxdiff);
|
||||
|
||||
stillness = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
void IMU_Class::offset_point_t::calibration(void)
|
||||
{
|
||||
if (stillness * strength == 0) return;
|
||||
float distance = 0;
|
||||
float diffs[3];
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
// int p = ((average_shifter) ? avg_value : prev_value)[i];
|
||||
auto p = prev_value[i];
|
||||
// auto p = avg_value[i];
|
||||
// auto p = (prev_value[i] + avg_value[i]) >> 1;
|
||||
float f = p - value[i];
|
||||
diffs[i] = f;
|
||||
distance += f * f;
|
||||
}
|
||||
distance = sqrtf(distance * (1.0f / (65536.0f * 65536.0f)));
|
||||
if (distance < 1.0f) { return; }
|
||||
|
||||
// 誤差を求める (目的の半径と現在の距離の差)
|
||||
float measure_error = distance - radius;
|
||||
// if (signbit(measure_error)) { return; }
|
||||
// float tole_half = tolerance;
|
||||
float force = fabsf(measure_error);
|
||||
// if (force <= 0.0f) { return; }
|
||||
if (force <= tolerance) { return; }
|
||||
// if (force > tolerance) { force = tolerance; }
|
||||
if (measure_error < 0.0f) { force = -force; }
|
||||
float fk = force * (stillness * strength) / (distance * (255.0f * 255.0f));
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
value[i] += roundf(diffs[i] * fk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
268
libraries/M5Unified/src/utility/IMU_Class.hpp
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_IMU_CLASS_H__
|
||||
#define __M5_IMU_CLASS_H__
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
#include "imu/IMU_Base.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
enum imu_t
|
||||
{ imu_none,
|
||||
imu_unknown,
|
||||
imu_sh200q,
|
||||
imu_mpu6050,
|
||||
imu_mpu6886,
|
||||
imu_mpu9250,
|
||||
imu_bmi270,
|
||||
};
|
||||
|
||||
class IMU_Class
|
||||
{
|
||||
public:
|
||||
|
||||
struct imu_3d_t
|
||||
{
|
||||
union
|
||||
{
|
||||
float value[3];
|
||||
struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct imu_data_t
|
||||
{
|
||||
uint32_t usec;
|
||||
union
|
||||
{
|
||||
float value[9];
|
||||
imu_3d_t sensor[3];
|
||||
struct
|
||||
{
|
||||
imu_3d_t accel;
|
||||
imu_3d_t gyro;
|
||||
imu_3d_t mag;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
enum axis_t
|
||||
{
|
||||
axis_x_pos = 0,
|
||||
axis_x_neg = 1,
|
||||
axis_y_pos = 2,
|
||||
axis_y_neg = 3,
|
||||
axis_z_pos = 4,
|
||||
axis_z_neg = 5,
|
||||
};
|
||||
|
||||
enum sensor_index_t
|
||||
{
|
||||
sensor_index_accel = 0,
|
||||
sensor_index_gyro = 1,
|
||||
sensor_index_mag = 2,
|
||||
};
|
||||
|
||||
enum sensor_mask_t
|
||||
{
|
||||
sensor_mask_none = 0,
|
||||
sensor_mask_accel = 1 << sensor_index_accel,
|
||||
sensor_mask_gyro = 1 << sensor_index_gyro,
|
||||
sensor_mask_mag = 1 << sensor_index_mag,
|
||||
};
|
||||
|
||||
bool begin(I2C_Class* i2c = nullptr, board_t board = board_t::board_unknown);
|
||||
bool init(I2C_Class* i2c = nullptr) { return begin(i2c); }
|
||||
bool sleep(void);
|
||||
|
||||
void setClock(std::uint32_t freq);
|
||||
|
||||
sensor_mask_t update(void);
|
||||
|
||||
void getImuData(imu_data_t* imu_data);
|
||||
|
||||
const imu_data_t& getImuData(void) { getImuData(&_last_data); return _last_data; }
|
||||
|
||||
// 軸の順序を指定する。デフォルトはX+,Y+,Z+
|
||||
bool setAxisOrder(axis_t axis0, axis_t axis1, axis_t axis2);
|
||||
|
||||
// 軸の順序を右手系で指定する。最初の2軸のみ指定し、3軸目は省略
|
||||
bool setAxisOrderRightHanded(axis_t axis0, axis_t axis1);
|
||||
|
||||
// 軸の順序を左手系で指定する。最初の2軸のみ指定し、3軸目は省略
|
||||
bool setAxisOrderLeftHanded(axis_t axis0, axis_t axis1);
|
||||
|
||||
bool getAccel(float* ax, float* ay, float* az);
|
||||
bool getGyro(float* gx, float* gy, float* gz);
|
||||
bool getMag(float* mx, float* my, float* mz);
|
||||
bool getAccelData(float* ax, float* ay, float* az) { return getAccel(ax, ay, az); }
|
||||
bool getGyroData(float* gx, float* gy, float* gz) { return getGyro(gx, gy, gz); }
|
||||
bool getGyroMag(float* mx, float* my, float* mz) { return getMag(mx, my, mz); }
|
||||
bool getTemp(float *t);
|
||||
|
||||
bool isEnabled(void) const { return _imu != imu_none; }
|
||||
|
||||
imu_t getType(void) const { return _imu; }
|
||||
|
||||
// 実装予定
|
||||
// void getAhrsData(float *pitch, float *roll, float *yaw);
|
||||
|
||||
// 廃止
|
||||
// void setRotation(uint_fast8_t rotation) { _rotation = rotation & 3; };
|
||||
|
||||
bool setINTPinActiveLogic(bool level);
|
||||
|
||||
// 各センサの自動オフセット調整機能の強さを指定する。 0=自動調整なし 1~255=自動調整あり
|
||||
void setCalibration(uint8_t accel_strength, uint8_t gyro_strength, uint8_t mag_strength);
|
||||
|
||||
// 現在のオフセット調整値をNVSに保存する
|
||||
bool saveOffsetToNVS(void);
|
||||
|
||||
// NVSからオフセット調整値を読み込む
|
||||
bool loadOffsetFromNVS(void);
|
||||
|
||||
// オフセットデータをクリアする
|
||||
void clearOffsetData(void);
|
||||
|
||||
// OffsetData は RawData<<16 スケール(16bit固定小数扱い)
|
||||
void setOffsetData(size_t index, int32_t value);
|
||||
|
||||
int32_t getOffsetData(size_t index);
|
||||
|
||||
int16_t getRawData(size_t index);
|
||||
|
||||
IMU_Base* getImuInstancePtr(int idx) const { return _imu_instance[idx].get(); }
|
||||
|
||||
private:
|
||||
|
||||
struct offset_point_t
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t value[3];
|
||||
struct
|
||||
{
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t z;
|
||||
};
|
||||
};
|
||||
union
|
||||
{
|
||||
int32_t prev_value[3];
|
||||
struct
|
||||
{
|
||||
int32_t prev_x;
|
||||
int32_t prev_y;
|
||||
int32_t prev_z;
|
||||
};
|
||||
};
|
||||
union
|
||||
{
|
||||
int32_t avg_value[3];
|
||||
struct
|
||||
{
|
||||
int32_t avg_x;
|
||||
int32_t avg_y;
|
||||
int32_t avg_z;
|
||||
};
|
||||
};
|
||||
float radius;
|
||||
float tolerance;
|
||||
uint16_t noise_level;
|
||||
uint8_t average_shifter; // 前回値への移動平均設定
|
||||
uint8_t stillness; // 移動量の少なさ(変動が大きい時0になり、静止時に255に近付く)
|
||||
uint8_t strength; // キャリブレーションの強さ(ユーザー指定)
|
||||
|
||||
std::uint_fast8_t updateStillness(const IMU_Base::point3d_i16_t& dst);
|
||||
void calibration(void);
|
||||
inline void setValue16(size_t index, int16_t val) { value[index] = val << 16; }
|
||||
inline int32_t getValue16(size_t index) const { return value[index] >> 16; }
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct imu_offset_data_t
|
||||
{
|
||||
union
|
||||
{
|
||||
offset_point_t sensor[3];
|
||||
struct
|
||||
{
|
||||
offset_point_t accel;
|
||||
offset_point_t gyro;
|
||||
offset_point_t mag;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void _update_convert_param(void);
|
||||
void _update_axis_order(void);
|
||||
void _proc_calibration(void);
|
||||
|
||||
// update成功時のマイクロ秒情報
|
||||
uint32_t _latest_micros;
|
||||
|
||||
// センサのインスタンス保持用 ([0]=加速度+ジャイロ / [1]=地磁気)
|
||||
std::unique_ptr<IMU_Base> _imu_instance[2];
|
||||
|
||||
// 生の値を測定値に変換するための補正値
|
||||
IMU_Base::imu_convert_param_t _convert_param;
|
||||
|
||||
// updateで得た最新の生値
|
||||
IMU_Base::imu_raw_data_t _raw_data;
|
||||
|
||||
// オフセット補正用の値
|
||||
imu_offset_data_t _offset_data;
|
||||
|
||||
// 最後に得たセンサ値
|
||||
imu_data_t _last_data;
|
||||
|
||||
// 最後に動きがあった時のusec
|
||||
uint32_t _moving_micros;
|
||||
|
||||
// ユーザ側で任意の軸割当を行うための設定項目
|
||||
uint8_t _assign_axis_x;
|
||||
uint8_t _assign_axis_y;
|
||||
uint8_t _assign_axis_z;
|
||||
|
||||
sensor_mask_t _calibration_flg;
|
||||
|
||||
sensor_mask_t _has_sensor_mask;
|
||||
|
||||
imu_t _imu = imu_t::imu_none;
|
||||
|
||||
// 機種別設定とユーザー設定の両方を反映した後のデータ取得順の値
|
||||
uint32_t _axis_order_3bit_x9;
|
||||
|
||||
enum internal_axisorder_t : uint8_t
|
||||
{
|
||||
axis_invert_x = 1,
|
||||
axis_invert_y = 2,
|
||||
axis_invert_z = 4,
|
||||
axis_order_shift = 3,
|
||||
axis_order_xyz = 0 << axis_order_shift,
|
||||
axis_order_xzy = 1 << axis_order_shift,
|
||||
axis_order_yxz = 2 << axis_order_shift,
|
||||
axis_order_yzx = 3 << axis_order_shift,
|
||||
axis_order_zxy = 4 << axis_order_shift,
|
||||
axis_order_zyx = 5 << axis_order_shift,
|
||||
};
|
||||
|
||||
// 機種別の軸の不一致を統一するための設定項目(システム自動設定)
|
||||
internal_axisorder_t _internal_axisorder_fixed[3] = { (internal_axisorder_t)0, (internal_axisorder_t)0, (internal_axisorder_t)0 };
|
||||
|
||||
// ユーザーによる設定値
|
||||
internal_axisorder_t _internal_axisorder_user = (internal_axisorder_t)0;
|
||||
};
|
||||
|
||||
typedef IMU_Class::imu_3d_t imu_3d_t;
|
||||
typedef IMU_Class::imu_data_t imu_data_t;
|
||||
}
|
||||
#endif
|
||||
75
libraries/M5Unified/src/utility/INA3221_Class.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "INA3221_Class.hpp"
|
||||
|
||||
#if __has_include(<esp_log.h>)
|
||||
#include <esp_log.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
bool INA3221_Class::begin(void)
|
||||
{
|
||||
uint16_t id = readRegister16(0xFF);
|
||||
#if defined (ESP_LOGV)
|
||||
// ESP_LOGV("INA3221", "regFFh:%04x", id);
|
||||
#endif
|
||||
_init = (id == 0x3220);
|
||||
return _init;
|
||||
}
|
||||
|
||||
void INA3221_Class::setShuntRes(uint8_t channel, uint32_t res)
|
||||
{
|
||||
if (channel < INA3221_CH_NUM_MAX) {
|
||||
_shunt_res[channel] = res;
|
||||
}
|
||||
}
|
||||
|
||||
int_fast16_t INA3221_Class::getBusMilliVoltage(uint8_t channel)
|
||||
{
|
||||
int_fast16_t res = 0;
|
||||
if (channel < INA3221_CH_NUM_MAX) {
|
||||
res = (int16_t)readRegister16(INA3221_CH1_BUS_V + (channel * 2));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int32_t INA3221_Class::getShuntMilliVoltage(uint8_t channel)
|
||||
{
|
||||
int32_t res = 0;
|
||||
if (channel < INA3221_CH_NUM_MAX) {
|
||||
res = (int16_t)readRegister16(INA3221_CH1_SHUNT_V + (channel * 2));
|
||||
res *= 5;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
float INA3221_Class::getBusVoltage(uint8_t channel)
|
||||
{
|
||||
return getBusMilliVoltage(channel) / 1000.0f;
|
||||
}
|
||||
|
||||
float INA3221_Class::getShuntVoltage(uint8_t channel)
|
||||
{
|
||||
return getShuntMilliVoltage(channel) / 1000.0f;
|
||||
}
|
||||
|
||||
float INA3221_Class::getCurrent(uint8_t channel)
|
||||
{
|
||||
float res = 0.0f;
|
||||
if (channel < INA3221_CH_NUM_MAX) {
|
||||
res = getShuntVoltage(channel) / _shunt_res[channel];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t INA3221_Class::readRegister16(std::uint8_t addr)
|
||||
{
|
||||
std::uint8_t buf[2] = {0};
|
||||
readRegister(addr, buf, 2);
|
||||
return buf[0] << 8 | buf[1];
|
||||
}
|
||||
}
|
||||
53
libraries/M5Unified/src/utility/INA3221_Class.hpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_INA3221_CLASS_H__
|
||||
#define __M5_INA3221_CLASS_H__
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class INA3221_Class : public I2C_Device
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t INA3221_CH1_SHUNT_V = 0x01;
|
||||
static constexpr uint8_t INA3221_CH1_BUS_V = 0x02;
|
||||
static constexpr uint8_t INA3221_CH2_SHUNT_V = 0x03;
|
||||
static constexpr uint8_t INA3221_CH2_BUS_V = 0x04;
|
||||
static constexpr uint8_t INA3221_CH3_SHUNT_V = 0x05;
|
||||
static constexpr uint8_t INA3221_CH3_BUS_V = 0x06;
|
||||
static constexpr std::uint8_t INA3221_CH_NUM_MAX = 3;
|
||||
|
||||
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x40;
|
||||
|
||||
INA3221_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C)
|
||||
: I2C_Device ( i2c_addr, freq, i2c )
|
||||
{
|
||||
_shunt_res[0] = 10;
|
||||
_shunt_res[1] = 10;
|
||||
_shunt_res[2] = 10;
|
||||
}
|
||||
|
||||
bool begin(void);
|
||||
|
||||
/// Get the voltage of the specified channel.
|
||||
/// @param channel 0=1ch / 1=2ch / 2=3ch / other=invalid
|
||||
/// @return voltage [mV]
|
||||
float getBusVoltage(uint8_t channel);
|
||||
float getShuntVoltage(uint8_t channel);
|
||||
float getCurrent(uint8_t channel);
|
||||
|
||||
int_fast16_t getBusMilliVoltage(uint8_t channel);
|
||||
int32_t getShuntMilliVoltage(uint8_t channel);
|
||||
|
||||
void setShuntRes(uint8_t channel, uint32_t res);
|
||||
|
||||
private:
|
||||
std::size_t readRegister16(std::uint8_t addr);
|
||||
|
||||
uint32_t _shunt_res[INA3221_CH_NUM_MAX];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
121
libraries/M5Unified/src/utility/IP5306_Class.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "IP5306_Class.hpp"
|
||||
|
||||
#if __has_include(<esp_log.h>)
|
||||
#include <esp_log.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
static constexpr std::uint8_t REG_SYS_CTL0 = 0x00;
|
||||
static constexpr std::uint8_t REG_SYS_CTL1 = 0x01;
|
||||
static constexpr std::uint8_t REG_SYS_CTL2 = 0x02;
|
||||
static constexpr std::uint8_t REG_READ0 = 0x70;
|
||||
static constexpr std::uint8_t REG_READ1 = 0x71;
|
||||
static constexpr std::uint8_t REG_READ2 = 0x72;
|
||||
static constexpr std::uint8_t REG_READ3 = 0x77;
|
||||
static constexpr std::uint8_t REG_READ4 = 0x78;
|
||||
static constexpr std::uint8_t REG_CHG_CTL0 = 0x20;
|
||||
static constexpr std::uint8_t REG_CHG_CTL1 = 0x21;
|
||||
static constexpr std::uint8_t REG_CHG_CTL2 = 0x22;
|
||||
static constexpr std::uint8_t REG_CHG_CTL3 = 0x23;
|
||||
static constexpr std::uint8_t REG_CHG_DIG_CTL0 = 0x24;
|
||||
|
||||
static constexpr std::uint8_t BOOST_OUT_BIT = 0x02;
|
||||
|
||||
bool IP5306_Class::begin(void)
|
||||
{
|
||||
std::uint8_t val = 0;
|
||||
writeRegister(0x06, &val, 1); // reg06h WLED flashlight disabled
|
||||
val = 2;
|
||||
_init = writeRegister(0x06, &val, 1);
|
||||
if (_init)
|
||||
{
|
||||
#if defined (ESP_LOGV)
|
||||
// ESP_LOGV("IP5306", "found");
|
||||
#endif
|
||||
}
|
||||
return _init;
|
||||
}
|
||||
|
||||
std::int8_t IP5306_Class::getBatteryLevel(void)
|
||||
{
|
||||
std::uint8_t data;
|
||||
if (readRegister(REG_READ4, &data, 1)) {
|
||||
switch (data >> 4) {
|
||||
case 0x00: return 100;
|
||||
case 0x08: return 75;
|
||||
case 0x0C: return 50;
|
||||
case 0x0E: return 25;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void IP5306_Class::setBatteryCharge(bool enable)
|
||||
{
|
||||
static constexpr std::uint8_t CHARGE_OUT_BIT = 0x10;
|
||||
|
||||
std::uint8_t val = 0;
|
||||
if (readRegister(REG_SYS_CTL0, &val, 1))
|
||||
{
|
||||
writeRegister8(REG_SYS_CTL0, enable ? (val | CHARGE_OUT_BIT) : (val & (~CHARGE_OUT_BIT)));
|
||||
}
|
||||
}
|
||||
|
||||
void IP5306_Class::setChargeCurrent(std::uint16_t max_mA)
|
||||
{
|
||||
max_mA = (max_mA > 50) ? (max_mA - 50) / 100 : 0;
|
||||
if (max_mA > 31) { max_mA = 31; }
|
||||
|
||||
std::uint8_t val = 0;
|
||||
if (readRegister(REG_CHG_DIG_CTL0, &val, 1))
|
||||
{
|
||||
writeRegister8(REG_CHG_DIG_CTL0, (val & 0xE0) + max_mA);
|
||||
}
|
||||
}
|
||||
|
||||
void IP5306_Class::setChargeVoltage(std::uint16_t max_mV)
|
||||
{
|
||||
max_mV = (max_mV / 10);
|
||||
max_mV = (max_mV > 410) ? max_mV - 410 : 0;
|
||||
if (max_mV > 436 - 410) { max_mV = 436 - 410; }
|
||||
static constexpr std::uint8_t table[] =
|
||||
{ 430 - 410 /// 4300mV
|
||||
, 435 - 410 /// 4350mV
|
||||
, 440 - 410 /// 4400mV
|
||||
, 255
|
||||
};
|
||||
size_t i = 0;
|
||||
while (table[i] <= max_mV) { ++i; }
|
||||
|
||||
static constexpr std::uint8_t regdata[4] =
|
||||
{ 0x02 // 4.2v + boost 28mV
|
||||
, 0x05 // 4.3v + boost 14mV
|
||||
, 0x09 // 4.35v + boost 14mV
|
||||
, 0x0D // 4.4v + boost 14mV
|
||||
};
|
||||
writeRegister8(REG_CHG_CTL2, regdata[i]);
|
||||
}
|
||||
|
||||
bool IP5306_Class::isCharging(void)
|
||||
{
|
||||
std::uint8_t val = 0;
|
||||
return (readRegister(0x71, &val, 1)) && (val % 0x0C);
|
||||
}
|
||||
|
||||
bool IP5306_Class::setPowerBoostKeepOn(bool en) {
|
||||
std::uint8_t data;
|
||||
if (readRegister(REG_SYS_CTL0, &data, 1) == true)
|
||||
{
|
||||
data = en ? (data | BOOST_OUT_BIT) : (data & (~BOOST_OUT_BIT));
|
||||
return writeRegister(REG_SYS_CTL0, &data, 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
49
libraries/M5Unified/src/utility/IP5306_Class.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_IP5306_CLASS_H__
|
||||
#define __M5_IP5306_CLASS_H__
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class IP5306_Class : public I2C_Device
|
||||
{
|
||||
public:
|
||||
|
||||
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x75;
|
||||
|
||||
IP5306_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C)
|
||||
: I2C_Device ( i2c_addr, freq, i2c )
|
||||
{}
|
||||
|
||||
bool begin(void);
|
||||
|
||||
/// Get the remaining battery power.
|
||||
/// @return 0-100 level
|
||||
std::int8_t getBatteryLevel(void);
|
||||
|
||||
/// set battery charge enable.
|
||||
/// @param enable true=enable / false=disable
|
||||
void setBatteryCharge(bool enable);
|
||||
|
||||
/// set battery charge current
|
||||
/// @param max_mA milli ampere. (150 - 3150).
|
||||
void setChargeCurrent(std::uint16_t max_mA);
|
||||
|
||||
/// set battery charge voltage
|
||||
/// @param max_mV milli volt. (4200 - 4400).
|
||||
void setChargeVoltage(std::uint16_t max_mV);
|
||||
|
||||
/// Get whether the battery is currently charging or not.
|
||||
bool isCharging(void);
|
||||
|
||||
/// Set whether or not to continue supplying power even at low loads.
|
||||
bool setPowerBoostKeepOn(bool en);
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
164
libraries/M5Unified/src/utility/Log_Class.cpp
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "Log_Class.hpp"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if defined ( M5UNIFIED_PC_BUILD )
|
||||
#include <iostream>
|
||||
static constexpr const uint8_t log_colors_serial[] = { 98, 91, 93, 92, 96, 97, };
|
||||
#else
|
||||
static constexpr const uint8_t log_colors_serial[] = { 38, 31, 33, 32, 36, 37, };
|
||||
#endif
|
||||
static constexpr const uint8_t log_colors_display[] = { 0xFF, 0xE0, 0xFC, 0x18, 0x1F, 0x92, };
|
||||
|
||||
namespace m5
|
||||
{
|
||||
constexpr const char Log_Class::str_crlf[3];
|
||||
|
||||
const char* Log_Class::pathToFileName(const char* path)
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t pos = 0;
|
||||
char * p = (char *)path;
|
||||
while(*p)
|
||||
{
|
||||
i++;
|
||||
if(*p == '/' || *p == '\\')
|
||||
{
|
||||
pos = i;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return path+pos;
|
||||
}
|
||||
|
||||
void Log_Class::operator() (esp_log_level_t level, const char* format, ...)
|
||||
{
|
||||
if (_level_maximum < level) { return; }
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
output(level, true, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Log_Class::printf(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
output(esp_log_level_t::ESP_LOG_NONE, false, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Log_Class::update_level(void)
|
||||
{
|
||||
_level_maximum = std::max(std::max(_log_level[log_target_serial], _log_level[log_target_display]), _log_level[log_target_callback]);
|
||||
}
|
||||
|
||||
void Log_Class::output(esp_log_level_t level, bool suffix, const char* __restrict format, va_list arg)
|
||||
{
|
||||
char loc_buf[64];
|
||||
char * str = loc_buf;
|
||||
va_list copy;
|
||||
va_copy(copy, arg);
|
||||
int len = vsnprintf(str, sizeof(loc_buf), format, copy);
|
||||
va_end(copy);
|
||||
if (len < 0) { return; }
|
||||
if ((size_t)len >= sizeof(loc_buf))
|
||||
{
|
||||
auto tmp = (char*) alloca(len + 1);
|
||||
if (tmp)
|
||||
{
|
||||
str = tmp;
|
||||
len = vsnprintf(str, len+1, format, arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (_log_level[log_target_serial] >= level)
|
||||
{
|
||||
const char* suf = (suffix && _suffix[log_target_serial]) ? _suffix[log_target_serial] : "";
|
||||
|
||||
if (level != ESP_LOG_NONE && _use_color[log_target_serial])
|
||||
{
|
||||
::printf("\033[0;%dm%s\033[0m%s", log_colors_serial[level], str, suf);
|
||||
}
|
||||
else
|
||||
{
|
||||
::printf("%s%s", str, suf);
|
||||
}
|
||||
|
||||
#if defined(M5UNIFIED_PC_BUILD)
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_display && _log_level[log_target_display] >= level)
|
||||
{
|
||||
if (level != ESP_LOG_NONE && _use_color[log_target_display])
|
||||
{
|
||||
auto style = _display->getTextStyle();
|
||||
if (style.fore_rgb888 == style.back_rgb888)
|
||||
{
|
||||
_display->setTextColor(log_colors_display[level]);
|
||||
}
|
||||
else
|
||||
{
|
||||
_display->setTextColor(log_colors_display[level], m5gfx::color_convert<m5gfx::rgb332_t, m5gfx::rgb888_t>(style.back_rgb888));
|
||||
}
|
||||
_display->print(str);
|
||||
_display->setTextStyle(style);
|
||||
}
|
||||
else
|
||||
{
|
||||
_display->print(str);
|
||||
}
|
||||
if (suffix && _suffix[log_target_display]) { _display->print(_suffix[log_target_display]); }
|
||||
}
|
||||
|
||||
if (_log_level[log_target_callback] >= level && _callback != nullptr)
|
||||
{
|
||||
_callback(level, _log_level[log_target_callback], str);
|
||||
if (suffix && _suffix[log_target_callback]) { _callback(level, _log_level[log_target_callback], _suffix[log_target_callback]); }
|
||||
}
|
||||
}
|
||||
|
||||
void Log_Class::setDisplay(M5GFX* target)
|
||||
{
|
||||
_display = target;
|
||||
}
|
||||
|
||||
void Log_Class::dump(const void* a, uint32_t len, esp_log_level_t level)
|
||||
{
|
||||
len = (len + 3) >> 2;
|
||||
if (!len) return;
|
||||
auto addr = reinterpret_cast<uint32_t*>((uintptr_t)a & ~0x03);
|
||||
char buf[84];
|
||||
do {
|
||||
int pos = snprintf(buf, sizeof(buf), "0x%08" PRIxPTR "|", (uintptr_t)addr);
|
||||
// printf("0x%08x|", (uintptr_t)addr);
|
||||
int l = len > 4 ? 4 : len;
|
||||
for (int i = 0; i < l; ++i) {
|
||||
unsigned int tmp = addr[i];
|
||||
pos += snprintf(&buf[pos], (int)sizeof(buf) - pos, " %02x %02x %02x %02x ", tmp&0xFF, (tmp>>8)&0xFF, (tmp>>16)&0xFF,(tmp>>24));
|
||||
}
|
||||
for (int i = l; i < 4; ++i) {
|
||||
pos += snprintf(&buf[pos], (int)sizeof(buf) - pos, " __ __ __ __ ");
|
||||
}
|
||||
buf[pos] = '|';
|
||||
++pos;
|
||||
for (int i = 0; i < l; ++i) {
|
||||
unsigned int tmp = addr[i];
|
||||
pos += snprintf(&buf[pos], (int)sizeof(buf) - pos, "%c%c%c%c"
|
||||
, std::max(' ', (char)tmp)
|
||||
, std::max(' ', (char)(tmp>>8))
|
||||
, std::max(' ', (char)(tmp>>16))
|
||||
, std::max(' ', (char)(tmp>>24)));
|
||||
}
|
||||
buf[pos] = 0;
|
||||
operator()(level, buf);
|
||||
addr += l;
|
||||
len -= l;
|
||||
} while (len);
|
||||
}
|
||||
}
|
||||
128
libraries/M5Unified/src/utility/Log_Class.hpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_Log_Class_H__
|
||||
#define __M5_Log_Class_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#if !defined ( M5UNIFIED_PC_BUILD )
|
||||
#include <esp_log.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <functional>
|
||||
|
||||
#include <M5GFX.h>
|
||||
|
||||
/// Output log with source info.
|
||||
#ifndef M5UNIFIED_LOG_FORMAT
|
||||
#define M5UNIFIED_LOG_FORMAT(letter, format) "[%6u][" #letter "][%s:%u] %s(): " format, m5gfx::millis(), m5::Log_Class::pathToFileName(__FILE__), __LINE__, __FUNCTION__
|
||||
#endif
|
||||
|
||||
/// Output Error log with source info.
|
||||
#define M5_LOGE(format, ...) M5.Log(ESP_LOG_ERROR , M5UNIFIED_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
|
||||
/// Output Warn log with source info.
|
||||
#define M5_LOGW(format, ...) M5.Log(ESP_LOG_WARN , M5UNIFIED_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
|
||||
/// Output Info log with source info.
|
||||
#define M5_LOGI(format, ...) M5.Log(ESP_LOG_INFO , M5UNIFIED_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
|
||||
/// Output Debug log with source info.
|
||||
#define M5_LOGD(format, ...) M5.Log(ESP_LOG_DEBUG , M5UNIFIED_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
|
||||
/// Output Verbose log with source info.
|
||||
#define M5_LOGV(format, ...) M5.Log(ESP_LOG_VERBOSE, M5UNIFIED_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
|
||||
namespace m5
|
||||
{
|
||||
enum log_target_t : uint8_t
|
||||
{
|
||||
log_target_serial,
|
||||
log_target_display,
|
||||
log_target_callback,
|
||||
log_target_max,
|
||||
};
|
||||
|
||||
class Log_Class
|
||||
{
|
||||
public:
|
||||
/// Output log.
|
||||
void operator() (esp_log_level_t level, const char* format, ...);
|
||||
|
||||
/// Output text in a specified format.
|
||||
/// @attention Output regardless of log level setting.
|
||||
void printf(const char* format, ...);
|
||||
|
||||
/// Output text.
|
||||
/// @attention Output regardless of log level setting.
|
||||
void print(const char* string) { return printf("%s", string); }
|
||||
|
||||
/// Output text with line feeds.
|
||||
/// @attention Output regardless of log level setting.
|
||||
void println(const char* string) { return printf("%s\n", string); }
|
||||
|
||||
/// Output line feeds.
|
||||
/// @attention Output regardless of log level setting.
|
||||
void println(void) { return printf(str_crlf); }
|
||||
|
||||
/// Set whether or not to change the color for each log level.
|
||||
void setEnableColor(log_target_t target, bool enable) { if (target < log_target_max) { _use_color[target] = enable; } }
|
||||
|
||||
/// Get whether or not to change the color for each log level.
|
||||
bool getEnableColor(log_target_t target) const { return _use_color[target]; }
|
||||
|
||||
/// Set log level.
|
||||
void setLogLevel(log_target_t target, esp_log_level_t level) { if (target < log_target_max) { _log_level[target] = level; update_level(); } }
|
||||
|
||||
/// Get log level for serial output.
|
||||
esp_log_level_t getLogLevel(log_target_t target) const { return _log_level[target]; }
|
||||
|
||||
/// Set the text to be added to the end of the log.
|
||||
void setSuffix(log_target_t target, const char* suffix) { if (target < log_target_max) { _suffix[target] = suffix; } }
|
||||
|
||||
/// set logging callback function / functor .
|
||||
/// @param function Pointer to a user-defined function that takes three arguments: esp_log_level_t , bool, const char*.
|
||||
void setCallback(std::function<void(esp_log_level_t log_level, bool use_color, const char* log_text)> function) { _callback = function; };
|
||||
|
||||
/// Set the display to show logs.
|
||||
/// @param target target display.
|
||||
void setDisplay(M5GFX* target);
|
||||
|
||||
/// Set the display to show logs.
|
||||
/// @param target target display.
|
||||
void setDisplay(M5GFX& target) { setDisplay(&target); }
|
||||
|
||||
|
||||
void dump(const void* addr, uint32_t len, esp_log_level_t level = esp_log_level_t::ESP_LOG_NONE);
|
||||
|
||||
/// not for use.
|
||||
static const char* pathToFileName(const char * path);
|
||||
|
||||
private:
|
||||
M5GFX* _display = nullptr;
|
||||
|
||||
static constexpr const char str_crlf[3] = "\r\n";
|
||||
static constexpr const char *str_lf = &str_crlf[1];
|
||||
|
||||
void output(esp_log_level_t level, bool suffix, const char* __restrict format, va_list arg);
|
||||
void update_level(void);
|
||||
|
||||
std::function<void(esp_log_level_t log_level, bool use_color, const char* log_text)> _callback;
|
||||
|
||||
#if defined ( CORE_DEBUG_LEVEL )
|
||||
esp_log_level_t _level_maximum = (esp_log_level_t)CORE_DEBUG_LEVEL;
|
||||
#elif defined ( CONFIG_LOG_DEFAULT_LEVEL )
|
||||
esp_log_level_t _level_maximum = (esp_log_level_t)CONFIG_LOG_DEFAULT_LEVEL;
|
||||
#else
|
||||
esp_log_level_t _level_maximum = esp_log_level_t::ESP_LOG_VERBOSE;
|
||||
#endif
|
||||
esp_log_level_t _log_level[log_target_max] = { _level_maximum, _level_maximum, _level_maximum };
|
||||
|
||||
const char* _suffix[log_target_max] = { str_lf, str_lf, str_crlf };
|
||||
|
||||
bool _use_color[log_target_max] = { true, true, true };
|
||||
};
|
||||
}
|
||||
#endif
|
||||
80
libraries/M5Unified/src/utility/M5Timer.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "M5Timer.h"
|
||||
#include <M5GFX.h>
|
||||
|
||||
M5Timer::M5Timer(void) {
|
||||
_enable_count = 0;
|
||||
for (uint_fast8_t i = 0; i < MAX_TIMERS; i++) {
|
||||
_timer_info[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void M5Timer::timer_info_t::clear(void) {
|
||||
_callback = 0;
|
||||
_enabled = false;
|
||||
_interval = 0;
|
||||
_remain = 0;
|
||||
}
|
||||
|
||||
void M5Timer::timer_info_t::set(uint32_t interval_msec, timer_callback cb, uint32_t times) {
|
||||
_callback = cb;
|
||||
_interval = interval_msec;
|
||||
_remain = times;
|
||||
_enabled = true;
|
||||
_prev_msec = m5gfx::millis();
|
||||
}
|
||||
|
||||
bool M5Timer::timer_info_t::run(uint32_t current_msec) {
|
||||
if (_callback != nullptr && current_msec - _prev_msec >= _interval) {
|
||||
_prev_msec += _interval;
|
||||
|
||||
if (_enabled) {
|
||||
_callback();
|
||||
// Check the remaining number of executions; if 0, assume unlimited.
|
||||
if (_remain != 0) {
|
||||
return --_remain;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void M5Timer::run(void) {
|
||||
auto msec = m5gfx::millis();
|
||||
|
||||
for (auto &t : _timer_info) {
|
||||
if (!t.getCallback()) { continue; }
|
||||
if (!t.run(msec)) {
|
||||
t.clear();
|
||||
_enable_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int_fast8_t M5Timer::setTimer(uint32_t interval, timer_callback cb, uint32_t times) {
|
||||
if (cb == nullptr || _enable_count >= MAX_TIMERS) {
|
||||
return -1;
|
||||
}
|
||||
for (uint_fast8_t i = 0; i < MAX_TIMERS; i++) {
|
||||
if (_timer_info[i].getCallback()) { continue; }
|
||||
_timer_info[i].set(interval, cb, times);
|
||||
_enable_count++;
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void M5Timer::deleteTimer(int_fast8_t id) {
|
||||
if ((uint_fast8_t)id < MAX_TIMERS && _timer_info[id].getCallback() != nullptr) {
|
||||
_timer_info[id].clear();
|
||||
_enable_count--;
|
||||
}
|
||||
}
|
||||
|
||||
void M5Timer::restartTimer(int_fast8_t id) {
|
||||
if ((uint_fast8_t)id < MAX_TIMERS) {
|
||||
_timer_info[id].setPreviousMsec(m5gfx::millis());
|
||||
}
|
||||
}
|
||||
124
libraries/M5Unified/src/utility/M5Timer.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_Timer_H__
|
||||
#define __M5_Timer_H__
|
||||
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef std::function<void(void)> timer_callback;
|
||||
|
||||
class M5Timer {
|
||||
public:
|
||||
/// maximum number of timers
|
||||
static constexpr const uint32_t MAX_TIMERS = 10;
|
||||
|
||||
/// constructor
|
||||
M5Timer(void);
|
||||
|
||||
/// this function must be called inside loop()
|
||||
void run(void);
|
||||
|
||||
/// Call function every interval [msec] for specified times.
|
||||
/// @param interval_msec interval [msec]
|
||||
/// @param function target function.
|
||||
/// @return -1 = failed / 0 or more = target timer id.
|
||||
int_fast8_t setTimer(uint32_t interval_msec, timer_callback function, uint32_t times);
|
||||
|
||||
/// Call function every interval [msec].
|
||||
/// @param interval_msec interval [msec]
|
||||
/// @param function target function.
|
||||
/// @return -1 = failed / 0 or more = target timer id.
|
||||
inline int_fast8_t setInterval(uint32_t interval_msec, timer_callback function) {
|
||||
return setTimer(interval_msec, function, 0);
|
||||
}
|
||||
|
||||
/// Call function once after interval [msec].
|
||||
/// @param interval_msec interval [msec]
|
||||
/// @param function target function.
|
||||
/// @return -1 = failed / 0 or more = target timer id.
|
||||
inline int_fast8_t setTimeout(uint32_t interval_msec, timer_callback function) {
|
||||
return setTimer(interval_msec, function, 1);
|
||||
}
|
||||
|
||||
/// Destroy the specified timer.
|
||||
/// @param id target timer id.
|
||||
void deleteTimer(int_fast8_t id);
|
||||
|
||||
/// Restart the specified timer.
|
||||
/// @param id target timer id.
|
||||
void restartTimer(int_fast8_t id);
|
||||
|
||||
/// Enables the specified timer.
|
||||
/// @param id target timer id.
|
||||
void enable(int_fast8_t id) {
|
||||
if ((uint_fast8_t)id < MAX_TIMERS) {
|
||||
_timer_info[id].setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the specified timer.
|
||||
/// @param id target timer id.
|
||||
void disable(int_fast8_t id) {
|
||||
if ((uint_fast8_t)id < MAX_TIMERS) {
|
||||
_timer_info[id].setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables the specified timer if it is currently disabled, and vice versa.
|
||||
/// @param id target timer id.
|
||||
void toggle(int_fast8_t id) {
|
||||
if ((uint_fast8_t)id < MAX_TIMERS) {
|
||||
_timer_info[id].toggle();
|
||||
}
|
||||
}
|
||||
|
||||
/// gets true if the specified timer is enabled
|
||||
/// @param id target timer id.
|
||||
/// @return false=disabled / true=enabled.
|
||||
bool isEnabled(int_fast8_t id) const {
|
||||
return ((uint_fast8_t)id < MAX_TIMERS) ? _timer_info[id].getEnabled() : false;
|
||||
}
|
||||
|
||||
/// gets the number of used timers.
|
||||
/// @return number of used timers.
|
||||
uint_fast8_t getNumTimers(void) const {
|
||||
return _enable_count;
|
||||
}
|
||||
|
||||
/// gets the number of available timers.
|
||||
/// @return number of available timers.
|
||||
uint_fast8_t getNumAvailableTimers(void) const {
|
||||
return MAX_TIMERS - _enable_count;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class timer_info_t {
|
||||
public:
|
||||
void set(uint32_t interval_msec, timer_callback function, uint32_t times);
|
||||
bool run(uint32_t interval_msec);
|
||||
void clear(void);
|
||||
|
||||
inline timer_callback getCallback(void) const { return _callback; }
|
||||
inline bool getEnabled(void) const { return _enabled; }
|
||||
inline void setEnabled(bool flg) { _enabled = flg; }
|
||||
inline void toggle(void) { _enabled = !_enabled; }
|
||||
inline void setPreviousMsec(uint32_t msec) { _prev_msec = msec; }
|
||||
|
||||
private:
|
||||
timer_callback _callback = nullptr;
|
||||
uint32_t _prev_msec = 0;
|
||||
uint32_t _interval = 0;
|
||||
uint32_t _remain = 0;
|
||||
bool _enabled = false;
|
||||
};
|
||||
|
||||
timer_info_t _timer_info[MAX_TIMERS];
|
||||
|
||||
uint8_t _enable_count = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
783
libraries/M5Unified/src/utility/Mic_Class.cpp
Normal file
|
|
@ -0,0 +1,783 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "Mic_Class.hpp"
|
||||
|
||||
#include "../M5Unified.hpp"
|
||||
|
||||
#if __has_include (<esp_idf_version.h>)
|
||||
#include <esp_idf_version.h>
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#define NON_BREAK ;[[fallthrough]];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __has_include (<soc/pcr_struct.h>)
|
||||
#include <soc/pcr_struct.h>
|
||||
#endif
|
||||
|
||||
#ifndef NON_BREAK
|
||||
#define NON_BREAK ;
|
||||
#endif
|
||||
|
||||
#if __has_include(<sdkconfig.h>)
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_log.h>
|
||||
#include <math.h>
|
||||
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32C3 ) || defined ( CONFIG_IDF_TARGET_ESP32C6 ) || defined ( CONFIG_IDF_TARGET_ESP32S3 ) || defined ( CONFIG_IDF_TARGET_ESP32P4 )
|
||||
#if __has_include(<driver/i2s_std.h>)
|
||||
#if __has_include(<hal/i2s_ll.h>)
|
||||
#include <hal/i2s_ll.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
#if __has_include (<hal/adc_ll.h>)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
#include <hal/adc_ll.h>
|
||||
#include <driver/rtc_io.h>
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
#if defined ( ESP_PLATFORM )
|
||||
#if defined (ESP_IDF_VERSION_VAL)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
#define COMM_FORMAT_I2S (I2S_COMM_FORMAT_STAND_I2S)
|
||||
#define COMM_FORMAT_MSB (I2S_COMM_FORMAT_STAND_MSB)
|
||||
#endif
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 3)
|
||||
#define SAMPLE_RATE_TYPE uint32_t
|
||||
#endif
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define MIC_CLASS_ADC_WIDTH_BITS ADC_WIDTH_BIT_12
|
||||
#define MIC_CLASS_ADC_ATTEN_DB ADC_ATTEN_DB_12
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef COMM_FORMAT_I2S
|
||||
#define COMM_FORMAT_I2S (I2S_COMM_FORMAT_I2S)
|
||||
#define COMM_FORMAT_MSB (I2S_COMM_FORMAT_I2S_MSB)
|
||||
#endif
|
||||
|
||||
#ifndef SAMPLE_RATE_TYPE
|
||||
#define SAMPLE_RATE_TYPE int
|
||||
#endif
|
||||
|
||||
#ifndef MIC_CLASS_ADC_WIDTH_BITS
|
||||
#define MIC_CLASS_ADC_WIDTH_BITS ADC_WIDTH_12Bit
|
||||
#define MIC_CLASS_ADC_ATTEN_DB ADC_ATTEN_11db
|
||||
#endif
|
||||
|
||||
|
||||
uint32_t Mic_Class::_calc_rec_rate(void) const
|
||||
{
|
||||
int rate = (_cfg.sample_rate * _cfg.over_sampling);
|
||||
return rate;
|
||||
}
|
||||
|
||||
#if __has_include(<driver/i2s_std.h>)
|
||||
|
||||
static i2s_chan_handle_t _i2s_handle[SOC_I2S_NUM] = { nullptr, };
|
||||
|
||||
static esp_err_t _i2s_start(i2s_port_t port) {
|
||||
return i2s_channel_enable(_i2s_handle[port]);
|
||||
}
|
||||
static esp_err_t _i2s_stop(i2s_port_t port)
|
||||
{
|
||||
return i2s_channel_disable(_i2s_handle[port]);
|
||||
}
|
||||
static esp_err_t _i2s_read(i2s_port_t port, void* buf, size_t len, size_t* result, TickType_t tick) {
|
||||
return i2s_channel_read(_i2s_handle[port], buf, len, result, tick);
|
||||
}
|
||||
static esp_err_t _i2s_driver_uninstall(i2s_port_t port)
|
||||
{
|
||||
if (_i2s_handle[port] != nullptr) {
|
||||
auto res = i2s_del_channel(_i2s_handle[port]);
|
||||
_i2s_handle[port] = nullptr;
|
||||
return res;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
|
||||
struct adc_digi_pattern_table_t {
|
||||
union {
|
||||
struct {
|
||||
uint8_t atten: 2; /*!< ADC sampling voltage attenuation configuration. Modification of attenuation affects the range of measurements.
|
||||
0: measurement range 0 - 800mV,
|
||||
1: measurement range 0 - 1100mV,
|
||||
2: measurement range 0 - 1350mV,
|
||||
3: measurement range 0 - 2600mV. */
|
||||
uint8_t bit_width: 2; /*!< ADC resolution.
|
||||
- 0: 9 bit;
|
||||
- 1: 10 bit;
|
||||
- 2: 11 bit;
|
||||
- 3: 12 bit. */
|
||||
int8_t channel: 4; /*!< ADC channel index. */
|
||||
};
|
||||
uint8_t val; /*!<Raw data value */
|
||||
};
|
||||
};
|
||||
|
||||
static esp_err_t _i2s_set_adc(i2s_port_t port, gpio_num_t pin_data_in) {
|
||||
if (port == I2S_NUM_0)
|
||||
{ /// レジスタを操作してADCモードの設定を有効にする ;
|
||||
if (((size_t)pin_data_in) > 39) { return ESP_FAIL; }
|
||||
static constexpr const uint8_t adc_table[] =
|
||||
{
|
||||
ADC2_GPIO0_CHANNEL , // GPIO 0
|
||||
255 ,
|
||||
ADC2_GPIO2_CHANNEL , // GPIO 2
|
||||
255 ,
|
||||
ADC2_GPIO4_CHANNEL , // GPIO 4
|
||||
255, 255, 255, 255, 255, 255, 255,
|
||||
ADC2_GPIO12_CHANNEL , // GPIO 12
|
||||
ADC2_GPIO13_CHANNEL , // GPIO 13
|
||||
ADC2_GPIO14_CHANNEL , // GPIO 14
|
||||
ADC2_GPIO15_CHANNEL , // GPIO 15
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
ADC2_GPIO25_CHANNEL , // GPIO 25
|
||||
ADC2_GPIO26_CHANNEL , // GPIO 26
|
||||
ADC2_GPIO27_CHANNEL , // GPIO 27
|
||||
255, 255, 255, 255,
|
||||
ADC1_GPIO32_CHANNEL , // GPIO 32
|
||||
ADC1_GPIO33_CHANNEL , // GPIO 33
|
||||
ADC1_GPIO34_CHANNEL , // GPIO 34
|
||||
ADC1_GPIO35_CHANNEL , // GPIO 35
|
||||
ADC1_GPIO36_CHANNEL , // GPIO 36
|
||||
ADC1_GPIO37_CHANNEL , // GPIO 37
|
||||
ADC1_GPIO38_CHANNEL , // GPIO 38
|
||||
ADC1_GPIO39_CHANNEL , // GPIO 39
|
||||
};
|
||||
int adc_ch = adc_table[pin_data_in];
|
||||
if (adc_ch == 255) { return ESP_FAIL; }
|
||||
|
||||
adc_unit_t unit = pin_data_in >= 32 ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||
|
||||
adc_oneshot_ll_set_output_bits(unit, ADC_BITWIDTH_12);
|
||||
|
||||
{
|
||||
rtc_gpio_init(pin_data_in);
|
||||
rtc_gpio_set_direction(pin_data_in, RTC_GPIO_MODE_DISABLED);
|
||||
rtc_gpio_pullup_dis(pin_data_in);
|
||||
rtc_gpio_pulldown_dis(pin_data_in);
|
||||
|
||||
adc_ll_digi_set_fsm_time(8/*ADC_HAL_FSM_RSTB_WAIT_DEFAULT*/,
|
||||
16/*ADC_HAL_FSM_START_WAIT_DEFAULT*/,
|
||||
100/*ADC_HAL_FSM_STANDBY_WAIT_DEFAULT*/);
|
||||
adc_ll_set_sample_cycle(2/*ADC_HAL_SAMPLE_CYCLE_DEFAULT*/);
|
||||
adc_ll_pwdet_set_cct(4/*ADC_HAL_PWDET_CCT_DEFAULT*/);
|
||||
adc_ll_digi_set_clk_div(16/*ADC_HAL_DIGI_SAR_CLK_DIV_DEFAULT*/);
|
||||
adc_ll_digi_output_invert(unit, ADC_LL_DIGI_DATA_INVERT_DEFAULT(unit));
|
||||
adc_ll_digi_set_convert_mode((unit == ADC_UNIT_1) ? ADC_LL_DIGI_CONV_ONLY_ADC1 : ADC_LL_DIGI_CONV_ONLY_ADC2);
|
||||
adc_ll_set_controller(unit, ADC_LL_CTRL_DIG);
|
||||
adc_ll_digi_clear_pattern_table(unit);
|
||||
adc_ll_digi_set_pattern_table_len(unit, 1);
|
||||
adc_digi_pattern_table_t pattern;
|
||||
pattern.atten = ADC_ATTEN_DB_12;
|
||||
pattern.bit_width = 3;
|
||||
pattern.channel = adc_ch;
|
||||
{
|
||||
uint8_t index = 0;
|
||||
uint8_t offset = 0;
|
||||
auto saradc_tab = (adc_ch == ADC_UNIT_1)
|
||||
? SYSCON.saradc_sar1_patt_tab
|
||||
: SYSCON.saradc_sar2_patt_tab;
|
||||
uint32_t tab = saradc_tab[index]; // Read old register value
|
||||
tab &= (~(0xFF000000 >> offset)); // clear old data
|
||||
tab |= ((uint32_t)pattern.val << 24) >> offset; // Fill in the new data
|
||||
saradc_tab[index] = tab; // Write back
|
||||
}
|
||||
SYSCON.saradc_ctrl.data_sar_sel = 0; // ADC_DIGI_FORMAT_12BIT;
|
||||
adc_ll_digi_convert_limit_enable(ADC_LL_DEFAULT_CONV_LIMIT_EN);
|
||||
adc_ll_digi_set_convert_limit_num(ADC_LL_DEFAULT_CONV_LIMIT_NUM);
|
||||
adc_ll_digi_set_data_source(1); //ADC_I2S_DATA_SRC_ADC;
|
||||
}
|
||||
I2S0.conf2.lcd_en = true;
|
||||
I2S0.conf.rx_right_first = 0;
|
||||
I2S0.conf.rx_msb_shift = 0;
|
||||
I2S0.conf.rx_mono = 0;
|
||||
I2S0.conf.rx_short_sync = 0;
|
||||
I2S0.fifo_conf.rx_fifo_mod = true;
|
||||
I2S0.conf_chan.rx_chan_mod = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static esp_err_t _i2s_start(i2s_port_t port)
|
||||
{
|
||||
return i2s_start(port);
|
||||
}
|
||||
static esp_err_t _i2s_stop(i2s_port_t port)
|
||||
{
|
||||
return i2s_stop(port);
|
||||
}
|
||||
static esp_err_t _i2s_read(i2s_port_t port, void* buf, size_t len, size_t* result, TickType_t tick)
|
||||
{
|
||||
return i2s_read(port, buf, len, result, tick);
|
||||
}
|
||||
static esp_err_t _i2s_driver_uninstall(i2s_port_t port)
|
||||
{
|
||||
return i2s_driver_uninstall(port);
|
||||
}
|
||||
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
static esp_err_t _i2s_set_adc(i2s_port_t port, gpio_num_t pin_data_in) {
|
||||
if (port == I2S_NUM_0)
|
||||
{ /// レジスタを操作してADCモードの設定を有効にする ;
|
||||
if (((size_t)pin_data_in) > 39) { return ESP_FAIL; }
|
||||
static constexpr const uint8_t adc_table[] =
|
||||
{
|
||||
ADC2_CHANNEL_1 , // GPIO 0
|
||||
255 ,
|
||||
ADC2_CHANNEL_2 , // GPIO 2
|
||||
255 ,
|
||||
ADC2_CHANNEL_0 , // GPIO 4
|
||||
255, 255, 255, 255, 255, 255, 255,
|
||||
ADC2_CHANNEL_5 , // GPIO 12
|
||||
ADC2_CHANNEL_4 , // GPIO 13
|
||||
ADC2_CHANNEL_6 , // GPIO 14
|
||||
ADC2_CHANNEL_3 , // GPIO 15
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
ADC2_CHANNEL_8 , // GPIO 25
|
||||
ADC2_CHANNEL_9 , // GPIO 26
|
||||
ADC2_CHANNEL_7 , // GPIO 27
|
||||
255, 255, 255, 255,
|
||||
ADC1_CHANNEL_4 , // GPIO 32
|
||||
ADC1_CHANNEL_5 , // GPIO 33
|
||||
ADC1_CHANNEL_6 , // GPIO 34
|
||||
ADC1_CHANNEL_7 , // GPIO 35
|
||||
ADC1_CHANNEL_0 , // GPIO 36
|
||||
ADC1_CHANNEL_1 , // GPIO 37
|
||||
ADC1_CHANNEL_2 , // GPIO 38
|
||||
ADC1_CHANNEL_3 , // GPIO 39
|
||||
};
|
||||
int adc_ch = adc_table[pin_data_in];
|
||||
if (adc_ch == 255) { return ESP_FAIL; }
|
||||
|
||||
adc_unit_t unit = pin_data_in >= 32 ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||
adc_set_data_width(unit, MIC_CLASS_ADC_WIDTH_BITS);
|
||||
esp_err_t err = i2s_set_adc_mode(unit, (adc1_channel_t)adc_ch);
|
||||
if (unit == ADC_UNIT_1)
|
||||
{
|
||||
adc1_config_channel_atten((adc1_channel_t)adc_ch, MIC_CLASS_ADC_ATTEN_DB);
|
||||
}
|
||||
else
|
||||
{
|
||||
adc2_config_channel_atten((adc2_channel_t)adc_ch, MIC_CLASS_ADC_ATTEN_DB);
|
||||
}
|
||||
I2S0.conf2.lcd_en = true;
|
||||
I2S0.conf.rx_right_first = 0;
|
||||
I2S0.conf.rx_msb_shift = 0;
|
||||
I2S0.conf.rx_mono = 0;
|
||||
I2S0.conf.rx_short_sync = 0;
|
||||
I2S0.fifo_conf.rx_fifo_mod = true;
|
||||
I2S0.conf_chan.rx_chan_mod = true;
|
||||
return err;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
esp_err_t Mic_Class::_setup_i2s(void)
|
||||
{
|
||||
if (_cfg.pin_data_in < 0) { return ESP_FAIL; }
|
||||
#if __has_include(<driver/i2s_std.h>)
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(_cfg.i2s_port, I2S_ROLE_MASTER);
|
||||
chan_cfg.dma_desc_num = _cfg.dma_buf_count;
|
||||
chan_cfg.dma_frame_num = _cfg.dma_buf_len;
|
||||
_i2s_driver_uninstall(_cfg.i2s_port);
|
||||
esp_err_t err = i2s_new_channel(&chan_cfg, nullptr, &_i2s_handle[_cfg.i2s_port]);
|
||||
if (err != ESP_OK) { return err; }
|
||||
|
||||
#if SOC_I2S_SUPPORTS_PDM_RX
|
||||
if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
|
||||
i2s_pdm_rx_config_t i2s_config;
|
||||
memset(&i2s_config, 0, sizeof(i2s_pdm_rx_config_t));
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32P4 )
|
||||
i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_DEFAULT;
|
||||
#else
|
||||
i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_PLL_160M;
|
||||
#endif
|
||||
i2s_config.clk_cfg.sample_rate_hz = 48000; // dummy setting
|
||||
i2s_config.clk_cfg.mclk_multiple = i2s_mclk_multiple_t::I2S_MCLK_MULTIPLE_128; // dummy setting
|
||||
i2s_config.slot_cfg.data_bit_width = i2s_data_bit_width_t::I2S_DATA_BIT_WIDTH_16BIT;
|
||||
i2s_config.slot_cfg.slot_bit_width = i2s_slot_bit_width_t::I2S_SLOT_BIT_WIDTH_16BIT;
|
||||
i2s_config.slot_cfg.slot_mode = (_cfg.stereo) ? i2s_slot_mode_t::I2S_SLOT_MODE_STEREO : i2s_slot_mode_t::I2S_SLOT_MODE_MONO;
|
||||
i2s_config.slot_cfg.slot_mask = (_cfg.stereo) ? i2s_pdm_slot_mask_t::I2S_PDM_SLOT_BOTH : (_cfg.left_channel ? i2s_pdm_slot_mask_t::I2S_PDM_SLOT_LEFT : i2s_pdm_slot_mask_t::I2S_PDM_SLOT_RIGHT);
|
||||
i2s_config.gpio_cfg.clk = (gpio_num_t)_cfg.pin_ws;
|
||||
i2s_config.gpio_cfg.din = (gpio_num_t)_cfg.pin_data_in;
|
||||
err = i2s_channel_init_pdm_rx_mode(_i2s_handle[_cfg.i2s_port], &i2s_config);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2s_std_config_t i2s_config;
|
||||
memset(&i2s_config, 0, sizeof(i2s_std_config_t));
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32P4 )
|
||||
i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_DEFAULT;
|
||||
#else
|
||||
i2s_config.clk_cfg.clk_src = i2s_clock_src_t::I2S_CLK_SRC_PLL_160M;
|
||||
#endif
|
||||
i2s_config.clk_cfg.sample_rate_hz = 48000; // dummy setting
|
||||
i2s_config.clk_cfg.mclk_multiple = i2s_mclk_multiple_t::I2S_MCLK_MULTIPLE_128; // dummy setting
|
||||
i2s_config.slot_cfg.data_bit_width = i2s_data_bit_width_t::I2S_DATA_BIT_WIDTH_16BIT;
|
||||
i2s_config.slot_cfg.slot_bit_width = i2s_slot_bit_width_t::I2S_SLOT_BIT_WIDTH_16BIT;
|
||||
i2s_config.slot_cfg.slot_mode = (_cfg.stereo) ? i2s_slot_mode_t::I2S_SLOT_MODE_STEREO : i2s_slot_mode_t::I2S_SLOT_MODE_MONO;
|
||||
i2s_config.slot_cfg.slot_mask = (_cfg.stereo) ? i2s_std_slot_mask_t::I2S_STD_SLOT_BOTH : (_cfg.left_channel ? i2s_std_slot_mask_t::I2S_STD_SLOT_LEFT : i2s_std_slot_mask_t::I2S_STD_SLOT_RIGHT);
|
||||
i2s_config.slot_cfg.ws_width = 16;
|
||||
i2s_config.slot_cfg.bit_shift = true;
|
||||
#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2
|
||||
i2s_config.slot_cfg.msb_right = false;
|
||||
#else
|
||||
i2s_config.slot_cfg.left_align = true;
|
||||
i2s_config.slot_cfg.big_endian = false;
|
||||
i2s_config.slot_cfg.bit_order_lsb = false;
|
||||
#endif
|
||||
i2s_config.gpio_cfg.bclk = (gpio_num_t)_cfg.pin_bck;
|
||||
i2s_config.gpio_cfg.ws = (gpio_num_t)_cfg.pin_ws;
|
||||
i2s_config.gpio_cfg.dout = (gpio_num_t)I2S_PIN_NO_CHANGE;
|
||||
i2s_config.gpio_cfg.mclk = (gpio_num_t)_cfg.pin_mck;
|
||||
i2s_config.gpio_cfg.din = (gpio_num_t)_cfg.pin_data_in;
|
||||
err = i2s_channel_init_std_mode(_i2s_handle[_cfg.i2s_port], &i2s_config);
|
||||
}
|
||||
|
||||
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
if (_cfg.use_adc)
|
||||
{
|
||||
err = _i2s_set_adc(_cfg.i2s_port, (gpio_num_t)_cfg.pin_data_in);
|
||||
}
|
||||
#endif
|
||||
|
||||
return err;
|
||||
|
||||
#else
|
||||
i2s_config_t i2s_config;
|
||||
memset(&i2s_config, 0, sizeof(i2s_config_t));
|
||||
i2s_config.mode = (i2s_mode_t)( I2S_MODE_MASTER | I2S_MODE_RX );
|
||||
i2s_config.sample_rate = 48000; // dummy setting.
|
||||
i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
|
||||
i2s_config.channel_format = _cfg.stereo ? I2S_CHANNEL_FMT_RIGHT_LEFT : _cfg.left_channel ? I2S_CHANNEL_FMT_ONLY_LEFT : I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||
i2s_config.communication_format = (i2s_comm_format_t)( COMM_FORMAT_I2S );
|
||||
i2s_config.dma_buf_count = _cfg.dma_buf_count;
|
||||
i2s_config.dma_buf_len = _cfg.dma_buf_len;
|
||||
|
||||
i2s_pin_config_t pin_config;
|
||||
memset(&pin_config, ~0u, sizeof(i2s_pin_config_t)); /// all pin set to I2S_PIN_NO_CHANGE
|
||||
#if defined (ESP_IDF_VERSION_VAL)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 1)
|
||||
pin_config.mck_io_num = _cfg.pin_mck;
|
||||
#endif
|
||||
#endif
|
||||
pin_config.bck_io_num = _cfg.pin_bck;
|
||||
pin_config.ws_io_num = _cfg.pin_ws;
|
||||
pin_config.data_in_num = _cfg.pin_data_in;
|
||||
|
||||
if (_cfg.pin_bck < 0 || _cfg.pin_ws < 0) {
|
||||
i2s_config.mode = (i2s_mode_t)( I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM );
|
||||
i2s_config.communication_format = i2s_comm_format_t::I2S_COMM_FORMAT_STAND_PCM_SHORT;
|
||||
}
|
||||
esp_err_t err;
|
||||
if (ESP_OK != (err = i2s_driver_install(_cfg.i2s_port, &i2s_config, 0, nullptr)))
|
||||
{
|
||||
_i2s_driver_uninstall(_cfg.i2s_port);
|
||||
err = i2s_driver_install(_cfg.i2s_port, &i2s_config, 0, nullptr);
|
||||
}
|
||||
if (err != ESP_OK) { return err; }
|
||||
|
||||
#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32)
|
||||
if (_cfg.use_adc)
|
||||
{
|
||||
err = _i2s_set_adc(_cfg.i2s_port, (gpio_num_t)_cfg.pin_data_in);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
err = i2s_set_pin(_cfg.i2s_port, &pin_config);
|
||||
}
|
||||
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
// クロックディバイダー計算用関数 (実装は Speaker_Class.cpp内)
|
||||
void calcClockDiv(uint32_t* div_a, uint32_t* div_b, uint32_t* div_n, uint32_t baseClock, uint32_t targetFreq);
|
||||
|
||||
void Mic_Class::mic_task(void* args)
|
||||
{
|
||||
auto self = (Mic_Class*)args;
|
||||
int oversampling = self->_cfg.over_sampling;
|
||||
if ( oversampling < 1) { oversampling = 1; }
|
||||
else if (oversampling > 8) { oversampling = 8; }
|
||||
|
||||
bool use_pdm = (self->_cfg.pin_bck < 0 && !self->_cfg.use_adc);
|
||||
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32C3 ) || defined (CONFIG_IDF_TARGET_ESP32C6) || defined ( CONFIG_IDF_TARGET_ESP32S3 )
|
||||
static constexpr uint32_t PLL_D2_CLK = 120*1000*1000; // 240 MHz/2
|
||||
#elif defined ( CONFIG_IDF_TARGET_ESP32P4 )
|
||||
static constexpr uint32_t PLL_D2_CLK = 20*1000*1000; // 20 MHz
|
||||
#else
|
||||
static constexpr uint32_t PLL_D2_CLK = 80*1000*1000; // 160 MHz/2
|
||||
#endif
|
||||
|
||||
uint32_t bits = (self->_cfg.use_adc) ? 1 : 16; /// 1サンプリング当たりの出力ビット数;
|
||||
uint32_t div_a, div_b, div_n;
|
||||
|
||||
// CoreS3 のマイクはmclkの倍率(div_m)の値を8以上に設定しないと精度が落ちる。
|
||||
uint32_t div_m = 8;
|
||||
|
||||
// PDM録音時、DSR(データサンプリングレート) 64に設定する
|
||||
if (use_pdm) { bits = 64; div_m = 2; }
|
||||
calcClockDiv(&div_a, &div_b, &div_n, PLL_D2_CLK / (bits * div_m), self->_cfg.sample_rate * oversampling);
|
||||
|
||||
auto dev = &I2S0;
|
||||
#if SOC_I2S_NUM >= 2
|
||||
if (self->_cfg.i2s_port == i2s_port_t::I2S_NUM_1) { dev = &I2S1; }
|
||||
#if SOC_I2S_NUM >= 3
|
||||
else if (self->_cfg.i2s_port == i2s_port_t::I2S_NUM_2) { dev = &I2S2; }
|
||||
#if SOC_I2S_NUM >= 4
|
||||
else if (self->_cfg.i2s_port == i2s_port_t::I2S_NUM_3) { dev = &I2S3; }
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32C3 ) || defined ( CONFIG_IDF_TARGET_ESP32C6 ) || defined ( CONFIG_IDF_TARGET_ESP32S3 ) || defined ( CONFIG_IDF_TARGET_ESP32P4 )
|
||||
|
||||
dev->rx_conf.rx_pdm_en = use_pdm;
|
||||
dev->rx_conf.rx_tdm_en = !use_pdm;
|
||||
#if defined ( I2S_RX_PDM2PCM_CONF_REG )
|
||||
dev->rx_pdm2pcm_conf.rx_pdm2pcm_en = use_pdm;
|
||||
dev->rx_pdm2pcm_conf.rx_pdm_sinc_dsr_16_en = 0;
|
||||
#elif defined (I2S_RX_PDM2PCM_EN)
|
||||
dev->rx_conf.rx_pdm2pcm_en = use_pdm;
|
||||
dev->rx_conf.rx_pdm_sinc_dsr_16_en = 0;
|
||||
#endif
|
||||
dev->rx_conf.rx_update = 1;
|
||||
|
||||
#if defined ( CONFIG_IDF_TARGET_ESP32P4 )
|
||||
dev->rx_conf.rx_bck_div_num = div_m - 1;
|
||||
#else
|
||||
dev->rx_conf1.rx_bck_div_num = div_m - 1;
|
||||
#endif
|
||||
|
||||
bool yn1 = (div_b > (div_a >> 1));
|
||||
if (yn1) {
|
||||
div_b = div_a - div_b;
|
||||
}
|
||||
int div_y = 1;
|
||||
int div_x = 0;
|
||||
if (div_b)
|
||||
{
|
||||
div_x = div_a / div_b - 1;
|
||||
div_y = div_a % div_b;
|
||||
|
||||
if (div_y == 0)
|
||||
{ // div_yが0になる場合、分数成分が無視される不具合があり、
|
||||
// 指定よりクロックが速くなってしまう。
|
||||
// 回避策として、誤差が少なくなる設定値を導入する。
|
||||
// これにより、誤差をクロック周期512回に1回程度のズレに抑える。;
|
||||
div_y = 1;
|
||||
div_b = 511;
|
||||
}
|
||||
}
|
||||
|
||||
#if __has_include(<driver/i2s_std.h>)
|
||||
i2s_ll_rx_set_raw_clk_div(dev, div_n, div_x, div_y, div_b, yn1);
|
||||
#endif
|
||||
|
||||
#if __has_include (<soc/pcr_struct.h>) // for C6
|
||||
PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = div_x;
|
||||
PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = div_y;
|
||||
PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = div_b;
|
||||
PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = yn1;
|
||||
PCR.i2s_rx_clkm_conf.i2s_rx_clkm_div_num = div_n;
|
||||
PCR.i2s_rx_clkm_conf.i2s_rx_clkm_sel = 1; // PLL_240M_CLK
|
||||
PCR.i2s_rx_clkm_conf.i2s_rx_clkm_en = 1;
|
||||
PCR.pll_div_clk_en.pll_240m_clk_en = 1;
|
||||
#elif defined ( I2S_RX_CLKM_DIV_X )
|
||||
dev->rx_clkm_div_conf.rx_clkm_div_x = div_x;
|
||||
dev->rx_clkm_div_conf.rx_clkm_div_y = div_y;
|
||||
dev->rx_clkm_div_conf.rx_clkm_div_z = div_b;
|
||||
dev->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1;
|
||||
dev->rx_clkm_conf.rx_clkm_div_num = div_n;
|
||||
dev->rx_clkm_conf.rx_clk_sel = 1; // PLL_240M_CLK
|
||||
dev->tx_clkm_conf.clk_en = 1;
|
||||
dev->rx_clkm_conf.rx_clk_active = 1;
|
||||
|
||||
dev->rx_conf.rx_update = 1;
|
||||
dev->rx_conf.rx_update = 0;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
if (use_pdm)
|
||||
{
|
||||
dev->pdm_conf.rx_sinc_dsr_16_en = 1; // 0=DSR64 / 1=DSR128
|
||||
dev->pdm_conf.pdm2pcm_conv_en = 1;
|
||||
dev->pdm_conf.rx_pdm_en = 1;
|
||||
}
|
||||
|
||||
dev->sample_rate_conf.rx_bck_div_num = div_m;
|
||||
dev->clkm_conf.clkm_div_a = div_a;
|
||||
dev->clkm_conf.clkm_div_b = div_b;
|
||||
dev->clkm_conf.clkm_div_num = div_n;
|
||||
dev->clkm_conf.clka_en = 0; // APLL disable : PLL_160M
|
||||
|
||||
// If RX is not reset here, BCK polarity may be inverted.
|
||||
dev->conf.rx_reset = 1;
|
||||
dev->conf.rx_fifo_reset = 1;
|
||||
dev->conf.rx_reset = 0;
|
||||
dev->conf.rx_fifo_reset = 0;
|
||||
|
||||
#endif
|
||||
|
||||
_i2s_start(self->_cfg.i2s_port);
|
||||
|
||||
int32_t gain = self->_cfg.magnification;
|
||||
const float f_gain = (float)gain / (oversampling << 1);
|
||||
size_t src_idx = ~0u;
|
||||
size_t src_len = 0;
|
||||
int32_t sum_value[4] = { 0,0 };
|
||||
int32_t prev_value[2] = { 0, 0 };
|
||||
const bool in_stereo = self->_cfg.stereo;
|
||||
int32_t os_remain = oversampling;
|
||||
const size_t dma_buf_len = self->_cfg.dma_buf_len;
|
||||
int16_t* src_buf = (int16_t*)alloca(dma_buf_len * sizeof(int16_t));
|
||||
memset(src_buf, 0, dma_buf_len * sizeof(int16_t));
|
||||
|
||||
_i2s_read(self->_cfg.i2s_port, src_buf, dma_buf_len, &src_len, portTICK_PERIOD_MS);
|
||||
_i2s_read(self->_cfg.i2s_port, src_buf, dma_buf_len, &src_len, portTICK_PERIOD_MS);
|
||||
|
||||
while (self->_task_running)
|
||||
{
|
||||
bool rec_flip = self->_rec_flip;
|
||||
recording_info_t* current_rec = &(self->_rec_info[!rec_flip]);
|
||||
recording_info_t* next_rec = &(self->_rec_info[ rec_flip]);
|
||||
|
||||
size_t dst_remain = current_rec->length;
|
||||
if (dst_remain == 0)
|
||||
{
|
||||
rec_flip = !rec_flip;
|
||||
self->_rec_flip = rec_flip;
|
||||
xSemaphoreGive(self->_task_semaphore);
|
||||
std::swap(current_rec, next_rec);
|
||||
dst_remain = current_rec->length;
|
||||
if (dst_remain == 0)
|
||||
{
|
||||
self->_is_recording = false;
|
||||
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
|
||||
src_idx = ~0u;
|
||||
src_len = 0;
|
||||
sum_value[0] = 0;
|
||||
sum_value[1] = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
self->_is_recording = true;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (src_idx >= src_len)
|
||||
{
|
||||
_i2s_read(self->_cfg.i2s_port, src_buf, dma_buf_len, &src_len, 100 / portTICK_PERIOD_MS);
|
||||
src_len >>= 1;
|
||||
src_idx = 0;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
sum_value[0] += src_buf[src_idx ];
|
||||
sum_value[1] += src_buf[src_idx+1];
|
||||
src_idx += 2;
|
||||
} while (--os_remain && (src_idx < src_len));
|
||||
|
||||
if (os_remain) { continue; }
|
||||
os_remain = oversampling;
|
||||
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32)
|
||||
auto sv0 = sum_value[1];
|
||||
auto sv1 = sum_value[0];
|
||||
#else
|
||||
auto sv0 = sum_value[0];
|
||||
auto sv1 = sum_value[1];
|
||||
#endif
|
||||
if (self->_cfg.use_adc) {
|
||||
sv0 -= 2048 * oversampling;
|
||||
sv1 -= 2048 * oversampling;
|
||||
}
|
||||
|
||||
auto value_tmp = (sv0 + sv1) << 3;
|
||||
int32_t offset = self->_offset;
|
||||
// Automatic zero level adjustment
|
||||
offset -= (value_tmp + offset + 16) >> 5;
|
||||
self->_offset = offset;
|
||||
offset = (offset + 8) >> 4;
|
||||
sum_value[0] = sv0 + offset;
|
||||
sum_value[1] = sv1 + offset;
|
||||
|
||||
int32_t noise_filter = self->_cfg.noise_filter_level;
|
||||
if (noise_filter)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
int32_t v = (sum_value[i] * (256 - noise_filter) + prev_value[i] * noise_filter + 128) >> 8;
|
||||
prev_value[i] = v;
|
||||
sum_value[i] = v * f_gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
sum_value[i] *= f_gain;
|
||||
}
|
||||
}
|
||||
|
||||
int output_num = 2;
|
||||
|
||||
if (in_stereo != current_rec->is_stereo)
|
||||
{
|
||||
if (in_stereo)
|
||||
{ // stereo -> mono convert.
|
||||
sum_value[0] = (sum_value[0] + sum_value[1] + 1) >> 1;
|
||||
output_num = 1;
|
||||
}
|
||||
else
|
||||
{ // mono -> stereo convert.
|
||||
auto tmp = sum_value[1];
|
||||
sum_value[3] = tmp;
|
||||
sum_value[2] = tmp;
|
||||
sum_value[1] = sum_value[0];
|
||||
output_num = 4;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < output_num; ++i)
|
||||
{
|
||||
auto value = sum_value[i];
|
||||
if (current_rec->is_16bit)
|
||||
{
|
||||
if ( value < INT16_MIN+16) { value = INT16_MIN+16; }
|
||||
else if (value > INT16_MAX-16) { value = INT16_MAX-16; }
|
||||
auto dst = (int16_t*)(current_rec->data);
|
||||
*dst++ = value;
|
||||
current_rec->data = dst;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = ((value + 128) >> 8) + 128;
|
||||
if ( value < 0) { value = 0; }
|
||||
else if (value > 255) { value = 255; }
|
||||
auto dst = (uint8_t*)(current_rec->data);
|
||||
*dst++ = value;
|
||||
current_rec->data = dst;
|
||||
}
|
||||
}
|
||||
sum_value[0] = 0;
|
||||
sum_value[1] = 0;
|
||||
dst_remain -= output_num;
|
||||
if ((int32_t)dst_remain <= 0)
|
||||
{
|
||||
current_rec->length = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self->_is_recording = false;
|
||||
_i2s_stop(self->_cfg.i2s_port);
|
||||
|
||||
self->_task_handle = nullptr;
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
bool Mic_Class::begin(void)
|
||||
{
|
||||
if (_task_running)
|
||||
{
|
||||
auto rate = _calc_rec_rate();
|
||||
if (_rec_sample_rate == rate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
do { vTaskDelay(1); } while (isRecording());
|
||||
end();
|
||||
_rec_sample_rate = rate;
|
||||
}
|
||||
|
||||
if (_task_semaphore == nullptr) { _task_semaphore = xSemaphoreCreateBinary(); }
|
||||
|
||||
bool res = true;
|
||||
if (_cb_set_enabled) { res = _cb_set_enabled(_cb_set_enabled_args, true); }
|
||||
|
||||
res = (ESP_OK == _setup_i2s()) && res;
|
||||
if (res)
|
||||
{
|
||||
size_t stack_size = 2048 + (_cfg.dma_buf_len * sizeof(uint16_t));
|
||||
_task_running = true;
|
||||
#if portNUM_PROCESSORS > 1
|
||||
if (_cfg.task_pinned_core < portNUM_PROCESSORS)
|
||||
{
|
||||
xTaskCreatePinnedToCore(mic_task, "mic_task", stack_size, this, _cfg.task_priority, &_task_handle, _cfg.task_pinned_core);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xTaskCreate(mic_task, "mic_task", stack_size, this, _cfg.task_priority, &_task_handle);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void Mic_Class::end(void)
|
||||
{
|
||||
if (!_task_running) { return; }
|
||||
_task_running = false;
|
||||
if (_task_handle)
|
||||
{
|
||||
if (_task_handle) { xTaskNotifyGive(_task_handle); }
|
||||
do { vTaskDelay(1); } while (_task_handle);
|
||||
}
|
||||
|
||||
if (_cb_set_enabled) { _cb_set_enabled(_cb_set_enabled_args, false); }
|
||||
_i2s_driver_uninstall(_cfg.i2s_port);
|
||||
}
|
||||
|
||||
bool Mic_Class::_rec_raw(void* recdata, size_t array_len, bool flg_16bit, uint32_t sample_rate, bool flg_stereo)
|
||||
{
|
||||
recording_info_t info;
|
||||
info.data = recdata;
|
||||
info.length = array_len;
|
||||
info.is_16bit = flg_16bit;
|
||||
info.is_stereo = flg_stereo;
|
||||
|
||||
_cfg.sample_rate = sample_rate;
|
||||
|
||||
if (!begin()) { return false; }
|
||||
if (array_len == 0) { return true; }
|
||||
while (_rec_info[_rec_flip].length) { xSemaphoreTake(_task_semaphore, 1); }
|
||||
_rec_info[_rec_flip] = info;
|
||||
if (this->_task_handle)
|
||||
{
|
||||
xTaskNotifyGive(this->_task_handle);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
199
libraries/M5Unified/src/utility/Mic_Class.hpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_Mic_Class_H__
|
||||
#define __M5_Mic_Class_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#if defined ( ESP_PLATFORM )
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
#include <soc/i2s_struct.h>
|
||||
|
||||
#if __has_include(<driver/i2s_std.h>)
|
||||
#include <driver/i2s_std.h>
|
||||
#include <driver/i2s_pdm.h>
|
||||
#else
|
||||
#include <driver/i2s.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef I2S_PIN_NO_CHANGE
|
||||
#define I2S_PIN_NO_CHANGE (-1)
|
||||
#endif
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class M5Unified;
|
||||
|
||||
enum input_channel_t : uint8_t
|
||||
{
|
||||
input_only_right = 0,
|
||||
input_only_left = 1,
|
||||
input_stereo = 2,
|
||||
};
|
||||
|
||||
struct mic_config_t
|
||||
{
|
||||
/// i2s_data_in (for mic)
|
||||
int pin_data_in = -1;
|
||||
|
||||
/// i2s_bclk
|
||||
int pin_bck = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// i2s_mclk
|
||||
int pin_mck = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// i2s_ws (lrck)
|
||||
int pin_ws = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// input sampling rate (Hz)
|
||||
uint32_t sample_rate = 16000;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t left_channel : 1;
|
||||
uint8_t stereo : 1;
|
||||
uint8_t reserve : 6;
|
||||
};
|
||||
input_channel_t input_channel = input_only_right;
|
||||
};
|
||||
|
||||
/// Sampling times of obtain the average value
|
||||
uint8_t over_sampling = 2;
|
||||
|
||||
/// multiplier for input value
|
||||
uint8_t magnification = 16;
|
||||
|
||||
/// Coefficient of the previous value, used for noise filtering.
|
||||
uint8_t noise_filter_level = 0;
|
||||
|
||||
/// use analog input mic ( need only pin_data_in )
|
||||
bool use_adc = false;
|
||||
|
||||
/// for I2S dma_buf_len
|
||||
size_t dma_buf_len = 128;
|
||||
|
||||
/// for I2S dma_buf_count
|
||||
size_t dma_buf_count = 8;
|
||||
|
||||
/// background task priority
|
||||
uint8_t task_priority = 2;
|
||||
|
||||
/// background task pinned core
|
||||
uint8_t task_pinned_core = -1;
|
||||
|
||||
/// I2S port
|
||||
i2s_port_t i2s_port = i2s_port_t::I2S_NUM_0;
|
||||
};
|
||||
|
||||
class Mic_Class
|
||||
{
|
||||
friend M5Unified;
|
||||
|
||||
public:
|
||||
|
||||
mic_config_t config(void) const { return _cfg; }
|
||||
void config(const mic_config_t& cfg) { _cfg = cfg; }
|
||||
|
||||
bool begin(void);
|
||||
|
||||
void end(void);
|
||||
|
||||
bool isRunning(void) const { return _task_running; }
|
||||
|
||||
bool isEnabled(void) const { return _cfg.pin_data_in >= 0; }
|
||||
|
||||
/// now in recording or not.
|
||||
/// @return 0=not recording / 1=recording (There's room in the queue) / 2=recording (There's no room in the queue.)
|
||||
size_t isRecording(void) const { return _is_recording ? ((bool)_rec_info[0].length) + ((bool)_rec_info[1].length) : 0; }
|
||||
|
||||
/// set recording sampling rate.
|
||||
/// @param sample_rate the sampling rate (Hz)
|
||||
void setSampleRate(uint32_t sample_rate) { _cfg.sample_rate = sample_rate; }
|
||||
|
||||
/// record raw sound wave data.
|
||||
/// @param rec_data Recording destination array.
|
||||
/// @param array_len Number of data array elements.
|
||||
/// @param sample_rate the sampling rate (Hz)
|
||||
/// @param stereo true=data is stereo / false=data is monaural.
|
||||
bool record(uint8_t* rec_data, size_t array_len, uint32_t sample_rate, bool stereo = false)
|
||||
{
|
||||
return _rec_raw(rec_data, array_len, false, sample_rate, stereo);
|
||||
}
|
||||
|
||||
/// record raw sound wave data.
|
||||
/// @param rec_data Recording destination array.
|
||||
/// @param array_len Number of data array elements.
|
||||
/// @param sample_rate the sampling rate (Hz)
|
||||
/// @param stereo true=data is stereo / false=data is monaural.
|
||||
bool record(int16_t* rec_data, size_t array_len, uint32_t sample_rate, bool stereo = false)
|
||||
{
|
||||
return _rec_raw(rec_data, array_len, true, sample_rate, stereo);
|
||||
}
|
||||
|
||||
/// record raw sound wave data.
|
||||
/// @param rec_data Recording destination array.
|
||||
/// @param array_len Number of data array elements.
|
||||
bool record(uint8_t* rec_data, size_t array_len)
|
||||
{
|
||||
return _rec_raw(rec_data, array_len, false, _cfg.sample_rate, false);
|
||||
}
|
||||
|
||||
/// record raw sound wave data.
|
||||
/// @param rec_data Recording destination array.
|
||||
/// @param array_len Number of data array elements.
|
||||
bool record(int16_t* rec_data, size_t array_len)
|
||||
{
|
||||
return _rec_raw(rec_data, array_len, true, _cfg.sample_rate, false);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void setCallback(void* args, bool(*func)(void*, bool)) { _cb_set_enabled = func; _cb_set_enabled_args = args; }
|
||||
|
||||
struct recording_info_t
|
||||
{
|
||||
void* data = nullptr;
|
||||
size_t length = 0;
|
||||
size_t index = 0;
|
||||
bool is_stereo = false;
|
||||
bool is_16bit = false;
|
||||
};
|
||||
|
||||
recording_info_t _rec_info[2];
|
||||
volatile bool _rec_flip = false;
|
||||
|
||||
static void mic_task(void* args);
|
||||
|
||||
uint32_t _calc_rec_rate(void) const;
|
||||
esp_err_t _setup_i2s(void);
|
||||
bool _rec_raw(void* recdata, size_t array_len, bool flg_16bit, uint32_t sample_rate, bool stereo);
|
||||
|
||||
mic_config_t _cfg;
|
||||
uint32_t _rec_sample_rate = 0;
|
||||
|
||||
bool (*_cb_set_enabled)(void* args, bool enabled) = nullptr;
|
||||
void* _cb_set_enabled_args = nullptr;
|
||||
|
||||
int32_t _offset = 0;
|
||||
volatile bool _task_running = false;
|
||||
volatile bool _is_recording = false;
|
||||
#if defined (SDL_h_)
|
||||
SDL_Thread* _task_handle = nullptr;
|
||||
#else
|
||||
TaskHandle_t _task_handle = nullptr;
|
||||
volatile SemaphoreHandle_t _task_semaphore = nullptr;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
111
libraries/M5Unified/src/utility/PI4IOE5V6408_Class.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* @file pi4ioe5v6408.cpp
|
||||
* @author Forairaaaaa
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2024-06-26
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
#include "PI4IOE5V6408_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
bool PI4IOE5V6408_Class::begin()
|
||||
{
|
||||
auto id = readRegister8(0x01);
|
||||
if (id == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// false input, true output
|
||||
void PI4IOE5V6408_Class::setDirection(uint8_t pin, bool direction)
|
||||
{
|
||||
// Input, set 0
|
||||
if (!direction) {
|
||||
auto data = readRegister8(0x03);
|
||||
data &= ~(1 << pin);
|
||||
writeRegister8(0x03, data);
|
||||
}
|
||||
// Output, set 1
|
||||
else {
|
||||
auto data = readRegister8(0x03);
|
||||
data |= (1 << pin);
|
||||
writeRegister8(0x03, data);
|
||||
}
|
||||
}
|
||||
|
||||
void PI4IOE5V6408_Class::enablePull(uint8_t pin, bool enablePull)
|
||||
{
|
||||
if (enablePull) {
|
||||
auto data = readRegister8(0x0B);
|
||||
data |= (1 << pin);
|
||||
writeRegister8(0x0B, data);
|
||||
} else {
|
||||
auto data = readRegister8(0x0B);
|
||||
data &= ~(1 << pin);
|
||||
writeRegister8(0x0B, data);
|
||||
}
|
||||
}
|
||||
|
||||
// false down, true up
|
||||
void PI4IOE5V6408_Class::setPullMode(uint8_t pin, bool mode)
|
||||
{
|
||||
if (mode) {
|
||||
auto data = readRegister8(0x0D);
|
||||
data |= (1 << pin);
|
||||
writeRegister8(0x0D, data);
|
||||
} else {
|
||||
auto data = readRegister8(0x0D);
|
||||
data &= ~(1 << pin);
|
||||
writeRegister8(0x0D, data);
|
||||
}
|
||||
}
|
||||
|
||||
void PI4IOE5V6408_Class::setHighImpedance(uint8_t pin, bool enable)
|
||||
{
|
||||
auto data = readRegister8(0x07);
|
||||
|
||||
if (enable)
|
||||
data |= (1 << pin);
|
||||
else
|
||||
data &= ~(1 << pin);
|
||||
|
||||
writeRegister8(0x07, data);
|
||||
}
|
||||
|
||||
void PI4IOE5V6408_Class::digitalWrite(uint8_t pin, bool level)
|
||||
{
|
||||
auto data = readRegister8(0x05);
|
||||
// spdlog::info("data: {}", data);
|
||||
|
||||
if (level)
|
||||
data |= (1 << pin);
|
||||
else
|
||||
data &= ~(1 << pin);
|
||||
|
||||
writeRegister8(0x05, data);
|
||||
}
|
||||
|
||||
bool PI4IOE5V6408_Class::digitalRead(uint8_t pin)
|
||||
{
|
||||
auto data = readRegister8(0x0F);
|
||||
return (data & (1 << pin)) != 0;
|
||||
}
|
||||
|
||||
void PI4IOE5V6408_Class::resetIrq()
|
||||
{
|
||||
readRegister8(0x13);
|
||||
}
|
||||
|
||||
void PI4IOE5V6408_Class::disableIrq()
|
||||
{
|
||||
writeRegister8(0x11, 0B11111111);
|
||||
}
|
||||
|
||||
void PI4IOE5V6408_Class::enableIrq()
|
||||
{
|
||||
writeRegister8(0x11, 0x0);
|
||||
}
|
||||
}
|
||||
52
libraries/M5Unified/src/utility/PI4IOE5V6408_Class.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @file pi4ioe5v6408.h
|
||||
* @author Forairaaaaa
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2024-06-26
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*
|
||||
*/
|
||||
#ifndef __M5_PI4IOE5V6408_H__
|
||||
#define __M5_PI4IOE5V6408_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
// https://www.diodes.com/assets/Datasheets/PI4IOE5V6408.pdf
|
||||
class PI4IOE5V6408_Class : public m5::I2C_Device
|
||||
{
|
||||
public:
|
||||
PI4IOE5V6408_Class(std::uint8_t i2c_addr = 0x43, std::uint32_t freq = 400000, m5::I2C_Class* i2c = &m5::In_I2C)
|
||||
: I2C_Device(i2c_addr, freq, i2c)
|
||||
{}
|
||||
|
||||
bool begin();
|
||||
|
||||
// false input, true output
|
||||
void setDirection(uint8_t pin, bool direction);
|
||||
|
||||
void enablePull(uint8_t pin, bool enablePull);
|
||||
|
||||
// false down, true up
|
||||
void setPullMode(uint8_t pin, bool mode);
|
||||
|
||||
void setHighImpedance(uint8_t pin, bool enable);
|
||||
|
||||
void digitalWrite(uint8_t pin, bool level);
|
||||
|
||||
bool digitalRead(uint8_t pin);
|
||||
|
||||
void resetIrq();
|
||||
|
||||
void disableIrq();
|
||||
|
||||
void enableIrq();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
1307
libraries/M5Unified/src/utility/Power_Class.cpp
Normal file
203
libraries/M5Unified/src/utility/Power_Class.hpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_Power_Class_H__
|
||||
#define __M5_Power_Class_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
#include "AXP192_Class.hpp"
|
||||
#include "AXP2101_Class.hpp"
|
||||
#include "IP5306_Class.hpp"
|
||||
#include "INA3221_Class.hpp"
|
||||
#include "RTC8563_Class.hpp"
|
||||
|
||||
#if __has_include (<sdkconfig.h>)
|
||||
#include <sdkconfig.h>
|
||||
#endif
|
||||
|
||||
#if __has_include (<esp_adc/adc_oneshot.h>) // ESP-IDF v5 or later
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#if __has_include(<esp_adc/adc_cali.h>)
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#endif
|
||||
#elif __has_include (<driver/adc.h>)
|
||||
#include <driver/adc.h>
|
||||
#include <esp_adc_cal.h>
|
||||
#endif
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class M5Unified;
|
||||
|
||||
enum ext_port_mask_t
|
||||
{ ext_none = 0
|
||||
// For individual control of external ports of M5Station
|
||||
, ext_PA = 0b00000001
|
||||
, ext_PB1 = 0b00000010
|
||||
, ext_PB2 = 0b00000100
|
||||
, ext_PC1 = 0b00001000
|
||||
, ext_PC2 = 0b00010000
|
||||
, ext_USB = 0b00100000 // M5Station external USB. ※ Not for CoreS3 main USB.
|
||||
, ext_MAIN = 0b10000000
|
||||
};
|
||||
|
||||
class Power_Class
|
||||
{
|
||||
friend M5Unified;
|
||||
public:
|
||||
|
||||
enum pmic_t
|
||||
{ pmic_unknown
|
||||
, pmic_adc
|
||||
, pmic_axp192
|
||||
, pmic_ip5306
|
||||
, pmic_axp2101
|
||||
};
|
||||
|
||||
enum is_charging_t
|
||||
{ is_discharging = 0
|
||||
, is_charging
|
||||
, charge_unknown
|
||||
};
|
||||
|
||||
bool begin(void);
|
||||
|
||||
/// Set power output of the external ports.
|
||||
/// @param enable true=output / false=input
|
||||
/// @param port_mask for M5Station. ext_port (bitmask).
|
||||
void setExtOutput(bool enable, ext_port_mask_t port_mask = (ext_port_mask_t)0xFF);
|
||||
|
||||
/// deprecated : Change to "setExtOutput"
|
||||
[[deprecated("Change to setExtOutput")]]
|
||||
void setExtPower(bool enable, ext_port_mask_t port_mask = (ext_port_mask_t)0xFF) { setExtOutput(enable, port_mask); }
|
||||
|
||||
/// Get power output of the external ports.
|
||||
/// @return true=output enabled / false=output disabled
|
||||
bool getExtOutput(void);
|
||||
|
||||
/// Set power output of the main USB port.
|
||||
/// @param enable true=output / false=input
|
||||
/// @attention for M5Stack CoreS3 main USB port.
|
||||
/// @attention ※ Not for M5Station external USB.
|
||||
void setUsbOutput(bool enable);
|
||||
|
||||
/// Get power output of the main USB port.
|
||||
/// @return true=output enabled / false=output disabled
|
||||
/// @attention for M5Stack CoreS3 main USB port.
|
||||
/// @attention ※ Not for M5Station external USB.
|
||||
bool getUsbOutput(void);
|
||||
|
||||
/// Turn on/off the power LED.
|
||||
/// @param brightness 0=OFF: 1~255=ON (Set brightness if possible.)
|
||||
void setLed(uint8_t brightness = 255);
|
||||
|
||||
/// all power off.
|
||||
void powerOff(void);
|
||||
|
||||
/// sleep and timer boot. The boot condition can be specified by the argument.
|
||||
/// @param seconds Number of seconds to boot.
|
||||
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タイマー起動が出来ない。;
|
||||
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タイマー起動が出来ない。;
|
||||
void timerSleep(const rtc_date_t& date, const rtc_time_t& time);
|
||||
|
||||
/// ESP32 deepsleep
|
||||
/// @param seconds Number of micro seconds to wakeup.
|
||||
void deepSleep(std::uint64_t micro_seconds = 0, bool touch_wakeup = true);
|
||||
|
||||
/// ESP32 lightsleep
|
||||
/// @param seconds Number of micro seconds to wakeup.
|
||||
void lightSleep(std::uint64_t micro_seconds = 0, bool touch_wakeup = true);
|
||||
|
||||
/// Get the remaining battery power.
|
||||
/// @return 0-100 level
|
||||
std::int32_t getBatteryLevel(void);
|
||||
|
||||
/// set battery charge enable.
|
||||
/// @param enable true=enable / false=disable
|
||||
void setBatteryCharge(bool enable);
|
||||
|
||||
/// set battery charge current
|
||||
/// @param max_mA milli ampere.
|
||||
/// @attention Non-functioning models : CoreInk , M5Paper , M5Stack(with non I2C IP5306)
|
||||
void setChargeCurrent(std::uint16_t max_mA);
|
||||
|
||||
/// set battery charge voltage
|
||||
/// @param max_mV milli volt.
|
||||
/// @attention Non-functioning models : CoreInk , M5Paper , M5Stack(with non I2C IP5306)
|
||||
void setChargeVoltage(std::uint16_t max_mV);
|
||||
|
||||
/// Get whether the battery is currently charging or not.
|
||||
/// @attention Non-functioning models : CoreInk , M5Paper , M5Stack(with non I2C IP5306)
|
||||
is_charging_t isCharging(void);
|
||||
|
||||
/// Get VBUS voltage
|
||||
/// @return VBUS voltage [mV] / -1=not supported model
|
||||
/// @attention Only for models with AXP192 or AXP2101
|
||||
int16_t getVBUSVoltage(void);
|
||||
|
||||
/// Get battery voltage
|
||||
/// @return battery voltage [mV]
|
||||
int16_t getBatteryVoltage(void);
|
||||
|
||||
/// get battery current
|
||||
/// @return battery current [mA] ( +=charge / -=discharge )
|
||||
int32_t getBatteryCurrent(void);
|
||||
|
||||
/// Get Power Key Press condition.
|
||||
/// @return 0=none / 1=long pressed / 2=short clicked / 3=both
|
||||
/// @attention Only for models with AXP192 or AXP2101
|
||||
/// @attention Once this function is called, the value is reset to 0, and the next time it is pressed on, the value changes.
|
||||
uint8_t getKeyState(void);
|
||||
|
||||
/// Operate the vibration motor
|
||||
/// @param level Vibration strength of the motor. (0=stop)
|
||||
void setVibration(uint8_t level);
|
||||
|
||||
pmic_t getType(void) const { return _pmic; }
|
||||
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
AXP2101_Class Axp2101;
|
||||
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#else
|
||||
|
||||
AXP2101_Class Axp2101;
|
||||
AXP192_Class Axp192;
|
||||
IP5306_Class Ip5306;
|
||||
// secondery INA3221 for M5Station.
|
||||
INA3221_Class Ina3221[2] = { { 0x40 }, { 0x41 } };
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::int32_t _getBatteryAdcRaw(void);
|
||||
void _powerOff(bool withTimer);
|
||||
void _timerSleep(void);
|
||||
|
||||
float _adc_ratio = 0;
|
||||
std::uint8_t _wakeupPin = 255;
|
||||
std::uint8_t _rtcIntPin = 255;
|
||||
pmic_t _pmic = pmic_t::pmic_unknown;
|
||||
#if !defined (M5UNIFIED_PC_BUILD)
|
||||
uint8_t _batAdcCh;
|
||||
uint8_t _batAdcUnit;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
311
libraries/M5Unified/src/utility/RTC8563_Class.cpp
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "RTC8563_Class.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
tm rtc_datetime_t::get_tm(void) const
|
||||
{
|
||||
tm t_st = {
|
||||
time.seconds,
|
||||
time.minutes,
|
||||
time.hours,
|
||||
date.date,
|
||||
date.month - 1,
|
||||
date.year - 1900,
|
||||
date.weekDay,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
return t_st;
|
||||
}
|
||||
|
||||
void rtc_datetime_t::set_tm(tm& datetime)
|
||||
{
|
||||
date = rtc_date_t { datetime };
|
||||
time = rtc_time_t { datetime };
|
||||
}
|
||||
|
||||
static std::uint8_t bcd2ToByte(std::uint8_t value)
|
||||
{
|
||||
return ((value >> 4) * 10) + (value & 0x0F);
|
||||
}
|
||||
|
||||
static std::uint8_t byteToBcd2(std::uint8_t value)
|
||||
{
|
||||
std::uint_fast8_t bcdhigh = value / 10;
|
||||
return (bcdhigh << 4) | (value - (bcdhigh * 10));
|
||||
}
|
||||
|
||||
bool RTC8563_Class::begin(I2C_Class* i2c)
|
||||
{
|
||||
if (i2c)
|
||||
{
|
||||
_i2c = i2c;
|
||||
i2c->begin();
|
||||
}
|
||||
/// TimerCameraの内蔵RTCが初期化に失敗することがあったため、最初に空打ちする;
|
||||
writeRegister8(0x00, 0x00);
|
||||
_init = writeRegister8(0x00, 0x00) && writeRegister8(0x0E, 0x03);
|
||||
return _init;
|
||||
}
|
||||
|
||||
bool RTC8563_Class::getVoltLow(void)
|
||||
{
|
||||
return readRegister8(0x02) & 0x80; // RTCC_VLSEC_MASK
|
||||
}
|
||||
|
||||
bool RTC8563_Class::getDateTime(rtc_datetime_t* datetime) const
|
||||
{
|
||||
std::uint8_t buf[7] = { 0 };
|
||||
|
||||
if (!isEnabled() || !readRegister(0x02, buf, 7)) { return false; }
|
||||
|
||||
datetime->time.seconds = bcd2ToByte(buf[0] & 0x7f);
|
||||
datetime->time.minutes = bcd2ToByte(buf[1] & 0x7f);
|
||||
datetime->time.hours = bcd2ToByte(buf[2] & 0x3f);
|
||||
|
||||
datetime->date.date = bcd2ToByte(buf[3] & 0x3f);
|
||||
datetime->date.weekDay = bcd2ToByte(buf[4] & 0x07);
|
||||
datetime->date.month = bcd2ToByte(buf[5] & 0x1f);
|
||||
datetime->date.year = bcd2ToByte(buf[6] & 0xff)
|
||||
+ ((0x80 & buf[5]) ? 1900 : 2000);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RTC8563_Class::getTime(rtc_time_t* time) const
|
||||
{
|
||||
std::uint8_t buf[3] = { 0 };
|
||||
|
||||
if (!isEnabled() || !readRegister(0x02, buf, 3)) { return false; }
|
||||
|
||||
time->seconds = bcd2ToByte(buf[0] & 0x7f);
|
||||
time->minutes = bcd2ToByte(buf[1] & 0x7f);
|
||||
time->hours = bcd2ToByte(buf[2] & 0x3f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RTC8563_Class::setTime(const rtc_time_t& time)
|
||||
{
|
||||
std::uint8_t buf[] =
|
||||
{ byteToBcd2(time.seconds)
|
||||
, byteToBcd2(time.minutes)
|
||||
, byteToBcd2(time.hours)
|
||||
};
|
||||
writeRegister(0x02, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool RTC8563_Class::getDate(rtc_date_t* date) const
|
||||
{
|
||||
std::uint8_t buf[4] = {0};
|
||||
|
||||
if (!readRegister(0x05, buf, 4)) { return false; }
|
||||
|
||||
date->date = bcd2ToByte(buf[0] & 0x3f);
|
||||
date->weekDay = bcd2ToByte(buf[1] & 0x07);
|
||||
date->month = bcd2ToByte(buf[2] & 0x1f);
|
||||
date->year = bcd2ToByte(buf[3] & 0xff)
|
||||
+ ((0x80 & buf[2]) ? 1900 : 2000);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RTC8563_Class::setDate(const rtc_date_t& date)
|
||||
{
|
||||
std::uint8_t w = date.weekDay;
|
||||
if (w > 6 && date.year >= 1900 && ((std::size_t)(date.month - 1)) < 12)
|
||||
{ /// weekDay auto adjust
|
||||
int32_t year = date.year;
|
||||
int32_t month = date.month;
|
||||
int32_t day = date.date;
|
||||
if (month < 3) {
|
||||
year--;
|
||||
month += 12;
|
||||
}
|
||||
int32_t ydiv100 = year / 100;
|
||||
w = (year + (year >> 2) - ydiv100 + (ydiv100 >> 2) + (13 * month + 8) / 5 + day) % 7;
|
||||
}
|
||||
|
||||
std::uint8_t buf[] =
|
||||
{ byteToBcd2(date.date)
|
||||
, w
|
||||
, (std::uint8_t)(byteToBcd2(date.month) + (date.year < 2000 ? 0x80 : 0))
|
||||
, byteToBcd2(date.year % 100)
|
||||
};
|
||||
writeRegister(0x05, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
int RTC8563_Class::setAlarmIRQ(int afterSeconds)
|
||||
{
|
||||
std::uint8_t reg_value = readRegister8(0x01) & ~0x0C;
|
||||
|
||||
if (afterSeconds < 0)
|
||||
{ // disable timer
|
||||
writeRegister8(0x01, reg_value & ~0x01);
|
||||
writeRegister8(0x0E, 0x03);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::size_t div = 1;
|
||||
std::uint8_t type_value = 0x82;
|
||||
if (afterSeconds < 270)
|
||||
{
|
||||
if (afterSeconds > 255) { afterSeconds = 255; }
|
||||
}
|
||||
else
|
||||
{
|
||||
div = 60;
|
||||
afterSeconds = (afterSeconds + 30) / div;
|
||||
if (afterSeconds > 255) { afterSeconds = 255; }
|
||||
type_value = 0x83;
|
||||
}
|
||||
|
||||
writeRegister8(0x0E, type_value);
|
||||
writeRegister8(0x0F, afterSeconds);
|
||||
|
||||
writeRegister8(0x01, (reg_value | 0x01) & ~0x80);
|
||||
return afterSeconds * div;
|
||||
}
|
||||
|
||||
int RTC8563_Class::setAlarmIRQ(const rtc_time_t &time)
|
||||
{
|
||||
union
|
||||
{
|
||||
std::uint32_t raw = ~0;
|
||||
std::uint8_t buf[4];
|
||||
};
|
||||
bool irq_enable = false;
|
||||
|
||||
if (time.minutes >= 0)
|
||||
{
|
||||
irq_enable = true;
|
||||
buf[0] = byteToBcd2(time.minutes) & 0x7f;
|
||||
}
|
||||
|
||||
if (time.hours >= 0)
|
||||
{
|
||||
irq_enable = true;
|
||||
buf[1] = byteToBcd2(time.hours) & 0x3f;
|
||||
}
|
||||
|
||||
writeRegister(0x09, buf, 4);
|
||||
|
||||
if (irq_enable)
|
||||
{
|
||||
bitOn(0x01, 0x02);
|
||||
} else {
|
||||
bitOff(0x01, 0x02);
|
||||
}
|
||||
|
||||
return irq_enable;
|
||||
}
|
||||
|
||||
int RTC8563_Class::setAlarmIRQ(const rtc_date_t &date, const rtc_time_t &time)
|
||||
{
|
||||
union
|
||||
{
|
||||
std::uint32_t raw = ~0;
|
||||
std::uint8_t buf[4];
|
||||
};
|
||||
bool irq_enable = false;
|
||||
|
||||
if (time.minutes >= 0)
|
||||
{
|
||||
irq_enable = true;
|
||||
buf[0] = byteToBcd2(time.minutes) & 0x7f;
|
||||
}
|
||||
|
||||
if (time.hours >= 0)
|
||||
{
|
||||
irq_enable = true;
|
||||
buf[1] = byteToBcd2(time.hours) & 0x3f;
|
||||
}
|
||||
|
||||
if (date.date >= 0)
|
||||
{
|
||||
irq_enable = true;
|
||||
buf[2] = byteToBcd2(date.date) & 0x3f;
|
||||
}
|
||||
|
||||
if (date.weekDay >= 0)
|
||||
{
|
||||
irq_enable = true;
|
||||
buf[3] = byteToBcd2(date.weekDay) & 0x07;
|
||||
}
|
||||
|
||||
writeRegister(0x09, buf, 4);
|
||||
|
||||
if (irq_enable)
|
||||
{
|
||||
bitOn(0x01, 0x02);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitOff(0x01, 0x02);
|
||||
}
|
||||
|
||||
return irq_enable;
|
||||
}
|
||||
|
||||
bool RTC8563_Class::getIRQstatus(void)
|
||||
{
|
||||
return _init && (0x0C & readRegister8(0x01));
|
||||
}
|
||||
|
||||
void RTC8563_Class::clearIRQ(void)
|
||||
{
|
||||
if (!_init) { return; }
|
||||
bitOff(0x01, 0x0C);
|
||||
}
|
||||
|
||||
void RTC8563_Class::disableIRQ(void)
|
||||
{
|
||||
if (!_init) { return; }
|
||||
// disable alerm (bit7:1=disabled)
|
||||
static constexpr const std::uint8_t buf[4] = { 0x80, 0x80, 0x80, 0x80 };
|
||||
writeRegister(0x09, buf, 4);
|
||||
|
||||
// disable timer (bit7:0=disabled)
|
||||
writeRegister8(0x0E, 0);
|
||||
|
||||
// clear flag and INT enable bits
|
||||
writeRegister8(0x01, 0x00);
|
||||
}
|
||||
|
||||
void RTC8563_Class::setSystemTimeFromRtc(struct timezone* tz)
|
||||
{
|
||||
#if defined (M5UNIFIED_PC_BUILD)
|
||||
(void)tz;
|
||||
#else
|
||||
rtc_datetime_t dt;
|
||||
if (getDateTime(&dt))
|
||||
{
|
||||
tm t_st;
|
||||
t_st.tm_isdst = -1;
|
||||
t_st.tm_year = dt.date.year - 1900;
|
||||
t_st.tm_mon = dt.date.month - 1;
|
||||
t_st.tm_mday = dt.date.date;
|
||||
t_st.tm_hour = dt.time.hours;
|
||||
t_st.tm_min = dt.time.minutes;
|
||||
t_st.tm_sec = dt.time.seconds;
|
||||
timeval now;
|
||||
// mktime(3) uses localtime, force UTC
|
||||
char *oldtz = getenv("TZ");
|
||||
setenv("TZ", "GMT0", 1);
|
||||
tzset(); // Workaround for https://github.com/espressif/esp-idf/issues/11455
|
||||
now.tv_sec = mktime(&t_st);
|
||||
if (oldtz)
|
||||
{
|
||||
setenv("TZ", oldtz, 1);
|
||||
} else {
|
||||
unsetenv("TZ");
|
||||
}
|
||||
now.tv_usec = 0;
|
||||
settimeofday(&now, tz);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
153
libraries/M5Unified/src/utility/RTC8563_Class.hpp
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_RTC8563_CLASS_H__
|
||||
#define __M5_RTC8563_CLASS_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#include "I2C_Class.hpp"
|
||||
|
||||
#if __has_include(<sys/time.h>)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
typedef void timezone;
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
namespace m5
|
||||
{
|
||||
struct __attribute__((packed)) rtc_time_t
|
||||
{
|
||||
std::int8_t hours;
|
||||
std::int8_t minutes;
|
||||
std::int8_t seconds;
|
||||
|
||||
rtc_time_t(std::int8_t hours_ = -1, std::int8_t minutes_ = -1, std::int8_t seconds_ = -1)
|
||||
: hours { hours_ }
|
||||
, minutes { minutes_ }
|
||||
, seconds { seconds_ }
|
||||
{}
|
||||
|
||||
rtc_time_t(const tm& t)
|
||||
: hours { (int8_t)t.tm_hour }
|
||||
, minutes { (int8_t)t.tm_min }
|
||||
, seconds { (int8_t)t.tm_sec }
|
||||
{}
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) rtc_date_t
|
||||
{
|
||||
/// year 1900-2099
|
||||
std::int16_t year;
|
||||
|
||||
/// month 1-12
|
||||
std::int8_t month;
|
||||
|
||||
/// date 1-31
|
||||
std::int8_t date;
|
||||
|
||||
/// weekDay 0:sun / 1:mon / 2:tue / 3:wed / 4:thu / 5:fri / 6:sat
|
||||
std::int8_t weekDay;
|
||||
|
||||
rtc_date_t(std::int16_t year_ = 2000, std::int8_t month_ = 1, std::int8_t date_ = -1, std::int8_t weekDay_ = -1)
|
||||
: year { year_ }
|
||||
, month { month_ }
|
||||
, date { date_ }
|
||||
, weekDay { weekDay_ }
|
||||
{}
|
||||
|
||||
rtc_date_t(const tm& t)
|
||||
: year { (int16_t)(t.tm_year + 1900) }
|
||||
, month { (int8_t )(t.tm_mon + 1 ) }
|
||||
, date { (int8_t ) t.tm_mday }
|
||||
, weekDay { (int8_t ) t.tm_wday }
|
||||
{}
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) rtc_datetime_t
|
||||
{
|
||||
rtc_date_t date;
|
||||
rtc_time_t time;
|
||||
rtc_datetime_t() = default;
|
||||
rtc_datetime_t(const rtc_date_t& d, const rtc_time_t& t) : date { d }, time { t } {};
|
||||
rtc_datetime_t(const tm& t) : date { t }, time { t } {}
|
||||
tm get_tm(void) const;
|
||||
void set_tm(tm& time);
|
||||
void set_tm(tm* t) { if (t) set_tm(*t); }
|
||||
};
|
||||
|
||||
class RTC8563_Class : public I2C_Device
|
||||
{
|
||||
public:
|
||||
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x51;
|
||||
|
||||
RTC8563_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 400000, I2C_Class* i2c = &In_I2C)
|
||||
: I2C_Device ( i2c_addr, freq, i2c )
|
||||
{}
|
||||
|
||||
bool begin(I2C_Class* i2c = nullptr);
|
||||
|
||||
bool getVoltLow(void);
|
||||
|
||||
bool getTime(rtc_time_t* time) const;
|
||||
bool getDate(rtc_date_t* date) const;
|
||||
bool getDateTime(rtc_datetime_t* datetime) const;
|
||||
|
||||
void setTime(const rtc_time_t &time);
|
||||
void setTime(const rtc_time_t* const time) { if (time) { setTime(*time); } }
|
||||
|
||||
void setDate(const rtc_date_t &date);
|
||||
void setDate(const rtc_date_t* const date) { if (date) { setDate(*date); } }
|
||||
|
||||
void setDateTime(const rtc_datetime_t &datetime) { setDate(datetime.date); setTime(datetime.time); }
|
||||
void setDateTime(const rtc_datetime_t* const datetime) { if (datetime) { setDateTime(*datetime); } }
|
||||
void setDateTime(const tm* const datetime)
|
||||
{
|
||||
if (datetime)
|
||||
{
|
||||
rtc_datetime_t dt { *datetime };
|
||||
setDateTime(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set timer IRQ
|
||||
/// @param afterSeconds 1 - 15,300. If 256 or more, 1-minute cycle. (max 255 minute.)
|
||||
/// @return the set number of seconds.
|
||||
int setAlarmIRQ(int afterSeconds);
|
||||
|
||||
/// Set alarm by time
|
||||
int setAlarmIRQ(const rtc_time_t &time);
|
||||
int setAlarmIRQ(const rtc_date_t &date, const rtc_time_t &time);
|
||||
|
||||
void setSystemTimeFromRtc(struct timezone* tz = nullptr);
|
||||
|
||||
bool getIRQstatus(void);
|
||||
void clearIRQ(void);
|
||||
void disableIRQ(void);
|
||||
|
||||
rtc_time_t getTime(void) const
|
||||
{
|
||||
rtc_time_t time;
|
||||
getTime(&time);
|
||||
return time;
|
||||
}
|
||||
|
||||
rtc_date_t getDate(void) const
|
||||
{
|
||||
rtc_date_t date;
|
||||
getDate(&date);
|
||||
return date;
|
||||
}
|
||||
|
||||
rtc_datetime_t getDateTime(void) const
|
||||
{
|
||||
rtc_datetime_t res;
|
||||
getDateTime(&res);
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
1148
libraries/M5Unified/src/utility/Speaker_Class.cpp
Normal file
301
libraries/M5Unified/src/utility/Speaker_Class.hpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __M5_Speaker_Class_H__
|
||||
#define __M5_Speaker_Class_H__
|
||||
|
||||
#include "m5unified_common.h"
|
||||
|
||||
#if defined ( ESP_PLATFORM )
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#if __has_include(<driver/i2s_std.h>)
|
||||
#include <driver/i2s_std.h>
|
||||
#else
|
||||
#include <driver/i2s.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#ifndef I2S_PIN_NO_CHANGE
|
||||
#define I2S_PIN_NO_CHANGE (-1)
|
||||
#endif
|
||||
|
||||
namespace m5
|
||||
{
|
||||
class M5Unified;
|
||||
|
||||
struct speaker_config_t
|
||||
{
|
||||
/// i2s_data_out (for spk)
|
||||
int pin_data_out = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// i2s_bck
|
||||
int pin_bck = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// i2s_mclk
|
||||
int pin_mck = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// i2s_ws (lrck)
|
||||
int pin_ws = I2S_PIN_NO_CHANGE;
|
||||
|
||||
/// output sampling rate (Hz)
|
||||
uint32_t sample_rate = 48000;
|
||||
|
||||
/// use stereo output
|
||||
bool stereo = false;
|
||||
|
||||
/// use single gpio buzzer, ( need only pin_data_out )
|
||||
bool buzzer = false;
|
||||
|
||||
/// use DAC speaker, ( need only pin_data_out ) ( for ESP32, only GPIO_NUM_25 or GPIO_NUM_26 )
|
||||
/// ※ for ESP32, need `i2s_port = I2S_NUM_0`. ( DAC+I2S_NUM_1 is not available )
|
||||
bool use_dac = false;
|
||||
|
||||
/// Zero level reference value when using DAC ( 0=Dynamic change )
|
||||
uint8_t dac_zero_level = 0;
|
||||
|
||||
/// multiplier for output value
|
||||
uint8_t magnification = 16;
|
||||
|
||||
/// for I2S dma_buf_len (max 1024)
|
||||
size_t dma_buf_len = 256;
|
||||
|
||||
/// for I2S dma_buf_count
|
||||
size_t dma_buf_count = 8;
|
||||
|
||||
/// background task priority
|
||||
uint8_t task_priority = 2;
|
||||
|
||||
/// background task pinned core
|
||||
uint8_t task_pinned_core = ~0;
|
||||
|
||||
/// I2S port
|
||||
i2s_port_t i2s_port = i2s_port_t::I2S_NUM_0;
|
||||
};
|
||||
|
||||
class Speaker_Class
|
||||
{
|
||||
friend M5Unified;
|
||||
public:
|
||||
virtual ~Speaker_Class(void) {}
|
||||
|
||||
speaker_config_t config(void) const { return _cfg; }
|
||||
void config(const speaker_config_t& cfg) { _cfg = cfg; }
|
||||
|
||||
bool begin(void);
|
||||
|
||||
void end(void);
|
||||
|
||||
bool isRunning(void) const { return _task_running; }
|
||||
|
||||
bool isEnabled(void) const
|
||||
{
|
||||
#if defined (ESP_PLATFORM)
|
||||
return _cfg.pin_data_out >= 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// now in playing or not.
|
||||
/// @return false=not playing / true=playing
|
||||
bool isPlaying(void) const volatile { return _play_channel_bits.load(); }
|
||||
|
||||
/// now in playing or not.
|
||||
/// @param channel virtual channel number. (0~7), (default = automatically selected)
|
||||
/// @return 0=not playing / 1=playing (There's room in the queue) / 2=playing (There's no room in the queue.)
|
||||
size_t isPlaying(uint8_t channel) const volatile { return (channel < sound_channel_max) ? ((bool)_ch_info[channel].wavinfo[0].repeat) + ((bool)_ch_info[channel].wavinfo[1].repeat) : 0; }
|
||||
|
||||
/// Get the number of channels that are playing.
|
||||
/// @return number of channels that are playing.
|
||||
size_t getPlayingChannels(void) const volatile { return __builtin_popcount(_play_channel_bits.load()); }
|
||||
|
||||
/// sets the output master volume of the sound.
|
||||
/// @param master_volume master volume (0~255)
|
||||
void setVolume(uint8_t master_volume) { _master_volume = master_volume; }
|
||||
|
||||
/// gets the output master volume of the sound.
|
||||
/// @return master volume.
|
||||
uint8_t getVolume(void) const { return _master_volume; }
|
||||
|
||||
/// sets the output volume of the sound for the all virtual channel.
|
||||
/// @param volume channel volume (0~255)
|
||||
void setAllChannelVolume(uint8_t volume) { for (size_t ch = 0; ch < sound_channel_max; ++ch) { _ch_info[ch].volume = volume; } }
|
||||
|
||||
/// sets the output volume of the sound for the specified virtual channel.
|
||||
/// @param channel virtual channel number. (0~7)
|
||||
/// @param volume channel volume (0~255)
|
||||
void setChannelVolume(uint8_t channel, uint8_t volume) { if (channel < sound_channel_max) { _ch_info[channel].volume = volume; } }
|
||||
|
||||
/// gets the output volume of the sound for the specified virtual channel.
|
||||
/// @param channel virtual channel number. (0~7)
|
||||
/// @return channel volume.
|
||||
uint8_t getChannelVolume(uint8_t channel) const { return (channel < sound_channel_max) ? _ch_info[channel].volume : 0; }
|
||||
|
||||
/// stop sound output.
|
||||
void stop(void);
|
||||
|
||||
/// stop sound output for the specified virtual channel.
|
||||
/// @param channel virtual channel number. (0~7)
|
||||
void stop(uint8_t channel);
|
||||
|
||||
/// play simple tone sound.
|
||||
/// @param frequency tone frequency (Hz)
|
||||
/// @param duration tone duration (msec)
|
||||
/// @param channel virtual channel number. (0~7), (default = automatically selected)
|
||||
/// @param stop_current_sound true=start a new output without waiting for the current one to finish.
|
||||
/// @param raw_data Single amplitude audio data. 8bit unsigned wav.
|
||||
/// @param array_len size of raw_data.
|
||||
/// @param stereo true=data is stereo / false=data is mono.
|
||||
bool tone(float frequency, uint32_t duration, int channel, bool stop_current_sound, const uint8_t* raw_data, size_t array_len, bool stereo = false)
|
||||
{
|
||||
return _play_raw(raw_data, array_len, false, false, frequency * (array_len >> stereo), stereo, (duration != UINT32_MAX) ? (uint32_t)(duration * frequency / 1000) : UINT32_MAX, channel, stop_current_sound, true);
|
||||
}
|
||||
|
||||
/// play simple tone sound.
|
||||
/// @param frequency tone frequency (Hz)
|
||||
/// @param duration tone duration (msec)
|
||||
/// @param channel virtual channel number. (0~7), (default = automatically selected)
|
||||
bool tone(float frequency, uint32_t duration = UINT32_MAX, int channel = -1, bool stop_current_sound = true) { return tone(frequency, duration, channel, stop_current_sound, _default_tone_wav, sizeof(_default_tone_wav), false); }
|
||||
|
||||
/// play raw sound wave data. (for signed 8bit wav data)
|
||||
/// @param raw_data wave data.
|
||||
/// @param array_len Number of data array elements.
|
||||
/// @param sample_rate the sampling rate (Hz) (default = 44100)
|
||||
/// @param stereo true=data is stereo / false=data is monaural.
|
||||
/// @param repeat number of times played repeatedly. (default = 1)
|
||||
/// @param channel virtual channel number (If omitted, use an available channel.)
|
||||
/// @param stop_current_sound true=start a new output without waiting for the current one to finish.
|
||||
/// @attention If you want to use the data generated at runtime, you can either have three buffers and use them in sequence, or have two buffers and use them alternately, then split them in half and call playRaw twice.
|
||||
/// @attention If noise is present in the output sounds, consider increasing the priority of the task that generates the data.
|
||||
bool playRaw(const int8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false)
|
||||
{
|
||||
return _play_raw(static_cast<const void* >(raw_data), array_len, false, true, sample_rate, stereo, repeat, channel, stop_current_sound, false);
|
||||
}
|
||||
[[deprecated("The playRAW function has been renamed to playRaw")]]
|
||||
bool playRAW(const int8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false)
|
||||
{
|
||||
return _play_raw(static_cast<const void* >(raw_data), array_len, false, true, sample_rate, stereo, repeat, channel, stop_current_sound, false);
|
||||
}
|
||||
|
||||
/// play raw sound wave data. (for unsigned 8bit wav data)
|
||||
/// @param raw_data wave data.
|
||||
/// @param array_len Number of data array elements.
|
||||
/// @param sample_rate the sampling rate (Hz) (default = 44100)
|
||||
/// @param stereo true=data is stereo / false=data is monaural.
|
||||
/// @param repeat number of times played repeatedly. (default = 1)
|
||||
/// @param channel virtual channel number (If omitted, use an available channel.)
|
||||
/// @param stop_current_sound true=start a new output without waiting for the current one to finish.
|
||||
/// @attention If you want to use the data generated at runtime, you can either have three buffers and use them in sequence, or have two buffers and use them alternately, then split them in half and call playRaw twice.
|
||||
/// @attention If noise is present in the output sounds, consider increasing the priority of the task that generates the data.
|
||||
bool playRaw(const uint8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false)
|
||||
{
|
||||
return _play_raw(static_cast<const void* >(raw_data), array_len, false, false, sample_rate, stereo, repeat, channel, stop_current_sound, false);
|
||||
}
|
||||
[[deprecated("The playRAW function has been renamed to playRaw")]]
|
||||
bool playRAW(const uint8_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false)
|
||||
{
|
||||
return _play_raw(static_cast<const void* >(raw_data), array_len, false, false, sample_rate, stereo, repeat, channel, stop_current_sound, false);
|
||||
}
|
||||
|
||||
/// play raw sound wave data. (for signed 16bit wav data)
|
||||
/// @param raw_data wave data.
|
||||
/// @param array_len Number of data array elements.
|
||||
/// @param sample_rate the sampling rate (Hz) (default = 44100)
|
||||
/// @param stereo true=data is stereo / false=data is monaural.
|
||||
/// @param repeat number of times played repeatedly. (default = 1)
|
||||
/// @param channel virtual channel number (If omitted, use an available channel.)
|
||||
/// @param stop_current_sound true=start a new output without waiting for the current one to finish.
|
||||
/// @attention If you want to use the data generated at runtime, you can either have three buffers and use them in sequence, or have two buffers and use them alternately, then split them in half and call playRaw twice.
|
||||
/// @attention If noise is present in the output sounds, consider increasing the priority of the task that generates the data.
|
||||
bool playRaw(const int16_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false)
|
||||
{
|
||||
return _play_raw(static_cast<const void* >(raw_data), array_len, true, true, sample_rate, stereo, repeat, channel, stop_current_sound, false);
|
||||
}
|
||||
|
||||
/// @deprecated "playRAW" function has been renamed to "playRaw"
|
||||
[[deprecated("The playRAW function has been renamed to playRaw")]]
|
||||
bool playRAW(const int16_t* raw_data, size_t array_len, uint32_t sample_rate = 44100, bool stereo = false, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false)
|
||||
{
|
||||
return _play_raw(static_cast<const void* >(raw_data), array_len, true, true, sample_rate, stereo, repeat, channel, stop_current_sound, false);
|
||||
}
|
||||
|
||||
/// play WAV format sound data.
|
||||
/// @param wav_data wave data. (WAV header included)
|
||||
/// @param repeat number of times played repeatedly. (default = 1)
|
||||
/// @param channel virtual channel number (If omitted, use an available channel.)
|
||||
/// @param stop_current_sound true=start a new output without waiting for the current one to finish.
|
||||
bool playWav(const uint8_t* wav_data, size_t data_len = ~0u, uint32_t repeat = 1, int channel = -1, bool stop_current_sound = false);
|
||||
|
||||
protected:
|
||||
|
||||
static constexpr const size_t sound_channel_max = 8;
|
||||
|
||||
static const uint8_t _default_tone_wav[16];
|
||||
|
||||
void setCallback(void* args, bool(*func)(void*, bool)) { _cb_set_enabled = func; _cb_set_enabled_args = args; }
|
||||
|
||||
struct wav_info_t
|
||||
{
|
||||
volatile uint32_t repeat = 0; /// -1 mean infinity repeat
|
||||
uint32_t sample_rate_x256 = 0;
|
||||
const void* data = nullptr;
|
||||
size_t length = 0;
|
||||
union
|
||||
{
|
||||
volatile uint8_t flg = 0;
|
||||
struct
|
||||
{
|
||||
uint8_t is_stereo : 1;
|
||||
uint8_t is_16bit : 1;
|
||||
uint8_t is_signed : 1;
|
||||
uint8_t stop_current : 1;
|
||||
uint8_t no_clear_index : 1;
|
||||
};
|
||||
};
|
||||
void clear(void);
|
||||
};
|
||||
|
||||
struct channel_info_t
|
||||
{
|
||||
wav_info_t wavinfo[2]; // current/next flip info.
|
||||
size_t index = 0;
|
||||
int diff = 0;
|
||||
volatile uint8_t volume = 255; // channel volume (not master volume)
|
||||
volatile bool flip = false;
|
||||
|
||||
float liner_buf[2][2] = { { 0, 0 }, { 0, 0 } };
|
||||
};
|
||||
|
||||
channel_info_t _ch_info[sound_channel_max];
|
||||
|
||||
static void spk_task(void* args);
|
||||
|
||||
esp_err_t _setup_i2s(void);
|
||||
bool _play_raw(const void* wav, size_t array_len, bool flg_16bit, bool flg_signed, float sample_rate, bool flg_stereo, uint32_t repeat_count, int channel, bool stop_current_sound, bool no_clear_index);
|
||||
bool _set_next_wav(size_t ch, const wav_info_t& wav);
|
||||
|
||||
speaker_config_t _cfg;
|
||||
volatile uint8_t _master_volume = 64;
|
||||
|
||||
bool (*_cb_set_enabled)(void* args, bool enabled) = nullptr;
|
||||
void* _cb_set_enabled_args = nullptr;
|
||||
|
||||
volatile bool _task_running = false;
|
||||
std::atomic<uint16_t> _play_channel_bits = { 0 };
|
||||
#if defined (SDL_h_)
|
||||
SDL_Thread* _task_handle = nullptr;
|
||||
#else
|
||||
TaskHandle_t _task_handle = nullptr;
|
||||
volatile SemaphoreHandle_t _task_semaphore = nullptr;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
147
libraries/M5Unified/src/utility/Touch_Class.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright (c) M5Stack. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "Touch_Class.hpp"
|
||||
|
||||
namespace m5
|
||||
{
|
||||
void Touch_Class::update(std::uint32_t msec)
|
||||
{
|
||||
if (msec - _last_msec <= TOUCH_MIN_UPDATE_MSEC) /// Avoid high frequency updates
|
||||
{
|
||||
if (_detail_count == 0) { return; }
|
||||
std::size_t count = 0;
|
||||
for (std::size_t i = 0; i < TOUCH_MAX_POINTS; ++i)
|
||||
{
|
||||
count += update_detail(&_touch_detail[i], msec);
|
||||
}
|
||||
_detail_count = count;
|
||||
return;
|
||||
}
|
||||
|
||||
_last_msec = msec;
|
||||
std::size_t count = _gfx->getTouchRaw(_touch_raw, TOUCH_MAX_POINTS);
|
||||
if (!(count || _detail_count)) { return; }
|
||||
|
||||
uint32_t updated_id = 0;
|
||||
if (count)
|
||||
{
|
||||
m5gfx::touch_point_t tp[TOUCH_MAX_POINTS];
|
||||
memcpy(tp, _touch_raw, sizeof(m5gfx::touch_point_t) * count);
|
||||
_gfx->convertRawXY(tp, count);
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (tp[i].id < TOUCH_MAX_POINTS)
|
||||
{
|
||||
updated_id |= 1 << tp[i].id;
|
||||
update_detail(&_touch_detail[tp[i].id], msec, true, &tp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
for (std::size_t i = 0; i < TOUCH_MAX_POINTS; ++i)
|
||||
{
|
||||
if ((!(updated_id & (1 << i)))
|
||||
&& update_detail(&_touch_detail[i], msec, false, nullptr)
|
||||
&& (count < TOUCH_MAX_POINTS))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
_detail_count = count;
|
||||
}
|
||||
|
||||
bool Touch_Class::update_detail(touch_detail_t* det, std::uint32_t msec, bool pressed, m5gfx::touch_point_t* tp)
|
||||
{
|
||||
touch_state_t tm = det->state;
|
||||
if (tm == touch_state_t::none && !pressed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
tm = static_cast<touch_state_t>(tm & ~touch_state_t::mask_change);
|
||||
if (pressed)
|
||||
{
|
||||
det->prev_x = det->x;
|
||||
det->prev_y = det->y;
|
||||
det->size = tp->size;
|
||||
det->id = tp->id;
|
||||
if (!(tm & touch_state_t::mask_moving))
|
||||
{ // Processing when not flicked.
|
||||
if (tm & touch_state_t::mask_touch)
|
||||
{ // Not immediately after the touch.
|
||||
if (abs(det->base_x - tp->x) > _flickThresh
|
||||
|| abs(det->base_y - tp->y) > _flickThresh)
|
||||
{
|
||||
det->prev = det->base;
|
||||
tm = static_cast<touch_state_t>(tm | touch_state_t::flick_begin);
|
||||
}
|
||||
else
|
||||
if ((tm == touch) && (msec - det->base_msec > _msecHold))
|
||||
{
|
||||
tm = touch_state_t::hold_begin;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*(static_cast<m5gfx::touch_point_t*>(det)) = *tp;
|
||||
tm = touch_state_t::touch_begin;
|
||||
|
||||
if (msec - det->base_msec > _msecHold
|
||||
|| abs(det->base_x - tp->x) > ((_flickThresh+1) << 2) // TODO:検討、タッチ座標が大きくずれた場合にカウントをクリアするか否か
|
||||
|| abs(det->base_y - tp->y) > ((_flickThresh+1) << 2)
|
||||
)
|
||||
{ det->click_count = 0; }
|
||||
det->base_msec = msec;
|
||||
det->base_x = tp->x;
|
||||
det->base_y = tp->y;
|
||||
det->prev = det->base;
|
||||
}
|
||||
}
|
||||
if (tm & mask_moving)
|
||||
{
|
||||
det->x = tp->x;
|
||||
det->y = tp->y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tm = (tm & touch_state_t::mask_touch)
|
||||
? static_cast<touch_state_t>((tm | touch_state_t::mask_change) & ~touch_state_t::mask_touch)
|
||||
: touch_state_t::none;
|
||||
|
||||
if (tm == touch_state_t::touch_end) {
|
||||
// 連続タップ判定のためにbase_msecを更新
|
||||
det->base_msec = msec;
|
||||
det->click_count++;
|
||||
}
|
||||
}
|
||||
det->state = tm;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Touch_Class::update_detail(touch_detail_t* det, std::uint32_t msec)
|
||||
{
|
||||
touch_state_t tm = det->state;
|
||||
if (tm == touch_state_t::none)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
tm = static_cast<touch_state_t>(tm & ~touch_state_t::mask_change);
|
||||
if (tm & touch)
|
||||
{
|
||||
det->prev_x = det->x;
|
||||
det->prev_y = det->y;
|
||||
if ((tm == touch) && (msec - det->base_msec > _msecHold))
|
||||
{
|
||||
tm = touch_state_t::hold_begin;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tm = touch_state_t::none;
|
||||
}
|
||||
det->state = tm;
|
||||
return true;
|
||||
}
|
||||
}
|
||||