From 59e55fe3fed713f536523de2245f05f0bfe0cb34 Mon Sep 17 00:00:00 2001 From: JackCarterSmith Date: Sun, 4 May 2025 13:20:08 +0200 Subject: [PATCH] Rework from official 16f241e commit --- Core/Inc/axp2101.h | 336 ++++++++++++++++++ Core/Inc/backlight.h | 19 + Core/Inc/batt.h | 18 + Core/Inc/eeprom.h | 87 +++++ Core/Inc/fifo.h | 22 ++ Core/Inc/keyboard.h | 92 +++++ Core/Inc/main.h | 24 +- Core/Inc/regs.h | 57 +++ Core/Src/axp2101.c | 311 +++++++++++++++++ Core/Src/backlight.c | 94 +++++ Core/Src/batt.c | 63 ++++ Core/Src/eeprom.c | 427 +++++++++++++++++++++++ Core/Src/fifo.c | 59 ++++ Core/Src/keyboard.c | 408 ++++++++++++++++++++++ Core/Src/main.c | 654 +++++++++++++++++++++++++++++++++-- Core/Src/regs.c | 110 ++++++ Core/Src/stm32f1xx_hal_msp.c | 4 +- Core/Src/stm32f1xx_it.c | 2 +- README.md | 1 + picocalc_BIOS_jcs.ioc | 10 +- 20 files changed, 2759 insertions(+), 39 deletions(-) create mode 100644 Core/Inc/axp2101.h create mode 100644 Core/Inc/backlight.h create mode 100644 Core/Inc/batt.h create mode 100644 Core/Inc/eeprom.h create mode 100644 Core/Inc/fifo.h create mode 100644 Core/Inc/keyboard.h create mode 100644 Core/Inc/regs.h create mode 100644 Core/Src/axp2101.c create mode 100644 Core/Src/backlight.c create mode 100644 Core/Src/batt.c create mode 100644 Core/Src/eeprom.c create mode 100644 Core/Src/fifo.c create mode 100644 Core/Src/keyboard.c create mode 100644 Core/Src/regs.c diff --git a/Core/Inc/axp2101.h b/Core/Inc/axp2101.h new file mode 100644 index 0000000..8d17034 --- /dev/null +++ b/Core/Inc/axp2101.h @@ -0,0 +1,336 @@ +#include "stm32f1xx_hal.h" + + +#ifndef AXP2101_H_ +#define AXP2101_H_ + +/* + * --------------------- I2C registers ------------------------ + */ +#define XPOWERS_AXP2101_CHIP_ID (0x4A) + +#define XPOWERS_AXP2101_STATUS1 (0x00) +#define XPOWERS_AXP2101_STATUS2 (0x01) +#define XPOWERS_AXP2101_IC_TYPE (0x03) + + +//#define XPOWERS_AXP2101_DATA_BUFFER1 (0x04) +//#define XPOWERS_AXP2101_DATA_BUFFER2 (0x05) +//#define XPOWERS_AXP2101_DATA_BUFFER3 (0x06) +//#define XPOWERS_AXP2101_DATA_BUFFER4 (0x07) +//#define XPOWERS_AXP2101_DATA_BUFFER_SIZE (4u) + +#define XPOWERS_AXP2101_COMMON_CONFIG (0x10) +//#define XPOWERS_AXP2101_BATFET_CTRL (0x12) +//#define XPOWERS_AXP2101_DIE_TEMP_CTRL (0x13) +//#define XPOWERS_AXP2101_MIN_SYS_VOL_CTRL (0x14) +//#define XPOWERS_AXP2101_INPUT_VOL_LIMIT_CTRL (0x15) +//#define XPOWERS_AXP2101_INPUT_CUR_LIMIT_CTRL (0x16) +//#define XPOWERS_AXP2101_RESET_FUEL_GAUGE (0x17) +//#define XPOWERS_AXP2101_CHARGE_GAUGE_WDT_CTRL (0x18) + + +//#define XPOWERS_AXP2101_WDT_CTRL (0x19) +#define XPOWERS_AXP2101_LOW_BAT_WARN_SET (0x1A) + + +//#define XPOWERS_AXP2101_PWRON_STATUS (0x20) +//#define XPOWERS_AXP2101_PWROFF_STATUS (0x21) +//#define XPOWERS_AXP2101_PWROFF_EN (0x22) +//#define XPOWERS_AXP2101_DC_OVP_UVP_CTRL (0x23) +#define XPOWERS_AXP2101_VOFF_SET (0x24) +//#define XPOWERS_AXP2101_PWROK_SEQU_CTRL (0x25) +//#define XPOWERS_AXP2101_SLEEP_WAKEUP_CTRL (0x26) +//#define XPOWERS_AXP2101_IRQ_OFF_ON_LEVEL_CTRL (0x27) + +//#define XPOWERS_AXP2101_FAST_PWRON_SET0 (0x28) +//#define XPOWERS_AXP2101_FAST_PWRON_SET1 (0x29) +//#define XPOWERS_AXP2101_FAST_PWRON_SET2 (0x2A) +//#define XPOWERS_AXP2101_FAST_PWRON_CTRL (0x2B) + +#define XPOWERS_AXP2101_ADC_CHANNEL_CTRL (0x30) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST0 (0x34) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST1 (0x35) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST2 (0x36) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST3 (0x37) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST4 (0x38) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST5 (0x39) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST6 (0x3A) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST7 (0x3B) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST8 (0x3C) +//#define XPOWERS_AXP2101_ADC_DATA_RELUST9 (0x3D) + +/* + * ------------------ Interrupt registers --------------------- + */ +#define XPOWERS_AXP2101_INTEN1 (0x40) +#define XPOWERS_AXP2101_INTEN2 (0x41) +#define XPOWERS_AXP2101_INTEN3 (0x42) + +/* + * -------------------- Status registers ----------------------- + */ +#define XPOWERS_AXP2101_INTSTS1 (0x48) +#define XPOWERS_AXP2101_INTSTS2 (0x49) +#define XPOWERS_AXP2101_INTSTS3 (0x4A) +#define XPOWERS_AXP2101_INTSTS_CNT (3) + +#define XPOWERS_AXP2101_TS_PIN_CTRL (0x50) +#define XPOWERS_AXP2101_TS_HYSL2H_SET (0x52) +#define XPOWERS_AXP2101_TS_LYSL2H_SET (0x53) + + +#define XPOWERS_AXP2101_VLTF_CHG_SET (0x54) +#define XPOWERS_AXP2101_VHLTF_CHG_SET (0x55) +#define XPOWERS_AXP2101_VLTF_WORK_SET (0x56) +#define XPOWERS_AXP2101_VHLTF_WORK_SET (0x57) + + +#define XPOWERS_AXP2101_JIETA_EN_CTRL (0x58) +#define XPOWERS_AXP2101_JIETA_SET0 (0x59) +#define XPOWERS_AXP2101_JIETA_SET1 (0x5A) +#define XPOWERS_AXP2101_JIETA_SET2 (0x5B) + + +#define XPOWERS_AXP2101_IPRECHG_SET (0x61) +#define XPOWERS_AXP2101_ICC_CHG_SET (0x62) +#define XPOWERS_AXP2101_ITERM_CHG_SET_CTRL (0x63) + +#define XPOWERS_AXP2101_CV_CHG_VOL_SET (0x64) + +#define XPOWERS_AXP2101_THE_REGU_THRES_SET (0x65) +#define XPOWERS_AXP2101_CHG_TIMEOUT_SET_CTRL (0x67) + +#define XPOWERS_AXP2101_BAT_DET_CTRL (0x68) +#define XPOWERS_AXP2101_CHGLED_SET_CTRL (0x69) + +#define XPOWERS_AXP2101_BTN_VOL_MIN (2600) +#define XPOWERS_AXP2101_BTN_VOL_MAX (3300) +#define XPOWERS_AXP2101_BTN_VOL_STEPS (100) + + +#define XPOWERS_AXP2101_BTN_BAT_CHG_VOL_SET (0x6A) + + +#define XPOWERS_AXP2101_DC_ONOFF_DVM_CTRL (0x80) +#define XPOWERS_AXP2101_DC_FORCE_PWM_CTRL (0x81) +#define XPOWERS_AXP2101_DC_VOL0_CTRL (0x82) +#define XPOWERS_AXP2101_DC_VOL1_CTRL (0x83) +#define XPOWERS_AXP2101_DC_VOL2_CTRL (0x84) +#define XPOWERS_AXP2101_DC_VOL3_CTRL (0x85) +#define XPOWERS_AXP2101_DC_VOL4_CTRL (0x86) + + +#define XPOWERS_AXP2101_LDO_ONOFF_CTRL0 (0x90) +#define XPOWERS_AXP2101_LDO_ONOFF_CTRL1 (0x91) +#define XPOWERS_AXP2101_LDO_VOL0_CTRL (0x92) +#define XPOWERS_AXP2101_LDO_VOL1_CTRL (0x93) +#define XPOWERS_AXP2101_LDO_VOL2_CTRL (0x94) +#define XPOWERS_AXP2101_LDO_VOL3_CTRL (0x95) +#define XPOWERS_AXP2101_LDO_VOL4_CTRL (0x96) +#define XPOWERS_AXP2101_LDO_VOL5_CTRL (0x97) +#define XPOWERS_AXP2101_LDO_VOL6_CTRL (0x98) +#define XPOWERS_AXP2101_LDO_VOL7_CTRL (0x99) +#define XPOWERS_AXP2101_LDO_VOL8_CTRL (0x9A) + + +#define XPOWERS_AXP2101_BAT_PARAME (0xA1) +#define XPOWERS_AXP2101_FUEL_GAUGE_CTRL (0xA2) +#define XPOWERS_AXP2101_BAT_PERCENT_DATA (0xA4) + +/* + * ------------------------ DCDC 1~5 --------------------------- + */ +#define XPOWERS_AXP2101_DCDC1_VOL_MIN (1500) +#define XPOWERS_AXP2101_DCDC1_VOL_MAX (3400) +#define XPOWERS_AXP2101_DCDC1_VOL_STEPS (100u) + +#define XPOWERS_AXP2101_DCDC2_VOL1_MIN (500u) +#define XPOWERS_AXP2101_DCDC2_VOL1_MAX (1200u) +#define XPOWERS_AXP2101_DCDC2_VOL2_MIN (1220u) +#define XPOWERS_AXP2101_DCDC2_VOL2_MAX (1540u) + +#define XPOWERS_AXP2101_DCDC2_VOL_STEPS1 (10u) +#define XPOWERS_AXP2101_DCDC2_VOL_STEPS2 (20u) + +#define XPOWERS_AXP2101_DCDC2_VOL_STEPS1_BASE (0u) +#define XPOWERS_AXP2101_DCDC2_VOL_STEPS2_BASE (71) + + +#define XPOWERS_AXP2101_DCDC3_VOL1_MIN (500u) +#define XPOWERS_AXP2101_DCDC3_VOL1_MAX (1200u) +#define XPOWERS_AXP2101_DCDC3_VOL2_MIN (1220u) +#define XPOWERS_AXP2101_DCDC3_VOL2_MAX (1540u) +#define XPOWERS_AXP2101_DCDC3_VOL3_MIN (1600u) +#define XPOWERS_AXP2101_DCDC3_VOL3_MAX (3400u) + +#define XPOWERS_AXP2101_DCDC3_VOL_MIN (500) +#define XPOWERS_AXP2101_DCDC3_VOL_MAX (3400) + +#define XPOWERS_AXP2101_DCDC3_VOL_STEPS1 (10u) +#define XPOWERS_AXP2101_DCDC3_VOL_STEPS2 (20u) +#define XPOWERS_AXP2101_DCDC3_VOL_STEPS3 (100u) + +#define XPOWERS_AXP2101_DCDC3_VOL_STEPS1_BASE (0u) +#define XPOWERS_AXP2101_DCDC3_VOL_STEPS2_BASE (71) +#define XPOWERS_AXP2101_DCDC3_VOL_STEPS3_BASE (88) + + + +#define XPOWERS_AXP2101_DCDC4_VOL1_MIN (500u) +#define XPOWERS_AXP2101_DCDC4_VOL1_MAX (1200u) +#define XPOWERS_AXP2101_DCDC4_VOL2_MIN (1220u) +#define XPOWERS_AXP2101_DCDC4_VOL2_MAX (1840u) + +#define XPOWERS_AXP2101_DCDC4_VOL_STEPS1 (10u) +#define XPOWERS_AXP2101_DCDC4_VOL_STEPS2 (20u) + +#define XPOWERS_AXP2101_DCDC4_VOL_STEPS1_BASE (0u) +#define XPOWERS_AXP2101_DCDC4_VOL_STEPS2_BASE (71) + + + +#define XPOWERS_AXP2101_DCDC5_VOL_1200MV (1200) +#define XPOWERS_AXP2101_DCDC5_VOL_VAL (0x19) +#define XPOWERS_AXP2101_DCDC5_VOL_MIN (1400) +#define XPOWERS_AXP2101_DCDC5_VOL_MAX (3700) +#define XPOWERS_AXP2101_DCDC5_VOL_STEPS (100u) + +#define XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MIN (2600) +#define XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MAX (3300) +#define XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_STEPS (100) + +/* + * ----------------------- ALDO 1~4 -------------------------- + */ +#define XPOWERS_AXP2101_ALDO1_VOL_MIN (500) +#define XPOWERS_AXP2101_ALDO1_VOL_MAX (3500) +#define XPOWERS_AXP2101_ALDO1_VOL_STEPS (100u) + +#define XPOWERS_AXP2101_ALDO2_VOL_MIN (500) +#define XPOWERS_AXP2101_ALDO2_VOL_MAX (3500) +#define XPOWERS_AXP2101_ALDO2_VOL_STEPS (100u) + + +#define XPOWERS_AXP2101_ALDO3_VOL_MIN (500) +#define XPOWERS_AXP2101_ALDO3_VOL_MAX (3500) +#define XPOWERS_AXP2101_ALDO3_VOL_STEPS (100u) + + +#define XPOWERS_AXP2101_ALDO4_VOL_MIN (500) +#define XPOWERS_AXP2101_ALDO4_VOL_MAX (3500) +#define XPOWERS_AXP2101_ALDO4_VOL_STEPS (100u) + +/* + * ----------------------- BLDO 1~2 -------------------------- + */ +#define XPOWERS_AXP2101_BLDO1_VOL_MIN (500) +#define XPOWERS_AXP2101_BLDO1_VOL_MAX (3500) +#define XPOWERS_AXP2101_BLDO1_VOL_STEPS (100u) + +#define XPOWERS_AXP2101_BLDO2_VOL_MIN (500) +#define XPOWERS_AXP2101_BLDO2_VOL_MAX (3500) +#define XPOWERS_AXP2101_BLDO2_VOL_STEPS (100u) + +/* + * ----------------------- CPUSLDO -------------------------- + */ +#define XPOWERS_AXP2101_CPUSLDO_VOL_MIN (500) +#define XPOWERS_AXP2101_CPUSLDO_VOL_MAX (1400) +#define XPOWERS_AXP2101_CPUSLDO_VOL_STEPS (50) + + +/* + * ----------------------- DLDO 1~2 -------------------------- + */ +#define XPOWERS_AXP2101_DLDO1_VOL_MIN (500) +#define XPOWERS_AXP2101_DLDO1_VOL_MAX (3400) +#define XPOWERS_AXP2101_DLDO1_VOL_STEPS (100u) + +#define XPOWERS_AXP2101_DLDO2_VOL_MIN (500) +#define XPOWERS_AXP2101_DLDO2_VOL_MAX (3400) +#define XPOWERS_AXP2101_DLDO2_VOL_STEPS (100u) + + +#define XPOWERS_AXP2101_CONVERSION(raw) (22.0 + (7274 - raw) / 20.0) +#define _BV(b) (1UL << (uint32_t)(b)) + +/** + * @brief Charging led mode parameters. + */ +typedef enum __xpowers_chg_led_mode { + XPOWERS_CHG_LED_OFF, + XPOWERS_CHG_LED_BLINK_1HZ, + XPOWERS_CHG_LED_BLINK_4HZ, + XPOWERS_CHG_LED_ON, + XPOWERS_CHG_LED_CTRL_CHG, // The charging indicator is controlled by the charger +} xpowers_chg_led_mode_t; + +/** + * @brief axp2101 interrupt control mask parameters. + */ +typedef enum __xpowers_axp2101_irq { + //! IRQ1 REG 40H + XPOWERS_AXP2101_BAT_NOR_UNDER_TEMP_IRQ = _BV(0), // Battery Under Temperature in Work + XPOWERS_AXP2101_BAT_NOR_OVER_TEMP_IRQ = _BV(1), // Battery Over Temperature in Work mode + XPOWERS_AXP2101_BAT_CHG_UNDER_TEMP_IRQ = _BV(2), // Battery Under Temperature in Charge mode IRQ(bcut_irq) + XPOWERS_AXP2101_BAT_CHG_OVER_TEMP_IRQ = _BV(3), // Battery Over Temperature in Charge mode IRQ(bcot_irq) enable + XPOWERS_AXP2101_GAUGE_NEW_SOC_IRQ = _BV(4), // Gauge New SOC IRQ(lowsoc_irq) enable ??? + XPOWERS_AXP2101_WDT_TIMEOUT_IRQ = _BV(5), // Gauge Watchdog Timeout IRQ(gwdt_irq) enable + XPOWERS_AXP2101_WARNING_LEVEL1_IRQ = _BV(6), // SOC drop to Warning Level1 IRQ(socwl1_irq) enable + XPOWERS_AXP2101_WARNING_LEVEL2_IRQ = _BV(7), // SOC drop to Warning Level2 IRQ(socwl2_irq) enable + + //! IRQ2 REG 41H + XPOWERS_AXP2101_PKEY_POSITIVE_IRQ = _BV(8), // POWERON Positive Edge IRQ(ponpe_irq_en) enable + XPOWERS_AXP2101_PKEY_NEGATIVE_IRQ = _BV(9), // POWERON Negative Edge IRQ(ponne_irq_en) enable + XPOWERS_AXP2101_PKEY_LONG_IRQ = _BV(10), // POWERON Long PRESS IRQ(ponlp_irq) enable + XPOWERS_AXP2101_PKEY_SHORT_IRQ = _BV(11), // POWERON Short PRESS IRQ(ponsp_irq_en) enable + XPOWERS_AXP2101_BAT_REMOVE_IRQ = _BV(12), // Battery Remove IRQ(bremove_irq) enable + XPOWERS_AXP2101_BAT_INSERT_IRQ = _BV(13), // Battery Insert IRQ(binsert_irq) enabl + XPOWERS_AXP2101_VBUS_REMOVE_IRQ = _BV(14), // VBUS Remove IRQ(vremove_irq) enabl + XPOWERS_AXP2101_VBUS_INSERT_IRQ = _BV(15), // VBUS Insert IRQ(vinsert_irq) enable + + //! IRQ3 REG 42H + XPOWERS_AXP2101_BAT_OVER_VOL_IRQ = _BV(16), // Battery Over Voltage Protection IRQ(bovp_irq) enable + XPOWERS_AXP2101_CHAGER_TIMER_IRQ = _BV(17), // Charger Safety Timer1/2 expire IRQ(chgte_irq) enable + XPOWERS_AXP2101_DIE_OVER_TEMP_IRQ = _BV(18), // DIE Over Temperature level1 IRQ(dotl1_irq) enable + XPOWERS_AXP2101_BAT_CHG_START_IRQ = _BV(19), // Charger start IRQ(chgst_irq) enable + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ = _BV(20), // Battery charge done IRQ(chgdn_irq) enable + XPOWERS_AXP2101_BATFET_OVER_CURR_IRQ = _BV(21), // BATFET Over Current Protection IRQ(bocp_irq) enable + XPOWERS_AXP2101_LDO_OVER_CURR_IRQ = _BV(22), // LDO Over Current IRQ(ldooc_irq) enable + XPOWERS_AXP2101_WDT_EXPIRE_IRQ = _BV(23), // Watchdog Expire IRQ(wdexp_irq) enable + + XPOWERS_AXP2101_ALL_IRQ = (0xFFFFFFFFUL) +} xpowers_axp2101_irq_t; + + +uint32_t AXP2101_shutdown(void); +uint32_t AXP2101_disableTSPinMeasure(void); +uint32_t AXP2101_enableBattDetection(void); +uint32_t AXP2101_enableBattVoltageMeasure(void); +uint32_t AXP2101_enableSystemVoltageMeasure(void); +uint32_t AXP2101_enableVbusVoltageMeasure(void); +uint32_t AXP2101_enableIRQ(uint32_t opt); +uint32_t AXP2101_disableIRQ(uint32_t opt); +uint32_t AXP2101_clearIrqStatus(void); + +uint8_t AXP2101_isDropWarningLevel1Irq(void); +uint8_t AXP2101_isVbusRemoveIrq(void); +uint8_t AXP2101_isBatInsertIrq(void); +uint8_t AXP2101_isBatRemoveIrq(void); +uint8_t AXP2101_isPekeyShortPressIrq(void); +uint8_t AXP2101_isPekeyLongPressIrq(void); +uint8_t AXP2101_isBatChargeDoneIrq(void); +uint8_t AXP2101_isBatChargeStartIrq(void); + +uint32_t AXP2101_setLowBatWarnThreshold(uint8_t percentage); +uint32_t AXP2101_setLowBatShutdownThreshold(uint8_t opt); +uint32_t AXP2101_setSysPowerDownVoltage(uint16_t value); +uint32_t AXP2101_setChargingLedMode(uint8_t mode); + +uint8_t AXP2101_isBatteryConnect(void); +uint8_t AXP2101_isCharging(void); +uint32_t AXP2101_getIrqStatus(uint32_t* out_value); +uint32_t AXP2101_getBatteryPercent(uint8_t* out_value); + +#endif /* AXP2101_H_ */ diff --git a/Core/Inc/backlight.h b/Core/Inc/backlight.h new file mode 100644 index 0000000..1071d24 --- /dev/null +++ b/Core/Inc/backlight.h @@ -0,0 +1,19 @@ +#include "stm32f1xx_hal.h" + + +#ifndef BACKLIGHT_H_ +#define BACKLIGHT_H_ + +void lcd_backlight_update_from_reg(void); +void lcd_backlight_on(void); +void lcd_backlight_off(void); +void lcd_backlight_update(uint8_t step); +void lcd_backlight_update_up(void); +void lcd_backlight_update_down(void); + +void kbd_backlight_on(void); +void kbd_backlight_off(void); +void kbd_backlight_update(uint8_t step); +void kbd_backlight_update_loop(void); + +#endif /* BACKLIGHT_H_ */ diff --git a/Core/Inc/batt.h b/Core/Inc/batt.h new file mode 100644 index 0000000..730897b --- /dev/null +++ b/Core/Inc/batt.h @@ -0,0 +1,18 @@ +#include "stm32f1xx_hal.h" + + +#ifndef BATT_H_ +#define BATT_H_ + + +#define LOW_BAT_VAL (20) + + +void show_bat_segs(void); + +void low_bat(void); + +void start_chg(void); +void stop_chg(void); + +#endif /* BATT_H_ */ diff --git a/Core/Inc/eeprom.h b/Core/Inc/eeprom.h new file mode 100644 index 0000000..3543a37 --- /dev/null +++ b/Core/Inc/eeprom.h @@ -0,0 +1,87 @@ +//EEPROM emulation library for STM32F1XX with HAL-Driver +//V2.0 + + +//define to prevent recursive inclusion +#ifndef __EEPROM_H +#define __EEPROM_H + +//includes +#include "stm32f1xx_hal.h" + +//-------------------------------------------library configuration------------------------------------------- + +//number of variables (maximum variable name is EEPROM_VARIABLE_COUNT - 1) +//keep in mind it is limited by page size +//maximum is also determined by your variable sizes +//space utilization ratio X = (2 + 4*COUNT_16BIT + 6*COUNT_32BIT + 10*COUNT_64BIT) / PAGE_SIZE +//if X is high, variable changes more often require a page transfer --> lifetime of the flash can be reduced significantly +//depending on your variable change rate, X should be at least <50% +#define EEPROM_VARIABLE_COUNT (uint16_t) 4 + +//flash size of used STM32F1XX device in KByte +#define EEPROM_FLASH_SIZE (uint16_t) 64 + +//-------------------------------------------------constants------------------------------------------------- + +//EEPROM emulation start address in flash: use last two pages of flash memory +#define EEPROM_START_ADDRESS (uint32_t) (0x08000000 + 1024*EEPROM_FLASH_SIZE - 2*FLASH_PAGE_SIZE) + +//used flash pages for EEPROM emulation +typedef enum +{ + EEPROM_PAGE0 = EEPROM_START_ADDRESS, //Page0 + EEPROM_PAGE1 = EEPROM_START_ADDRESS + FLASH_PAGE_SIZE, //Page1 + EEPROM_PAGE_NONE = 0x00000000 //no page +} EEPROM_Page; + +//page status +typedef enum +{ + EEPROM_ERASED = 0xFFFF, //Page is empty + EEPROM_RECEIVING = 0xEEEE, //Page is marked to receive data + EEPROM_VALID = 0x0000 //Page containing valid data +} EEPROM_PageStatus; + +//results +typedef enum +{ + EEPROM_SUCCESS = 0x00, //Method successful / HAL_OK + EEPROM_ERROR = 0x01, //Error: HAL_ERROR occurred + EEPROM_BUSY = 0x02, //Error: HAL_BUSY occurred + EEPROM_TIMEOUT = 0x03, //Error: HAL_TIMEOUT occurred + EEPROM_NO_VALID_PAGE = 0x04, //Error: no valid page found + EEPROM_NOT_ASSIGNED = 0x05, //Error: variable was never assigned + EEPROM_INVALID_NAME = 0x06, //Error: variable name to high for variable count + EEPROM_FULL = 0x07 //Error: EEPROM is full +} EEPROM_Result; + +//sizes ( halfwords = 2 ^ (size-1) ) +typedef enum +{ + EEPROM_SIZE_DELETED = 0x00, //variable is deleted (no size) + EEPROM_SIZE16 = 0x01, //variable size = 16 bit = 1 Halfword + EEPROM_SIZE32 = 0x02, //variable size = 32 bit = 2 Halfwords + EEPROM_SIZE64 = 0x03 //variable size = 64 bit = 4 Halfwords +} EEPROM_Size; + +typedef union + { + int16_t Int16; + int32_t Int32; + int64_t Int64; + uint16_t uInt16; + uint32_t uInt32; + uint64_t uInt64; + float Float; + double Double; + } EEPROM_Value; + +//----------------------------------------------public functions--------------------------------------------- + +EEPROM_Result EEPROM_Init(); +EEPROM_Result EEPROM_ReadVariable(uint16_t VariableName, EEPROM_Value* Value); +EEPROM_Result EEPROM_WriteVariable(uint16_t VariableName, EEPROM_Value Value, EEPROM_Size Size); +EEPROM_Result EEPROM_DeleteVariable(uint16_t VariableName); + +#endif diff --git a/Core/Inc/fifo.h b/Core/Inc/fifo.h new file mode 100644 index 0000000..ae1459c --- /dev/null +++ b/Core/Inc/fifo.h @@ -0,0 +1,22 @@ +#include "stm32f1xx_hal.h" +#include "keyboard.h" + + +#ifndef FIFO_H_ +#define FIFO_H_ + +#define FIFO_SIZE 31 + +struct fifo_item { + uint8_t key; + enum key_state state; +}; + + +uint8_t fifo_count(void); +void fifo_flush(void); +uint8_t fifo_enqueue(const struct fifo_item item); +void fifo_enqueue_force(const struct fifo_item item); +void fifo_dequeue(struct fifo_item* const outItem); + +#endif /* FIFO_H_ */ diff --git a/Core/Inc/keyboard.h b/Core/Inc/keyboard.h new file mode 100644 index 0000000..11b871d --- /dev/null +++ b/Core/Inc/keyboard.h @@ -0,0 +1,92 @@ +#include "stm32f1xx_hal.h" +#include "main.h" + +#ifndef KEYBOARD_H_ +#define KEYBOARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum key_state { + KEY_STATE_IDLE = 0, + KEY_STATE_PRESSED, + KEY_STATE_HOLD, + KEY_STATE_RELEASED, +}; + +#define NUM_OF_COLS 8 +#define NUM_OF_ROWS 7 + +#define KEY_LIST_SIZE 10 + +#define KEY_POLL_TIME 16 // in ms, scanning freq +#define KEY_HOLD_TIME 300 // in ms, delay before switching to "rapid-trigger" mode +#define KEY_REPEAT_TIME 100 // in ms, delay between each triggers in "rapid-trigger" mode + +#define KEY_JOY_UP 0x01 +#define KEY_JOY_DOWN 0x02 +#define KEY_JOY_LEFT 0x03 +#define KEY_JOY_RIGHT 0x04 +#define KEY_JOY_CENTER 0x05 +#define KEY_BTN_LEFT1 0x06 +#define KEY_BTN_RIGHT1 0x07 + +#define KEY_BACKSPACE 0x08 +#define KEY_TAB 0x09 +#define KEY_ENTER 0x0A +// 0x0D - CARRIAGE RETURN +#define KEY_BTN_LEFT2 0x11 +#define KEY_BTN_RIGHT2 0x12 + + +#define KEY_MOD_ALT 0xA1 +#define KEY_MOD_SHL 0xA2 +#define KEY_MOD_SHR 0xA3 +#define KEY_MOD_SYM 0xA4 +#define KEY_MOD_CTRL 0xA5 + +#define KEY_ESC 0xB1 +#define KEY_UP 0xb5 +#define KEY_DOWN 0xb6 +#define KEY_LEFT 0xb4 +#define KEY_RIGHT 0xb7 + +#define KEY_BREAK 0xd0 // == KEY_PAUSE +#define KEY_INSERT 0xD1 +#define KEY_HOME 0xD2 +#define KEY_DEL 0xD4 +#define KEY_END 0xD5 +#define KEY_PAGE_UP 0xd6 +#define KEY_PAGE_DOWN 0xd7 + +#define KEY_CAPS_LOCK 0xC1 + +#define KEY_F1 0x81 +#define KEY_F2 0x82 +#define KEY_F3 0x83 +#define KEY_F4 0x84 +#define KEY_F5 0x85 +#define KEY_F6 0x86 +#define KEY_F7 0x87 +#define KEY_F8 0x88 +#define KEY_F9 0x89 +#define KEY_F10 0x90 + + +typedef void (*key_callback)(char, enum key_state); +typedef void (*lock_callback)(uint8_t, uint8_t); + + +void keyboard_set_key_callback(key_callback callback); +void keyboard_set_lock_callback(lock_callback callback); +uint8_t keyboard_get_capslock(void); +uint8_t keyboard_get_numlock(void); +void keyboard_process(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* KEYBOARD_H_ */ diff --git a/Core/Inc/main.h b/Core/Inc/main.h index 4bf6e54..87f1ea7 100644 --- a/Core/Inc/main.h +++ b/Core/Inc/main.h @@ -1,18 +1,12 @@ /* USER CODE BEGIN Header */ /** ****************************************************************************** - * @file : main.h - * @brief : Header for main.c file. - * This file contains the common defines of the application. - ****************************************************************************** - * @attention * - * Copyright (c) 2025 STMicroelectronics. + * Copyright (c) 2025 C.ARE (JackCarterSmith). * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ @@ -42,7 +36,7 @@ extern "C" { /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include "stm32f1xx_ll_tim.h" /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ @@ -52,7 +46,14 @@ extern "C" { /* Exported constants --------------------------------------------------------*/ /* USER CODE BEGIN EC */ +extern TIM_HandleTypeDef htim1; +extern TIM_HandleTypeDef htim3; +extern I2C_HandleTypeDef hi2c2; +extern volatile uint32_t systicks_counter; +extern volatile uint8_t pmu_irq; +extern uint8_t io_matrix[9]; +extern uint8_t js_bits; /* USER CODE END EC */ /* Exported macro ------------------------------------------------------------*/ @@ -66,7 +67,9 @@ void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim); void Error_Handler(void); /* USER CODE BEGIN EFP */ +__STATIC_INLINE uint32_t uptime_ms(void) { return systicks_counter; } +void flash_one_time(uint32_t ts, uint8_t restore_status); /* USER CODE END EFP */ /* Private defines -----------------------------------------------------------*/ @@ -155,7 +158,10 @@ void Error_Handler(void); #define PICO_SDA_GPIO_Port GPIOB /* USER CODE BEGIN Private defines */ - +#define EEPROM_VAR_ID (0) // 16b: Init ID: 0xCA1C +#define EEPROM_VAR_CFG (1) // 16b: 0x00 + CFG reg +#define EEPROM_VAR_KBD (2) // 16b: DEB + FRQ regs +#define EEPROM_VAR_BCKL (3) // 16b: LCD + KBD backlight step indice /* USER CODE END Private defines */ #ifdef __cplusplus diff --git a/Core/Inc/regs.h b/Core/Inc/regs.h new file mode 100644 index 0000000..7717731 --- /dev/null +++ b/Core/Inc/regs.h @@ -0,0 +1,57 @@ +#include "stm32f1xx_hal.h" + + +#ifndef REGS_H_ +#define REGS_H_ + +enum reg_id +{ + REG_ID_VER = 0x01, // fw version + REG_ID_CFG = 0x02, // config + REG_ID_INT = 0x03, // interrupt status + REG_ID_KEY = 0x04, // key status + REG_ID_BKL = 0x05, // backlight steps (0-9) + REG_ID_DEB = 0x06, // debounce cfg + REG_ID_FRQ = 0x07, // poll freq cfg + REG_ID_RST = 0x08, // reset + REG_ID_FIF = 0x09, // fifo + REG_ID_BK2 = 0x0A, // keyboard backlight (0-9) + REG_ID_BAT = 0x0B, // battery + REG_ID_C64_MTX = 0x0C,// read c64 matrix + REG_ID_C64_JS = 0x0D, // joystick io bits + REG_ID_LAST, +}; + +#define CFG_OVERFLOW_ON (1 << 0) //When a FIFO overflow happens, should the new entry still be pushed, overwriting the oldest one. If 0 then new entry is lost. +#define CFG_OVERFLOW_INT (1 << 1) //Should an interrupt be generated when a FIFO overflow happens +#define CFG_CAPSLOCK_INT (1 << 2) //Should an interrupt be generated when Caps Lock is toggled. +#define CFG_NUMLOCK_INT (1 << 3) //Should an interrupt be generated when Num Lock is toggled. +#define CFG_KEY_INT (1 << 4) +#define CFG_PANIC_INT (1 << 5) +#define CFG_REPORT_MODS (1 << 6) // Should Alt, Sym and Shifts be reported as well +#define CFG_USE_MODS (1 << 7) // Should Alt, Sym and Shifts modify the keys reported +// CFG_STICKY_MODS // Pressing and releasing a mod affects next key pressed + +#define INT_OVERFLOW (1 << 0) +#define INT_CAPSLOCK (1 << 1) +#define INT_NUMLOCK (1 << 2) +#define INT_KEY (1 << 3) +#define INT_PANIC (1 << 4) + +#define KEY_CAPSLOCK (1 << 5) +#define KEY_NUMLOCK (1 << 6) +#define KEY_COUNT_MASK 0x1F //0x1F == 31 + +//#define VER_VAL ((VERSION_MAJOR << 4) | (VERSION_MINOR << 0)) + + +uint8_t reg_get_value(enum reg_id reg); +uint8_t* reg_raw_access(void); +void reg_set_value(enum reg_id reg, uint8_t value); +uint8_t reg_is_bit_set(enum reg_id reg, uint8_t bit); +void reg_set_bit(enum reg_id reg, uint8_t bit); +void reg_init(void); +uint32_t reg_check_and_save_eeprom(void); +void reg_sync(void); + +#endif /* REGS_H_ */ diff --git a/Core/Src/axp2101.c b/Core/Src/axp2101.c new file mode 100644 index 0000000..bcbb370 --- /dev/null +++ b/Core/Src/axp2101.c @@ -0,0 +1,311 @@ +#include "axp2101.h" +#include "main.h" +#include "stm32_assert.h" + + +static uint8_t statusRegister[XPOWERS_AXP2101_INTSTS_CNT] = {0}; +static uint8_t intRegister[XPOWERS_AXP2101_INTSTS_CNT] = {0}; + + +__STATIC_INLINE uint8_t clrRegisterBit(uint8_t registers, uint8_t bit) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, registers, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value &= (uint8_t)(~_BV(bit)); + return HAL_I2C_Mem_Write(&hi2c2, 0x68, registers, 1, ®_value, 1, 60); +} + +__STATIC_INLINE uint8_t getRegisterBit(uint8_t registers, uint8_t bit) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, registers, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + return reg_value & _BV(bit); +} + +__STATIC_INLINE uint8_t setRegisterBit(uint8_t registers, uint8_t bit) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, registers, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value |= (uint8_t)(_BV(bit)); + return HAL_I2C_Mem_Write(&hi2c2, 0x68, registers, 1, ®_value, 1, 60); +} + +uint32_t setInterruptImpl(uint32_t opts, uint8_t enable) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status = HAL_OK; + + if (opts & 0x0000FF) { + HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_INTEN1, 1, ®_value, 1, 60); + intRegister[0] = enable ? (uint8_t)(reg_value | (opts & 0xFF)) : (uint8_t)(reg_value & (~(opts & 0xFF))); + status |= HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_INTEN1, 1, &intRegister[0], 1, 60); + } + if (opts & 0x00FF00) { + HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_INTEN2, 1, ®_value, 1, 60); + intRegister[1] = enable ? (uint8_t)(reg_value | (opts >> 8)) : (uint8_t)(reg_value & (~(opts >> 8))); + status |= HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_INTEN2, 1, &intRegister[1], 1, 60); + } + if (opts & 0xFF0000) { + HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_INTEN3, 1, ®_value, 1, 60); + intRegister[2] = enable ? (uint8_t)(reg_value | (opts >> 16)) : (uint8_t)(reg_value & (~(opts >> 16))); + status |= HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_INTEN3, 1, &intRegister[2], 1, 60); + } + + return (uint32_t)status; +} + + +uint32_t AXP2101_shutdown(void) { + return setRegisterBit(XPOWERS_AXP2101_COMMON_CONFIG, 0); +} + +uint32_t AXP2101_disableTSPinMeasure(void) { + return clrRegisterBit(XPOWERS_AXP2101_ADC_CHANNEL_CTRL, 1); +} + +uint32_t AXP2101_enableBattDetection(void) { + return setRegisterBit(XPOWERS_AXP2101_BAT_DET_CTRL, 0); +} + +uint32_t AXP2101_enableBattVoltageMeasure(void) { + return setRegisterBit(XPOWERS_AXP2101_ADC_CHANNEL_CTRL, 0); +} + +uint32_t AXP2101_enableSystemVoltageMeasure(void) { + return setRegisterBit(XPOWERS_AXP2101_ADC_CHANNEL_CTRL, 3); +} + +uint32_t AXP2101_enableVbusVoltageMeasure(void) { + return setRegisterBit(XPOWERS_AXP2101_ADC_CHANNEL_CTRL, 2); +} + +uint32_t AXP2101_enableIRQ(uint32_t opt) { + return setInterruptImpl(opt, 1); +} + +uint32_t AXP2101_disableIRQ(uint32_t opt) { + return setInterruptImpl(opt, 0); +} + +uint32_t AXP2101_clearIrqStatus(void) { + HAL_StatusTypeDef status = HAL_OK; + uint8_t fbuff = 0xFF; + + for (size_t i = 0; i < XPOWERS_AXP2101_INTSTS_CNT; i++) { + status |= HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_INTSTS1 + (uint8_t)i, 1, &fbuff, 1, 60); + statusRegister[i] = 0; + } + + return (uint32_t)status; +} + +uint8_t AXP2101_isDropWarningLevel1Irq(void) { + uint8_t mask = XPOWERS_AXP2101_WARNING_LEVEL1_IRQ >> 8; + if (intRegister[0] & mask) + return ((statusRegister[0] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isVbusRemoveIrq(void) { + uint8_t mask = XPOWERS_AXP2101_VBUS_REMOVE_IRQ >> 8; + if (intRegister[1] & mask) + return ((statusRegister[1] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isBatInsertIrq(void) { + uint8_t mask = XPOWERS_AXP2101_BAT_INSERT_IRQ >> 8; + if (intRegister[1] & mask) + return ((statusRegister[1] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isBatRemoveIrq(void) { + uint8_t mask = XPOWERS_AXP2101_BAT_REMOVE_IRQ >> 8; + if (intRegister[1] & mask) + return ((statusRegister[1] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isPekeyShortPressIrq(void) { + uint8_t mask = XPOWERS_AXP2101_PKEY_SHORT_IRQ >> 8; + if (intRegister[1] & mask) + return ((statusRegister[1] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isPekeyLongPressIrq(void) { + uint8_t mask = XPOWERS_AXP2101_PKEY_LONG_IRQ >> 8; + if (intRegister[1] & mask) + return ((statusRegister[1] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isBatChargeDoneIrq(void) { + uint8_t mask = XPOWERS_AXP2101_BAT_CHG_DONE_IRQ >> 8; + if (intRegister[2] & mask) + return ((statusRegister[2] & mask) == mask); + return 0; +} + +uint8_t AXP2101_isBatChargeStartIrq(void) { + uint8_t mask = XPOWERS_AXP2101_BAT_CHG_START_IRQ >> 8; + if (intRegister[2] & mask) + return ((statusRegister[2] & mask) == mask); + return 0; +} + + +// value in mV +uint32_t AXP2101_setSysPowerDownVoltage(uint16_t value) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + if (value % XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_STEPS) { + //log_e("Mistake ! The steps is must %u mV", XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_STEPS); + return 10; + } + if (value < XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MIN) { + //log_e("Mistake ! The minimum settable voltage of VSYS is %u mV", XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MIN); + return 10; + } else if (value > XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MAX) { + //log_e("Mistake ! The maximum settable voltage of VSYS is %u mV", XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MAX); + return 10; + } + + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_VOFF_SET, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value &= 0xF8; + reg_value |= (uint8_t)((value - XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_MIN) / XPOWERS_AXP2101_VSYS_VOL_THRESHOLD_STEPS); + return (uint32_t)HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_VOFF_SET, 1, ®_value, 1, 60); +} + +uint32_t AXP2101_setChargingLedMode(uint8_t mode) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + switch (mode) { + case XPOWERS_CHG_LED_OFF: + // clrRegisterBit(XPOWERS_AXP2101_CHGLED_SET_CTRL, 0); + // break; + case XPOWERS_CHG_LED_BLINK_1HZ: + case XPOWERS_CHG_LED_BLINK_4HZ: + case XPOWERS_CHG_LED_ON: + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_CHGLED_SET_CTRL, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value &= 0xC8; + reg_value |= 0x05; //use manual ctrl + reg_value |= (mode << 4); + + status = HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_CHGLED_SET_CTRL, 1, ®_value, 1, 60); + break; + case XPOWERS_CHG_LED_CTRL_CHG: + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_CHGLED_SET_CTRL, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value &= 0xF9; + reg_value |= 0x01; // use type A mode + //reg_value |= 0x02; // use type B mode + + status = HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_CHGLED_SET_CTRL, 1, ®_value, 1, 60); + break; + default: + status = 10; + break; + } + + return (uint32_t)status; +} + +/** + * @brief Low battery warning threshold 5-20%, 1% per step + * @param percentage: 5 ~ 20 + * @retval Status code + */ +uint32_t AXP2101_setLowBatWarnThreshold(uint8_t percentage) { + if (percentage < 5 || percentage > 20) + return 1; + + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_LOW_BAT_WARN_SET, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value &= 0x0F; + reg_value |= (uint8_t)((percentage - 5) << 4); + return HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_LOW_BAT_WARN_SET, 1, ®_value, 1, 60); +} + +/** + * @brief Low battery shutdown threshold 0-15%, 1% per step + * @param opt: 0 ~ 15 + * @retval Status code + */ +uint32_t AXP2101_setLowBatShutdownThreshold(uint8_t opt) { + uint8_t reg_value = 0; + HAL_StatusTypeDef status; + + if (opt > 15) + opt = 15; + + status = HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_LOW_BAT_WARN_SET, 1, ®_value, 1, 60); + if (status != HAL_OK) + return 1; + + reg_value &= 0xF0; + reg_value |= opt; + return HAL_I2C_Mem_Write(&hi2c2, 0x68, XPOWERS_AXP2101_LOW_BAT_WARN_SET, 1, ®_value, 1, 60); +} + + +uint8_t AXP2101_isBatteryConnect(void) { + return getRegisterBit(XPOWERS_AXP2101_STATUS1, 3); +} + +uint8_t AXP2101_isCharging(void) { + uint8_t reg_value = 0; + + HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_STATUS2, 1, ®_value, 1, 60); + + return (reg_value >> 5) == 0x01; +} + +uint32_t AXP2101_getIrqStatus(uint32_t* out_value) { + HAL_StatusTypeDef status = HAL_OK; + + status |= HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_INTSTS1, 1, &statusRegister[0], 1, 60); + status |= HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_INTSTS2, 1, &statusRegister[1], 1, 60); + status |= HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_INTSTS3, 1, &statusRegister[2], 1, 60); + + ((uint8_t*)&out_value)[0] = statusRegister[0]; + ((uint8_t*)&out_value)[0] = statusRegister[1]; + ((uint8_t*)&out_value)[0] = statusRegister[2]; + ((uint8_t*)&out_value)[3] = 0xFF; + + return (uint32_t)status; +} + +uint32_t AXP2101_getBatteryPercent(uint8_t* out_value) { + if (!AXP2101_isBatteryConnect()) + return 1; + + return HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_BAT_PERCENT_DATA, 1, out_value, 1, 60); +} diff --git a/Core/Src/backlight.c b/Core/Src/backlight.c new file mode 100644 index 0000000..a7b0682 --- /dev/null +++ b/Core/Src/backlight.c @@ -0,0 +1,94 @@ +#include "backlight.h" +#include "main.h" +#include "regs.h" + + +// LCD backlight curve based on brightness measurements for specific value of PWM duty cycle. +// Using this, I've established a custom command curve. +#define LCD_BCKL_STEPS 10 +//static const uint16_t lcd_bckl_steps[LCD_BCKL_STEPS] = {20, 60, 96, 134, 166, 192, 210, 256, 358, 460}; +static const uint16_t lcd_bckl_steps[LCD_BCKL_STEPS] = {10, 20, 40, 60, 80, 110, 150, 200, 256, 440}; + +#define KBD_BCKL_STEPS 4 +static const uint16_t kbd_bckl_steps[KBD_BCKL_STEPS] = {0, 40, 110, 260}; + + +inline void lcd_backlight_update_from_reg(void) { + uint16_t val = 0; + + val = lcd_bckl_steps[reg_get_value(REG_ID_BKL) % LCD_BCKL_STEPS]; + __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, val); +} + +inline void lcd_backlight_on(void) { + lcd_backlight_update_from_reg(); +} + +inline void lcd_backlight_off(void) { + __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0x0000); +} + +inline void lcd_backlight_update(uint8_t step) { + uint16_t val = 0; + const uint8_t index = step % LCD_BCKL_STEPS; + + val = lcd_bckl_steps[index]; + __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, val); + + reg_set_value(REG_ID_BKL, index); +} + +inline void lcd_backlight_update_up(void) { + uint16_t val = 0; + uint8_t index = reg_get_value(REG_ID_BKL) % LCD_BCKL_STEPS; + + index = index < (LCD_BCKL_STEPS-1) ? index + 1 : (LCD_BCKL_STEPS-1); + val = lcd_bckl_steps[index]; + __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, val); + + reg_set_value(REG_ID_BKL, index); +} + +inline void lcd_backlight_update_down(void) { + uint16_t val = 0; + uint8_t index = reg_get_value(REG_ID_BKL) % LCD_BCKL_STEPS; + + index = index > 0 ? index - 1 : 0; + val = lcd_bckl_steps[index]; + __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, val); + + reg_set_value(REG_ID_BKL, index); +} + + +inline void kbd_backlight_on(void) { + uint16_t val = 0; + + val = kbd_bckl_steps[reg_get_value(REG_ID_BK2) % KBD_BCKL_STEPS]; + __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, val); +} + +inline void kbd_backlight_off(void) { + __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, 0x0000); +} + +inline void kbd_backlight_update(uint8_t step) { + uint16_t val = 0; + const uint8_t index = step % KBD_BCKL_STEPS; + + val = kbd_bckl_steps[index]; + __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, val); + + reg_set_value(REG_ID_BK2, index); +} + +inline void kbd_backlight_update_loop(void) { + uint16_t val = 0; + uint8_t index = reg_get_value(REG_ID_BK2) % KBD_BCKL_STEPS; + + index = index < (KBD_BCKL_STEPS-1) ? index + 1 : 0; + val = kbd_bckl_steps[index]; + __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, val); + + reg_set_value(REG_ID_BK2, index); +} diff --git a/Core/Src/batt.c b/Core/Src/batt.c new file mode 100644 index 0000000..fb5d5a5 --- /dev/null +++ b/Core/Src/batt.c @@ -0,0 +1,63 @@ +#include "batt.h" +#include "main.h" +#include "axp2101.h" + + +static uint32_t low_bat_count = 0; + +void show_bat_segs(void) { + if (AXP2101_isBatteryConnect() == 0) + return; + + uint8_t pcnt; + if (AXP2101_getBatteryPercent(&pcnt) != HAL_OK) + return; + uint8_t prev_state = (LL_GPIO_IsOutputPinSet(SYS_LED_GPIO_Port, SYS_LED_Pin) == 0); + uint8_t blink_cnt; + + if(pcnt > 0 && pcnt < 33) + blink_cnt = 1; + else if(pcnt >= 33 && pcnt < 66) + blink_cnt = 1; + else if(pcnt >= 66 && pcnt <= 100) + blink_cnt = 1; + + flash_one_time(blink_cnt, prev_state); + + if (AXP2101_isCharging()) + start_chg(); +} + +// CAUTION: This is related to the battery charging and discharging logic. If you're not sure what you're doing, please don't modify it, as it could damage the battery. +void low_bat(void) { + uint8_t pcnt; + if (AXP2101_getBatteryPercent(&pcnt) != HAL_OK) + return; + + if ((pcnt >= 0) && (pcnt <= (uint8_t)LOW_BAT_VAL)) { + low_bat_count++; + LL_GPIO_SetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + + if (pcnt <= 1) { + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_BLINK_4HZ); + if(pcnt == 0 && low_bat_count >= 4) + AXP2101_shutdown(); + } else { + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_ON); + } + } else { + low_bat_count = 0; + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_OFF); + } +} + +void start_chg(void) { + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_BLINK_1HZ); +} + +void stop_chg(void) { + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_OFF); + low_bat(); +} diff --git a/Core/Src/eeprom.c b/Core/Src/eeprom.c new file mode 100644 index 0000000..5d7023a --- /dev/null +++ b/Core/Src/eeprom.c @@ -0,0 +1,427 @@ +//EEPROM emulation library for STM32F1XX with HAL-Driver +//V2.0 + + +//includes +#include "eeprom.h" + + +//private function prototypes; +static EEPROM_Result EEPROM_PageTransfer(); +static EEPROM_Result EEPROM_SetPageStatus(EEPROM_Page Page, EEPROM_PageStatus PageStatus); +static EEPROM_Result EEPROM_PageToIndex(EEPROM_Page Page); + + +//global variables +static uint8_t EEPROM_SizeTable[EEPROM_VARIABLE_COUNT]; //EEPROM_SizeTable[i]: actual size of variable i (as EEPROM_Size) +static uint16_t EEPROM_Index[EEPROM_VARIABLE_COUNT]; //EEPROM_Index[i]: actual address of variable i (physical address = EEPROM_START_ADDRESS + EEPROM_Index[i]) + //if EEPROM_Index[i] = 0 variable i not assigned + +static uint32_t EEPROM_ValidPage = EEPROM_PAGE_NONE; +static uint32_t EEPROM_ReceivingPage = EEPROM_PAGE_NONE; +static uint32_t EEPROM_ErasedPage = EEPROM_PAGE_NONE; + +static uint32_t EEPROM_NextIndex = 0; + + +// initialize the EEPROM & restore the pages to a known good state in case of page's status corruption after a power loss +// - unlock flash +// - read each page status and check if valid +// - if invalid page status, format EEPROM +// - set global variables ValidPage, ReceivingPage and ErasedPage +// - build address index +// - resume page transfer if needed +// +// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT +EEPROM_Result EEPROM_Init() +{ + EEPROM_Result result; + + //unlock the flash memory + HAL_FLASH_Unlock(); + + //read each page status and check if valid + EEPROM_PageStatus PageStatus0 = *((__IO uint16_t*) EEPROM_PAGE0); + EEPROM_PageStatus PageStatus1 = *((__IO uint16_t*) EEPROM_PAGE1); + uint8_t InvalidState = 0; + if (PageStatus0 != EEPROM_VALID && PageStatus0 != EEPROM_RECEIVING && PageStatus0 != EEPROM_ERASED) InvalidState = 1; + if (PageStatus1 != EEPROM_VALID && PageStatus1 != EEPROM_RECEIVING && PageStatus1 != EEPROM_ERASED) InvalidState = 1; + if (PageStatus0 == PageStatus1) InvalidState = 1; + + // if invalid page status, format EEPROM (erase both pages and set page0 as valid) + if (InvalidState) + { + FLASH_EraseInitTypeDef EraseDefinitions; + EraseDefinitions.TypeErase = FLASH_TYPEERASE_PAGES; + EraseDefinitions.Banks = FLASH_BANK_1; + EraseDefinitions.PageAddress = EEPROM_PAGE0; + EraseDefinitions.NbPages = 2; + uint32_t PageError; + + result = HAL_FLASHEx_Erase(&EraseDefinitions, &PageError); + if (result != EEPROM_SUCCESS) return result; + + result = HAL_FLASH_Program(EEPROM_SIZE16, EEPROM_PAGE0, EEPROM_VALID); + if (result != EEPROM_SUCCESS) return result; + + PageStatus0 = EEPROM_VALID; + PageStatus1 = EEPROM_ERASED; + } + + //set global variables ValidPage, ReceivingPage and ErasedPage (one stays EEPROM_PAGE_NONE) + if (PageStatus0 == EEPROM_VALID) EEPROM_ValidPage = EEPROM_PAGE0; + if (PageStatus1 == EEPROM_VALID) EEPROM_ValidPage = EEPROM_PAGE1; + if (PageStatus0 == EEPROM_RECEIVING) EEPROM_ReceivingPage = EEPROM_PAGE0; + if (PageStatus1 == EEPROM_RECEIVING) EEPROM_ReceivingPage = EEPROM_PAGE1; + if (PageStatus0 == EEPROM_ERASED) EEPROM_ErasedPage = EEPROM_PAGE0; + if (PageStatus1 == EEPROM_ERASED) EEPROM_ErasedPage = EEPROM_PAGE1; + + //build address index (addresses from receiving page are dominant) + EEPROM_PageToIndex(EEPROM_ValidPage); + EEPROM_PageToIndex(EEPROM_ReceivingPage); + + //if needed, resume page transfer or just mark receiving page as valid + if (EEPROM_ReceivingPage != EEPROM_PAGE_NONE) + { + if (EEPROM_ValidPage == EEPROM_PAGE_NONE) + { + result = EEPROM_SetPageStatus(EEPROM_ReceivingPage, EEPROM_VALID); + if (result != EEPROM_SUCCESS) return result; + } + else + { + result = EEPROM_PageTransfer(EEPROM_ValidPage, EEPROM_ReceivingPage); + if (result != EEPROM_SUCCESS) return result; + } + } + + return EEPROM_SUCCESS; +} + + +// returns the last stored variable value which correspond to the passed variable name +// - check if variable name exists +// - check if variable was assigned +// - read variable value from physical address with right size +// +// VariableName: name (number) of the variable to read +// Value: outputs the variable value +// return: EEPROM_SUCCESS, EEPROM_INVALID_NAME, EEPROM_NOT_ASSIGNED +EEPROM_Result EEPROM_ReadVariable(uint16_t VariableName, EEPROM_Value* Value) +{ + //check if variable name exists + if (VariableName >= EEPROM_VARIABLE_COUNT) return EEPROM_INVALID_NAME; + + //check if variable was assigned + uint32_t Address = EEPROM_START_ADDRESS + EEPROM_Index[VariableName]; + if (Address == EEPROM_PAGE0) return EEPROM_NOT_ASSIGNED; + + //read variable value from physical address with right size + switch (EEPROM_SizeTable[VariableName]) + { + case EEPROM_SIZE16: (*Value).uInt16 = *((__IO uint16_t*) Address); break; + case EEPROM_SIZE32: (*Value).uInt32 = *((__IO uint32_t*) Address); break; + case EEPROM_SIZE64: (*Value).uInt64 = *((__IO uint32_t*) Address) | ((uint64_t) *((__IO uint32_t*) (Address + 4)) << 32); break; + default: return EEPROM_NOT_ASSIGNED; + } + + return EEPROM_SUCCESS; +} + + +// writes variable in EEPROM if page not full +// - get writing page's end address +// - calculate memory usage of variable +// - check if page full +// - check if data is too much to store on one page +// - mark the target page as receiving +// - change next index to receiving page +// - write the variable to target page +// - do page transfer +// - else (if enough space) +// - write variable value +// - create and write variable header (size and name) +// - update index & size table +// - update next index +// +// VariableName: name (number) of the variable to write +// Value: value to be written +// Size: size of "Value" as EEPROM_Size +// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT +EEPROM_Result EEPROM_WriteVariable(uint16_t VariableName, EEPROM_Value Value, uint8_t Size) +{ + EEPROM_Result result; + + //get writing page's end address (prefer writing to receiving page) + EEPROM_Page WritingPage = EEPROM_ValidPage; + if (EEPROM_ReceivingPage != EEPROM_PAGE_NONE) WritingPage = EEPROM_ReceivingPage; + if (WritingPage == EEPROM_PAGE_NONE) return EEPROM_NO_VALID_PAGE; + uint32_t PageEndAddress = WritingPage + FLASH_PAGE_SIZE; + + //calculate memory usage of variable + uint8_t Bytes = 2 + (1 << Size); + if (Size == EEPROM_SIZE_DELETED) Bytes = 2; + + //check if enough free space or page full + if (EEPROM_NextIndex == 0 || PageEndAddress - EEPROM_NextIndex < Bytes) + { + //check if data is too much to store on one page + uint16_t RequiredMemory = 2; + for (uint16_t i = 0; i < EEPROM_VARIABLE_COUNT; i++) + { + if (i == VariableName) RequiredMemory += 2 + (1 << Size); + else if (EEPROM_SizeTable[i] != EEPROM_SIZE_DELETED) RequiredMemory += 2 + (1 << EEPROM_SizeTable[i]); + } + if (RequiredMemory > FLASH_PAGE_SIZE) return EEPROM_FULL; + + //mark the empty page as receiving + result = EEPROM_SetPageStatus(EEPROM_ErasedPage, EEPROM_RECEIVING); + if (result != EEPROM_SUCCESS) return result; + + //change next index to receiving page + EEPROM_NextIndex = EEPROM_ReceivingPage + 2; + + //write the variable to receiving page (by calling this function again) + result = EEPROM_WriteVariable(VariableName, Value, Size); + if (result != EEPROM_SUCCESS) return result; + + //do page transfer + result = EEPROM_PageTransfer(); + if (result != EEPROM_SUCCESS) return result; + } + + //else (if enough space) + else + { + //write variable value + if (Size != EEPROM_SIZE_DELETED) + { + result = HAL_FLASH_Program(Size, EEPROM_NextIndex + 2, Value.uInt64); + if (result != EEPROM_SUCCESS) return result; + } + + //create and write variable header (size and name) + uint16_t VariableHeader = VariableName + (Size << 14); + result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, EEPROM_NextIndex, VariableHeader); + if (result != EEPROM_SUCCESS) return result; + + //update index & size table + EEPROM_Index[VariableName] = EEPROM_NextIndex + 2 - EEPROM_START_ADDRESS; + EEPROM_SizeTable[VariableName] = Size; + if (Size == EEPROM_SIZE_DELETED) EEPROM_Index[VariableName] = 0; + + //update next index + EEPROM_NextIndex += Bytes; + if (EEPROM_NextIndex >= PageEndAddress) EEPROM_NextIndex = 0; + } + + return EEPROM_SUCCESS; +} + + +//marks a variable as deleted so it can't be read anymore and is discarded on next page transfer +// - call write variable with size EEPROM_SIZE_DELETED +// +// VariableName: name (number) of the variable to delete +// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT +EEPROM_Result EEPROM_DeleteVariable(uint16_t VariableName) +{ + return EEPROM_WriteVariable(VariableName, (EEPROM_Value) (uint16_t) 0, EEPROM_SIZE_DELETED); +} + + +// transfers latest variable values from valid page to receiving page +// - get start & end address of valid page (source) +// - copy each variable +// - check if is stored on the source page +// - read variable value +// - write variable to receiving page +// - erase source page +// - mark receiving page as valid +// +// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT +static EEPROM_Result EEPROM_PageTransfer() +{ + EEPROM_Result result; + EEPROM_Value Value; + + //get start & end address of valid page (source) (as offset to EEPROM start) + uint16_t StartAddress = EEPROM_ValidPage - EEPROM_START_ADDRESS; + uint16_t EndAddress = EEPROM_ValidPage - EEPROM_START_ADDRESS + FLASH_PAGE_SIZE; + + //copy each variable + for (uint16_t i = 0; i < EEPROM_VARIABLE_COUNT; i++) + { + //check if is stored on the source page + if (StartAddress < EEPROM_Index[i] && EEPROM_Index[i] < EndAddress) + { + //read variable value (if possible) + if (EEPROM_ReadVariable(i, &Value) == EEPROM_SUCCESS) + { + //write variable to receiving page + result = EEPROM_WriteVariable(i, Value, EEPROM_SizeTable[i]); + if (result != EEPROM_SUCCESS) return result; + } + } + } + + //erase source page + result = EEPROM_SetPageStatus(EEPROM_ValidPage, EEPROM_ERASED); + if (result != EEPROM_SUCCESS) return result; + + //mark receiving page as valid + result = EEPROM_SetPageStatus(EEPROM_ReceivingPage, EEPROM_VALID); + if (result != EEPROM_SUCCESS) return result; + + return EEPROM_SUCCESS; +} + + +// sets the page status and updates references from global variables +// - check if erase operation required +// - remove every variable from index, that is stored on erase page +// - setup erase definitions +// - erase page +// - else write status to flash +// - update global page status variables +// +// Page: page to change the status (as EEPROM_Page) +// PageStatus: page status to set for page (as EEPROM_PageStatus) +// return: EEPROM_SUCCESS, EEPROM_ERROR, EEPROM_BUSY or EEPROM_TIMEOUT +static EEPROM_Result EEPROM_SetPageStatus(EEPROM_Page Page, EEPROM_PageStatus PageStatus) +{ + EEPROM_Result result; + + //check if erase operation required + if (PageStatus == EEPROM_ERASED) + { + //remove every variable from index, that is stored on erase page + uint16_t StartAddress = Page - EEPROM_START_ADDRESS; + uint16_t EndAddress = Page - EEPROM_START_ADDRESS + FLASH_PAGE_SIZE; + for (uint16_t i = 0; i < EEPROM_VARIABLE_COUNT; i++) + { + if (StartAddress < EEPROM_Index[i] && EEPROM_Index[i] < EndAddress) EEPROM_Index[i] = 0; + } + + //setup erase definitions + FLASH_EraseInitTypeDef EraseDefinitions; + EraseDefinitions.TypeErase = FLASH_TYPEERASE_PAGES; + EraseDefinitions.Banks = FLASH_BANK_1; + EraseDefinitions.PageAddress = Page; + EraseDefinitions.NbPages = 1; + uint32_t PageError; + + //erase page + result = HAL_FLASHEx_Erase(&EraseDefinitions, &PageError); + if (result != EEPROM_SUCCESS) return result; + } + + //else write status to flash + else + { + result = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, Page, PageStatus); + if (result != EEPROM_SUCCESS) return result; + } + + //update global page status variables (remove page from old status and attach to new status) + if (EEPROM_ValidPage == Page) EEPROM_ValidPage = EEPROM_PAGE_NONE; + else if (EEPROM_ReceivingPage == Page) EEPROM_ReceivingPage = EEPROM_PAGE_NONE; + else if (EEPROM_ErasedPage == Page) EEPROM_ErasedPage = EEPROM_PAGE_NONE; + + if (PageStatus == EEPROM_VALID) EEPROM_ValidPage = Page; + else if (PageStatus == EEPROM_RECEIVING) EEPROM_ReceivingPage = Page; + else if (PageStatus == EEPROM_ERASED) EEPROM_ErasedPage = Page; + + return EEPROM_SUCCESS; +} + + +// reads the whole page, fills the index with variable addresses and the size table with variable sizes +// - declare variables +// - ignore call when Page is PAGE_NONE +// - get page addresses +// - loop through page addresses +// - read potential variable header +// - if no header written +// - loop through next 4 halfword and check if there is anything written +// - while looping count the size of written data +// - if no data found, last variable of page was reached (return) +// - else (if header written) +// - get size code +// - check for valid name +// - if everything valid, update the index and the size table +// - calculate size in bytes from size code +// - go to next address on page +// - set next free flash address +// - return on loop end +// +// Page: page to search for variables +// return: EEPROM_SUCCESS +static EEPROM_Result EEPROM_PageToIndex(EEPROM_Page Page) +{ + //declare variables + uint16_t VariableHeader; //header of current variable (first 2 bits size code, rest name) + uint8_t SizeCode; //size of current variable as Size code + uint8_t Size; //size of current variable in bytes + uint16_t Name; //name of current variable + + //ignore call when Page is PAGE_NONE + if (Page == EEPROM_PAGE_NONE) return EEPROM_SUCCESS; + + //get page addresses + uint32_t Address = Page + 2; + uint32_t PageEndAddress = Page + FLASH_PAGE_SIZE; + + //loop through page starting after page header + while (Address < PageEndAddress) + { + //read potential variable header + VariableHeader = *((__IO uint16_t*) Address); + + //if no header written (causes: end of data reached or reset while writing) + if (VariableHeader == 0xFFFF) + { + //loop through next 4 halfword and check if there is anything written + Size = 0; + for (uint8_t i = 2; i <= 8; i += 2) + { + if (Address + i >= PageEndAddress) break; + //while looping count the size of written data (resulting from reset while writing) + if (*((__IO uint16_t*) (Address + i)) != 0xFFFF) Size = i; + } + //if no data found, last variable of page was reached (end loop) + if (Size == 0) break; + } + + //else (if header written, proper variable value is following) + else + { + //get size code + SizeCode = VariableHeader >> 14; + + //check for valid name (VARIABLE_COUNT might have been reduced between builds, but old variables are still in flash) + Name = VariableHeader & 0b0011111111111111; + if (Name < EEPROM_VARIABLE_COUNT) + { + //if everything valid, update the index and the size table + EEPROM_Index[Name] = Address + 2 - EEPROM_START_ADDRESS; + EEPROM_SizeTable[Name] = SizeCode; + if (SizeCode == EEPROM_SIZE_DELETED) EEPROM_Index[Name] = 0; + } + + //calculate size in bytes from size code + Size = 1 << SizeCode; + if (SizeCode == EEPROM_SIZE_DELETED) Size = 0; + } + + //go to next address on page + Address = Address + 2 + Size; + } + + //set next free flash address + EEPROM_NextIndex = Address; + if (Address >= PageEndAddress) EEPROM_NextIndex = 0; + + //return on loop end + return EEPROM_SUCCESS; +} diff --git a/Core/Src/fifo.c b/Core/Src/fifo.c new file mode 100644 index 0000000..b32ad72 --- /dev/null +++ b/Core/Src/fifo.c @@ -0,0 +1,59 @@ +#include "fifo.h" + + +static struct fifo_item fifo[FIFO_SIZE] = {0}; +static uint8_t count = 0; +static volatile uint8_t read_idx = 0; +static volatile uint8_t write_idx = 0; + + +inline uint8_t fifo_count(void) { + return count; +} + +void fifo_flush(void) { + write_idx = 0; + read_idx = 0; + count = 0; +} + +uint8_t fifo_enqueue(const struct fifo_item item) { + if (count >= FIFO_SIZE) + return 0; + + fifo[write_idx].state = item.state; + fifo[write_idx].key = item.key; + + write_idx++; + write_idx %= FIFO_SIZE; + ++count; + + return 1; +} + +void fifo_enqueue_force(const struct fifo_item item) { + if (fifo_enqueue(item)) + return; + + fifo[write_idx].state = item.state; + fifo[write_idx].key = item.key; + write_idx++; + write_idx %= FIFO_SIZE; + + read_idx++; + read_idx %= FIFO_SIZE; +} + +void fifo_dequeue(struct fifo_item* const outItem) { + if (outItem == NULL) + return; + + if (count == 0) + return; + + outItem->state = fifo[read_idx].state; + outItem->key = fifo[read_idx].key; + read_idx++; + read_idx %= FIFO_SIZE; + --count; +} diff --git a/Core/Src/keyboard.c b/Core/Src/keyboard.c new file mode 100644 index 0000000..05193dc --- /dev/null +++ b/Core/Src/keyboard.c @@ -0,0 +1,408 @@ +#include "keyboard.h" +#include "regs.h" +#include "backlight.h" +#include "batt.h" + + +enum mod { + MOD_NONE = 0, + MOD_SYM, + MOD_ALT, + MOD_SHL, + MOD_SHR, + MOD_CTRL, + MOD_LAST, +}; + +struct entry { + uint8_t chr; + uint8_t symb; + enum mod mod; +}; +#define INIT_ENTRY1(a) {a,a,MOD_NONE} +#define INIT_ENTRY2(a,b) {a,b,MOD_NONE} + +struct list_item { + const struct entry *p_entry; + uint32_t hold_start_time; + uint32_t last_repeat_time; + enum key_state state; + uint8_t mods[MOD_LAST]; +}; + +struct gpio_pin { + GPIO_TypeDef *GPIOx; + uint32_t PinMask; +}; + + + +static const struct entry kbd_entries[][NUM_OF_COLS] = { + {INIT_ENTRY2(KEY_F5,KEY_F10), INIT_ENTRY2(KEY_F4,KEY_F9), INIT_ENTRY2(KEY_F3,KEY_F8),INIT_ENTRY2(KEY_F2,KEY_F7), INIT_ENTRY2(KEY_F1,KEY_F6), INIT_ENTRY2('`','~'),INIT_ENTRY2('3','#'), INIT_ENTRY2('2','@')}, + {INIT_ENTRY1(KEY_BACKSPACE), INIT_ENTRY2(KEY_DEL,KEY_END),INIT_ENTRY1(KEY_CAPS_LOCK),INIT_ENTRY2(KEY_TAB,KEY_HOME),INIT_ENTRY2(KEY_ESC,KEY_BREAK),INIT_ENTRY2('4','$'),INIT_ENTRY1('E'), INIT_ENTRY1('W')}, + {INIT_ENTRY1('P'), INIT_ENTRY2('=','+'), INIT_ENTRY2('-','_'), INIT_ENTRY2('\\','|'), INIT_ENTRY2('/','?'), INIT_ENTRY1('R'), INIT_ENTRY1('S'), INIT_ENTRY2('1','!')}, + {INIT_ENTRY2(KEY_ENTER,KEY_INSERT), INIT_ENTRY2('8','*'), INIT_ENTRY2('7','&'), INIT_ENTRY2('6','^'), INIT_ENTRY2('5','%'), INIT_ENTRY1('F'), INIT_ENTRY1('X'), INIT_ENTRY1('Q')}, + {INIT_ENTRY2('.','>'), INIT_ENTRY1('I'), INIT_ENTRY1('U'), INIT_ENTRY1('Y'), INIT_ENTRY1('T'), INIT_ENTRY1('V'), INIT_ENTRY2(';',':'), INIT_ENTRY1('A')}, + {INIT_ENTRY1('L'), INIT_ENTRY1('K'), INIT_ENTRY1('J'), INIT_ENTRY1('H'), INIT_ENTRY1('G'), INIT_ENTRY1('C'), INIT_ENTRY2('\'','"'),INIT_ENTRY1('Z')}, + {INIT_ENTRY1('O'), INIT_ENTRY2(',','<'), INIT_ENTRY1('M'), INIT_ENTRY1('N'), INIT_ENTRY1('B'), INIT_ENTRY1('D'), INIT_ENTRY1(' '), INIT_ENTRY1(0x00)} +}; + +static const struct entry btn_entries[12] = { + {.mod = MOD_ALT}, + {.mod = MOD_CTRL}, + {.mod = MOD_SHL}, + {.mod = MOD_SHR}, + INIT_ENTRY2('0',')'), + INIT_ENTRY2('9','('), + INIT_ENTRY2(']','}'), + INIT_ENTRY2('[','{'), + INIT_ENTRY1(KEY_RIGHT), + INIT_ENTRY2(KEY_UP,KEY_PAGE_UP), + INIT_ENTRY2(KEY_DOWN,KEY_PAGE_DOWN), + INIT_ENTRY1(KEY_LEFT) +}; + +static const struct gpio_pin row_pins[NUM_OF_ROWS] = { + {ROW_1_GPIO_Port, ROW_1_Pin}, + {ROW_2_GPIO_Port, ROW_2_Pin}, + {ROW_3_GPIO_Port, ROW_3_Pin}, + {ROW_4_GPIO_Port, ROW_4_Pin}, + {ROW_5_GPIO_Port, ROW_5_Pin}, + {ROW_6_GPIO_Port, ROW_6_Pin}, + {ROW_7_GPIO_Port, ROW_7_Pin} +}; + +static const struct gpio_pin col_pins[NUM_OF_COLS] = { + {COL_1_GPIO_Port, COL_1_Pin}, + {COL_2_GPIO_Port, COL_2_Pin}, + {COL_3_GPIO_Port, COL_3_Pin}, + {COL_4_GPIO_Port, COL_4_Pin}, + {COL_5_GPIO_Port, COL_5_Pin}, + {COL_6_GPIO_Port, COL_6_Pin}, + {COL_7_GPIO_Port, COL_7_Pin}, + {COL_8_GPIO_Port, COL_8_Pin} +}; + +static const struct gpio_pin btn_pins[12] = { + {KEY_1_GPIO_Port, KEY_1_Pin}, + {KEY_2_GPIO_Port, KEY_2_Pin}, + {KEY_3_GPIO_Port, KEY_3_Pin}, + {KEY_4_GPIO_Port, KEY_4_Pin}, + {KEY_5_GPIO_Port, KEY_5_Pin}, + {KEY_6_GPIO_Port, KEY_6_Pin}, + {KEY_7_GPIO_Port, KEY_7_Pin}, + {KEY_8_GPIO_Port, KEY_8_Pin}, + {KEY_9_GPIO_Port, KEY_9_Pin}, + {KEY_10_GPIO_Port, KEY_10_Pin}, + {KEY_11_GPIO_Port, KEY_11_Pin}, + {KEY_12_GPIO_Port, KEY_12_Pin} +}; + +static struct list_item keys_list[KEY_LIST_SIZE]; +static lock_callback _lock_callback = NULL; +static key_callback _key_callback= NULL; +static uint32_t last_process_time; +static uint8_t mods[MOD_LAST]; +static uint8_t capslock_changed = 0; +static uint8_t capslock = 0; +static uint8_t numlock_changed = 0; +static uint8_t numlock = 0; + + +inline void keyboard_set_key_callback(key_callback callback) { + _key_callback = callback; +} + +inline void keyboard_set_lock_callback(lock_callback callback) { + _lock_callback = callback; +} + +inline uint8_t keyboard_get_capslock(void) { + return capslock & 0x1; +} + +inline uint8_t keyboard_get_numlock(void) { + return numlock & 0x1; +} + +static void transition_to(struct list_item * const p_item, const enum key_state next_state) { + uint8_t output = 1; + const struct entry * const p_entry = p_item->p_entry; + + p_item->state = next_state; + + if (!_key_callback || !p_entry) + return; + + char chr = p_entry->chr; + + switch (p_entry->mod) { + case MOD_ALT: + if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS)) + chr = KEY_MOD_ALT; + break; + + case MOD_SHL: + if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS)) + chr = KEY_MOD_SHL; + break; + + case MOD_SHR: + if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS)) + chr = KEY_MOD_SHR; + break; + + case MOD_SYM: + if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS)) + chr = KEY_MOD_SYM; + break; + + case MOD_CTRL: + if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS)) + chr = KEY_MOD_CTRL; + break; + + default: //toggle operation + if (chr == KEY_CAPS_LOCK && next_state == KEY_STATE_PRESSED) { + capslock = (capslock & 0x1) ^ 0x1; + capslock_changed = 1; + } + + if (reg_is_bit_set(REG_ID_CFG, CFG_USE_MODS)) { + const uint8_t shift = (mods[MOD_SHL] || mods[MOD_SHR]); + const uint8_t alt = mods[MOD_ALT] | numlock; + //const uint8_t ctrl = mods[MOD_CTRL]; + + if (shift && (chr <'A' || chr >'Z')) { + chr = p_entry->symb; + } else if (capslock && (chr >= 'A' && chr <= 'Z')) { + //pass + } else if (alt) { + //ctrl for operators + if (next_state == KEY_STATE_PRESSED) { + if (chr == ',' || chr == '.' || chr == ' ' || chr == 'B') { + output = 0; + } else if (chr == 'I') { + output = 1; + chr = KEY_INSERT; + } + } + + if (next_state == KEY_STATE_RELEASED) { + if (chr == ',' || chr == '.' || chr == ' ' || chr == 'B') { + output = 0; + } else if(chr == 'I'){ + output = 1; + chr = KEY_INSERT; + } + } + + if(next_state == KEY_STATE_RELEASED) { + if(chr ==',') + lcd_backlight_update_down(); + else if(chr =='.') + lcd_backlight_update_up(); + else if(chr == ' ') + //loop update keyboard backlight + kbd_backlight_update_loop(); + else if(chr == 'B') + show_bat_segs(); + } + } else if (!shift && (chr >= 'A' && chr <= 'Z')) { + chr = (chr + ' '); // uppercase to lowercase for a to z + } + } + break; + } + + if (chr != 0 && output == 1) { + if(next_state == KEY_STATE_HOLD && + ((chr >= 32 && chr <= 127) || chr == KEY_ENTER || chr == KEY_TAB || chr == KEY_DEL || chr == KEY_BACKSPACE || chr == KEY_UP || chr == KEY_DOWN || chr == KEY_RIGHT || chr == KEY_LEFT) ) + _key_callback(chr, KEY_STATE_PRESSED); + else + _key_callback(chr, next_state); + } +} + +static void next_item_state(struct list_item* const p_item, const uint8_t pressed) { + switch (p_item->state) { + default: + case KEY_STATE_IDLE: + if (pressed) { + if (p_item->p_entry->mod != MOD_NONE) + mods[p_item->p_entry->mod] = 1; + + if (!capslock_changed && mods[MOD_SHR] && mods[MOD_ALT]) { + capslock = 1; + capslock_changed = 1; + } + + if (!numlock_changed && mods[MOD_SHL] && mods[MOD_ALT]) { + numlock = 1; + numlock_changed = 1; + } + + if (!capslock_changed && (mods[MOD_SHL] || mods[MOD_SHR])) { + capslock = 0; + capslock_changed = 1; + } + + if (!numlock_changed && (mods[MOD_SHL] || mods[MOD_SHR])) { + numlock = 0; + numlock_changed = 1; + } + + if (!mods[MOD_ALT]) { + capslock_changed = 0; + numlock_changed = 0; + } + + if (_lock_callback && (capslock_changed || numlock_changed)) + _lock_callback(capslock_changed, numlock_changed); + + transition_to(p_item, KEY_STATE_PRESSED); + + p_item->hold_start_time = uptime_ms(); + } + break; + + case KEY_STATE_PRESSED: + if (uptime_ms() > p_item->hold_start_time + KEY_HOLD_TIME) { + transition_to(p_item, KEY_STATE_HOLD); + p_item->last_repeat_time = uptime_ms(); + } else if (!pressed) + transition_to(p_item, KEY_STATE_RELEASED); + break; + + case KEY_STATE_HOLD: + if (!pressed) + transition_to(p_item, KEY_STATE_RELEASED); + else { + if (uptime_ms() > p_item->hold_start_time + KEY_HOLD_TIME) { + if(uptime_ms() > p_item->last_repeat_time + KEY_REPEAT_TIME) { + transition_to(p_item, KEY_STATE_HOLD); + p_item->last_repeat_time = uptime_ms(); + } + } + } + break; + + case KEY_STATE_RELEASED: + if (p_item->p_entry->mod != MOD_NONE) + mods[p_item->p_entry->mod] = 0; + + p_item->p_entry = NULL; + transition_to(p_item, KEY_STATE_IDLE); + break; + } +} + +void keyboard_process(void) { + js_bits = 0xFF; + + if (uptime_ms() <= (last_process_time + KEY_POLL_TIME)) + return; + + // Scan for columns + for (uint8_t c = 0; c < NUM_OF_COLS; ++c) { + uint8_t col_value = 0; + // Enable the columns signal - OD logic + LL_GPIO_ResetOutputPin(col_pins[c].GPIOx, col_pins[c].PinMask); + + // Scan for rows + for (uint8_t r = 0; r < NUM_OF_ROWS; ++r) { + const uint8_t pressed = (LL_GPIO_IsInputPinSet(row_pins[r].GPIOx, row_pins[r].PinMask) == 0); + uint8_t row_bit = (1 << r); + + if (pressed) { + if (c == 1 && r == 4) + js_bits &= ~row_bit; + col_value &= ~row_bit; + } else { + if (c == 1 && r == 4) { + js_bits |= row_bit; + } + col_value |= row_bit; + } + + const int32_t key_idx = (int32_t)((r * NUM_OF_COLS) + c); + int32_t list_idx = -1; + for (int32_t i = 0; i < KEY_LIST_SIZE; ++i) { + if (keys_list[i].p_entry != &((const struct entry*)kbd_entries)[key_idx]) + continue; + + list_idx = i; + break; + } + + if (list_idx > -1) { + next_item_state(&keys_list[list_idx], pressed); + continue; + } + + if (!pressed) + continue; + + for (uint32_t i = 0; i < KEY_LIST_SIZE; ++i) { + if (keys_list[i].p_entry != NULL) + continue; + + keys_list[i].p_entry = &((const struct entry*)kbd_entries)[key_idx]; + keys_list[i].state = KEY_STATE_IDLE; + next_item_state(&keys_list[i], pressed); + + break; + } + } + // Disable the columns signal - OD logic + LL_GPIO_SetOutputPin(col_pins[c].GPIOx, col_pins[c].PinMask); + + io_matrix[c] = col_value; + for (uint8_t b = 0; b < 12; ++b) { + const uint8_t pressed = (LL_GPIO_IsInputPinSet(btn_pins[b].GPIOx, btn_pins[b].PinMask) == 0); + if (b < 8) { // read BTN1->BTN8 + if (pressed) + io_matrix[b] &= (uint8_t)(~(1 << 7)); + else + io_matrix[b] |= (1 << 7); + } else { //c64 joystick arrow keys + //B12=left,, B11=down,B10 = up,B9 = right + if (pressed) + js_bits &= (uint8_t)(~(1 << (b - 8))); + else + js_bits |= (1 << (b - 8)); + } + + int8_t list_idx = -1; + for (int8_t i = 0; i < KEY_LIST_SIZE; ++i) { + if (keys_list[i].p_entry != &((const struct entry*)btn_entries)[b]) + continue; + + list_idx = i; + break; + } + + if (list_idx > -1) { + next_item_state(&keys_list[list_idx], pressed); + continue; + } + + if (!pressed) + continue; + + for (uint8_t i = 0 ; i < KEY_LIST_SIZE; ++i) { + if (keys_list[i].p_entry != NULL) + continue; + + keys_list[i].p_entry = &((const struct entry*)btn_entries)[b]; + keys_list[i].state = KEY_STATE_IDLE; + next_item_state(&keys_list[i], pressed); + + break; + } + } + + io_matrix[8] = 0xFF; + last_process_time = uptime_ms(); + } +} diff --git a/Core/Src/main.c b/Core/Src/main.c index 4ce765e..34e73ac 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -1,19 +1,17 @@ /* USER CODE BEGIN Header */ /** ****************************************************************************** - * @file : main.c - * @brief : Main program body - ****************************************************************************** - * @attention * - * Copyright (c) 2025 STMicroelectronics. + * Copyright (c) 2025 C.ARE (JackCarterSmith). * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** + * + * SYS_LED and COL_x are open-drain, output logic is inverted. + * */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ @@ -21,22 +19,42 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include "axp2101.h" +#include "backlight.h" +#include "batt.h" +#include "eeprom.h" +#include "fifo.h" +#include "keyboard.h" +#include "regs.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ - +enum i2cs_state { + //I2CS_STATE_HALT, + I2CS_STATE_IDLE, + I2CS_STATE_REG_REQUEST, + I2CS_STATE_REG_ANSWER +}; /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ +//#define DEFAULT_LCD_BL (205) // ~40% PWM@7.81kHz (9 bits resolution) +//#define DEFAULT_KBD_BL (20) // ~4% PWM@7.81kHz (9 bits resolution) +#define DEFAULT_LCD_BL (3) //step-4 (~50%) +#define DEFAULT_KBD_BL (0) //step-1 (0%) +#define I2CS_REARM_TIMEOUT 500 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ - +#ifdef DEBUG +#define DEBUG_UART_MSG(msg) HAL_UART_Transmit(&huart1, (uint8_t*)msg, sizeof(msg)-1, 1000) +//#define DEBUG_UART_MSG2(d,s) HAL_UART_Transmit(&huart1, (uint8_t*)d, s, 200) +#define DEBUG_UART_MSG2(d,sz, swp) uart_rawdata_write(d,sz,swp) +#endif /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ @@ -51,7 +69,27 @@ UART_HandleTypeDef huart1; UART_HandleTypeDef huart3; /* USER CODE BEGIN PV */ +volatile uint32_t systicks_counter = 0; // 1 MHz systick counter - TODO: implement overflow self-reset mechanism +volatile uint32_t pmu_check_counter = 0; +volatile uint32_t i2cs_rearm_counter = 0; +#ifdef DEBUG +static const uint8_t hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +#endif +static uint8_t i2cs_r_buff[2]; +static volatile uint8_t i2cs_r_idx = 0; +static uint8_t i2cs_w_buff[31 + 1]; // The last one must be only a 0 value +static volatile uint8_t i2cs_w_idx = 0; +static volatile uint8_t i2cs_w_len = 0; +static enum i2cs_state i2cs_state = I2CS_STATE_IDLE; + +static uint8_t keycb_start = 0; +static uint32_t head_phone_status = 0; +volatile uint8_t pmu_irq = 0; +static uint32_t pmu_online = 0; + +uint8_t io_matrix[9] = {0}; //for IO matrix,last byte is the restore key(c64 only) +uint8_t js_bits = 0xFF; // c64 joystick bits /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ @@ -67,11 +105,170 @@ static void MX_TIM1_Init(void); static void MX_TIM3_Init(void); static void MX_TIM2_Init(void); /* USER CODE BEGIN PFP */ - +//static void lock_cb(uint8_t caps_changed, uint8_t num_changed); +static void key_cb(char key, enum key_state state); +static void hw_check_HP_presence(void); +static void sync_bat(void); +static void check_pmu_int(void); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ +extern void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { + if (htim == &htim2) { + systicks_counter += 1; + i2cs_rearm_counter += 1; + } +} + +extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) { + if (hi2c == &hi2c1) { + // I2C slave addr match error detection + if (AddrMatchCode != 0x3E) // 0x1F << 1 + return; + + if (TransferDirection == I2C_DIRECTION_TRANSMIT) { + if (i2cs_state == I2CS_STATE_IDLE) { + i2cs_state = I2CS_STATE_REG_REQUEST; + + i2cs_r_idx = 0; + HAL_I2C_Slave_Sequential_Receive_IT(hi2c, i2cs_r_buff, 1, I2C_FIRST_FRAME); // This write the first received byte to i2cs_r_buff[0] + + i2cs_rearm_counter = 0; + } + } + + if (TransferDirection == I2C_DIRECTION_RECEIVE) { + if (i2cs_state == I2CS_STATE_REG_REQUEST) { + const uint8_t is_write = (uint8_t)(i2cs_r_buff[0] & (1 << 7)); + const uint8_t reg = (uint8_t)(i2cs_r_buff[0] & ~(1 << 7)); + + i2cs_w_buff[0] = reg; + i2cs_w_len = 2; + + if (reg == REG_ID_BKL) { // We wait an another byte for these registers + if (is_write) + lcd_backlight_update(i2cs_r_buff[1]); + i2cs_w_buff[1] = reg_get_value(REG_ID_BKL); + } else if (reg == REG_ID_BK2) { + if (is_write) + kbd_backlight_update(i2cs_r_buff[1]); + i2cs_w_buff[1] = reg_get_value(REG_ID_BK2); + } else if (reg == REG_ID_FIF) { + struct fifo_item item = {0}; + fifo_dequeue(&item); + i2cs_w_buff[0] = item.state; + i2cs_w_buff[1] = item.key; + } else if (reg == REG_ID_BAT) { + i2cs_w_buff[1] = reg_get_value(REG_ID_BAT); + } else if (reg == REG_ID_KEY) { + i2cs_w_buff[0] = fifo_count(); + i2cs_w_buff[0] |= keyboard_get_numlock() ? KEY_NUMLOCK : 0x00; + i2cs_w_buff[0] |= keyboard_get_capslock() ? KEY_CAPSLOCK : 0x00; + } else if (reg == REG_ID_C64_MTX) { + //memcpy(write_buffer + 1, io_matrix, sizeof(io_matrix)); + *((uint32_t*)(&i2cs_w_buff[1]) + 0) = *((uint32_t*)(io_matrix) + 0); + *((uint32_t*)(&i2cs_w_buff[1]) + 1) = *((uint32_t*)(io_matrix) + 1); + i2cs_w_buff[9] = io_matrix[8]; + i2cs_w_len = 10; + } else if (reg == REG_ID_C64_JS) { + i2cs_w_buff[1] = js_bits; + } else { + i2cs_w_buff[0] = 0; + i2cs_w_buff[1] = 0; + } + + i2cs_state = I2CS_STATE_REG_ANSWER; + i2cs_w_idx = 0; + + HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, i2cs_w_buff, i2cs_w_len, I2C_FIRST_AND_LAST_FRAME); + + i2cs_rearm_counter = 0; + } + } + } +} + +extern void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) { + if (hi2c == &hi2c1) { + i2cs_r_idx++; + + if (i2cs_state == I2CS_STATE_REG_REQUEST) { + const uint8_t is_write = (uint8_t)(i2cs_r_buff[0] & (1 << 7)); + const uint8_t reg = (uint8_t)(i2cs_r_buff[0] & ~(1 << 7)); + + if (reg == REG_ID_BKL) { // We wait an another byte for these registers + if (is_write) { + HAL_I2C_Slave_Sequential_Receive_IT(hi2c, i2cs_r_buff + i2cs_r_idx, 1, I2C_NEXT_FRAME); // This write the second received byte to i2cs_r_buff[1] + } + } else if (reg == REG_ID_BK2) { + if (is_write) { + HAL_I2C_Slave_Sequential_Receive_IT(hi2c, i2cs_r_buff + i2cs_r_idx, 1, I2C_NEXT_FRAME); // This write the second received byte to i2cs_r_buff[1] + } + } + } + } +} + +/*extern void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c) { + if (hi2c == &hi2c1) { + if (i2cs_state == I2CS_STATE_REG_ANSWER) { + if (++i2cs_w_idx < i2cs_w_len) { + HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, i2cs_w_buff + i2cs_w_idx, 1, I2C_NEXT_FRAME); // This write the next answer byte on I2C bus + } else { + i2cs_w_buff[31] = 0; + HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, &i2cs_w_buff[31], 1, I2C_NEXT_FRAME); // send a 0 value to avoid stalling - TODO: usefull? can we use I2C_LAST_FRAME instead? + } + + i2cs_rearm_counter = 0; + } + } +}*/ + +extern void HAL_I2C_ListenCpltCallback (I2C_HandleTypeDef *hi2c) { + if (hi2c == &hi2c1) { + if (i2cs_state == I2CS_STATE_REG_ANSWER) + i2cs_state = I2CS_STATE_IDLE; + + HAL_I2C_EnableListen_IT(hi2c); + } +} + +extern void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { + if (hi2c == &hi2c1) + if (HAL_I2C_GetError(hi2c) != HAL_I2C_ERROR_AF) + Error_Handler(); //TODO: replace with dedicated, non-blocking, error handler +} + +#ifdef DEBUG +void uart_rawdata_write(uint32_t c, size_t s, uint8_t swap) { + uint8_t r[4]; + uint32_t v = swap ? __REV(c) : c; + + HAL_UART_Transmit(&huart1, (uint8_t*)"0x", 2, 40); + for (size_t i = 0; i < s; i++) { + uint8_t index = swap ? (uint8_t)(4-s+i) : (uint8_t)i; + r[0] = hexmap[(((uint8_t*)&v)[index] & 0xF0) >> 4]; + r[1] = hexmap[((uint8_t*)&v)[index] & 0x0F]; + HAL_UART_Transmit(&huart1, r, 2, 40); + } +} +#endif + +void flash_one_time(uint32_t ts, uint8_t restore_status) { + for (size_t i = 0; i < ts; i++) { + LL_IWDG_ReloadCounter(IWDG); + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + HAL_Delay(400); + LL_GPIO_SetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + HAL_Delay(200); + } + + if (restore_status) + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); + else + LL_GPIO_SetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); +} /* USER CODE END 0 */ @@ -83,7 +280,7 @@ int main(void) { /* USER CODE BEGIN 1 */ - + int32_t result = 0; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ @@ -115,15 +312,151 @@ int main(void) MX_TIM2_Init(); /* USER CODE BEGIN 2 */ + LL_GPIO_ResetOutputPin(SYS_LED_GPIO_Port, SYS_LED_Pin); // I'm alive! + + // Start the systick timer + if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) + Error_Handler(); + + // EEPROM emulation init + if (EEPROM_Init() != EEPROM_SUCCESS) + Error_Handler(); +#ifdef DEBUG + DEBUG_UART_MSG("EEPROM init\n\r"); +#endif + + // Check EEPROM first run + EEPROM_ReadVariable(EEPROM_VAR_ID, (EEPROM_Value*)&result); + if ((uint16_t)result != 0xCA1C) { + EEPROM_WriteVariable(EEPROM_VAR_BCKL, (EEPROM_Value)(uint16_t)((DEFAULT_LCD_BL << 8) | DEFAULT_KBD_BL), EEPROM_SIZE16); + EEPROM_WriteVariable(EEPROM_VAR_KBD, (EEPROM_Value)(uint16_t)((10 << 8) | 5), EEPROM_SIZE16); + EEPROM_WriteVariable(EEPROM_VAR_CFG, (EEPROM_Value)(uint16_t)(CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS | CFG_REPORT_MODS), EEPROM_SIZE16); + EEPROM_WriteVariable(EEPROM_VAR_ID, (EEPROM_Value)(uint16_t)0xCA1C, EEPROM_SIZE16); +#ifdef DEBUG + DEBUG_UART_MSG("EEPROM first start!\n\r"); +#endif + } + + // I2C-Pico interface registers + reg_init(); + if (HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK) + Error_Handler(); + HAL_Delay(10); + + // Check for AXP2101 is accessible on secondary I2C bus + //result = HAL_I2C_IsDeviceReady(&hi2c2, 0x68, 3, 40); + //if (result == HAL_OK) { + result = 0; + HAL_I2C_Mem_Read(&hi2c2, 0x68, XPOWERS_AXP2101_IC_TYPE, 1, (uint8_t*)&result, 1, 60); + if (result == XPOWERS_AXP2101_CHIP_ID) { +#ifdef DEBUG + DEBUG_UART_MSG("PMU ID: "); + DEBUG_UART_MSG2((uint32_t)result, 1, 0); + DEBUG_UART_MSG("\n\r"); +#endif + pmu_online = 1; + } else { +#ifdef DEBUG + DEBUG_UART_MSG("PMU not online!\n\r"); +#endif + } + + // Start LCD and KBD backlight PWM + lcd_backlight_off(); + if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK) + Error_Handler(); + kbd_backlight_on(); + if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3) != HAL_OK) + Error_Handler(); +#ifdef DEBUG + DEBUG_UART_MSG("Bckl params: "); + DEBUG_UART_MSG2(((uint32_t)result >> 8), 1, 1); + DEBUG_UART_MSG(", "); + DEBUG_UART_MSG2(((uint32_t)result & 0xFF), 1, 1); + DEBUG_UART_MSG("\n\r"); +#endif + + keyboard_set_key_callback(key_cb); + + // Enable PICO power + LL_GPIO_SetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); +#ifdef DEBUG + DEBUG_UART_MSG("Pico started\n\r"); +#endif + + // Enable speaker Amp. power + LL_GPIO_SetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + + HAL_Delay(1000); + lcd_backlight_on(); + + // It is necessary to disable the detection function of the TS pin on the + // board without the battery temperature detection function, otherwise it will + // cause abnormal charging + AXP2101_setSysPowerDownVoltage(2800); + AXP2101_disableTSPinMeasure(); + // AXP2101_enableTemperatureMeasure(); + AXP2101_enableBattDetection(); + AXP2101_enableVbusVoltageMeasure(); + AXP2101_enableBattVoltageMeasure(); + AXP2101_enableSystemVoltageMeasure(); + + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + + AXP2101_disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + AXP2101_clearIrqStatus(); + AXP2101_enableIRQ(XPOWERS_AXP2101_BAT_INSERT_IRQ | + XPOWERS_AXP2101_BAT_REMOVE_IRQ | // BATTERY + XPOWERS_AXP2101_VBUS_INSERT_IRQ | + XPOWERS_AXP2101_VBUS_REMOVE_IRQ | // VBUS + XPOWERS_AXP2101_PKEY_SHORT_IRQ | + XPOWERS_AXP2101_PKEY_LONG_IRQ | // POWER KEY + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | + XPOWERS_AXP2101_BAT_CHG_START_IRQ // CHARGE + // XPOWERS_AXP2101_PKEY_NEGATIVE_IRQ | + // XPOWERS_AXP2101_PKEY_POSITIVE_IRQ | //POWER KEY + ); + // setLowBatWarnThreshold Range: 5% ~ 20% + // The following data is obtained from actual testing , Please see the description below for the test method. + // 20% ~= 3.7v + // 15% ~= 3.6v + // 10% ~= 3.55V + // 5% ~= 3.5V + // 1% ~= 3.4V + AXP2101_setLowBatWarnThreshold(20); // Set to trigger interrupt when reaching 20% + + // setLowBatShutdownThreshold Range: 0% ~ 15% + // The following data is obtained from actual testing , Please see the description below for the test method. + // 15% ~= 3.6v + // 10% ~= 3.55V + // 5% ~= 3.5V + // 1% ~= 3.4V + AXP2101_setLowBatShutdownThreshold(5); //This is related to the battery charging and discharging logic. If you're not sure what you're doing, please don't modify it, as it could damage the battery. + + keycb_start = 1; + sync_bat(); + low_bat(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { + LL_IWDG_ReloadCounter(IWDG); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ + // Save user registers in EEPROM if unsynced every 2.5s + reg_sync(); + + // Re-arm I2CS in case of lost master signal + if (i2cs_state != I2CS_STATE_IDLE && i2cs_rearm_counter > I2CS_REARM_TIMEOUT) + i2cs_state = I2CS_STATE_IDLE; + + check_pmu_int(); + keyboard_process(); + hw_check_HP_presence(); + //HAL_Delay(10); } /* USER CODE END 3 */ } @@ -207,7 +540,7 @@ static void MX_I2C1_Init(void) hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 10000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; - hi2c1.Init.OwnAddress1 = 124; + hi2c1.Init.OwnAddress1 = 62; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; @@ -270,7 +603,7 @@ static void MX_IWDG_Init(void) /* USER CODE END IWDG_Init 0 */ /* USER CODE BEGIN IWDG_Init 1 */ - +#ifndef DEBUG /* USER CODE END IWDG_Init 1 */ LL_IWDG_Enable(IWDG); LL_IWDG_EnableWriteAccess(IWDG); @@ -282,7 +615,7 @@ static void MX_IWDG_Init(void) LL_IWDG_ReloadCounter(IWDG); /* USER CODE BEGIN IWDG_Init 2 */ - +#endif /* USER CODE END IWDG_Init 2 */ } @@ -427,10 +760,10 @@ static void MX_TIM2_Init(void) /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; - htim2.Init.Prescaler = 0; + htim2.Init.Prescaler = 4-1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000; - htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; + htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { @@ -525,7 +858,7 @@ static void MX_USART1_UART_Init(void) /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ - +#ifdef DEBUG /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; @@ -540,7 +873,7 @@ static void MX_USART1_UART_Init(void) Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ - +#endif /* USER CODE END USART1_Init 2 */ } @@ -558,7 +891,7 @@ static void MX_USART3_UART_Init(void) /* USER CODE END USART3_Init 0 */ /* USER CODE BEGIN USART3_Init 1 */ - +#ifdef UART_PICO_INTERFACE /* USER CODE END USART3_Init 1 */ huart3.Instance = USART3; huart3.Init.BaudRate = 115200; @@ -573,7 +906,7 @@ static void MX_USART3_UART_Init(void) Error_Handler(); } /* USER CODE BEGIN USART3_Init 2 */ - +#endif /* USER CODE END USART3_Init 2 */ } @@ -680,7 +1013,283 @@ static void MX_GPIO_Init(void) } /* USER CODE BEGIN 4 */ +/* +static void lock_cb(uint8_t caps_changed, uint8_t num_changed) { + //uint8_t do_int = 0; + if (caps_changed && reg_is_bit_set(REG_ID_CFG, CFG_CAPSLOCK_INT)) { + reg_set_bit(REG_ID_INT, INT_CAPSLOCK); + //do_int = 1; + } + + if (num_changed && reg_is_bit_set(REG_ID_CFG, CFG_NUMLOCK_INT)) { + reg_set_bit(REG_ID_INT, INT_NUMLOCK); + //do_int = 1; + } + + // int_pin can be a LED + if (do_int) { + port_pin_set_output_level(int_pin, 0); + delay_ms(INT_DURATION_MS); + port_pin_set_output_level(int_pin, 1); + } +} +*/ + +static void key_cb(char key, enum key_state state) { + if (keycb_start == 0) { + fifo_flush(); + return; + } + + if (reg_is_bit_set(REG_ID_CFG, CFG_KEY_INT)) { + reg_set_bit(REG_ID_INT, INT_KEY); + } + +#ifdef DEBUG + // Serial1.println("key: 0x%02X/%d/%c, state: %d, blk: %d\r\n", key, key, key, state, reg_get_value(REG_ID_BKL)); + //HAL_UART_Transmit_IT(&huart1, HP_PLUG_MSG, HP_PLUG_MSG_LEN); +#endif + + const struct fifo_item item = {key, state}; + if (!fifo_enqueue(item)) { + if (reg_is_bit_set(REG_ID_CFG, CFG_OVERFLOW_INT)) { + reg_set_bit(REG_ID_INT, INT_OVERFLOW); // INT_OVERFLOW The interrupt was generated by FIFO overflow. + } + + if (reg_is_bit_set(REG_ID_CFG, CFG_OVERFLOW_ON)) fifo_enqueue_force(item); + } + +#ifdef DEBUG + //Serial1.println(key); + //HAL_UART_Transmit_IT(&huart1, HP_PLUG_MSG, HP_PLUG_MSG_LEN); +#endif +} + +__STATIC_INLINE void hw_check_HP_presence(void) { + uint32_t v = LL_GPIO_IsInputPinSet(HP_DET_GPIO_Port, HP_DET_Pin); + + if (v != head_phone_status) { + if (v != 0) { +#ifdef DEBUG + DEBUG_UART_MSG("HeadPhone inserted\n\r"); +#endif + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + } else { +#ifdef DEBUG + DEBUG_UART_MSG("HeadPhone removed\n\r"); +#endif + LL_GPIO_SetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + } + + head_phone_status = v; + } +} + +__STATIC_INLINE void sync_bat(void) { + uint8_t pcnt; + if (AXP2101_getBatteryPercent(&pcnt) != HAL_OK) + return; +#ifdef DEBUG + DEBUG_UART_MSG("check_pmu_int: "); + DEBUG_UART_MSG2((uint32_t)pcnt, 1, 0); + DEBUG_UART_MSG("\n\r"); +#endif + + if (pcnt > 100) { // disconnect + pcnt = 0; + } else { // battery connected + if (AXP2101_isCharging()) + pcnt |= (1 << 7); + low_bat(); + } + + reg_set_value(REG_ID_BAT, pcnt); +} + +__STATIC_INLINE void check_pmu_int(void) { + if (!pmu_online) + return; + + uint8_t pcnt; + + if (uptime_ms() - pmu_check_counter > 20000) { + pmu_check_counter = uptime_ms(); // reset time + AXP2101_getBatteryPercent(&pcnt); +#ifdef DEBUG + DEBUG_UART_MSG("check_pmu_int: "); + DEBUG_UART_MSG2((uint32_t)pcnt, 1, 0); + DEBUG_UART_MSG("\n\r"); +#endif + + if (pcnt > 100) { // disconnect + pcnt = 0; + } else { // battery connected + if (AXP2101_isCharging()) + pcnt |= (1 << 7); + low_bat(); + } + + reg_set_value(REG_ID_BAT,pcnt); + } + + if (pmu_irq) { + pmu_irq = 0; // Reset interrupt flag + + // Get PMU Interrupt Status Register + uint32_t status; + AXP2101_getIrqStatus(&status); +#ifdef DEBUG + DEBUG_UART_MSG("PMU IRQ status: "); + DEBUG_UART_MSG2(status, 4, 1); + DEBUG_UART_MSG("\n\r"); +#endif + + /* + // When the set low-voltage battery percentage warning threshold is reached, + // set the threshold through getLowBatWarnThreshold( 5% ~ 20% ) + if (PMU.isDropWarningLevel2Irq()) { + Serial1.println("isDropWarningLevel2"); + //report_bat(); + } + */ + + // When the set low-voltage battery percentage shutdown threshold is reached + // set the threshold through setLowBatShutdownThreshold() + //This is related to the battery charging and discharging logic. If you're not sure what you're doing, please don't modify it, as it could damage the battery. + if (AXP2101_isDropWarningLevel1Irq()) { +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isDropWarningLevel1\n\r"); +#endif + //report_bat(); + // + AXP2101_shutdown(); + } + /*if (PMU.isGaugeWdtTimeoutIrq()) { + Serial1.println("isWdtTimeout"); + } + if (PMU.isBatChargerOverTemperatureIrq()) { + Serial1.println("isBatChargeOverTemperature"); + } + if (PMU.isBatWorkOverTemperatureIrq()) { + Serial1.println("isBatWorkOverTemperature"); + } + if (PMU.isBatWorkUnderTemperatureIrq()) { + Serial1.println("isBatWorkUnderTemperature"); + } + if (PMU.isVbusInsertIrq()) { + Serial1.println("isVbusInsert"); + }*/ + if (AXP2101_isVbusRemoveIrq()) { +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isVbusRemove\n\r"); +#endif + stop_chg(); + } + if (AXP2101_isBatInsertIrq()) { + AXP2101_getBatteryPercent(&pcnt); + if (pcnt > 100) { // disconnect + pcnt = 0; + } else { // battery connected + pcnt |= (1 << 7); + } + reg_set_value(REG_ID_BAT, pcnt); +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isBatInsert\n\r"); +#endif + } + if (AXP2101_isBatRemoveIrq()) { + reg_set_value(REG_ID_BAT,0); +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isBatRemove\n\r"); +#endif + stop_chg(); + } + + /*if (AXP2101_isPekeyShortPressIrq()) { + Serial1.println("isPekeyShortPress"); + // enterPmuSleep(); + + Serial1.print("Read pmu data buffer ."); + uint8_t data[4] = {0}; + PMU.readDataBuffer(data, XPOWERS_AXP2101_DATA_BUFFER_SIZE); + for (int i = 0; i < 4; ++i) { + Serial1.print(data[i]); + Serial1.print(","); + } + Serial1.println(); + + printPMU(); + }*/ + + if (AXP2101_isPekeyLongPressIrq()) { +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isPekeyLongPress\n\r"); +#endif + //Serial1.println("write pmu data buffer ."); + //uint8_t data[4] = {1, 2, 3, 4}; + //PMU.writeDataBuffer(data, XPOWERS_AXP2101_DATA_BUFFER_SIZE); + + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + AXP2101_setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + AXP2101_shutdown(); + } + + /*if (PMU.isPekeyNegativeIrq()) { + Serial1.println("isPekeyNegative"); + } + if (PMU.isPekeyPositiveIrq()) { + Serial1.println("isPekeyPositive"); + } + + if (PMU.isLdoOverCurrentIrq()) { + Serial1.println("isLdoOverCurrentIrq"); + } + if (PMU.isBatfetOverCurrentIrq()) { + Serial1.println("isBatfetOverCurrentIrq"); + }*/ + if (AXP2101_isBatChargeDoneIrq()) { + AXP2101_getBatteryPercent(&pcnt); + if (pcnt > 100) { // disconnect + pcnt = 0; + } else { // battery connected + pcnt |= (1 << 7); + } + reg_set_value(REG_ID_BAT,pcnt); +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isBatChagerDone\n\r"); +#endif + stop_chg(); + } + if (AXP2101_isBatChargeStartIrq()) { + AXP2101_getBatteryPercent(&pcnt); + if (pcnt > 100) { // disconnect + pcnt = 0; + } else { // battery connected + pcnt |= (1 << 7); + } + reg_set_value(REG_ID_BAT,pcnt); +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isBatChagerStart\n\r"); +#endif + if(AXP2101_isBatteryConnect()) + start_chg(); + } + /*if (PMU.isBatDieOverTemperatureIrq()) { + Serial1.println("isBatDieOverTemperature"); + } + if (PMU.isChagerOverTimeoutIrq()) { + Serial1.println("isChagerOverTimeout"); + } + if (PMU.isBatOverVoltageIrq()) { + Serial1.println("isBatOverVoltage"); + }*/ + + // Clear PMU Interrupt Status Register + AXP2101_clearIrqStatus(); + } +} /* USER CODE END 4 */ /** @@ -692,8 +1301,9 @@ void Error_Handler(void) /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); - while (1) - { + while (1) { + //LL_GPIO_TogglePin(SYS_LED_GPIO_Port, SYS_LED_Pin); + HAL_Delay(500); } /* USER CODE END Error_Handler_Debug */ } diff --git a/Core/Src/regs.c b/Core/Src/regs.c new file mode 100644 index 0000000..78d97c4 --- /dev/null +++ b/Core/Src/regs.c @@ -0,0 +1,110 @@ +#include "regs.h" +#include "main.h" +#include "eeprom.h" +#include "backlight.h" + + +static uint8_t regs[REG_ID_LAST] = {0}; +static uint8_t regs_unsync[REG_ID_LAST] = {0}; +static uint32_t eeprom_refresh_counter; + +inline uint8_t reg_get_value(enum reg_id reg) { + if (reg >= REG_ID_LAST) + return 0; + + return regs[reg]; +} + +inline uint8_t* reg_raw_access(void) { + return regs; +} + +inline void reg_set_value(enum reg_id reg, uint8_t value) { + if (reg >= REG_ID_LAST) + return; + + regs[reg] = value; + regs_unsync[reg] = 1; + eeprom_refresh_counter = uptime_ms(); +} + +inline uint8_t reg_is_bit_set(enum reg_id reg, uint8_t bit) { + if (reg >= REG_ID_LAST) + return 0; + + return regs[reg] & bit; +} + +inline void reg_set_bit(enum reg_id reg, uint8_t bit) { + if (reg >= REG_ID_LAST) + return; + + regs[reg] |= bit; + regs_unsync[reg] = 1; + eeprom_refresh_counter = uptime_ms(); +} + +/* + * | Bit | Name | Description | +| ------ |:----------------:| ------------------------------------------------------------------:| +| 7 | CFG_USE_MODS | Should Alt, Sym and the Shift keys modify the keys being reported. | +| 6 | CFG_REPORT_MODS | Should Alt, Sym and the Shift keys be reported as well. | +| 5 | CFG_PANIC_INT | Currently not implemented. | +| 4 | CFG_KEY_INT | Should an interrupt be generated when a key is pressed. | +| 3 | CFG_NUMLOCK_INT | Should an interrupt be generated when Num Lock is toggled. | +| 2 | CFG_CAPSLOCK_INT | Should an interrupt be generated when Caps Lock is toggled. | +| 1 | CFG_OVERFLOW_INT | Should an interrupt be generated when a FIFO overflow happens. | +| 0 | CFG_OVERFLOW_ON | When a FIFO overflow happens, should the new entry still be pushed, overwriting the oldest one. If 0 then new entry is lost. | + */ +void reg_init(void) { + uint16_t buff; + + EEPROM_ReadVariable(EEPROM_VAR_CFG, (EEPROM_Value*)&buff); + regs[REG_ID_CFG] = (uint8_t)(buff & 0xFF); + + EEPROM_ReadVariable(EEPROM_VAR_KBD, (EEPROM_Value*)&buff); + regs[REG_ID_DEB] = (uint8_t)((buff >> 8) & 0xFF); + regs[REG_ID_FRQ] = (uint8_t)(buff & 0xFF); + + EEPROM_ReadVariable(EEPROM_VAR_BCKL, (EEPROM_Value*)&buff); + regs[REG_ID_BKL] = (uint8_t)((buff >> 8) & 0xFF); + regs[REG_ID_BK2] = (uint8_t)(buff & 0xFF); + + regs[REG_ID_BAT] = 0; //default .no battery ,no charging + + eeprom_refresh_counter = uptime_ms(); +} + +uint32_t reg_check_and_save_eeprom(void) { + uint32_t result = EEPROM_SUCCESS; + uint8_t need_save = 0; + + for (size_t i = 0; i < REG_ID_LAST; i++) + if (regs_unsync[i] == 1) { + need_save = 1; + break; + } + + if (need_save == 1) { + if (regs_unsync[REG_ID_CFG] == 1) + result |= EEPROM_WriteVariable(EEPROM_VAR_CFG, (EEPROM_Value)(uint16_t)regs[REG_ID_CFG], EEPROM_SIZE16); + + if (regs_unsync[REG_ID_DEB] == 1 || regs_unsync[REG_ID_FRQ] == 1) + result |= EEPROM_WriteVariable(EEPROM_VAR_KBD, (EEPROM_Value)(uint16_t)((regs[REG_ID_DEB] << 8) | regs[REG_ID_FRQ]), EEPROM_SIZE16); + + if (regs_unsync[REG_ID_BKL] == 1 || regs_unsync[REG_ID_BK2] == 1) + result |= EEPROM_WriteVariable(EEPROM_VAR_BCKL, (EEPROM_Value)(uint16_t)((regs[REG_ID_BKL] << 8) | regs[REG_ID_BK2]), EEPROM_SIZE16); + + for (size_t i = 0; i < REG_ID_LAST; i++) + regs_unsync[i] = 0; + } + + return result; +} + +void reg_sync(void) { + if (uptime_ms() > (eeprom_refresh_counter + 1500)) { + reg_check_and_save_eeprom(); + eeprom_refresh_counter = uptime_ms(); + } +} diff --git a/Core/Src/stm32f1xx_hal_msp.c b/Core/Src/stm32f1xx_hal_msp.c index 48e386b..720019d 100644 --- a/Core/Src/stm32f1xx_hal_msp.c +++ b/Core/Src/stm32f1xx_hal_msp.c @@ -113,9 +113,9 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) /* Peripheral clock enable */ __HAL_RCC_I2C1_CLK_ENABLE(); /* I2C1 interrupt Init */ - HAL_NVIC_SetPriority(I2C1_EV_IRQn, 4, 0); + HAL_NVIC_SetPriority(I2C1_EV_IRQn, 2, 0); HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); - HAL_NVIC_SetPriority(I2C1_ER_IRQn, 4, 0); + HAL_NVIC_SetPriority(I2C1_ER_IRQn, 2, 0); HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); /* USER CODE BEGIN I2C1_MspInit 1 */ diff --git a/Core/Src/stm32f1xx_it.c b/Core/Src/stm32f1xx_it.c index 23641b8..38b0bc2 100644 --- a/Core/Src/stm32f1xx_it.c +++ b/Core/Src/stm32f1xx_it.c @@ -213,7 +213,7 @@ void EXTI9_5_IRQHandler(void) { LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); /* USER CODE BEGIN LL_EXTI_LINE_9 */ - + pmu_irq = 1; /* USER CODE END LL_EXTI_LINE_9 */ } /* USER CODE BEGIN EXTI9_5_IRQn 1 */ diff --git a/README.md b/README.md index e03de2b..92c6172 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ The main differences with the original are followings: - drastic reduction in the STM32's electricity consumption when running (~3.5 mA), - removed stm32duino dependencies (use STM32HAL instead, maybe I'll switch to libopencm3 someday...), +- clean up (in progress) to reduce binary size (~25 KB) and allow more features to be implemented, - added configuration saving solution (using internal flash, including backlight option), - rewriten or added some debug UART interface message, - lighten AXP2101 PMIC driver. diff --git a/picocalc_BIOS_jcs.ioc b/picocalc_BIOS_jcs.ioc index 7862719..c7099cd 100644 --- a/picocalc_BIOS_jcs.ioc +++ b/picocalc_BIOS_jcs.ioc @@ -6,7 +6,7 @@ File.Version=6 GPIO.groupedBy=Group By Peripherals I2C1.ClockSpeed=10000 I2C1.IPParameters=OwnAddress,ClockSpeed -I2C1.OwnAddress=62 +I2C1.OwnAddress=0x1F I2C2.I2C_Mode=I2C_Standard I2C2.IPParameters=OwnAddress,I2C_Mode I2C2.OwnAddress=0 @@ -97,8 +97,8 @@ NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.EXTI9_5_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true NVIC.ForceEnableDMAVector=true NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false -NVIC.I2C1_ER_IRQn=true\:4\:0\:true\:false\:true\:true\:true\:true -NVIC.I2C1_EV_IRQn=true\:4\:0\:true\:false\:true\:true\:true\:true +NVIC.I2C1_ER_IRQn=true\:2\:0\:true\:false\:true\:true\:true\:true +NVIC.I2C1_EV_IRQn=true\:2\:0\:true\:false\:true\:true\:true\:true NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false @@ -456,11 +456,11 @@ TIM1.Period=800 TIM1.Prescaler=0 TIM1.Pulse-PWM\ Generation1\ CH1=0 TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE -TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV4 +TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV1 TIM2.CounterMode=TIM_COUNTERMODE_UP TIM2.IPParameters=AutoReloadPreload,CounterMode,Prescaler,ClockDivision,Period TIM2.Period=1000 -TIM2.Prescaler=0 +TIM2.Prescaler=4-1 TIM3.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE TIM3.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3 TIM3.IPParameters=Channel-PWM Generation3 CH3,Prescaler,AutoReloadPreload,Pulse-PWM Generation3 CH3,OCMode_PWM-PWM Generation3 CH3,Period