diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fd0923e..d0a6bd0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,8 @@ list(APPEND available_tests hw_wrover_kit_blinky i2c_bme280_test spi_4_line_devices_test + i2c_dht12_test + i2c_rtc8563_test ) list(FIND available_tests ${selected_test_project} test_found) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 479aeffd..e23c734e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,4 +9,4 @@ Below is the list of individuals that have contributed to the project. |[KerryRJ](https://github.com/KerryRJ)|[PR#71](https://github.com/PerMalmberg/Smooth/pull/71)| |[jeremyjh](https://github.com/jeremyjh)|[PR#87](https://github.com/PerMalmberg/Smooth/pull/87)| |[COM8](https://github.com/COM8)|[PR#98](https://github.com/PerMalmberg/Smooth/pull/98)| -|[enelson1001](https://github.com/enelson1001)|[PR#116](https://github.com/PerMalmberg/Smooth/pull/116)| +|[enelson1001](https://github.com/enelson1001)|[PR#116](https://github.com/PerMalmberg/Smooth/pull/116), [PR#124](https://github.com/PerMalmberg/Smooth/pull/124)| diff --git a/Documents/AXP173.zh-CN.en.pdf b/Documents/AXP173.zh-CN.en.pdf new file mode 100644 index 00000000..e549c11f Binary files /dev/null and b/Documents/AXP173.zh-CN.en.pdf differ diff --git a/Documents/AXP192 Datasheet v1.13_cn.zh-CN.en.pdf b/Documents/AXP192 Datasheet v1.13_cn.zh-CN.en.pdf new file mode 100644 index 00000000..8a78f0a2 Binary files /dev/null and b/Documents/AXP192 Datasheet v1.13_cn.zh-CN.en.pdf differ diff --git a/Documents/AXP202.pdf b/Documents/AXP202.pdf new file mode 100644 index 00000000..59437659 Binary files /dev/null and b/Documents/AXP202.pdf differ diff --git a/Documents/DHT12.pdf b/Documents/DHT12.pdf new file mode 100644 index 00000000..149459c4 Binary files /dev/null and b/Documents/DHT12.pdf differ diff --git a/Documents/PCF8563.pdf b/Documents/PCF8563.pdf new file mode 100644 index 00000000..6e600f6b Binary files /dev/null and b/Documents/PCF8563.pdf differ diff --git a/Documents/README-AXP.md b/Documents/README-AXP.md new file mode 100644 index 00000000..dd5ecd85 --- /dev/null +++ b/Documents/README-AXP.md @@ -0,0 +1,295 @@ +# How to use AxpPMU +This readme file explains how to use the AxpPMU.cpp associated files in a project. You can use the AxpPMU as a standalone class or you can inherit the class and override some functions and add additional functions. The AxpPMU class was designed to allow the user easily manipulate a register or registers to configure the device. The following are some of the functions provided by the AxpPMU class. +### Write a byte to a register +bool write_register(AxpRegister reg, uint8_t data_byte); +### Read a byte from register +bool read_register(AxpRegister reg, uint8_t& read_data); +### Write a bit to a register +bool write_register_bit(AxpRegister reg, bool bit, uint8_t bit_position); +### Read a bit from a register +bool read_register_bit(AxpRegister reg, bool& bit, uint8_t bit_position); +### Write an adjoining group of bits to a register +bool write_register_bits(AxpRegister reg, uint8_t mask, uint8_t left_shift_count, uint8_t write_data); +### Read an adjoining group of bits from a register +bool read_register_bits(AxpRegister reg, uint8_t mask, uint8_t right_shift_count, uint8_t& read_data); +### Write initialization registers to configure the chip +bool write_init_regs(const AxpInitReg* init_regs, size_t length); + + +## 1. An example to use as standalone class +The following is a simple example of using the AxpPMU as a standalone class for the M5StickC. The M5StickC has the AXP192 chip integrated into its product. + +First create an Axp192Init.h file that is used to configure/initialize the Axp192 on startup. This configuration of the AXP192 is for the M5StickC product other products will probably require a differnt configuration. +``` C++ code +Axp192Init.h + +namespace some_namespace_name +{ + static constexpr std::array axp192_init_cmds_1 = + { { + // #1 ------------------------------------------------------------------------------------- + // Configure LDO2 and LDO3 --- LDO2 = LDO3 = 3.0V + // b[7:4] = 1100 xxxx = set LDO2 to 3.00V = (3.00-1.8)/0.1 = 12 = 0xC + // b[3:0] = xxxx 1100 = set LDO3 to 3.00V = (3.00-1.8)/0.1 = 12 = 0xC + { smooth::application::sensor::AxpRegister::Reg28H_Ldo2_Ldo3_VSet, 0xCC }, + + // #2 ------------------------------------------------------------------------------------- + // Configure ADC sampling rate --- ADC sampling rate = 200Hz + // b[7:6] = 11xx xxxx = set adc sampling rate for 200Hz + // b[5:4] = xx11 xxxx = set TS output current to 80uA + // b3 = xxxx 0xxx = reserved or not used + // b2 = xxxx x0xx = set TS pin function to battery temeprature monitoring function + // b[1:0] = xxxx xx10 = set TS pin as input when ADC is sampling + { smooth::application::sensor::AxpRegister::Reg84H_Adc_Sample_Rate, 0xF2 }, + + // #3 ------------------------------------------------------------------------------------- + // Configure ADC --- Enable all ADC's + // b7 = 1xxx xxxx = enable battery voltage ADC + // b6 = x1xx xxxx = enable battery current ADC + // b5 = xx1x xxxx = enable ACIN voltage ADC + // b4 = xxx1 xxxx = enable ACIN current ADC + // b3 = xxxx 1xxx = enable VBUS voltage ADC + // b2 = xxxx x1xx = enable VBUS current ADC + // b1 = xxxx xx1x = enable APS voltage ADC + // b0 = xxxx xxx1 = enable TS pin ADC + { smooth::application::sensor::AxpRegister::Reg82H_Adc_Enable_1, 0xFF }, + + // #4 ------------------------------------------------------------------------------------- + // Configure battery charging control 1 --- Set charging current to 100mA + // b7 = 1xxx xxxx = enable battery charger + // b[6:5] = x10x xxxx = target voltage 4.20V + // b4 = xxx0 xxxx = end charging current when charging current is <10% + // b[3:0] = xxxx 0000 = charging current is 100mA + { smooth::application::sensor::AxpRegister::Reg33H_Chrg_Control_1, 0xC0 }, + + // #5 ------------------------------------------------------------------------------------- + // Configure battery charging control 2 --- + // b[7:6] = 01xx xxxx = precharge timeout 40 minutes + // b[5:3] = xx00 0xxx = ext charge current 300ma (not used) + // b2 = xxxx x0xx = disable external charging + // b[1:0] = xxxx xx01 = timeout constant current mode 8 hours + { smooth::application::sensor::AxpRegister::Reg34H_Chrg_Control_2, 0x41 }, + + // #6 ------------------------------------------------------------------------------------- + // Configure GPIO_0 pin function --- Configure GPIO to function as LDO + // b[7:3] = 0000 0xxx = reserved or not used + // b[2:0] = xxxx x010 = set GPIO0 pin function to LDO low noise output + { smooth::application::sensor::AxpRegister::Reg90H_Gpio0_Func_Set, 0x02 }, + + // #7 ------------------------------------------------------------------------------------- + // Configure GPIO_0 voltage --- GPIO0 output voltage to 3.3V + // b[7:4] = 1111 xxxx = set GPIO_0 (LDOio0) voltage to 3.3V + // b[3:0] = xxxx 0000 = reserved or not used + { smooth::application::sensor::AxpRegister::Reg91H_Gpio0_Volt_Set, 0xF0 }, + + // #8 ------------------------------------------------------------------------------------- + // Configure Power Enable Key (PEK) --- Automatic shutdown when key press is GT 6 seconds + // b[7:6] = 00xx xxxx = startup time for 128ms + // b[5:4] = xx00 xxxx = long press time for 1 second + // b3 = xxxx 1xxx = automatic shutdown when key pressed time exceeds shutdown time is enabled + // b2 = xxxx x0xx = PWROK signal delay after the power startup for 32mS + // b[1:0] = xxxx xx01 = shutdown time for 6 seconds + { smooth::application::sensor::AxpRegister::Reg36H_PEK_Key_Setting, 0x09 }, // change times + + // #9 ------------------------------------------------------------------------------------- + // Configure VBUS-IPSOUT Power Path --- Use VBUS as input power (if useable) regardless of N_VBUSEN + // b7 = 1xxx xxxx = use VBUS as input power (if useable) regardless of N_VBUSEN + // b6 = x0xx xxxx = VBUS Vhold voltage is not limited + // b[5:3] = xx00 0xxx = Vhold = 4.0v (not used because of bit b6) + // b2 = xxxx x0xx = unchanged or not used + // b[1:0] = xxxx xx00 = Vbus current limit = 900ma + { smooth::application::sensor::AxpRegister::Reg30H_Ips_Out_Mngmnt, 0x80 }, + + // #10 ------------------------------------------------------------------------------------ + // Configure battery charging high temperature voltage threshold on TS pin + // b[7:0] = 1111 1100 = voltage threshold = 252 * 16 * 0.0008 = 3.2256V + { smooth::application::sensor::AxpRegister::Reg39H_Vhth_Chrg_Set, 0xFC }, + + // #11 ------------------------------------------------------------------------------------ + // Configure backup battery charge control --- Enable backup battery charging + // b7 = 1xxx xxxx = enable backup battery charging + // b[6:5] = x10x xxxx = backup battery charging target voltage for 3.0V + // b[4:2] = xxx0 00xx = reserved or not used + // b[1:0] = xxxx xx10 = backup battery charging current for 200uA + { smooth::application::sensor::AxpRegister::Reg35H_Backup_Chrg_Ctl, 0xA2 }, + + // #12 ------------------------------------------------------------------------------------ + // Configure shutdown and Chrg LED --- Do not shutdown + // b7 = 0xxx xxxx = do not shutdown axp192 + // b6 = x1xx xxxx = enable battery monitoring + // b[5:4] = xx00 xxxx = set CHGLED pin to high resistance + // b3 = xxxx 0xxx = CHGLED pin function controlled by bits b[5:4] + // b2 = xxxx x0xx = reserved or not used + // b[1:0] = xxxx xx10 = shutdown delay after N_OE changes from low to high + { smooth::application::sensor::AxpRegister::Reg32H_Shutdn_ChrgLed, 0x42 }, + + // #13 ------------------------------------------------------------------------------------ + // Configure coulomb counter --- Enable coulomb counter + // b7 = 1xxx xxxx = enable coulomb counter + // b6 = x0xx xxxx = do not suspend coulomb counter + // b5 = xx0x xxxx = do not stop coulomb counter + // b[4:0] = reserved not used + { smooth::application::sensor::AxpRegister::RegB8H_Coulomb_Counter_Ctrl, 0x80 }, + + // #14 ------------------------------------------------------------------------------------ + // Configure power output control --- Enable all outputs except DC-DC2 + // b7 = 0xxx xxxx = reserved or not used + // b6 = x1xx xxxx = enable EXTEN for enabling external DC-DC regulator for HAT + // b5 = xx0x xxxx = reserved or not used + // b4 = xxx0 xxxx = disabled DC-DC2 power output + // b3 = xxxx 1xxx = enable LDO3 power output + // b2 = xxxx x1xx = enable LDO2 power output + // b1 = xxxx xx1x = enable DC-DC3 power output + // b0 = xxxx xxx1 = enable DC-DC1 power output + { smooth::application::sensor::AxpRegister::Reg12H_Power_Out_Ctrl, 0x4F } + } }; +} +``` + +Second, create M5StickC.h that uses the Axp192 +``` C++ code +M5StickC.h + +#pragma once + +#include // for unique_ptr +#include +#include + +namespace some_namespace_name +{ + class M5StickC + { + public: + /// Constructor + M5StickC(); + + /// Initialize the M5StickC + void initialize(); + + /// Set screen brightness + /// \param brightness The brightness level; 0x00=1.8V=Dark, 0x0F=3.3V=Bright + void set_screen_brightness(uint8_t brightness); + + /// Print the Axp192 measurements + void print_axp192_report(); + + private: + + /// Initialize the Axp192 device + void initialize_axp192(); + + smooth::core::io::i2c::Master i2c0_master; + std::unique_ptr axp192{}; + bool axp192_initialized { false }; + }; +} +``` +Third, create M5StickC.cpp that uses the Axp192. +``` C++ code +M5StickC.cpp + +#include "model/M5StickC.h" +#include "model/Axp192Init.h" + +#include +#include + +using namespace smooth::core::logging; +using namespace smooth::core::ipc; +using namespace smooth::application::sensor; + +namespace some_namespace_name +{ + // Class constants + static const char* TAG = "M5StickC"; + + // Constructor + M5StickC::M5StickC() : i2c0_master(I2C_NUM_0, // I2C Port 0 + GPIO_NUM_22, // SCL pin + true, // SCL internal pullup enabled + GPIO_NUM_21, // SDA pin + true, // SDA internal pullup enabled + 400 * 1000) // clock frequency - 400kHz + { + } + + // Initialize the M5StickC + void M5StickC::initialize() + { + initialize_axp192(); + } + + // Set screen brightness - LDO2 voltage is used to adjust screen brightness + void M5StickC::set_screen_brightness(uint8_t brightness) + { + // max brightness level is 12 = 3.00V + brightness = brightness > 12 ? 12 : brightness; + axp192->write_register_bits(AxpRegister::Reg28H_Ldo2_Ldo3_VSet, 0x0F, 4, brightness); + } + + // Initialize the AXP192 + void M5StickC::initialize_axp192() + { + auto device = i2c0_master.create_device(0x34); // AXP192 i2c device address = 0x34 + Log::info(TAG, "Scanning for Axp192 ---- {}", device->is_present() ? "device found" : "device NOT present"); + + if (device->is_present()) + { + Log::info(TAG, "Configuring AXP192"); + axp192_initialized = device->write_init_regs(axp192_init_cmds_1.data(), axp192_init_cmds_1.size()); + + if (axp192_initialized) + { + axp192 = std::move(device); + } + } + + Log::info(TAG, "Axp192 initialization --- {}", axp192_initialized ? "Succeeded" : "Failed"); + } + + // Print Ax192 measurements + void M5StickC::print_axp192_report() + { + float value; + Log::error(TAG, "==============================================="); + + axp192->get_ts_voltage(value); + Log::warning(TAG, "TS voltage = {:.2f} V", value); + + axp192->get_acin_voltage(value); + Log::warning(TAG, "ACIN voltage = {:.2f} V", value); + + axp192->get_acin_current(value); + Log::warning(TAG, "ACIN current = {:.2f} mA", value); + + axp192->get_vbus_voltage(value); + Log::warning(TAG, "VBUS voltage = {:.2f} V", value); + + axp192->get_vbus_current(value); + Log::warning(TAG, "VBUS current = {:.2f} mA", value); + + axp192->get_battery_voltage(value); + Log::warning(TAG, "BATT voltage = {:.2f} V", value); + + axp192->get_aps_voltage(value); + Log::warning(TAG, "APS voltage = {:.2f} V", value); + + axp192->get_axp_device_temperature(value); + Log::warning(TAG, "Device temperature = {:.2f} C", value); + + axp192->get_battery_charging_current(value); + Log::warning(TAG, "BATT charging current = {:.2f} mA", value); + + axp192->get_battery_discharging_current(value); + Log::warning(TAG, "BATT discharging current = {:.2f} mA", value); + + axp192->get_battery_power(value); + Log::warning(TAG, "BATT power = {:.2f} mW", value); + + axp192->get_battery_capacity(value); + Log::warning(TAG, "BATT capacity = {:.2f} mAh", value); + } +} +``` + diff --git a/Documents/SH1107_v2.1.pdf b/Documents/SH1107_v2.1.pdf new file mode 100644 index 00000000..9fc3d070 Binary files /dev/null and b/Documents/SH1107_v2.1.pdf differ diff --git a/Documents/ST7735S_v1.3.pdf b/Documents/ST7735S_v1.3.pdf new file mode 100644 index 00000000..9d270ebc Binary files /dev/null and b/Documents/ST7735S_v1.3.pdf differ diff --git a/README.md b/README.md index d125d771..cb725bad 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,14 @@ Smooth is developed on a Linux machine so how well it compiles using the Windows - BME280 - Displays - ILI9341 + - ST7735 + - SH1107 - I2C - BME280 - MCP23017 + - DHT12 + - AxpPMU + - PCF8563 - RGB LED, i.e. WS2812(B), SK6812, WS2813, (a.k.a NeoPixel). - Filesystem helpers @@ -261,4 +266,4 @@ int main(int /*argc*/, char** /*argv*/) #endif } -``` \ No newline at end of file +``` diff --git a/lib/files.cmake b/lib/files.cmake index 64beac50..6f5aa269 100644 --- a/lib/files.cmake +++ b/lib/files.cmake @@ -4,13 +4,16 @@ set(smooth_dir ${CMAKE_CURRENT_LIST_DIR}/smooth) set(smooth_inc_dir ${CMAKE_CURRENT_LIST_DIR}/smooth/include/smooth) set(SMOOTH_SOURCES - ${smooth_dir}/application/display/ILI9341.cpp + ${smooth_dir}/application/display/DisplaySpi.cpp ${smooth_dir}/application/hash/base64.cpp ${smooth_dir}/application/hash/sha.cpp ${smooth_dir}/application/io/i2c/ADS1115.cpp ${smooth_dir}/application/io/i2c/BME280.cpp ${smooth_dir}/application/io/i2c/MCP23017.cpp ${smooth_dir}/application/io/i2c/MCP23017.cpp + ${smooth_dir}/application/io/i2c/DHT12.cpp + ${smooth_dir}/application/io/i2c/AxpPMU.cpp + ${smooth_dir}/application/io/i2c/PCF8563.cpp ${smooth_dir}/application/io/spi/BME280SPI.cpp ${smooth_dir}/application/io/spi/BME280Core.cpp ${smooth_dir}/application/io/wiegand/Wiegand.cpp @@ -93,12 +96,21 @@ set(SMOOTH_SOURCES ${smooth_dir}/core/timer/Timer.cpp ${smooth_dir}/core/timer/TimerService.cpp ${smooth_dir}/core/util/string_util.cpp + ${smooth_inc_dir}/application/display/DisplayPin.h + ${smooth_inc_dir}/application/display/DisplaySpi.h + ${smooth_inc_dir}/application/display/DisplayTypes.h + ${smooth_inc_dir}/application/display/DisplayCommands.h ${smooth_inc_dir}/application/display/ILI9341.h - ${smooth_inc_dir}/application/display/ILI9341_init_cmds.h + ${smooth_inc_dir}/application/display/SH1107.h + ${smooth_inc_dir}/application/display/ST7735.h ${smooth_inc_dir}/application/io/spi/BME280SPI.h ${smooth_inc_dir}/application/io/spi/BME280Core.h ${smooth_inc_dir}/application/io/i2c/ADS1115.h ${smooth_inc_dir}/application/io/i2c/MCP23017.h + ${smooth_inc_dir}/application/io/i2c/DHT12.h + ${smooth_inc_dir}/application/io/i2c/AxpPMU.h + ${smooth_inc_dir}/application/io/i2c/AxpRegisters.h + ${smooth_inc_dir}/application/io/i2c/PCF8563.h ${smooth_inc_dir}/application/network/http/HTTPProtocol.h ${smooth_inc_dir}/application/network/http/HTTPServer.h ${smooth_inc_dir}/application/network/http/HTTPServerClient.h @@ -167,7 +179,6 @@ set(SMOOTH_SOURCES ${smooth_inc_dir}/core/io/spi/SPIDevice.h ${smooth_inc_dir}/core/io/spi/SpiDmaFixedBuffer.h ${smooth_inc_dir}/core/io/Input.h - ${smooth_inc_dir}/core/io/Input.h ${smooth_inc_dir}/core/io/InterruptInput.h ${smooth_inc_dir}/core/io/InterruptInputCB.h ${smooth_inc_dir}/core/io/Output.h diff --git a/lib/smooth/application/display/ILI9341.cpp b/lib/smooth/application/display/DisplaySpi.cpp similarity index 50% rename from lib/smooth/application/display/ILI9341.cpp rename to lib/smooth/application/display/DisplaySpi.cpp index 37494a36..cd7a40c4 100644 --- a/lib/smooth/application/display/ILI9341.cpp +++ b/lib/smooth/application/display/DisplaySpi.cpp @@ -16,8 +16,8 @@ limitations under the License. */ #include #include -#include "smooth/application/display/ILI9341.h" -#include "smooth/application/display/ILI9341_init_cmds.h" +#include "smooth/application/display/DisplaySpi.h" +#include "smooth/application/display/DisplayCommands.h" #include "smooth/core/io/spi/SpiDmaFixedBuffer.h" #include "smooth/core/logging/log.h" @@ -26,26 +26,24 @@ using namespace smooth::core::io::spi; namespace smooth::application::display { - static const char* TAG = "ILI9314"; + static const char* TAG = "DisplaySpi"; static const bool PIN_HIGH = true; static const bool PIN_LOW = false; - ILI9341::ILI9341(std::mutex& guard, - gpio_num_t chip_select_pin, - gpio_num_t data_command_pin, - gpio_num_t reset_pin, - gpio_num_t back_light_pin, - uint8_t spi_command_bits, - uint8_t spi_address_bits, - uint8_t bits_between_address_and_data_phase, - uint8_t spi_mode, - uint8_t spi_positive_duty_cycle, - uint8_t spi_cs_ena_posttrans, - int spi_clock_speed_hz, - uint32_t spi_device_flags, - int spi_queue_size, - bool use_pre_transaction_callback, - bool use_post_transaction_callback) + DisplaySpi::DisplaySpi(std::mutex& guard, + gpio_num_t chip_select_pin, + gpio_num_t data_command_pin, + uint8_t spi_command_bits, + uint8_t spi_address_bits, + uint8_t bits_between_address_and_data_phase, + uint8_t spi_mode, + uint8_t spi_positive_duty_cycle, + uint8_t spi_cs_ena_posttrans, + int spi_clock_speed_hz, + uint32_t spi_device_flags, + int spi_queue_size, + bool use_pre_transaction_callback, + bool use_post_transaction_callback) : SPIDevice(guard, spi_command_bits, spi_address_bits, @@ -59,134 +57,187 @@ namespace smooth::application::display use_pre_transaction_callback, use_post_transaction_callback), - reset_pin(reset_pin, true, false, false), - backlight_pin(back_light_pin, true, false, false), - dc_pin(data_command_pin, true, false, false), - cs_pin(chip_select_pin, true, false, false) + dc_pin(data_command_pin, true, false, false), // GPIO_MODE_OUTPUT, no pullup, no pulldown + cs_pin(chip_select_pin, true, false, false) // GPIO_MODE_OUTPUT, no pullup, no pulldown { } // Initialize the display - bool ILI9341::init(spi_host_device_t host) + bool DisplaySpi::init(spi_host_device_t host) { + // preset chip select pin high + cs_pin.set(PIN_HIGH); + // spi_transaction will not control chip select return initialize(host, GPIO_NUM_NC); } - // I have found different displays set the value of madctl differently - // for the same rotation. For example M5Stack sets madctl to 0x68 for - // portrait while the adafruit 2.88 dipslay sets madctl to 0x48. To make - // driver more versatile I have used madctl_value instead of the typical - // 0-3 screen_rotation_value that is used in switch statement. + // Most display ICs use MADCTL to set screen orientation. Typically there + // are four different screen orientations - Portrait, Landsacpe, + // Portrait-Flipped and Landscape-Flipped. The problem is there is not + // a universal standard for setting the MADCTL value for a particular + // screen oreintation. For example M5Stack uses a MADCT value of 0x68 + // for portrait while the Adafruit 2.88 dipslay uses a MADCTL value of 0x48. + // To make driver more versatile the function set_madctl is used to allow user + // to set the screen orientation instead of the typical 0-3 + // screen_rotation_constant that is used in switch statement. // If you are using LittlevGL then you need to set the appropriate screen // width and screen height for the rotation you have chosen in the lvconf.h file. - bool ILI9341::set_rotation(uint8_t madctl_value) + bool DisplaySpi::set_madctl(uint8_t value, uint8_t madctl_reg) { - bool res = lcd_wr_cmd(0x36); - res &= lcd_wr_data(&madctl_value, 1); + return send_cmd(madctl_reg) & send_data(&value, 1); + } - return res; + // Hardware reset + void DisplaySpi::hw_reset(bool active_low, + std::chrono::milliseconds active_time, + std::chrono::milliseconds delay_time) + { + if (reset_pin != nullptr) + { + if (active_low) + { + reset_pin->set_output_level(PIN_LOW); // force the display chip to reset + std::this_thread::sleep_for(active_time); + reset_pin->set_output_level(PIN_HIGH); + std::this_thread::sleep_for(delay_time); + } + else + { + reset_pin->set_output_level(PIN_HIGH); // force the display chip to reset + std::this_thread::sleep_for(active_time); + reset_pin->set_output_level(PIN_HIGH); + std::this_thread::sleep_for(delay_time); + } + } } - // Reset the display - void ILI9341::reset_display() + // Software reset + bool DisplaySpi::sw_reset(std::chrono::milliseconds delay_time) { - reset_pin.set(PIN_LOW); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - reset_pin.set(PIN_HIGH); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool res = send_cmd(static_cast(LcdCmd::SWRESET)); + std::this_thread::sleep_for(delay_time); + + return res; } // Send initialize sequence of commands and data to display - bool ILI9341::send_init_cmds() + bool DisplaySpi::send_init_cmds(const DisplayInitCmd* init_cmds, size_t length) { bool res = true; - uint16_t seq_num = 0; - - while (ili_init_cmds.at(seq_num).databytes != 0xff) + // check res everytime thru loop + for (size_t index = 0; res && index < length; index++) { - // check if delay is needed; send only command then delay - if (ili_init_cmds.at(seq_num).databytes & 0x80) + // send command + res &= send_cmd(init_cmds[index].cmd); + + // send data + uint8_t data_length = init_cmds[index].length; + + if (data_length >= 1 && data_length < 0x80) { - res &= lcd_wr_cmd(ili_init_cmds.at(seq_num).cmd); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res &= send_data(init_cmds[index].data, data_length); } - else + + // check to see if we have to delay before advancing to next sequence in ints_cmds + if (data_length >= 0x80) { - res &= lcd_wr_cmd(ili_init_cmds.at(seq_num).cmd); - res &= lcd_wr_data(ili_init_cmds.at(seq_num).data, ili_init_cmds.at(seq_num).databytes & 0x1F); + uint8_t delay_data_length = static_cast(data_length & 0x7F); + size_t delay_time = init_cmds[index].data[delay_data_length]; + + // check delay_data_length to see if there is data to send before delaying + if (delay_data_length > 0) + { + res &= send_data(init_cmds[index].data, delay_data_length); + } + + // delay value of 255 is really 500 + delay_time = delay_time == 255 ? 500 : delay_time; + std::this_thread::sleep_for(std::chrono::milliseconds(delay_time)); } - - // advance to next sequence in ili_ints_cmds - seq_num++; } return res; } - // Send a command to the LCD. Uses spi_device_polling_write, which waits - // until the transfer is complete. - // - // Since command transactions are usually small, they are handled in polling - // mode for higher speed. The overhead of interrupt transactions is more than - // just waiting for the transaction to complete. - bool ILI9341::lcd_wr_cmd(const uint8_t cmd) + // Send muliple commands to the display. + bool DisplaySpi::send_cmds(const uint8_t* cmds, size_t length) + { + // reset current transaction counter + current_transaction = 0; + + // setup pre and post control pin states + prepare_pins_for_cmd_transaction(0); + + // create transaction + std::lock_guard lock(get_guard()); + spi_transaction_t trans{}; // zero out the transaction + trans.length = length * 8; + trans.tx_buffer = cmds; + + return polling_write(trans); + } + + // Send a single command to the display. + bool DisplaySpi::send_cmd(const uint8_t cmd) { // reset current transaction counter current_transaction = 0; // setup pre and post control pin states - dc_pretrans_pin_states.at(0) = PIN_LOW; - cs_pretrans_pin_states.at(0) = PIN_LOW; - cs_posttrans_pin_states.at(0) = PIN_HIGH; + prepare_pins_for_cmd_transaction(0); + // create transaction std::lock_guard lock(get_guard()); - spi_transaction_t trans; - std::memset(&trans, 0, sizeof(trans)); //Zero out the transaction - trans.rx_buffer = nullptr; - trans.rxlength = 0; + spi_transaction_t trans{}; // zero out the transaction trans.length = 8; trans.tx_buffer = &cmd; - bool res = polling_write(trans); - - return res; + return polling_write(trans); } - // Send data to the LCD. Uses spi_device_polling_write, which waits until the - // transfer is complete. - bool ILI9341::lcd_wr_data(const uint8_t* data, size_t length) + // Send data to the display + bool DisplaySpi::send_data(const uint8_t* data, size_t length) { // reset current transaction counter current_transaction = 0; // setup pre and post control pin states - dc_pretrans_pin_states.at(0) = PIN_HIGH; - cs_pretrans_pin_states.at(0) = PIN_LOW; - cs_posttrans_pin_states.at(0) = PIN_HIGH; + prepare_pins_for_data_transaction(0); + // create transaction std::lock_guard lock(get_guard()); - spi_transaction_t trans; - std::memset(&trans, 0, sizeof(trans)); //Zero out the transaction - trans.rx_buffer = nullptr; - trans.rxlength = 0; + spi_transaction_t trans{}; // zero out the transaction trans.length = length * 8; trans.tx_buffer = data; - bool res = polling_write(trans); + return polling_write(trans); + } - return res; + // Prepare pins for command transaction + void DisplaySpi::prepare_pins_for_cmd_transaction(size_t trans_num) + { + dc_pretrans_pin_states[trans_num] = PIN_LOW; + cs_pretrans_pin_states[trans_num] = PIN_LOW; + cs_posttrans_pin_states[trans_num] = PIN_HIGH; + } + + // Prepare pins for data transaction + void DisplaySpi::prepare_pins_for_data_transaction(size_t trans_num) + { + dc_pretrans_pin_states[trans_num] = PIN_HIGH; + cs_pretrans_pin_states[trans_num] = PIN_LOW; + cs_posttrans_pin_states[trans_num] = PIN_HIGH; } // To send a set of lines we have to send a command, 2 data bytes, another command, // 2 more data bytes and another command before sending the lines of data itself; - // a total of 6 transactions. (We can't put all of this in just one transaction - // because the D/C line needs to be toggled in the middle.) + // a total of 6 transactions. We can't put all of this in just one transaction + // because the D/C line needs to be toggled in the middle. // This routine queues these commands up as interrupt transactions so they get - // sent faster (compared to calling spi_device_transmit several times), and - // meanwhile the lines for next transactions can get calculated. - bool ILI9341::send_lines(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const uint8_t* data, size_t length) + // sent faster (compared to calling spi_device_transmit several times) + bool DisplaySpi::send_lines(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const uint8_t* data, size_t length) { // if length is 0 nothing to do and probably an error so return bool res = length > 0; @@ -198,70 +249,55 @@ namespace smooth::application::display // reset current transaction counter current_transaction = 0; - // Transaction descriptors. Declared static so they're not allocated on the - // stack; we need this memory even when this function is finished because - // the SPI driver needs access to it even while we're already calculating - // the next line. + // create transactions static std::array trans{}; std::size_t x = 0; std::for_each(trans.begin(), trans.end(), [&](auto& current) { - //Zero out the transaction + // zero out the transaction std::memset(¤t, 0, sizeof(current)); if ((x & 1) == 0) { - // Even transfers are commands - current.rx_buffer = nullptr; - current.rxlength = 0; + // even transfers are commands current.length = 8; - - // setup pre and post states - dc_pretrans_pin_states[x] = PIN_LOW; - cs_pretrans_pin_states[x] = PIN_LOW; - cs_posttrans_pin_states[x] = PIN_HIGH; + prepare_pins_for_cmd_transaction(x); } else { - // Odd transfers are data - current.rx_buffer = nullptr; - current.rxlength = 0; + // odd transfers are data current.length = 8 * 4; - - // setup pre and post states - dc_pretrans_pin_states[x] = PIN_HIGH; - cs_pretrans_pin_states[x] = PIN_LOW; - cs_posttrans_pin_states[x] = PIN_HIGH; + prepare_pins_for_data_transaction(x); } current.flags = SPI_TRANS_USE_TXDATA; x++; }); - // Column addresses - trans[0].tx_data[0] = 0x2A; + // column addresses + trans[0].tx_data[0] = 0x2A; // CASET trans[1].tx_data[0] = static_cast((x1 >> 8) & 0xFF); // Start Col High trans[1].tx_data[1] = static_cast(x1 & 0xFF); // Start Col Low trans[1].tx_data[2] = static_cast((x2 >> 8) & 0xFF); // End Col High trans[1].tx_data[3] = static_cast(x2 & 0xFF); // End Col Low - // Page addresses - trans[2].tx_data[0] = 0x2B; + // page addresses + trans[2].tx_data[0] = 0x2B; // PASET trans[3].tx_data[0] = static_cast((y1 >> 8) & 0xFF); // Start page high trans[3].tx_data[1] = static_cast(y1 & 0xFF); // start page low trans[3].tx_data[2] = static_cast((y2 >> 8) & 0xFF); // end page high trans[3].tx_data[3] = static_cast(y2 & 0xFF); // end page low - // Ram Write - trans[4].tx_data[0] = 0x2C; // memory write + // ram Write + trans[4].tx_data[0] = 0x2C; // RAMWR - RAM write - // Data to send + // data to send trans[5].tx_buffer = data; trans[5].length = length * 8; trans[5].flags = 0; // clear flags - // Queue all transactions. + // queue all transactions. std::for_each(trans.begin(), trans.end(), [&](auto& t) { res &= queue_transaction(t); @@ -272,7 +308,7 @@ namespace smooth::application::display } // Wait for queued transactions to finish - bool ILI9341::wait_for_send_lines_to_finish() + bool DisplaySpi::wait_for_send_lines_to_finish() { bool res = true; @@ -286,18 +322,18 @@ namespace smooth::application::display return res; } - // There are a few registers that can be read from the ILI9341 and some - // EXTENDED Command set of registers that some displays may support (page 85) + // There are a few registers that can be read from the display chip and some + // EXTENDED Command set of registers that some display chips may support. // // See pages 36-39 of the ili9341 data sheet that relates to the following discussion. - // When reading more than 1 parameter, the ILI9341 requires one dummy clock cycle before + // When reading more than 1 parameter, the ili9341 requires one dummy clock cycle before // reading the data. For example Reg 0x04 Read Display ID info requires 25 clock cycles. // It is not easy to do this with the ESP32 SPI, so what we will do is add an extra byte // to read and produce 32 clock cycles. The data read will have to be manipulated because // the MSB bit of the first parameter read contains the dummy clock data bit. We need to // shift out the dummy data bit and recovery the MSB bit from the extra byte added to the read. // NOTE: FOR READING PARAMS SCK must be 16MHz or less the datasheet recommends 10MHz. - bool ILI9341::read_params(uint8_t cmd, std::vector& data, uint32_t param_count) + bool DisplaySpi::read_params(uint8_t cmd, std::vector& data, uint32_t param_count) { SpiDmaFixedBuffer rxdata; @@ -331,7 +367,7 @@ namespace smooth::application::display } // Read a register or a sequence of registers from the display - bool ILI9341::read(uint8_t cmd, uint8_t* rxdata, size_t length) + bool DisplaySpi::read(uint8_t cmd, uint8_t* rxdata, size_t length) { // if length is 0 nothing to do and probably an error so return bool res = length > 0; @@ -341,43 +377,33 @@ namespace smooth::application::display std::lock_guard lock(get_guard()); // create 2 spi transactions; one for command and one for reading parameters - std::array trans{}; - - std::for_each(trans.begin(), trans.end(), [](auto& t) - { - //Zero out the transaction - std::memset(&t, 0, sizeof(t)); - }); + std::array trans{}; // zero out the transaction // reset current transaction counter current_transaction = 0; - // configure command transaction + // create command transaction auto& cmd_trans = trans[0]; - cmd_trans.rx_buffer = nullptr; - cmd_trans.rxlength = 0; cmd_trans.length = 8; cmd_trans.tx_data[0] = cmd; cmd_trans.flags = SPI_TRANS_USE_TXDATA; // setup pre and post control pin states - dc_pretrans_pin_states[0] = PIN_LOW; - cs_pretrans_pin_states[0] = PIN_LOW; - cs_posttrans_pin_states[0] = PIN_LOW; // keep chip select low + prepare_pins_for_cmd_transaction(0); - // configure read parameters transaction + // for reading parameter(s) we need to keep post trans cs pin low + cs_posttrans_pin_states[0] = PIN_LOW; + + // create read parameters transaction auto& read_params = trans[1]; read_params.rx_buffer = rxdata; read_params.rxlength = 8 * length; read_params.length = 8 * length; - read_params.tx_buffer = nullptr; // setup pre and post control pin states - dc_pretrans_pin_states[1] = PIN_HIGH; - cs_pretrans_pin_states[1] = PIN_LOW; - cs_posttrans_pin_states[1] = PIN_HIGH; + prepare_pins_for_data_transaction(1); - // Queue the 2 transactions to be sent + // queue the 2 transactions to be sent std::for_each(trans.begin(), trans.end(), [&](auto& t) { res &= queue_transaction(t); @@ -392,4 +418,25 @@ namespace smooth::application::display return res; } + + // Add reset pin + void DisplaySpi::add_reset_pin(std::unique_ptr reset_pin) + { + reset_pin = std::move(reset_pin); + } + + // Add backlight pin + void DisplaySpi::add_backlight_pin(std::unique_ptr bk_light_pin) + { + backlight_pin = std::move(bk_light_pin); + } + + // Set backlight pin level + void DisplaySpi::set_back_light(bool level) + { + if (backlight_pin != nullptr) + { + backlight_pin->set_output_level(level); + } + } } diff --git a/lib/smooth/application/io/i2c/AxpPMU.cpp b/lib/smooth/application/io/i2c/AxpPMU.cpp new file mode 100644 index 00000000..635724fe --- /dev/null +++ b/lib/smooth/application/io/i2c/AxpPMU.cpp @@ -0,0 +1,345 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include +#include "smooth/application/io/i2c/AxpPMU.h" +#include "smooth/core/util/FixedBuffer.h" +#include "smooth/core/logging/log.h" + +using namespace smooth::core; +using namespace smooth::core::util; +using namespace smooth::core::logging; + +namespace smooth::application::sensor +{ + // Constructor + AxpPMU::AxpPMU(i2c_port_t port, uint8_t address, std::mutex& guard) + : I2CMasterDevice(port, address, guard) + { + } + + ///////////////////////////////////////////////////////////////////////////////// + // Core read and write functions + ///////////////////////////////////////////////////////////////////////////////// + + // Write a data byte to a register + bool AxpPMU::write_register(AxpRegister reg, uint8_t write_data) + { + std::vector data{ static_cast(reg) }; + data.push_back(write_data); + + return write(address, data); + } + + // Read a byte from register + bool AxpPMU::read_register(AxpRegister reg, uint8_t& read_data) + { + util::FixedBuffer rd_data; + bool res = read(address, static_cast(reg), rd_data); + read_data = rd_data[0]; + + return res; + } + + // Write (set/clear) a bit in a register + bool AxpPMU::write_register_bit(AxpRegister reg, bool bit, uint8_t bit_position) + { + uint8_t read_data; + bool res = read_register(reg, read_data) + & write_register(reg, + static_cast((read_data & ~(1 << bit_position)) | bit << bit_position)); + + return res; + } + + // Read register bit + bool AxpPMU::read_register_bit(AxpRegister reg, bool& bit, uint8_t bit_position) + { + uint8_t read_data; + bool res = read_register(reg, read_data); + bit = (read_data & (1 << bit_position)) != 0; + + return res; + } + + // Write adjoining group of bits to register + bool AxpPMU::write_register_bits(AxpRegister reg, uint8_t mask, uint8_t left_shift_count, uint8_t write_data) + { + uint8_t rd_data; + bool res = read_register(reg, rd_data) + & write_register(reg, static_cast((rd_data & mask) | (write_data << left_shift_count))); + + return res; + } + + // Write init_regs to initialize the axp device + bool AxpPMU::write_init_regs(const AxpInitReg* init_regs, size_t length) + { + bool res = true; + + // check res everytime thru loop + for (size_t index = 0; res && index < length; index++) + { + res = write_register(init_regs[index].reg, init_regs[index].data); + } + + return res; + } + + // Read adjoining group of bits from register + bool AxpPMU::read_register_bits(AxpRegister reg, uint8_t mask, uint8_t right_shift_count, uint8_t& read_data) + { + uint8_t rd_data; + bool res = read_register(reg, rd_data); + read_data = static_cast((rd_data & mask) >> right_shift_count); + + return res; + } + + // Read 12 bits + bool AxpPMU::read_12_bits(AxpRegister start_reg, uint16_t& data) + { + FixedBuffer rd_buf; + bool res = read(address, static_cast(start_reg), rd_buf); + data = static_cast(rd_buf[0] << 4 | rd_buf[1]); + + return res; + } + + // Read 13 bits + bool AxpPMU::read_13_bits(AxpRegister start_reg, uint16_t& data) + { + FixedBuffer rd_buf; + bool res = read(address, static_cast(start_reg), rd_buf); + data = static_cast(rd_buf[0] << 5 | rd_buf[1]); + + return res; + } + + // Read 24 bits + bool AxpPMU::read_24_bits(AxpRegister start_reg, uint32_t& data) + { + FixedBuffer rd_buf; + bool res = read(address, static_cast(start_reg), rd_buf); + data = static_cast(rd_buf[0] << 16 | rd_buf[1] << 8 | rd_buf[2]); + + return res; + } + + // Read 32 bits + bool AxpPMU::read_32_bits(AxpRegister start_reg, uint32_t& data) + { + FixedBuffer rd_buf; + bool res = read(address, static_cast(start_reg), rd_buf); + data = static_cast(rd_buf[0] << 24 | rd_buf[1] << 16 | rd_buf[2] << 8 | rd_buf[3]); + + return res; + } + + ///////////////////////////////////////////////////////////////////////////////// + // ADC data + ///////////////////////////////////////////////////////////////////////////////// + + // Get the ACIN voltage - range = 0V to 6.9615V + bool AxpPMU::get_acin_voltage(float& acin_voltage) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg56H_Acin_Volt_HI8, step_count); + acin_voltage = static_cast(step_count * 0.0017); + + return res; + } + + // Get the ACIN current - range = 0mA to 2.5594A + bool AxpPMU::get_acin_current(float& acin_current) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg58H_Acin_Curr_HI8, step_count); + acin_current = static_cast(step_count * 0.625); + + return res; + } + + // Get the VBUS voltage - range = 0V to 6.9615V + bool AxpPMU::get_vbus_voltage(float& vbus_voltage) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg5AH_Vbus_Volt_HI8, step_count); + vbus_voltage = static_cast(step_count * 0.0017); + + return res; + } + + // Get the VBUS current - range = 0mA to 1.5356A + bool AxpPMU::get_vbus_current(float& vbus_current) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg5CH_Vbus_Curr_HI8, step_count); + vbus_current = static_cast(step_count * 0.375); + + return res; + } + + // Get the internal temperature of the AXP device - range = -144.7C to +264.8C + bool AxpPMU::get_axp_device_temperature(float& device_temp) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg5EH_Intr_Temp_HI8, step_count); + device_temp = static_cast(-144.7 + (step_count * 0.1)); + + return res; + } + + // Get battery temperature sensor voltage - range 0V to 3.276V + bool AxpPMU::get_ts_voltage(float& ts_voltage) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg62H_Batt_Temp_HI8, step_count); + ts_voltage = static_cast(step_count * 0.0008); + + return res; + } + + // Get GPIO0 voltage (when used as ADC) - range 0V to 2.0475V or 0.7V to 2.7475V + bool AxpPMU::get_gpio0_voltage(float& gpio_voltage) + { + return calc_gpio_input_voltage(AxpRegister::Reg64H_Gpio0_Volt_HI8, 0, gpio_voltage); + } + + // Get GPIO1 voltage (when used as ADC) - range 0V to 2.0475V or 0.7V to 2.7475V + bool AxpPMU::get_gpio1_voltage(float& gpio_voltage) + { + return calc_gpio_input_voltage(AxpRegister::Reg64H_Gpio0_Volt_HI8, 1, gpio_voltage); + } + + // Get GPIO2 voltage (when used as ADC)- range 0V to 2.0475V or 0.7V to 2.7475V + bool AxpPMU::get_gpio2_voltage(float& gpio_voltage) + { + return calc_gpio_input_voltage(AxpRegister::Reg64H_Gpio0_Volt_HI8, 2, gpio_voltage); + } + + // Get GPIO3 voltage (when used as ADC) - range 0V to 2.0475V or 0.7V to 2.7475V + bool AxpPMU::get_gpio3_voltage(float& gpio_voltage) + { + return calc_gpio_input_voltage(AxpRegister::Reg64H_Gpio0_Volt_HI8, 3, gpio_voltage); + } + + // Get battery power - in milliwatts + bool AxpPMU::get_battery_power(float& mw_batt_power) + { + uint32_t power_count; + bool res = read_24_bits(AxpRegister::Reg70H_Batt_Power_HI8, power_count); + mw_batt_power = static_cast((power_count * 0.5 * 1.1) / 1000); + + return res; + } + + // Get battery voltage - range = 0mV to 4.5045V + bool AxpPMU::get_battery_voltage(float& batt_voltage) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg78H_Batt_Volt_HI8, step_count); + batt_voltage = static_cast(step_count * 0.0011); + + return res; + } + + // Get battery charging current in mA + bool AxpPMU::get_battery_charging_current(float& batt_chrg_current) + { + uint16_t step_count; + bool res = read_13_bits(AxpRegister::Reg7AH_Batt_Chrg_Curr_HI8, step_count); + batt_chrg_current = static_cast(step_count * 0.5); + + return res; + } + + // Get battery discharging current in mA + bool AxpPMU::get_battery_discharging_current(float& batt_dischrg_current) + { + uint16_t step_count; + bool res = read_13_bits(AxpRegister::Reg7CH_Batt_DisChg_Curr_HI8, step_count); + batt_dischrg_current = static_cast(step_count * 0.5); + + return res; + } + + // Get APS voltage - range = 0mV to 5.733V + // Approx 5V when connected to USB, and 4V when connected to battery + bool AxpPMU::get_aps_voltage(float& aps_voltage) + { + uint16_t step_count; + bool res = read_12_bits(AxpRegister::Reg7EH_Aps_Voltage_HI8, step_count); + aps_voltage = static_cast(step_count * 0.0014); + + return res; + } + + // Calculate GPIO input voltage (when used as ADC) + bool AxpPMU::calc_gpio_input_voltage(AxpRegister gpio_volt_adc_reg, uint8_t gpio_num, float& voltage) + { + uint16_t step_count; + bool is_range_0v7_to_2V7; + + bool res = read_12_bits(gpio_volt_adc_reg, step_count) + & read_register_bit(AxpRegister::Reg85H_Adc_Input_Range, is_range_0v7_to_2V7, gpio_num); + + if (is_range_0v7_to_2V7) + { + voltage = static_cast(0.7 + (step_count * 0.0005)); + } + else + { + voltage = static_cast(step_count * 0.0005); + } + + return res; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Coulomb data + ///////////////////////////////////////////////////////////////////////////////// + + // Get battery capacity in mAh + bool AxpPMU::get_battery_capacity(float& mah_batt_capacity) + { + uint32_t chrg_coulomb; + uint32_t dischrg_coulomb; + uint8_t adc_sample_rate; + + bool res = read_32_bits(AxpRegister::RegB0H_Batt_Chrg_Coulomb3, chrg_coulomb) + & read_32_bits(AxpRegister::RegB4H_Batt_DisChg_Coulomb3, dischrg_coulomb) + & read_register_bits(AxpRegister::Reg84H_Adc_Sample_Rate, 0xC0, 6, adc_sample_rate); + + adc_sample_rate = static_cast(25 * pow(2, adc_sample_rate)); + mah_batt_capacity = + static_cast(((65536 * 0.5 * (chrg_coulomb - dischrg_coulomb)) / adc_sample_rate) / 3600); + + return res; + } + + // Clear coulomb counter + bool AxpPMU::clear_coulomb_counter() + { + return write_register_bit(AxpRegister::RegB8H_Coulomb_Counter_Ctrl, true, 5); + } + + // Suspend coulomb counter + bool AxpPMU::suspend_coulomb_counter() + { + return write_register_bit(AxpRegister::RegB8H_Coulomb_Counter_Ctrl, true, 6); + } +} diff --git a/lib/smooth/application/io/i2c/BME280.cpp b/lib/smooth/application/io/i2c/BME280.cpp index b348ad51..b5208e7f 100644 --- a/lib/smooth/application/io/i2c/BME280.cpp +++ b/lib/smooth/application/io/i2c/BME280.cpp @@ -23,7 +23,7 @@ using namespace smooth::core::logging; namespace smooth::application::sensor { - static const char* TAG = "BME280SPI"; + static const char* TAG = "BME280I2C"; BME280::BME280(i2c_port_t port, uint8_t address, std::mutex& guard) : I2CMasterDevice(port, address, guard) diff --git a/lib/smooth/application/io/i2c/DHT12.cpp b/lib/smooth/application/io/i2c/DHT12.cpp new file mode 100644 index 00000000..331fa51a --- /dev/null +++ b/lib/smooth/application/io/i2c/DHT12.cpp @@ -0,0 +1,77 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include +#include "smooth/application/io/i2c/DHT12.h" + +using namespace smooth::core; + +namespace smooth::application::sensor +{ + // Constructor + DHT12::DHT12(i2c_port_t port, uint8_t address, std::mutex& guard) + : I2CMasterDevice(port, address, guard) + { + } + + // Read the raw data register from the DHT12, the last reagister is the checksum byte + bool DHT12::read_raw_registers(core::util::FixedBuffer& raw_data) + { + return read(address, HumidityWholePart, raw_data); + } + + // Is the checksum valid + bool DHT12::is_checksum_valid(core::util::FixedBuffer& raw_data) + { + uint8_t sum = static_cast(raw_data[HumidityWholePart] + raw_data[HumidityFractionalPart] + + raw_data[TemperatureWholePart] + raw_data[TemperatureFractionalPart]); + + return raw_data[Checksum] == sum; + } + + // Write device select; this write is required before we can read the data from the DHT12 + bool DHT12::write_device_select() + { + std::vector data{ HumidityWholePart }; + + return write(address, data, true); + } + + // Read humidity and temperature + bool DHT12::read_measurements(float& humidity, float& temperature) + { + core::util::FixedBuffer raw_data{}; + + bool res = write_device_select() && read_raw_registers(raw_data) && is_checksum_valid(raw_data); + + // if res is OK then convert whole and fractional parts to floats + if (res) + { + humidity = static_cast(raw_data[HumidityWholePart]) + + static_cast(raw_data[HumidityFractionalPart] * 0.1); + + temperature = static_cast(raw_data[TemperatureWholePart]) + + static_cast((raw_data[TemperatureFractionalPart] & 0x0F) * 0.1); + + if (raw_data[TemperatureFractionalPart] & 0x80) + { + temperature *= -1; + } + } + + return res; + } +} diff --git a/lib/smooth/application/io/i2c/PCF8563.cpp b/lib/smooth/application/io/i2c/PCF8563.cpp new file mode 100644 index 00000000..bdda673a --- /dev/null +++ b/lib/smooth/application/io/i2c/PCF8563.cpp @@ -0,0 +1,239 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include +#include "smooth/application/io/i2c/PCF8563.h" +#include "smooth/core/logging/log.h" + +using namespace smooth::core; +using namespace smooth::core::logging; + +namespace smooth::application::sensor +{ + // Class constants + static const char* TAG = "PCF8563"; + + // Constructor + PCF8563::PCF8563(i2c_port_t port, uint8_t address, std::mutex& guard) + : I2CMasterDevice(port, address, guard) + { + } + + // Get time + bool PCF8563::get_rtc_time(RtcTime& rtc_time) + { + util::FixedBuffer rd_data; + bool res = read(address, static_cast(Rtc8563Register::Seconds), rd_data); + bool is_time_valid = !((rd_data[0] & 0x80) >> 7); + + if (res & is_time_valid) + { + rtc_time.seconds = bcd_to_decimal(rd_data[0] & 0x7F); + rtc_time.minutes = bcd_to_decimal(rd_data[1] & 0x7F); + rtc_time.hours24 = bcd_to_decimal(rd_data[2] & 0x3F); + rtc_time.days = bcd_to_decimal(rd_data[3] & 0x3F); + rtc_time.weekdays = static_cast(bcd_to_decimal(rd_data[4] & 0x07)); + rtc_time.months = static_cast(bcd_to_decimal(rd_data[5] & 0x1F)); + rtc_time.years = static_cast(2000 + bcd_to_decimal(rd_data[6])); + } + + return res & is_time_valid; + } + + // Set time + bool PCF8563::set_rtc_time(RtcTime& rtc_time) + { + // validate rtc time values + validate_time(rtc_time.seconds, "RTC seconds", 0, 59); + validate_time(rtc_time.minutes, "RTC minutes", 0, 59); + validate_time(rtc_time.hours24, "RTC hours", 0, 23); + validate_year(rtc_time.years); + validate_time(rtc_time.days, "RTC days", 1, + number_of_days_in_month(rtc_time.months, rtc_time.years)); + + // write rtc time values to chip + std::vector data{ static_cast(Rtc8563Register::Seconds) }; + data.push_back(decimal_to_bcd(rtc_time.seconds)); + data.push_back(decimal_to_bcd(rtc_time.minutes)); + data.push_back(decimal_to_bcd(rtc_time.hours24)); + data.push_back(decimal_to_bcd(rtc_time.days)); + data.push_back(static_cast(rtc_time.weekdays)); + data.push_back(decimal_to_bcd(static_cast(rtc_time.months))); + data.push_back(decimal_to_bcd(static_cast(rtc_time.years - 2000))); + + return write(address, data); + } + + // Convert BCD to decimal + uint8_t PCF8563::bcd_to_decimal(uint8_t bcd) + { + return static_cast((10 * ((bcd & 0xf0) >> 4)) + (bcd & 0xf)); + } + + // Convert decimal to BCD + uint8_t PCF8563::decimal_to_bcd(uint8_t decimal) + { + return static_cast(((decimal / 10) << 4) | (decimal % 10)); + } + + // The number of days in the month + uint8_t PCF8563::number_of_days_in_month(Month month, uint16_t year) + { + uint8_t days = 31; + + if ((month == Month::April) | (month == Month::June) | (month == Month::September) | (month == Month::November)) + { + days = 30; + } + + if (month == Month::February) + { + // if leap year then days = 29 otherwise days = 28 + days = ((year % 4 == 0 && year % 100 != 0) | (year % 400 == 0)) ? 29 : 28; + } + + return days; + } + + // Get alarm time + bool PCF8563::get_alarm_time(AlarmTime& alarm_time) + { + util::FixedBuffer rd_data; + bool res = read(address, static_cast(Rtc8563Register::MinuteAlarm), rd_data); + + if (res) + { + alarm_time.ena_alrm_minute = (rd_data[0] & 0x80) >> 7; + alarm_time.ena_alrm_hour = (rd_data[1] & 0x80) >> 7; + alarm_time.ena_alrm_day = (rd_data[2] & 0x80) >> 7; + alarm_time.ena_alrm_weekday = (rd_data[3] & 0x80) >> 7; + + alarm_time.minute = bcd_to_decimal(rd_data[0] & 0x7F); + alarm_time.hour24 = bcd_to_decimal(rd_data[1] & 0x3F); + alarm_time.day = bcd_to_decimal(rd_data[2] & 0x3F); + alarm_time.weekday = static_cast(bcd_to_decimal(rd_data[3] & 0x07)); + } + + return res; + } + + // Set Alarm time + bool PCF8563::set_alarm_time(AlarmTime& alarm_time) + { + // Get current month and year from rtc chip + util::FixedBuffer rd_data; + bool res = read(address, static_cast(Rtc8563Register::Months), rd_data); + + if (res) + { + // get month and year from chip + Month month = static_cast(bcd_to_decimal(rd_data[0] & 0x1F)); + uint16_t year = static_cast(2000 + bcd_to_decimal(rd_data[1])); + + // validate alarm time values + validate_time(alarm_time.minute, "ALARM minute", 0, 59); + validate_time(alarm_time.hour24, "ALARM hour", 0, 23); + validate_time(alarm_time.day, "ALARM day", 1, number_of_days_in_month(month, year)); + + // write alarm time values to chip + std::vector data{ static_cast(Rtc8563Register::MinuteAlarm) }; + data.push_back(decimal_to_bcd(alarm_time.minute) + | static_cast(alarm_time.ena_alrm_minute ? 0x00 : 0x80)); + + data.push_back(decimal_to_bcd(alarm_time.hour24) + | static_cast(alarm_time.ena_alrm_hour ? 0x00 : 0x80)); + + data.push_back(decimal_to_bcd(alarm_time.day) + | static_cast(alarm_time.ena_alrm_day ? 0x00 : 0x80)); + + data.push_back(static_cast(alarm_time.weekday) + | static_cast(alarm_time.ena_alrm_weekday ? 0x00 : 0x80)); + + res &= write(address, data); + } + + return res; + } + + // Check to see if alarm flag is active + bool PCF8563::is_alarm_flag_active(bool& alarm_flag) + { + util::FixedBuffer rd_data; + bool res = read(address, static_cast(Rtc8563Register::ControlStatus2), rd_data); + alarm_flag = (rd_data[0] & 0x08) >> 3; + + return res; + } + + // Clear alarm flag + bool PCF8563::clear_alarm_flag() + { + util::FixedBuffer rd_data; + bool res = read(address, static_cast(Rtc8563Register::ControlStatus2), rd_data); + + std::vector data{ static_cast(Rtc8563Register::ControlStatus2) }; + data.push_back(rd_data[0] & 0x17); + + return res & write(address, data); + } + + // Get 12 hour time string + std::string PCF8563::get_12hr_time_string(uint8_t hours_24, uint8_t minutes, uint8_t seconds) + { + std::string hrs_str = hours_24 == 0 ? std::to_string(12) : std::to_string(hours_24 % 12); + std::string am_pm_str = hours_24 < 12 ? " AM" : " PM"; + + return hrs_str + add_colon_zero_padding(minutes) + add_colon_zero_padding(seconds) + am_pm_str; + } + + // Get 24 hour time string + std::string PCF8563::get_24hr_time_string(uint8_t hours_24, uint8_t minutes, uint8_t seconds) + { + return std::to_string(hours_24) + add_colon_zero_padding(minutes) + add_colon_zero_padding(seconds); + } + + // Add colon and zero padding to number + std::string PCF8563::add_colon_zero_padding(uint8_t time) + { + return time < 10 ? ":0" + std::to_string(time) : ":" + std::to_string(time); + } + + // Validate time + void PCF8563::validate_time(uint8_t& time, std::string err_msg, uint8_t min_limit, uint8_t max_limit) + { + if ((time > max_limit) | (time < min_limit)) + { + Log::error(TAG, + "Error - {} must be between {} and {}, setting to {}", + err_msg, + min_limit, + max_limit, + min_limit); + + time = min_limit; + } + } + + // Validate year + void PCF8563::validate_year(uint16_t& year) + { + if ((year > 2099) | (year < 2000)) + { + Log::error(TAG, "Error - RTC year must be between 2000 and 2009, setting to 2000"); + year = 2000; + } + } +} diff --git a/lib/smooth/include/smooth/application/display/DisplayCommands.h b/lib/smooth/include/smooth/application/display/DisplayCommands.h new file mode 100644 index 00000000..2f13a978 --- /dev/null +++ b/lib/smooth/include/smooth/application/display/DisplayCommands.h @@ -0,0 +1,100 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include + +namespace smooth::application::display +{ + // Common Lcd Display Commands + enum LcdCmd : uint8_t + { + // 0x00-0x0F + NOP = 0x00, // Non Operation Instruction + SWRESET = 0x01, // Software Reset + RDDID = 0x04, // Read Display ID + RDDST = 0x09, // Read Display Status + RDMODE = 0x0A, // Read Display Power Mode + RDMADCTL = 0x0B, // Read Display MADCTL + RDPIXFMT = 0x0C, // Read Display Pixel Format + RDIMGFMT = 0x0D, // Read Display Image Format + RDDSM = 0x0E, // Read Display Signal Mode + RDSELFDIAG = 0x0F, // Read Display Self-Diagnostic Result + + //0x10-0x1F + SLPIN = 0x10, // Enter Sleep Mode + SLPOUT = 0x11, // Sleep Out + PTLON = 0x12, // Partial Mode ON + NORON = 0x13, // Normal Display Mode ON + + //0x20-0x2F + INVOFF = 0x20, // Display Inversion Off + INVON = 0x21, // Display Inversion On + GAMMASET = 0x26, // Gamma Set + DISPOFF = 0x28, // Display Off + DISPON = 0x29, // Display On + CASET = 0x2A, // Column Address Set + RASET = 0x2B, // Row Address Set + RAMWR = 0x2C, // Memory Write + RAMRD = 0x2E, // Memory Read + + //0x30-0x3F + PTLAR = 0x30, // Partial Area + MADCTL = 0x36, // Memory Data Access Control + PIXFMT = 0x3A, // Interface Pixel Format + + //0xB0-0xBF + FRMCTR1 = 0xB1, // Frame Rate Control (In normal mode/ Full colors) + FRMCTR2 = 0xB2, // Frame Rate Control (In Idle Mode/8 colors) + FRMCTR3 = 0xB3, // Frame Rate control (In Partial Mode/Full Colors) + INVCTR = 0xB4, // Display Inversion Control + DFUNCTR = 0xB6, // Display Function Control + ENTRYM = 0xB7, // Entry Mode Set + VCOMS = 0xBB, // VCOMS Setting + + // 0xC0-0xCF + PWCTR1 = 0xC0, // Power Control 1 + PWCTR2 = 0xC1, // Power Control 2 + PWCTR3 = 0xC2, // Power Control 3 (in Normal mode/ Full colors) + PWCTR4 = 0xC3, // Power Control 4 (in Idle mode/ 8-colors) + PWCTR5 = 0xC4, // Power Control 5 (in Partial mode/ full-colors) + VMCTR1 = 0xC5, // VCOM Control 1 + FRCTRL2 = 0xC6, // Frame Rate Control in Normal Mode + VMCTR2 = 0xC7, // VCOM Offset Control + POWERA = 0xCB, // Power control A + POWERB = 0xCF, // Power control B + + // 0xD0-0xDF + ST_PWCTR1 = 0xD0, // Power Control 1 - ST7789V + RDID1 = 0xDA, // Read ID1 Value + RDID2 = 0xDB, // Read ID2 Value + RDID3 = 0xDC, // Read ID3 Value + RDID4 = 0xDD, // Read ID4 Value + + //0xE0-0xEF + GMCTRP1 = 0xE0, // Positive Gamma Correction + GMCTRN1 = 0xE1, // Negative Gamma Correction + DTCA = 0xE8, // Driver timing control A + DTCB = 0xEA, // Driver timing control B + POWER_SEQ = 0xED, // Power on sequence control + + //0xF0-0xFF + THREEGAMMA_EN = 0xF2, // Enable 3Gamma + PRC = 0xF7, // Pump ratio control + PWCTR6 = 0xFC, // Power Control 5 (in Partial mode + Idle mode) + }; +} diff --git a/lib/smooth/include/smooth/application/display/DisplayPin.h b/lib/smooth/include/smooth/application/display/DisplayPin.h new file mode 100644 index 00000000..22cc3c78 --- /dev/null +++ b/lib/smooth/include/smooth/application/display/DisplayPin.h @@ -0,0 +1,44 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include "smooth/core/io/Output.h" + +namespace smooth::application::display +{ + class DisplayPin + { + public: + /// Constructor + DisplayPin(gpio_num_t pin, + bool pullup, + bool pulldn, + bool active_high) : display_pin(pin, true, pullup, pulldn) + { + active_high ? display_pin.set(false) : display_pin.set(true); + } + + /// Set output pin level + void set_output_level(bool level) + { + display_pin.set(level); + } + + private: + smooth::core::io::Output display_pin; + }; +} diff --git a/lib/smooth/include/smooth/application/display/DisplaySpi.h b/lib/smooth/include/smooth/application/display/DisplaySpi.h new file mode 100644 index 00000000..7a2fc593 --- /dev/null +++ b/lib/smooth/include/smooth/application/display/DisplaySpi.h @@ -0,0 +1,178 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include +#include +#include "smooth/core/io/Output.h" +#include "smooth/core/io/spi/Master.h" +#include "smooth/core/io/spi/SPIDevice.h" +#include "smooth/application/display/DisplayTypes.h" +#include "smooth/application/display/DisplayPin.h" + +namespace smooth::application::display +{ + class DisplaySpi : public core::io::spi::SPIDevice + { + public: + /// The constructor + DisplaySpi(std::mutex& guard, + gpio_num_t chip_select_pin, + gpio_num_t data_command_pin, + uint8_t spi_command_bits, + uint8_t spi_address_bits, + uint8_t bits_between_address_and_data_phase, + uint8_t spi_mode, + uint8_t spi_positive_duty_cycle, + uint8_t spi_cs_ena_posttrans, + int spi_clock_speed_hz, + uint32_t spi_device_flags, + int spi_queue_size, + bool use_pre_transaction_callback, + bool use_post_transaction_callback); + + /// Initialize the display + /// \param host The SPI host either VSPI or HSPI + /// \return true on success, false on failure + bool init(spi_host_device_t host); + + /// Set the MADCTL register to a value + /// \param value The MADCTL value + /// \param madctl_reg The MADCTL register address default set to 0x36 + /// \return true on success, false on failure + bool set_madctl(uint8_t value, uint8_t madctl_reg = 0x36); + + /// Hardware Reset + /// \param active_low If true the reset pin requires a LOW to force the chip to reset, + /// if false the reset pin requires a HIGH to force the chip to reset. + /// \param active_time The amount of time in milliseconds the pin will be in the active state + /// \param delay_time The amount of time in milliseconds after exiting the active state, + /// the pin is the non-active state (waiting for the chip to complete it's reset). + void hw_reset(bool active_low, std::chrono::milliseconds active_time, std::chrono::milliseconds delay_time); + + /// Software reset + /// \param delay_time The amount of time in milliseconds to delay. Some ammount + /// of delay time is required before one can send a new command. + /// \return true on success, false on failure + bool sw_reset(std::chrono::milliseconds delay_time); + + /// Send init commands - write multiple init commands to display + /// Send a sequence of commands and data to the display to perform intialization + /// \param init_cmds The pointer to the first byte in init_cmds + /// \param length The number of bytes in the init_cmds + /// \return true on success, false on failure + //bool send_init_cmds(const display_init_cmd_t* init_cmds, size_t length); + bool send_init_cmds(const DisplayInitCmd* init_cmds, size_t length); + + /// Send Commands - write multiple commands to display + /// \param cmds The pointer to the first byte in the cmds + /// \param length The number of bytes in the cmds + /// \return true on success, false on failure + bool send_cmds(const uint8_t* cmds, size_t length); + + /// Send Command - write single command to display + /// \param cmd The command (register address) to write + /// \return true on success, false on failure + bool send_cmd(const uint8_t cmd); + + /// Send Data - write data to display + /// \param data The pointer to the first byte in the data + /// \param length The number of bytes in the data + /// \return true on success, false on failure + bool send_data(const uint8_t* data, size_t length); + + /// Send lines + /// Sends color data to display starting at position x1,y1 and ending at position x2,y2 + /// \param x1 The top left corner x position + /// \param y1 The top left corner y position + /// \param x2 The bottom right corner x position + /// \param y2 The bottom right corner y position + /// \param data The pointer to the first byte in the data (color data) + /// \param length The number of bytes in the data + /// \return true on success, false on failure + bool send_lines(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const uint8_t* data, size_t length); + + /// Wait for send lines to finish; + /// Waits for all SPI transactions in send lines to complete + /// \return true on success, false on failure + bool wait_for_send_lines_to_finish(); + + /// Read parameters + /// \param cmd The command (address register) + /// \param data The address of the container to hold the parameter data + /// \param param_count The number of parameters to be read + /// \return true on success, false on failure + bool read_params(uint8_t cmd, std::vector& data, uint32_t param_count); + + /// Read + /// \param cmd The command or register address to read + /// \param rxdata The pointer to the first byte location of the receive data container + /// \param length The number of bytes to raed + /// \return true on success, false on failure + bool read(uint8_t cmd, uint8_t* rxdata, size_t length); + + /// Add reset pin + /// \param reset_pin + void add_reset_pin(std::unique_ptr reset_pin); + + /// add backlight pin + /// \param bk_light_pin + void add_backlight_pin(std::unique_ptr bk_light_pin); + + /// Set backlight + /// Turn backlight either ON or OFF + /// \param level True = PIN_HIGH, false = PIN_LOW + void set_back_light(bool level); + + private: + /// Prepare pins for command transaction + /// \param trans_num The transaction number; 0-5 + void prepare_pins_for_cmd_transaction(size_t trans_num); + + /// Prepare pins for data transaction + /// \param trans_num The transaction number; 0-5 + void prepare_pins_for_data_transaction(size_t trans_num); + + /// Pre Transmission Action + /// The operations that need to be performed before the spi transaction is started + void pre_transmission_action() override + { + dc_pin.set(dc_pretrans_pin_states.at(current_transaction)); + cs_pin.set(cs_pretrans_pin_states.at(current_transaction)); + } + + /// Post Transmission Action + /// The operations that need to be performed after the spi transaction has ended + void post_transmission_action() override + { + cs_pin.set(cs_posttrans_pin_states.at(current_transaction)); + current_transaction++; + } + + smooth::core::io::Output dc_pin; + smooth::core::io::Output cs_pin; + std::unique_ptr backlight_pin{}; + std::unique_ptr reset_pin{}; + + static constexpr int line_transaction_length = 6; + using PreTransPinState = std::array; + PreTransPinState dc_pretrans_pin_states{}; + PreTransPinState cs_pretrans_pin_states{}; + PreTransPinState cs_posttrans_pin_states{}; + std::size_t current_transaction{}; + }; +} diff --git a/lib/smooth/include/smooth/application/display/DisplayTypes.h b/lib/smooth/include/smooth/application/display/DisplayTypes.h new file mode 100644 index 00000000..77c945d6 --- /dev/null +++ b/lib/smooth/include/smooth/application/display/DisplayTypes.h @@ -0,0 +1,38 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +namespace smooth::application::display +{ + // display init command type structure + struct DisplayInitCmd + { + uint8_t cmd; + uint8_t data[16]; + uint8_t length; //Num of bytes in data; bit 7 = delay when set; + }; + + // Some display such as the ST7735x require offset for the start row and start column from the + // normal positions of 0. Different manufacturers may wire the TFT to map screen pixels to + // different areas of the CGRAM and therefore there is a need to account for this with the + // struct CGRamOffset + struct CGRamOffset + { + uint16_t col_offset; + uint16_t row_offset; + }; +} diff --git a/lib/smooth/include/smooth/application/display/ILI9341.h b/lib/smooth/include/smooth/application/display/ILI9341.h index d12ed90e..59214e52 100644 --- a/lib/smooth/include/smooth/application/display/ILI9341.h +++ b/lib/smooth/include/smooth/application/display/ILI9341.h @@ -18,145 +18,51 @@ limitations under the License. /************************************************************************************ SPECIAL NOTES SPECIAL NOTES SPECIAL NOTES - NOTE 1: This driver is written to work with jumpers on the ILI9341 set to the - following: IM0=0, IM1=1, IM2=1, IM3=1. This jumper setting configures the - ILI9341 to operates as a 4-wire 8-bit data serial interface II see page 26 - and 38 of datasheet for more details. + NOTE 1: The DisplaySpi is written to work with jumpers on the ILI9341 set to + the following: IM0=0, IM1=1, IM2=1, IM3=1. This jumper setting configures + the ILI9341 to operates as a 4-wire 8-bit data serial interface II see + page 26 and 38 of datasheet for more details. - NOTE 2: This driver implements both reading and writing to display. Read is used - for reading parameters from certain registers. The maximum spi clock speed is - 16MHz but data sheet recommends 10MHz. + NOTE 2: The maximum spi clock speed for reading parameters is 16MHz but data + sheet recommends 10MHz. NOTE 3: Most applications will not need the ability to read regsiters and are only writing to the display. In this case MISO can be set to GPIO_NUM_NC. Also the spi clock speed can be increased to either 26MHz or 40MHz. The 2 displays I - tested this driver with operated at 40MHz with no problem. + tested with DisplaySpi operated at 40MHz with no problem. ************************************************************************************/ #pragma once #include -#include -#include "smooth/core/io/Output.h" -#include "smooth/core/io/spi/Master.h" -#include "smooth/core/io/spi/SPIDevice.h" +#include "DisplayTypes.h" +#include "DisplayCommands.h" namespace smooth::application::display { - class ILI9341 : public core::io::spi::SPIDevice - { - public: - /// The constructor - ILI9341(std::mutex& guard, - gpio_num_t chip_select_pin, - gpio_num_t data_command_pin, - gpio_num_t reset_pin, - gpio_num_t back_light_pin, - uint8_t spi_command_bits, - uint8_t spi_address_bits, - uint8_t bits_between_address_and_data_phase, - uint8_t spi_mode, - uint8_t spi_positive_duty_cycle, - uint8_t spi_cs_ena_posttrans, - int spi_clock_speed_hz, - uint32_t spi_device_flags, - int spi_queue_size, - bool use_pre_transaction_callback, - bool use_post_transaction_callback); - - /// Set backlight - /// Turn backlight either ON or OFF - void set_back_light(bool on) - { - on ? backlight_pin.set() : backlight_pin.clr(); - } - - /// Initialize the display - /// \param host The SPI host either VSPI or HSPI - /// \return true on success, false on failure - bool init(spi_host_device_t host); - - /// Set the rotation of display - /// Set the rotation of display using the MADCTL value see page 127 of datasheet - /// \param madctl_value The MADCTL value - /// \return true on success, false on failure - bool set_rotation(uint8_t madctl_value); - - /// Reset the display - void reset_display(); - - /// Send init commands - /// Send a sequence of commands and data to the display to perform intialization - /// \return true on success, false on failure - bool send_init_cmds(); - - /// LCD write command - /// \param cmd The command (register address) to write - /// \return true on success, false on failure - bool lcd_wr_cmd(const uint8_t cmd); - - /// LCD write data - /// \param data The pointer to the first byte in the data - /// \param length The number of bytes in the data - /// \return true on success, false on failure - bool lcd_wr_data(const uint8_t* data, size_t length); - - /// Send lines - /// Sends color data to display starting at position x1,y1 and ending at position x2,y2 - /// \param x1 The top left corner x position - /// \param y1 The top left corner y position - /// \param x2 The bottom right corner x position - /// \param y2 The bottom right corner y position - /// \param data The pointer to the first byte in the data (color data) - /// \param length The number of bytes in the data - /// \return true on success, false on failure - bool send_lines(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const uint8_t* data, size_t length); - - /// Wait for send lines to finish; - /// Waits for all SPI transactions in send lines to complete - /// \return true on success, false on failure - bool wait_for_send_lines_to_finish(); - - /// Read parameters - /// \param cmd The command (address register) - /// \param data The address of the container to hold the parameter data - /// \param param_count The number of parameters to be read - /// \return true on success, false on failure - bool read_params(uint8_t cmd, std::vector& data, uint32_t param_count); - - /// Read - /// \param cmd The command or register address to read - /// \param rxdata The pointer to the first byte location of the receive data container - /// \param length The number of bytes to raed - /// \return true on success, false on failure - bool read(uint8_t cmd, uint8_t* rxdata, size_t length); - - private: - /// Pre Transmission Action - /// The operations that need to be performed before the spi transaction is started - void pre_transmission_action() override - { - dc_pin.set(dc_pretrans_pin_states.at(current_transaction)); - cs_pin.set(cs_pretrans_pin_states.at(current_transaction)); - } - - /// Post Transmission Action - /// The operations that need to be performed after the spi transaction has ended - void post_transmission_action() override - { - cs_pin.set(cs_posttrans_pin_states.at(current_transaction)); - current_transaction++; - } - - smooth::core::io::Output reset_pin; - smooth::core::io::Output backlight_pin; - smooth::core::io::Output dc_pin; - smooth::core::io::Output cs_pin; - - static constexpr int line_transaction_length = 6; - using PreTransPinState = std::array; - PreTransPinState dc_pretrans_pin_states{}; - PreTransPinState cs_pretrans_pin_states{}; - PreTransPinState cs_posttrans_pin_states{}; - std::size_t current_transaction{}; - }; + // init commands version 1 - 20 commands + static constexpr std::array ili9341_init_cmds_1 = + { { + { LcdCmd::POWERB, { 0x00, 0xC1, 0X30 }, 3 }, // 1: 0xCF - POWERB + { LcdCmd::POWER_SEQ, { 0x64, 0x03, 0X12, 0X81 }, 4 }, // 2: 0xED - POWER_SEQ + { LcdCmd::DTCA, { 0x85, 0x00, 0x78 }, 3 }, // 3: 0xE8 - DTCA + { LcdCmd::POWERA, { 0x39, 0x2C, 0x00, 0x34, 0x02 }, 5 }, // 4: 0xCB - POWERA + { LcdCmd::PRC, { 0x20 }, 1 }, // 5: 0xF7 - PRC + { LcdCmd::DTCB, { 0x00, 0x00 }, 2 }, // 6: 0xEA - DTCB + { LcdCmd::PWCTR1, { 0x23 }, 1 }, // 7: 0xC0 - PWCTR1 + { LcdCmd::PWCTR2, { 0x10 }, 1 }, // 8: 0xC1 - PWCTR2 + { LcdCmd::VMCTR1, { 0x3E, 0x28 }, 2 }, // 9: 0xC5 - VMCTR1 + { LcdCmd::VMCTR2, { 0x86 }, 1 }, // 10: 0xC7 - VMCTR2 + { LcdCmd::MADCTL, { 0x48 }, 1 }, // 11: 0x36 - MADCTL - portrait + { LcdCmd::PIXFMT, { 0x55 }, 1 }, // 12: 0x3A - PIXFMT - 16 bit color + { LcdCmd::FRMCTR1, { 0x00, 0x13 }, 2 }, // 13: 0xB1 - FRMCTR1 + { LcdCmd::DFUNCTR, { 0x08, 0x82, 0x27 }, 3 }, // 14: 0xB6 - DFUNCTR + { LcdCmd::THREEGAMMA_EN, { 0x00 }, 1 }, // 15: 0xF2 - 3GAMMA_EN + { LcdCmd::GAMMASET, { 0x01 }, 1 }, // 16: 0x26 - GAMMASET + { LcdCmd::GMCTRP1, { 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0X37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00 }, + 15 }, // 17: 0xE0 - GMCTRP1 + { LcdCmd::GMCTRN1, { 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F }, + 15 }, // 18: 0xE1 - GMCTRN1 + { LcdCmd::SLPOUT, { 0 }, 0x80 }, // 19: 0x11 - SLPOUT + { LcdCmd::DISPON, { 0 }, 0x80 }, // 20: 0x29 - DISPON + } }; } diff --git a/lib/smooth/include/smooth/application/display/ILI9341_init_cmds.h b/lib/smooth/include/smooth/application/display/ILI9341_init_cmds.h deleted file mode 100644 index b3bbdcd5..00000000 --- a/lib/smooth/include/smooth/application/display/ILI9341_init_cmds.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF -Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -#pragma once - -#include -#include - -namespace smooth::application::display -{ - typedef struct LcdInitCmdType - { - uint8_t cmd; - uint8_t data[16]; - uint8_t databytes; //Num of bytes in data; bit 7 = delay after set; 0xFF = end of cmds. - } lcd_init_cmd_t; - - // Command sequence to initialize ili9341. A command sequence contains a 3 items; - // (1) a command byte, (2) the data to be sent and (3) the number of bytes in the data. - // Note 1: Memory Access Control: - // M5Stack uses 0x08=landscape 0x68=portrait; - // Adafruit 2.8/3.2 Display uses 0x28=landscape 0x48=portrait; - // If using LittlevGL make sure lv_conf.h is configured for correct display rotation - // Note 2: Pixel Format Set: 0x55 = 16bit, 0x66 = 18bit; also change lv_conf.h - static constexpr const std::array ili_init_cmds = - { { - { 0xEF, { 0x03, 0x80, 0X02 }, 3 }, // command sequence number 0 - { 0xCF, { 0x00, 0xC1, 0X30 }, 3 }, // command sequence number 1 - { 0xED, { 0x64, 0x03, 0X12, 0X81 }, 4 }, - { 0xE8, { 0x85, 0x00, 0x78 }, 3 }, - { 0xCB, { 0x39, 0x2C, 0x00, 0x34, 0x02 }, 5 }, - { 0xF7, { 0x20 }, 1 }, - { 0xEA, { 0x00, 0x00 }, 2 }, - { 0xC0, { 0x23 }, 1 }, //Power control - { 0xC1, { 0x10 }, 1 }, //Power control - { 0xC5, { 0x3E, 0x28 }, 2 }, //VCOM control - { 0xC7, { 0x86 }, 1 }, //VCOM control - { 0x36, { 0x48 }, 1 }, //Memory Access Control See Note 1 above //0xA8=M5Stack - { 0x3A, { 0x55 }, 1 }, //Pixel Format Set See Note 2 above - { 0xB1, { 0x00, 0x13 }, 2 }, // 0x18 79Hz, 0x1B default 70Hz, 0x13 100Hz - { 0xB6, { 0x08, 0x82, 0x27 }, 3 }, - { 0xF2, { 0x00 }, 1 }, - { 0x26, { 0x01 }, 1 }, - { 0xE0, { 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0X37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00 }, 15 }, - { 0XE1, { 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F }, 15 }, - { 0x11, { 0 }, 0x80 }, // Sleep Out cmd plus delay - { 0x29, { 0 }, 0x80 }, // Display on cmd plus delay - { 0, { 0 }, 0xff }, // End of sequence // command sequence number 21 - } }; - -/* - // another sequence of commands that also works..... - static constexpr const std::array ili_init_cmds = - {{ - { 0x28, {0x00}, 0 }, // command sequence number 0 - { 0xCF, {0x00, 0x83, 0x30}, 3}, - { 0xED, {0x64, 0x03, 0x12, 0x81}, 4 }, - { 0xE8, {0x85, 0x01, 0x79}, 3 }, - { 0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, - { 0xF7, {0x20}, 1 }, - { 0xEA, {0x00, 0x00}, 2 }, - { 0xC0, {0x26}, 1 }, //Power control - { 0xC1, {0x11}, 1 }, //Power control - { 0xC5, {0x35, 0x3E}, 2 }, //VCOM control - { 0xC7, {0xBE}, 1 }, //VCOM control - { 0x36, {0x48}, 1 }, //Memory Access Control See Note 1 above //0x68 - { 0x3A, {0x55}, 1 }, //Pixel Format Set See Note 2 above - { 0xB1, {0x00, 0x1B}, 2 }, - { 0xF2, {0x08}, 1 }, - { 0x26, {0x01}, 1 }, - { 0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0x87, 0X32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15 }, - { 0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15 }, - { 0x2A, {0x00, 0x00, 0x00, 0xEF}, 4 }, - { 0x2B, {0x00, 0x00, 0x01, 0x3F}, 4 }, - { 0xB7, {0x07}, 1 }, - { 0xB6, {0x0A, 0x82, 0x27, 0x00}, 4}, - { 0x11, {0}, 0x80 }, // Sleep Out cmd plus delay - { 0x29, {0}, 0x80 }, // Display on cmd plus delay - { 0, {0}, 0xff }, // End of sequence // command sequence number 24 - }}; -*/ -} diff --git a/lib/smooth/include/smooth/application/display/SH1107.h b/lib/smooth/include/smooth/application/display/SH1107.h new file mode 100644 index 00000000..57a09f4b --- /dev/null +++ b/lib/smooth/include/smooth/application/display/SH1107.h @@ -0,0 +1,75 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include +#include + +namespace smooth::application::display +{ + enum SH1107Cmd : uint8_t + { + LowerColumnAddress = 0x00, + UpperColumnAddress = 0x10, + PageAddressMode = 0x20, + VerticalAddressMode = 0x21, + ContrastControl = 0x81, + SegmentReMapNormal = 0xA0, + SegnentReMapReverse = 0xA1, + MultiplexRation = 0xA8, + NormalDisplayStatus = 0XA4, + EntireDisplayStatus = 0xA5, + NormalDisplay = 0xA6, + ReverseDisplay = 0xA5, + DisplayOffset = 0xD3, + DcDcControlMode = 0xAD, + DcDcModeOff = 0x80, + DcDcModeOn = 0x81, + DisplayOff = 0xAE, + DisplayOn = 0xAF, + PageAddress0 = 0xB0, + CommonOutputScanDirPortrait = 0xC0, + CommonOutputScanDirLandscape = 0xC8, + ClockDivideRatio = 0xD5, + DischargePrecharge = 0xD9, + VcomDeselectLevel = 0xDB, + DisplayStartLine = 0xDC, + ReadModifyWrite = 0xE0, + End = 0xEE, + Nop = 0xE3 + }; + + // init commands version 1 + static const std::array sh1107_init_cmds_1 = + { + SH1107Cmd::DisplayOff, // 1: 0xAE turn display off + SH1107Cmd::DisplayStartLine, 0x00, // 2,3: 0xDC set display start line to zero + SH1107Cmd::ContrastControl, 0x2F, // 4,5: 0x81 set display contrast + SH1107Cmd::PageAddressMode, // 6: 0x20 set memory addressing mode to page addressing + SH1107Cmd::SegmentReMapNormal, // 7: 0xA0 set segment re-map to non-rotated display + SH1107Cmd::CommonOutputScanDirPortrait, // 8: 0xC0 set com output scan direction to portrait + SH1107Cmd::MultiplexRation, 0x7F, // 9,10: 0xA8 set multiplex ratio to 128 + SH1107Cmd::DisplayOffset, 0x60, // 11,12: 0xD3 set display offset to display start line COM96 + SH1107Cmd::ClockDivideRatio, 0x51, // 13,14: 0xD5 set div ratio=1, freq=fosc + SH1107Cmd::DischargePrecharge, 0x22, // 15,16: 0xD9 set pre-charge= 2DCLKs, dis-charge=2DCLKs + SH1107Cmd::VcomDeselectLevel, 0x35, // 17,18: 0xDB set VCOM deselect level = 0.770 + SH1107Cmd::PageAddress0, // 19: 0xB0 set page address to load display RAM data + SH1107Cmd::NormalDisplay, // 20: 0xA4 set entire display off + SH1107Cmd::NormalDisplay, // 21: 0xA6 non-inverted display (normal display) + SH1107Cmd::DisplayOn, // 22: 0XAF turn display on + }; +} diff --git a/lib/smooth/include/smooth/application/display/ST7735.h b/lib/smooth/include/smooth/application/display/ST7735.h new file mode 100644 index 00000000..a6bc90be --- /dev/null +++ b/lib/smooth/include/smooth/application/display/ST7735.h @@ -0,0 +1,146 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include +#include "DisplayTypes.h" +#include "DisplayCommands.h" + +namespace smooth::application::display +{ + /************************************************************************************ + SPECIAL NOTES SPECIAL NOTES SPECIAL NOTES + + The following is a collection of initialization sequences for the ST7735. Some + displays will require a combination of these init sequences depending upon the tab + color of the protective plastic screen cover that is shipped with a new display. + + Note 1. The M5StickC uses the ST7735S which requires special int_cmds sequence. + The following code is an example of how to initialize the ST7735S display. + send_init_cmds(init_cmds_R_part1.data(), init_cmds_R_part1.size()); + send_init_cmds(init_cmds_R_grn_tab_part2.data(), init_cmds_R_grn_tab_part2.size()); + send_cmd(LcdCmd::INVON); // display inversion on + send_init_cmds(init_cmds_R_part3.data(), init_cmds_R_part3.size()); + ************************************************************************************/ + + // init commands for black tab - 17 commands + static constexpr std::array init_cmds_7735B = + { { + { LcdCmd::SLPOUT, { 150 }, 0x80 }, // 1: SLPOUT(0x11) - Out of sleep mode, with delay 150ms + { LcdCmd::PIXFMT, { 05, 10 }, 0x81 }, // 2: PIXFMT(0x3A) - Set color mode: 16-bit color 5-6-5 color + // format, 10ms delay + { LcdCmd::FRMCTR1, { 0x00, 0x06, 0x03, 10 }, 83 }, // 3: FRMCTR1(0xB1) - fastest refresh, + // 6 lines front porch, 6 lines back porch, 10ms delay + { LcdCmd::MADCTL, { 0x08 }, 1 }, // 4: MADCTL(0x36) - portrait + { LcdCmd::DFUNCTR, { 0x15, 0x02 }, 2 }, // 5: DISSET5(0xB6) - clk cycle nonoverlap, cycle gate + { LcdCmd::INVCTR, { 0x00 }, 1 }, // 6: INVCTR(0xB4) - Line inversion + { LcdCmd::PWCTR1, { 0x02, 0x70, 10 }, 82 }, // 7: PWCTR1(0xC0) - GVDD = 4.7V, 1.0uA, delay 10ms + { LcdCmd::PWCTR2, { 0x05 }, 1 }, // 8: PWCTR2(0xC1) - VGH = 14.7V, VGL = -7.35V + { LcdCmd::PWCTR3, { 0x01, 0x02 }, 2 }, // 9: PWCTR3(0xC2) - OpAmp current small, Boost freq + { LcdCmd::VMCTR1, { 0x3C, 0x38, 10 }, 82 }, // 10: VMCTR1(0xC5) - VCOMH = 4V, VCOML = -1.1V, delay 10ms + { LcdCmd::PWCTR6, { 0x11, 0x15 }, 2 }, // 11: PWCTR6(0xFC) - + { LcdCmd::GMCTRP1, + { 0x09, 0x16, 0x09, 0x20, 0x21, 0x1B, 0x13, 0x19, 0x17, 0x15, 0x1E, 0x2B, 0x04, 0x05, 0x02, 0x0E }, + 16 }, // 12: Positive Gamma Correction(0xE0) + { LcdCmd::GMCTRN1, + { 0x0B, 0x14, 0x08, 0x1E, 0x22, 0x1D, 0x18, 0x1E, 0x1B, 0x1A, 0x24, 0x2B, 0x06, 0x06, 0x02, 0x0F }, + 16 }, // 13: Negative Gamma Correction(0xE1) - with 10ms delay + { LcdCmd::CASET, { 0x00, 0x02, 0x00, 0x81 }, 4 }, // 14: CASET(0x2A) - Column Address - Ystart=2, Yend=129 + { LcdCmd::RASET, { 0x00, 0x01, 0x00, 0xA0 }, 4 }, // 15: RASET(0x2B) - Row Address - Xstart=1, Xend=160 + { LcdCmd::NORON, { 10 }, 0x80 }, // 16: NORON(0x13) - Normal Display On - delay 10ms + { LcdCmd::DISPON, { 255 }, 0x80 } // 17: DISPON(0x29) - Main Screen Turn ON - delay 500ms + } }; + + // init commands for 7735R red or green tab, part 1 - 14 commands + static constexpr std::array init_cmds_R_part1 = + { { + { LcdCmd::SLPOUT, { 150 }, 0x80 }, // 1: SLPOUT(0x11) - Out of sleep mode, with delay 150ms + { LcdCmd::FRMCTR1, { 0x01, 0x2C, 0x2D }, 3 }, // 2: FRMCTR1(0xB1) - Normal Mode, Rate=fosc/(1X2+40)*(Line+2C+2D) + { LcdCmd::FRMCTR2, { 0x01, 0x2C, 0x2D }, 3 }, // 3: FRMCTR2(0xB2) - Idle Mode, Rate=fosc/(1X2+40)*(Line+2C+2D) + { LcdCmd::FRMCTR3, { 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D }, 6 }, // 4: FFRMCTR3(0xB3) - Partial Mode, + // Dot inv and Line inv + { LcdCmd::INVCTR, { 0x07 }, 1 }, // 5: INVCTR(0xB4) - No inversion + { LcdCmd::PWCTR1, { 0xA2, 0x02, 0x84 }, 3 }, // 6: PWCTR1(0xC0) - -4.6V, AUTO mode + { LcdCmd::PWCTR2, { 0xC5 }, 1 }, // 7: PWCTR2(0xC1) - VGH25=2.4V, VGSEL=-10, VGH=3*AVDD + { LcdCmd::PWCTR3, { 0x0A, 0x00 }, 2 }, // 8: PWCTR3(0xC2) - OpAmp current small, Boost freq + { LcdCmd::PWCTR4, { 0x8A, 0x2A }, 2 }, // 9: PWCTR4(0xC3) - BCLK/2, OpAmp current small, Medium low + { LcdCmd::PWCTR5, { 0x8A, 0xEE }, 2 }, // 10: PWCTR5(0xC4) - + { LcdCmd::VMCTR1, { 0x0E }, 1 }, // 11: VMCTR1(0xC5) - VCOM=-0.775v + { LcdCmd::INVOFF, { 0x00 }, 0 }, // 12: INVOFF(0x20) - Don't invert display + { LcdCmd::MADCTL, { 0xC8 }, 1 }, // 13: MADCTL(0x36) - 0xC8 = Landscape, 0x08 = portrait + { LcdCmd::PIXFMT, { 0x05, 10 }, 0x81 }, // 14: COLMOD(0x3A) - Color Mode - 16-bit color, delay 10ms + } }; + + // init commands for 7735R green tab only, part 2 - 2 commands + static constexpr std::array init_cmds_R_grn_tab_part2 = + { { + { LcdCmd::CASET, { 0x00, 0x02, 0x00, 0x81 }, 4 }, // 1: CASET(0x2A) - Column Address - Ystart=2, Yend=129 + { LcdCmd::RASET, { 0x00, 0x01, 0x00, 0xA0 }, 4 }, // 2: RASET(0x2B) - Row Address - Xstart=1, Xend=160 + } }; + + // init commands for 7735R red tab only, part 2 - 2 commands + static constexpr std::array init_cmds_R_red_tab_part2 = + { { + { LcdCmd::CASET, { 0x00, 0x00, 0x00, 0x7F }, 4 }, // 1: CASET(0x2A) - Column Address - Ystart=0, Yend=127 + { LcdCmd::RASET, { 0x00, 0x00, 0x00, 0x9F }, 4 }, // 2: RASET(0x2B) - Row Address - Xstart=0, Xend=159 + } }; + + // init commands for 7735R green tab (1.44), part 2 - 2 commands + static constexpr std::array init_cmds_R_grn_tab_144_part2 = + { { + { LcdCmd::CASET, { 0x00, 0x00, 0x00, 0x7F }, 4 }, // 1: CASET(0x2A) - Column Address - Ystart=0, Yend=127 + { LcdCmd::RASET, { 0x00, 0x00, 0x00, 0x7F }, 4 }, // 2: RASET(0x2B) - Row Address - Xstart=0, Xend=127 + } }; + + // init commands for 7735R green tab (mini 160x80), part 2 - 2 commands + static constexpr std::array init_cmds_R_grn_tab_mini_160x80_part2 = + { { + { LcdCmd::CASET, { 0x00, 0x00, 0x00, 0x4F }, 4 }, // 1: CASET(0x2A) - Column Address - Ystart=0, Yend=79 + { LcdCmd::RASET, { 0x00, 0x00, 0x00, 0x9F }, 4 }, // 2: RASET(0x2B) - Row Address - Xstart=0, Xend=159 + } }; + + // init commands for 7735R red or green tab, part 3 - 4 commands + static constexpr std::array init_cmds_R_part3 = + { { + { LcdCmd::GMCTRP1, + { 0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10 }, + 16 }, // 1: Positive Gamma Correction(0xE0) + { LcdCmd::GMCTRN1, + { 0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10 }, + 16 }, // 2: Negative Gamma Correction(0xE1) + { LcdCmd::NORON, { 10 }, 0x80 }, // 3: NORON(0x13) - Normal Display On - delay 10ms + { LcdCmd::DISPON, { 150 }, 0x80 } // 4: DISPON(0x29) - Main Screen Turn ON - delay 150ms + } }; + + /************************************************************************************ + SPECIAL NOTES SPECIAL NOTES SPECIAL NOTES + + Some displays such as the ST7735x, offset the start row and start column from the + normal positions of 0. Different manufacturers may wire the TFT to map screen + pixels to different areas of the CGRAM The display driver for the ST7735 will + usually require row-offsets and col-offsets to make the display show properly on + the screen. If the display is cut off or has extra 'random' pixels on the top + and left, try using one of the following offsets. + ************************************************************************************/ + + static const CGRamOffset offsets_green_tab_160x80 = { 26, 1 }; + static const CGRamOffset offsets_green_tab_mini_160x80 = { 24, 0 }; + static const CGRamOffset offsets_green_tab_128 = { 0, 32 }; + static const CGRamOffset offsets_green_tab_1 = { 2, 1 }; + static const CGRamOffset offsets_green_tab_2 = { 2, 3 }; + static const CGRamOffset offsets_red_tab_160x80 = { 2, 3 }; +} diff --git a/lib/smooth/include/smooth/application/io/i2c/AxpPMU.h b/lib/smooth/include/smooth/application/io/i2c/AxpPMU.h new file mode 100644 index 00000000..0eed1803 --- /dev/null +++ b/lib/smooth/include/smooth/application/io/i2c/AxpPMU.h @@ -0,0 +1,239 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/************************************************************************************ + SPECIAL NOTES SPECIAL NOTES SPECIAL NOTES + + The AxpPMU provides a set of the most common functions that can be used to interact + with an AXP device such as the AXP192, AXP173 or AXP202. + These functions allow the the user to: + 1. Write - A single bit, multiple bits or a single byte + 2. Read - A single bit, multiple bits, a single byte or multiple bytes + 3. Read - Signals captured by the ADC and get the calulated results + 4. Read - The calculated battery capacity + + --------------------------------------------------------------------------------- + Pin Axp173 Axp192 Axp202 + --------------------------------------------------------------------------------- + DC1 0.7V-3.5V @1.2A 0.7V-3.5V @ 1.2A Not Available + DC2 0.7V-2.275V @ 1.6A 0.7V-2.275V @ 1.6A 0.7V-2.275V @ 1.6A + DC3 Not Available 0.7V-3.5V @ 700mA 0.7V-3.5V @ 1.2A + LDO1 3.3V @ 30mA 3.3V @ 30mA 3.3V @ 30mA + LDO2 1.8V-3.3V @ 200mA 1.8V-3.3V @ 200mA 1.8V-3.3V @ 200mA + LDO3 1.8V-3.3V @ 200mA 1.8V-3.3V @ 200mA 0.7V-3.3V @ 200mA + LDO4 0.7V-3.5V @ 500mA Not Available 1.8V-3.3V @ 200mA + LDO5io0 Not Available 1.8V-3.3V @ 50mA 1.8V-3.3V @ 50mA +*************************************************************************************/ +#pragma once + +#include "smooth/core/io/i2c/I2CMasterDevice.h" +#include "smooth/application/io/i2c/AxpRegisters.h" + +namespace smooth::application::sensor +{ + class AxpPMU : public core::io::i2c::I2CMasterDevice + { + public: + /// Constructor + AxpPMU(i2c_port_t port, uint8_t address, std::mutex& guard); + + ///////////////////////////////////////////////////////////////////////////////// + // Core read and write functions + ///////////////////////////////////////////////////////////////////////////////// + + /// Write a byte to a register + /// \param reg The register to write to + /// \param data_byte The data byte to write to the register + /// \return true on success, false on failure. + bool write_register(AxpRegister reg, uint8_t data_byte); + + /// Read a byte from register + /// \param reg The register to read + /// \param read_data Holds the data byte read + /// \return true on success, false on failure. + bool read_register(AxpRegister reg, uint8_t& read_data); + + /// Write a register bit + /// \param reg The register that will be written + /// \param bit The bit value to write + /// \param bit_postion The bit position of bit we want to write + /// \return true on success, false on failure. + bool write_register_bit(AxpRegister reg, bool bit, uint8_t bit_position); + + /// Read a register bit + /// \param reg The register that will be read + /// \param bit Holds the value of the bit read + /// \param bit_postion The bit position of bit we want to read + /// \return true on success, false on failure. + bool read_register_bit(AxpRegister reg, bool& bit, uint8_t bit_position); + + /// Read an adjoining group of bits from register + /// \param reg The register to read + /// \param read_data Holds the bits read + /// \param mask The bits we want to clear are set to 0, bits we want to stay the same are set to 1 + /// \param right_shift_count After masking the read_data will shifted right this number of bits + /// \return true on success, false on failure. + bool read_register_bits(AxpRegister reg, uint8_t mask, uint8_t right_shift_count, uint8_t& read_data); + + /// Write an adjoining group of bits to a register + /// \param reg The register to write + /// \param write_data The data to write + /// \param mask The bits we want to clear are set to 0, bits we want to stay the same are set to 1 + /// \param left_shift_count After masking the read_data will shifted left this number of bits + /// \return true on success, false on failure. + bool write_register_bits(AxpRegister reg, uint8_t mask, uint8_t left_shift_count, uint8_t write_data); + + /// Write init registers to AXP device - used to configure (initialize) the axp device + /// Each product that uses an AXP device will probably require the axp device to be configured uniquely + /// uniquely per product requirements. This function provide a fast and easy way to send registers + /// with data to configure the axp device. Typically axp_init_reg_t will be placed in a std::array + /// container and pass the pointer to the first element and the arry size to this function. + /// \param init_regs The pointer to the first byte in init_regs + /// \param length The number of axp_init_reg's + /// \return true on success, false on failure. + bool write_init_regs(const AxpInitReg* init_regs, size_t length); + + /// Read 12 bits + /// \param start_reg The starting register to begin the read + /// \param data The 12 bits are located in b11-b0; b15-b12 are 0 + /// \return true on success, false on failure. + bool read_12_bits(AxpRegister start_reg, uint16_t& data); + + /// Read 13 bits + /// \param start_reg The starting register to begin the read + /// \param data The 13 bits are located in b12-b0; b15-b13 are 0 + /// \return true on success, false on failure. + bool read_13_bits(AxpRegister start_reg, uint16_t& data); + + /// Read 24 bits + /// \param start_reg The starting register to begin the read + /// \param data The 24 bits are located in b23-b0; b31-b24 are 0 + /// \return true on success, false on failure. + bool read_24_bits(AxpRegister start_reg, uint32_t& data); + + /// Read 32 bits + /// \param start_reg The starting register to begin the read + /// \param data The 32 bits + /// \return true on success, false on failure. + bool read_32_bits(AxpRegister start_reg, uint32_t& data); + + ///////////////////////////////////////////////////////////////////////////////// + // ADC data functions + ///////////////////////////////////////////////////////////////////////////////// + + /// Get ACIN voltage + /// \param acin_voltage The ACIN voltage in volts + /// \return true on success, false on failure. + bool get_acin_voltage(float& acin_voltage); + + /// Get ACIN current + /// \param acin_current The ACIN current in mA + /// \return true on success, false on failure. + bool get_acin_current(float& acin_current); + + /// Get VBUS voltage + /// \param vbus_voltage The supply voltage from the USB source in volts + /// \return true on success, false on failure. + bool get_vbus_voltage(float& vbus_voltage); + + /// Get VBUS current + /// \param vbus_current The current from the USB source + /// \return true on success, false on failure. + bool get_vbus_current(float& vbus_current); + + /// Get the internal temperature of the AXP device + /// \param device_temp The internal temperature of the AXP device in degree celsius + /// \return true on success, false on failure. + bool get_axp_device_temperature(float& device_temp); + + /// Get TS voltage (battery temperature sensor voltage) + /// \param ts_voltage The TS voltage in volts + /// \return true on success, false on failure. + bool get_ts_voltage(float& ts_voltage); + + // Get GPIO0 voltage + /// \param gpio_voltage The GPIO0 voltage + /// \return true on success, false on failure. + virtual bool get_gpio0_voltage(float& gpio_voltage); + + /// Get GPIO1 voltage + /// \param gpio_voltage The GPIO1 voltage + /// \return true on success, false on failure. + virtual bool get_gpio1_voltage(float& gpio_voltage); + + /// Get GPIO2 voltage + /// \param gpio_voltage The GPIO2 voltage + /// \return true on success, false on failure. + virtual bool get_gpio2_voltage(float& gpio_voltage); + + /// Get GPIO3 voltage + /// \param gpio_voltage The GPIO3 voltage + /// \return true on success, false on failure. + virtual bool get_gpio3_voltage(float& gpio_voltage); + + /// Get Battery Power (Instantaneous) + /// \param batt_power + /// \return true on success, false on failure. + bool get_battery_power(float& mw_batt_power); + + /// Get Battery voltage + /// \param batt_voltage The battery voltage in volts + /// \return true on success, false on failure. + bool get_battery_voltage(float& batt_voltage); + + /// Get Battery charging current + /// \param batt_chrg_current The battery charging current in mA + /// \return true on success, false on failure. + bool get_battery_charging_current(float& batt_chrg_current); + + /// Get Battery discharging current + /// \param batt_dischrg_current The battery discharging current in mA + /// \return true on success, false on failure. + bool get_battery_discharging_current(float& batt_dischrg_current); + + /// Get APS voltage + /// \param aps_voltage The APS voltage in volts + /// \return true on success, false on failure. + bool get_aps_voltage(float& aps_voltage); + + ///////////////////////////////////////////////////////////////////////////////// + // Coulomb data functions + ///////////////////////////////////////////////////////////////////////////////// + + /// Get battery capacity in mAh + /// The current in milliamps which can be sustained by the battery for an hour + /// \param mah_batt_capacity Battery capacity in mAh + /// \return true on success, false on failure. + bool get_battery_capacity(float& mah_batt_capacity); + + /// Clear coulomb counter + /// \return true on success, false on failure. + bool clear_coulomb_counter(); + + /// Suspend coulomb counter + /// \return true on success, false on failure. + bool suspend_coulomb_counter(); + + private: + /// Calculate the GPIO voltage (when used as a ADC input) + /// \param gpio_volt_adc_reg The GPIO voltage ADC register + /// \param gpio_num The GPIO number; 0-3 + /// \param voltage Holds the calculated voltage + /// \return true on success, false on failure. + bool calc_gpio_input_voltage(AxpRegister gpio_volt_adc_reg, uint8_t gpio_num, float& voltage); + }; +} diff --git a/lib/smooth/include/smooth/application/io/i2c/AxpRegisters.h b/lib/smooth/include/smooth/application/io/i2c/AxpRegisters.h new file mode 100644 index 00000000..1c214552 --- /dev/null +++ b/lib/smooth/include/smooth/application/io/i2c/AxpRegisters.h @@ -0,0 +1,141 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include +#include + +namespace smooth::application::sensor +{ + enum class AxpRegister : uint8_t + { + // Group 1 - Power Control + Reg01H_Pwr_Chrg_Status = 0x00, + Reg02H_Usb_Vbus_Status = 0x01, + Reg12H_Power_Out_Ctrl = 0x12, + Reg23H_Dcdc2_Volt_Set = 0x23, + Reg25H_Dcdc2_Ldo3_Vrc = 0x25, + Reg26H_Dcdc1_Volt_Set = 0x26, + Reg27H_Dcdc3_Volt_Set = 0x27, + Reg28H_Ldo2_Ldo3_VSet = 0x28, + Reg29H_Ldo3_Volt_Sett = 0x29, + Reg30H_Ips_Out_Mngmnt = 0x30, + Reg31H_Voff_Volt_Sett = 0x31, + Reg32H_Shutdn_ChrgLed = 0x32, + Reg33H_Chrg_Control_1 = 0x33, + Reg34H_Chrg_Control_2 = 0x34, + Reg35H_Backup_Chrg_Ctl = 0x35, + Reg36H_PEK_Key_Setting = 0x36, + Reg37H_Dcdc_Freq_Set = 0x37, + Reg38H_Vlth_Chrg_Set = 0x38, + Reg39H_Vhth_Chrg_Set = 0x39, + Reg3AH_APS_VWarning1 = 0x3A, + Reg3BH_APS_VWarning2 = 0x3B, + Reg3CH_Vlth_DisChrg_Set = 0x3C, + Reg3DH_Vhth_DisChrg_Set = 0x3D, + Reg80H_Dcdc_Mode_Set = 0x80, + Reg82H_Adc_Enable_1 = 0x82, + Reg83H_Adc_Enable_2 = 0x83, + Reg84H_Adc_Sample_Rate = 0x84, + Reg85H_Adc_Input_Range = 0x85, + Reg86H_Adc_Irq_RE_Thld = 0x86, + Reg87H_Adc_Irq_FE_Thld = 0x87, + Reg8AH_Timer_Control = 0x8A, + Reg8BH_Vbus_Detect_Srp = 0x8B, + Reg8FH_Ovr_Temp_Shutdn = 0x8F, + + // Group 2 - GPIO Control + Reg90H_Gpio0_Func_Set = 0x90, + Reg91H_Gpio0_Volt_Set = 0x91, + Reg92H_Gpio1_Func_Set = 0x92, + Reg93H_Gpio2_Func_Set = 0x93, + Reg94H_Gpio_012_Status = 0x94, + Reg95H_Gpio_34_Fnc_Set = 0x95, + Reg96H_Gpio_34_Status = 0x96, + Reg97H_Gpio_012_PD_Ctl = 0x97, + Reg98H_Pwm1_Freq_Set = 0x98, + Reg99H_Pwm1_Duty_Set1 = 0x99, + Reg9AH_Pwm1_Duty_Set2 = 0x9A, + Reg9BH_Pwm2_Freq_Set = 0x9B, + Reg9CH_Pwm2_Duty_Set1 = 0x9C, + Reg9DH_Pwm2_Duty_Set2 = 0x9D, + Reg9EH_NRSTO_Func_Set = 0x9E, + + // Group 3 - Interrupt Control + Reg40H_Irq_Enable_Grp1 = 0x40, + Reg41H_Irq_Enable_Grp2 = 0x41, + Reg42H_Irq_Enable_Grp3 = 0x42, + Reg43H_Irq_Enable_Grp4 = 0x43, + Reg4AH_Irq_Enable_Grp5 = 0x4A, + Reg44H_Irq_Status_Grp1 = 0x44, + Reg45H_Irq_Status_Grp2 = 0x45, + Reg46H_Irq_Status_Grp3 = 0x46, + Reg47H_Irq_Status_Grp4 = 0x47, + Reg4DH_Irq_Status_Grp5 = 0x4D, + + // Group 4 - ADC Values + Reg56H_Acin_Volt_HI8 = 0x56, + Reg57H_Acin_Volt_LO4 = 0x57, + Reg58H_Acin_Curr_HI8 = 0x58, + Reg59H_Acin_Curr_LO4 = 0x59, + Reg5AH_Vbus_Volt_HI8 = 0x5A, + Reg5BH_Vbus_Volt_LO4 = 0x5B, + Reg5CH_Vbus_Curr_HI8 = 0x5C, + Reg5DH_Vbus_Curr_LO4 = 0x5D, + Reg5EH_Intr_Temp_HI8 = 0x5E, + Reg5FH_Intr_Temp_LO4 = 0x5F, + Reg62H_Batt_Temp_HI8 = 0x62, + Reg63H_Batt_Temp_LO4 = 0x63, + Reg64H_Gpio0_Volt_HI8 = 0x64, + Reg65H_Gpio0_Volt_LO4 = 0x65, + Reg66H_Gpio1_Volt_HI8 = 0x66, + Reg67H_Gpio1_Volt_LO4 = 0x67, + Reg68H_Gpio2_Volt_HI8 = 0x68, + Reg69H_Gpio2_Volt_LO4 = 0x69, + Reg6AH_Gpio3_Volt_HI8 = 0x6A, + Reg6BH_Gpio3_Volt_LO4 = 0x6B, + Reg70H_Batt_Power_HI8 = 0x70, + Reg71H_Batt_Power_MI8 = 0x71, + Reg72H_Batt_Power_LO8 = 0x72, + Reg78H_Batt_Volt_HI8 = 0x78, + Reg79H_Batt_Volt_LO4 = 0x79, + Reg7AH_Batt_Chrg_Curr_HI8 = 0x7A, + Reg7BH_Batt_Chrg_Curr_LO5 = 0x7B, + Reg7CH_Batt_DisChg_Curr_HI8 = 0x7C, + Reg7DH_Batt_DisChg_Curr_LO5 = 0x7D, + Reg7EH_Aps_Voltage_HI8 = 0x7E, + Reg7FH_Aps_Voltage_LO4 = 0x7F, + + // Group 5 - Battery Coulomb Counter + RegB0H_Batt_Chrg_Coulomb3 = 0xB0, + RegB1H_Batt_Chrg_Coulomb2 = 0xB1, + RegB2H_Batt_Chrg_Coulomb1 = 0xB2, + RegB3H_Batt_Chrg_Coulomb0 = 0xB3, + RegB4H_Batt_DisChg_Coulomb3 = 0xB4, + RegB5H_Batt_DisChg_Coulomb2 = 0xB5, + RegB6H_Batt_DisChg_Coulomb1 = 0xB6, + RegB7H_Batt_DisChg_Coulomb0 = 0xB7, + RegB8H_Coulomb_Counter_Ctrl = 0xB8, + RegB9_Batt_Fuel_Guage = 0xB9 + }; + + struct AxpInitReg + { + AxpRegister reg; + uint8_t data; + }; +} diff --git a/lib/smooth/include/smooth/application/io/i2c/DHT12.h b/lib/smooth/include/smooth/application/io/i2c/DHT12.h new file mode 100644 index 00000000..5b751202 --- /dev/null +++ b/lib/smooth/include/smooth/application/io/i2c/DHT12.h @@ -0,0 +1,61 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include "smooth/core/io/i2c/I2CMasterDevice.h" +#include "smooth/core/util/FixedBuffer.h" + +namespace smooth::application::sensor +{ + class DHT12 + : public core::io::i2c::I2CMasterDevice + { + public: + enum Registers + { + HumidityWholePart = 0x00, + HumidityFractionalPart, + TemperatureWholePart, + TemperatureFractionalPart, + Checksum + }; + + DHT12(i2c_port_t port, uint8_t address, std::mutex& guard); + + /// Reads the temperature and humidity + /// \param humidity The address to store the result from reading the humidity from the device + /// \param temperature The address to store the result from reading the temperature from the device + /// \return true on success, false on failure. + bool read_measurements(float& humidity, float& temperature); + + /// Validate the checksum + /// \param raw_data The address of the FixedBuffer that contains all the register data read from the DHT12 + /// \return Return true if checksum is valid, false if checksum is not valid + bool is_checksum_valid(core::util::FixedBuffer& raw_data); + + /// Read the raw data registers from the DHT12 + /// \param raw_data The address of the FixedBuffer that will hold the register data read from the DHT12 + /// \param return Return true if read was successful, false if not + bool read_raw_registers(core::util::FixedBuffer& raw_data); + + /// Write device select, write the starting register to begin reading from + /// \param return Return true if write was successful, false if not + bool write_device_select(); + + private: + }; +} diff --git a/lib/smooth/include/smooth/application/io/i2c/PCF8563.h b/lib/smooth/include/smooth/application/io/i2c/PCF8563.h new file mode 100644 index 00000000..6bf9f141 --- /dev/null +++ b/lib/smooth/include/smooth/application/io/i2c/PCF8563.h @@ -0,0 +1,182 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include "smooth/core/io/i2c/I2CMasterDevice.h" +#include "smooth/core/util/FixedBuffer.h" + +namespace smooth::application::sensor +{ + class PCF8563 + : public core::io::i2c::I2CMasterDevice + { + public: + enum class Rtc8563Register : uint8_t + { + ControlStatus1 = 0x00, + ControlStatus2, + Seconds, + Minutes, + Hours, + Days, + Weekdays, + Months, + Years, + MinuteAlarm, + HourAlarm, + DayAlarm, + WeekdayAlarm, + ClockOutControl, + TimerControl, + Timer + }; + + enum class DayOfWeek : uint8_t + { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday + }; + + static const constexpr char* DayOfWeekStrings[] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + + enum class Month : uint8_t + { + January, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December + }; + + static const constexpr char* MonthStrings[] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + struct RtcTime + { + uint8_t seconds; // 0-59 + uint8_t minutes; // 0-59 + uint8_t hours24; // 0-23 + uint8_t days; // 1-31 depending upon month + DayOfWeek weekdays; // Sunday-Saturday + Month months; // January-December + uint16_t years; // 2000-2099 + }; + + struct AlarmTime + { + bool ena_alrm_minute; // true = enable, false = disable + uint8_t minute; // 0-59 + bool ena_alrm_hour; // true = enable, false = disable + uint8_t hour24; // 0-23 + bool ena_alrm_day; // true = enable, false = disable + uint8_t day; // 1-31 depending upon month + bool ena_alrm_weekday; // true = enable, false = disable + DayOfWeek weekday; // Sunday-Saturday + }; + + PCF8563(i2c_port_t port, uint8_t address, std::mutex& guard); + + /// Get the rtc time + /// \param rtc_time The RtcTime struct that will contain the time data + /// \return true on success, false on failure. + bool get_rtc_time(RtcTime& rtc_time); + + /// Set the rtc time + /// \param rtc_time The RtcTime struct that contains the time data + /// \return true on success, false on failure. + bool set_rtc_time(RtcTime& rtc_time); + + /// Get the alarm time + /// \param alarm_time The AlarmTime struct that will contain the time data + /// \return true on success, false on failure. + bool get_alarm_time(AlarmTime& alarm_time); + + /// Set the alarm time + /// \param alarm_time The AlarmTime struct that contains the time data + /// \return true on success, false on failure. + bool set_alarm_time(AlarmTime& alarm_time); + + /// Is alarm flag active - poll this function to see if alarm time has triggered + /// \param alarm_flag If true alarm is active if false alarm id decactivated + /// \return true on success, false on failure. + bool is_alarm_flag_active(bool& alarm_flag); + + /// Clear the alarm flag + /// \return true on success, false on failure. + bool clear_alarm_flag(); + + /// Get the 12 hour time string + /// \param hours_24 The decimal 24 hours time + /// \param minutes The decimal minutes time + /// \param seconds The decimal seconds time + /// \return Return the formated time string - hr:min:sec am/pm + std::string get_12hr_time_string(uint8_t hours_24, uint8_t minutes, uint8_t seconds); + + /// Get the 24 hour time string + /// \param hours_24 The decimal 24 hours time + /// \param minutes The decimal minutes time + /// \param seconds The decimal seconds time + /// \return Return the formated time string - hr:min:sec + std::string get_24hr_time_string(uint8_t hours_24, uint8_t minutes, uint8_t seconds); + + private: + /// BCD to decimal + /// \param bcd The bcd value to be convert to decimal + /// \return Return the decimal value of bcd + uint8_t bcd_to_decimal(uint8_t bcd); + + /// Decimal to BCD + /// \param decimal The decimal value to be converted to BCD + /// \return Return the BCD value of decimal + uint8_t decimal_to_bcd(uint8_t decimal); + + /// Number of days in a month + /// \param month The month used to determine the number of days in the month + /// \param year The year used to determine the number of days in the month + /// \return Return the number of days in the month + uint8_t number_of_days_in_month(Month month, uint16_t year); + + /// Add leading colon and zero padding if necessary + /// \param time The time unit to add colon and pad with zero if necessary + /// \return Return the colon and zero padded number in string format + std::string add_colon_zero_padding(uint8_t time); + + /// Validate the unit of time + /// \param time The unit of time to validate + /// \param err_msg The text string to place at the start of the error message + /// \param min_limit The minimum time unit amount + /// \param max_limit The maximum time unit amoint + void validate_time(uint8_t& time, std::string err_msg, uint8_t min_limit, uint8_t max_limit); + + /// Validate year + /// \param year The year value that will be validated + void validate_year(uint16_t& year); + }; +} diff --git a/test/i2c_dht12_test/CMakeLists.txt b/test/i2c_dht12_test/CMakeLists.txt new file mode 100644 index 00000000..46786d51 --- /dev/null +++ b/test/i2c_dht12_test/CMakeLists.txt @@ -0,0 +1,35 @@ +#[[ +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +]] + + + +get_filename_component(TEST_PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +set(TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/generated_test_smooth_${TEST_PROJECT}.cpp) +configure_file(${CMAKE_CURRENT_LIST_DIR}/../test.cpp.in ${TEST_SRC}) +set(TEST_PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# As project() isn't scriptable and the entire file is evaluated we work around the limitation by generating +# the actual file used for the respective platform. +if(NOT "${COMPONENT_DIR}" STREQUAL "") + configure_file(${CMAKE_CURRENT_LIST_DIR}/../test_project_template_esp.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/generated_test_esp.cmake @ONLY) + include(${CMAKE_CURRENT_BINARY_DIR}/generated_test_esp.cmake) +else() + configure_file(${CMAKE_CURRENT_LIST_DIR}/../test_project_template_linux.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/generated_test_linux.cmake @ONLY) + include(${CMAKE_CURRENT_BINARY_DIR}/generated_test_linux.cmake) +endif() + diff --git a/test/i2c_dht12_test/i2c_dht12_test.cpp b/test/i2c_dht12_test/i2c_dht12_test.cpp new file mode 100644 index 00000000..47d2153c --- /dev/null +++ b/test/i2c_dht12_test/i2c_dht12_test.cpp @@ -0,0 +1,105 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/**************************************************************************************** + * Typical results from running program on M5Stack + * + * W (262499) APP: ============ APP TICK TICK ============= + * I (262499) MemStat: Mem type | 8-bit free | Smallest block | Minimum free | 32-bit free | Smallest block | Minimum free + * I (262506) MemStat: INTERNAL | 216740 | 113804 | 216048 | 273296 | 113804 | 272596 + * I (262517) MemStat: DMA | 216740 | 113804 | 216048 | 216740 | 113804 | 216048 + * I (262529) MemStat: SPIRAM | 0 | 0 | 0 | 0 | 0 | 0 + * I (262540) MemStat: + * I (262543) MemStat: Name | Stack | Min free stack | Max used stack + * I (262551) MemStat: MainTask | 16384 | 12156 | 4228 + * I (262560) MemStat: SocketDispatcher | 20480 | 18392 | 2088 + * I (262568) APP: ........................................ + * E (262577) APP: DHT12 Temperature (degC) = 20.6 + * E (262580) APP: DHT12 Humidity (%RH) = 41.5 + * I (262585) APP: ........................................ + ****************************************************************************************/ +#include +#include "i2c_dht12_test.h" +#include "smooth/core/logging/log.h" +#include "smooth/core/SystemStatistics.h" + +using namespace smooth::core; +using namespace std::chrono; +using namespace smooth::application::sensor; + +namespace i2c_dht12_test +{ + static const char* TAG = "APP"; + + App::App() : Application(APPLICATION_BASE_PRIO, seconds(4)), + i2c_master(I2C_NUM_0, // I2C Port 0 + GPIO_NUM_22, // SCL pin + false, // SCL internal pullup NOT enabled + GPIO_NUM_21, // SDA pin + false, // SDA internal pullup NOT enabled + 100 * 1000) // clock frequency - 100kHz + { + } + + void App::init() + { + Application::init(); + + dht12_initialized = init_i2c_dht12(); + Log::info(TAG, "DHT12 intialization --- {}", dht12_initialized ? "Succeeded" : "Failed"); + } + + void App::tick() + { + Log::warning(TAG, "============ APP TICK TICK ============="); + SystemStatistics::instance().dump(); + Log::info(TAG, "........................................" ); + + if (dht12_initialized) + { + print_sensor_measurements(); + } + } + + bool App::init_i2c_dht12() + { + bool res = true; + auto device = i2c_master.create_device(0x5C); // DHT12 i2c address + + Log::info(TAG, "Scanning for DHT12"); + + if (device->is_present()) + { + Log::info(TAG, "DHT12 is present"); + sensor = std::move(device); + } + else + { + Log::error(TAG, "DHT12 is NOT present"); + res = false; + } + + return res; + } + + void App::print_sensor_measurements() + { + float temperature, humidity; + sensor->read_measurements(humidity, temperature); + + Log::error(TAG, "DHT12 Temperature (degC) = {}", temperature); + Log::error(TAG, "DHT12 Humidity (%RH) = {}", humidity); + Log::info(TAG, "........................................" ); + } +} diff --git a/test/i2c_dht12_test/i2c_dht12_test.h b/test/i2c_dht12_test/i2c_dht12_test.h new file mode 100644 index 00000000..525035b9 --- /dev/null +++ b/test/i2c_dht12_test/i2c_dht12_test.h @@ -0,0 +1,41 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include "smooth/core/Application.h" +#include "smooth/core/task_priorities.h" +#include "smooth/core/io/i2c/Master.h" +#include "smooth/application/io/i2c/DHT12.h" + +namespace i2c_dht12_test +{ + class App : public smooth::core::Application + { + public: + App(); + + void init() override; + + void tick() override; + + void print_sensor_measurements(); + + private: + bool init_i2c_dht12(); + + smooth::core::io::i2c::Master i2c_master; + std::unique_ptr sensor{}; + bool dht12_initialized{ false }; + }; +} diff --git a/test/i2c_rtc8563_test/CMakeLists.txt b/test/i2c_rtc8563_test/CMakeLists.txt new file mode 100644 index 00000000..d9b01788 --- /dev/null +++ b/test/i2c_rtc8563_test/CMakeLists.txt @@ -0,0 +1,36 @@ +#[[ +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +]] + + + +get_filename_component(TEST_PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +set(TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/generated_test_smooth_${TEST_PROJECT}.cpp) +configure_file(${CMAKE_CURRENT_LIST_DIR}/../test.cpp.in ${TEST_SRC}) +set(TEST_PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# As project() isn't scriptable and the entire file is evaluated we work around the limitation by generating +# the actual file used for the respective platform. +if(NOT "${COMPONENT_DIR}" STREQUAL "") + configure_file(${CMAKE_CURRENT_LIST_DIR}/../test_project_template_esp.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/generated_test_esp.cmake @ONLY) + include(${CMAKE_CURRENT_BINARY_DIR}/generated_test_esp.cmake) +else() + configure_file(${CMAKE_CURRENT_LIST_DIR}/../test_project_template_linux.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/generated_test_linux.cmake @ONLY) + include(${CMAKE_CURRENT_BINARY_DIR}/generated_test_linux.cmake) +endif() + + diff --git a/test/i2c_rtc8563_test/i2c_rtc8563_test.cpp b/test/i2c_rtc8563_test/i2c_rtc8563_test.cpp new file mode 100644 index 00000000..b4b8b31e --- /dev/null +++ b/test/i2c_rtc8563_test/i2c_rtc8563_test.cpp @@ -0,0 +1,222 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/**************************************************************************************** + * Typical results from running program on M5StickC + * + * I (605) cpu_start: Starting scheduler on PRO CPU. + * I (0) cpu_start: Starting scheduler on APP CPU. + * I (669) FSLock: Max number of open files set to: 5 + * I (688) APP: Scanning for BM8563 ---- device found + * cI (702) APP: BM8563 initialization --- Succeeded + * I (702) APP: ********** Setting Time ********* + * I (703) APP: ********** Setting Alarm ********* + * W (60709) APP: ============ APP TICK TICK ============= + * I (60710) MemStat: Mem type | 8-bit free | Smallest block | Minimum free | 32-bit free | Smallest block | Minimum free + * I (60716) MemStat: INTERNAL | 217260 | 113804 | 216088 | 273816 | 113804 | 272636 + * I (60727) MemStat: DMA | 217260 | 113804 | 216088 | 217260 | 113804 | 216088 + * I (60739) MemStat: SPIRAM | 0 | 0 | 0 | 0 | 0 | 0 + * I (60750) MemStat: + * I (60753) MemStat: Name | Stack | Min free stack | Max used stack + * I (60761) MemStat: MainTask | 16384 | 13980 | 2404 + * I (60770) MemStat: SocketDispatcher | 20480 | 18384 | 2096 + * I (60778) APP: ........................................ + * I (60785) APP: Time = Tue 25 Feb 2020 - 1:09:00 PM + * I (60790) APP: Alarm set for -> 1:12:00 PM + * + * + * W (240967) APP: ============ APP TICK TICK ============= + * I (240967) MemStat: Mem type | 8-bit free | Smallest block | Minimum free | 32-bit free | Smallest block | Minimum free + * I (240974) MemStat: INTERNAL | 217260 | 113804 | 216088 | 273816 | 113804 | 272636 + * I (240985) MemStat: DMA | 217260 | 113804 | 216088 | 217260 | 113804 | 216088 + * I (240997) MemStat: SPIRAM | 0 | 0 | 0 | 0 | 0 | 0 + * I (241008) MemStat: + * I (241011) MemStat: Name | Stack | Min free stack | Max used stack + * I (241019) MemStat: MainTask | 16384 | 13532 | 2852 + * I (241028) MemStat: SocketDispatcher | 20480 | 18384 | 2096 + * I (241036) APP: ........................................ + * I (241043) APP: Time = Tue 25 Feb 2020 - 1:12:00 PM + * I (241048) APP: Alarm set for -> 1:12:00 PM + * I (241052) APP: The Alarm Active Count = 1 + * + * + * I (3846334) APP: ........................................ + * I (3846341) APP: Time = Tue 25 Feb 2020 - 2:12:05 PM + * I (3846346) APP: Alarm set for -> 1:12:00 PM + * I (3846351) APP: The Alarm Active Count = 2 + ****************************************************************************************/ +#include +#include "i2c_rtc8563_test.h" +#include "smooth/core/logging/log.h" +#include "smooth/core/SystemStatistics.h" + +using namespace smooth::core; +using namespace std::chrono; +using namespace smooth::application::sensor; + +namespace i2c_rtc8563_test +{ + static const char* TAG = "APP"; + + App::App() : Application(APPLICATION_BASE_PRIO, seconds(60)), + i2c0_master(I2C_NUM_0, // I2C Port 0 + GPIO_NUM_22, // SCL pin + false, // SCL internal pullup NOT enabled + GPIO_NUM_21, // SDA pin + false, // SDA internal pullup NOT enabled + 400 * 1000) // clock frequency - 400kHz + { + } + + void App::init() + { + Application::init(); + + init_i2c_rtc8563(); + + if (rtc8563_initialized) + { + set_time(); + set_alarm(); + rtc8563->clear_alarm_flag(); + } + } + + void App::tick() + { + Log::warning(TAG, "============ APP TICK TICK ============="); + SystemStatistics::instance().dump(); + Log::info(TAG, "........................................" ); + + if (rtc8563_initialized) + { + get_time(); + get_alarm(); + + if (is_alarm_active()) + { + alarm_active_count += 1; + Log::info(TAG, "The Alarm Active Count = {}", alarm_active_count); + clear_alarm_active(); + } + } + } + + // Initialize the RTC8563 + void App::init_i2c_rtc8563() + { + auto device = i2c0_master.create_device(0x51); // AXP i2c device address 0x51 + Log::info(TAG, "Scanning for BM8563 ---- {}", device->is_present() ? "device found" : "device NOT present"); + + if (device->is_present()) + { + rtc8563_initialized = true; + rtc8563 = std::move(device); + } + + Log::info(TAG, "BM8563 initialization --- {}", rtc8563_initialized ? "Succeeded" : "Failed"); + } + + // Set the time + void App::set_time() + { + Log::info(TAG, "********** Setting Time *********"); + PCF8563::RtcTime tm; + tm.seconds = 0; + tm.minutes = 8; + tm.hours24 = 13; + tm.days = 25; + tm.weekdays = PCF8563::DayOfWeek::Tuesday; + tm.months = PCF8563::Month::February; + tm.years = 2020; + + if (!rtc8563->set_rtc_time(tm)) + { + Log::error(TAG, "Error setting RTC time"); + } + } + + // Get the time + void App::get_time() + { + PCF8563::RtcTime tm; + + if (rtc8563->get_rtc_time(tm)) + { + // Tue 25 Feb 2020 - 1:08:00 PM + Log::info(TAG, "Time = {} {} {} {} - {} ", + PCF8563::DayOfWeekStrings[static_cast(tm.weekdays)], + tm.days, + PCF8563::MonthStrings[static_cast(tm.months)], + tm.years, + rtc8563->get_12hr_time_string(tm.hours24, tm.minutes, tm.seconds)); + } + else + { + Log::error(TAG, "Error reading RTC time"); + } + } + + // Set alarm - alarm will activate 12 minutes past any hour of any day of any month of any weekday. + void App::set_alarm() + { + Log::info(TAG, "********** Setting Alarm *********"); + PCF8563::AlarmTime tm; + tm.minute = 12; + tm.hour24 = 13; + tm.day = 25; + tm.weekday = PCF8563::DayOfWeek::Tuesday; + tm.ena_alrm_minute = true; + tm.ena_alrm_hour = false; + tm.ena_alrm_day = false; + tm.ena_alrm_weekday = false; + + if (!rtc8563->set_alarm_time(tm)) + { + Log::error(TAG, "Error setting ALARM time"); + } + } + + // Get Alarm + void App::get_alarm() + { + PCF8563::AlarmTime tm; + + if (rtc8563->get_alarm_time(tm)) + { + // Tue 25 - 1:12:00 PM + Log::info(TAG, "Alarm set for -> {} ", + rtc8563->get_12hr_time_string(tm.hour24, tm.minute, 0)); + } + else + { + Log::error(TAG, "Error reading ALARM time"); + } + } + + // Is alarm active + bool App::is_alarm_active() + { + bool is_active; + rtc8563->is_alarm_flag_active(is_active); + + return is_active; + } + + // Clear alarm + void App::clear_alarm_active() + { + rtc8563->clear_alarm_flag(); + } +} diff --git a/test/i2c_rtc8563_test/i2c_rtc8563_test.h b/test/i2c_rtc8563_test/i2c_rtc8563_test.h new file mode 100644 index 00000000..2d3ee5d0 --- /dev/null +++ b/test/i2c_rtc8563_test/i2c_rtc8563_test.h @@ -0,0 +1,52 @@ +/* +Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF +Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#include "smooth/core/Application.h" +#include "smooth/core/task_priorities.h" +#include "smooth/core/io/i2c/Master.h" +#include "smooth/application/io/i2c/PCF8563.h" + +namespace i2c_rtc8563_test +{ + class App : public smooth::core::Application + { + public: + App(); + + void init() override; + + void tick() override; + + void set_time(); + + void get_time(); + + void set_alarm(); + + void get_alarm(); + + bool is_alarm_active(); + + void clear_alarm_active(); + + private: + void init_i2c_rtc8563(); + + smooth::core::io::i2c::Master i2c0_master; + std::unique_ptr rtc8563{}; + bool rtc8563_initialized{ false }; + int alarm_active_count{ 0 }; + }; +} diff --git a/test/spi_4_line_devices_test/spi_4_line_devices_test.cpp b/test/spi_4_line_devices_test/spi_4_line_devices_test.cpp index 107bb29f..96b5a221 100644 --- a/test/spi_4_line_devices_test/spi_4_line_devices_test.cpp +++ b/test/spi_4_line_devices_test/spi_4_line_devices_test.cpp @@ -48,6 +48,7 @@ limitations under the License. ****************************************************************************************/ #include #include "spi_4_line_devices_test.h" +#include "smooth/application/display/ILI9341.h" #include "smooth/core/logging/log.h" #include "smooth/core/SystemStatistics.h" @@ -118,11 +119,9 @@ namespace spi_4_line_devices_test bool App::init_ILI9341() { - auto device = spi_master.create_device( + auto device = spi_master.create_device( GPIO_NUM_14, // chip select gpio pin GPIO_NUM_27, // data command gpio pin - GPIO_NUM_33, // reset gpio pin - GPIO_NUM_32, // backlight gpio pin 0, // spi command_bits 0, // spi address_bits, 0, // bits_between_address_and_data_phase, @@ -140,9 +139,9 @@ namespace spi_4_line_devices_test if (res) { - device->reset_display(); - res &= device->send_init_cmds(); - device->set_back_light(true); + device->add_reset_pin(std::make_unique(GPIO_NUM_33, false, false, false)); + device->hw_reset(true, milliseconds(5), milliseconds(150)); + res &= device->send_init_cmds(ili9341_init_cmds_1.data(), ili9341_init_cmds_1.size()); display = std::move(device); } else diff --git a/test/spi_4_line_devices_test/spi_4_line_devices_test.h b/test/spi_4_line_devices_test/spi_4_line_devices_test.h index 858d54e6..4d1fcefb 100644 --- a/test/spi_4_line_devices_test/spi_4_line_devices_test.h +++ b/test/spi_4_line_devices_test/spi_4_line_devices_test.h @@ -15,7 +15,7 @@ limitations under the License. #include "smooth/core/Application.h" #include "smooth/core/task_priorities.h" -#include "smooth/application/display/ILI9341.h" +#include "smooth/application/display/DisplaySpi.h" #include "smooth/application/io/spi/BME280SPI.h" namespace spi_4_line_devices_test @@ -44,7 +44,7 @@ namespace spi_4_line_devices_test spi_host_device_t spi_host; smooth::core::io::spi::Master spi_master; - std::unique_ptr display{}; + std::unique_ptr display{}; std::unique_ptr thp_sensor{}; bool ili9341_initialized{ false }; bool bme280_initialized{ false };