diff --git a/Core/Inc/axp2101.h b/Core/Inc/axp2101.h index aa18577..18842ab 100644 --- a/Core/Inc/axp2101.h +++ b/Core/Inc/axp2101.h @@ -14,11 +14,11 @@ #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_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) @@ -49,16 +49,16 @@ //#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) +#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 --------------------- @@ -266,6 +266,15 @@ typedef enum __xpowers_chg_led_mode { XPOWERS_CHG_LED_CTRL_CHG, // The charging indicator is controlled by the charger } xpowers_chg_led_mode_t; +typedef enum { + XPOWERS_AXP2101_CHG_TRI_STATE, //tri_charge + XPOWERS_AXP2101_CHG_PRE_STATE, //pre_charge + XPOWERS_AXP2101_CHG_CC_STATE, //constant charge + XPOWERS_AXP2101_CHG_CV_STATE, //constant voltage + XPOWERS_AXP2101_CHG_DONE_STATE, //charge done + XPOWERS_AXP2101_CHG_STOP_STATE, //not charge +} xpowers_chg_status_t; + /* * ------------- axp2101 interrupt control mask ------------- */ @@ -325,9 +334,18 @@ uint32_t AXP2101_setLowBatShutdownThreshold(uint8_t opt); uint32_t AXP2101_setSysPowerDownVoltage(uint16_t value); uint32_t AXP2101_setChargingLedMode(uint8_t mode); +int8_t AXP2101_readDataBuffer(uint8_t *data, uint8_t size); uint8_t AXP2101_isBatteryConnect(void); uint8_t AXP2101_isCharging(void); +uint8_t AXP2101_isDischarge(void); +uint8_t AXP2101_isStandby(void); +uint8_t AXP2101_isVbusGood(void); +uint8_t AXP2101_isVbusIn(void); uint32_t AXP2101_getIrqStatus(uint32_t* out_value); uint32_t AXP2101_getBatteryPercent(uint8_t* out_value); +xpowers_chg_status_t AXP2101_getChargerStatus(void); +uint16_t AXP2101_getBattVoltage(void); +uint16_t AXP2101_getVbusVoltage(void); +uint16_t AXP2101_getSystemVoltage(void); #endif /* AXP2101_H_ */ diff --git a/Core/Src/axp2101.c b/Core/Src/axp2101.c index 88519e0..43ae2f4 100644 --- a/Core/Src/axp2101.c +++ b/Core/Src/axp2101.c @@ -12,6 +12,36 @@ static uint8_t statusRegister[XPOWERS_AXP2101_INTSTS_CNT] = {0}; static uint8_t intRegister[XPOWERS_AXP2101_INTSTS_CNT] = {0}; +__STATIC_INLINE int8_t readRegister(uint8_t reg, uint8_t *buf, uint8_t length) { + HAL_StatusTypeDef status; + + status = HAL_I2C_Mem_Read(&hi2c2, AXP2101_DEV_I2C_ID, reg, length, buf, 1, 60); + if (status != HAL_OK) + return -1; + + return 0; +} + +__STATIC_INLINE uint16_t readRegisterH6L8(uint8_t highReg, uint8_t lowReg) { + uint8_t h6; + uint8_t l8; + int8_t h6_s = readRegister(highReg, &h6, 1); + int8_t l8_s = readRegister(lowReg, &l8, 1); + if (h6_s == -1 || l8_s == -1) + return 0; + return ((h6 & 0x3F) << 8) | l8; +} + +__STATIC_INLINE uint16_t readRegisterH5L8(uint8_t highReg, uint8_t lowReg) { + uint8_t h5; + uint8_t l8; + int8_t h5_s = readRegister(highReg, &h5, 1); + int8_t l8_s = readRegister(lowReg, &l8, 1); + if (h5_s == -1 || l8_s == -1) + return 0; + return ((h5 & 0x1F) << 8) | l8; +} + __STATIC_INLINE uint8_t clrRegisterBit(uint8_t registers, uint8_t bit) { uint8_t reg_value = 0; HAL_StatusTypeDef status; @@ -281,6 +311,12 @@ uint32_t AXP2101_setLowBatShutdownThreshold(uint8_t opt) { } +int8_t AXP2101_readDataBuffer(uint8_t *data, uint8_t size) { + if (size > XPOWERS_AXP2101_DATA_BUFFER_SIZE) + return -1; + return readRegister(XPOWERS_AXP2101_DATA_BUFFER1, data, size); +} + uint8_t AXP2101_isBatteryConnect(void) { return getRegisterBit(XPOWERS_AXP2101_STATUS1, 3); } @@ -293,6 +329,26 @@ uint8_t AXP2101_isCharging(void) { return (reg_value >> 5) == 0x01; } +uint8_t AXP2101_isDischarge(void) { + uint8_t res = 0; + readRegister(XPOWERS_AXP2101_STATUS2, &res, 1); + return (res >> 5) == 0x02; +} + +uint8_t AXP2101_isStandby(void) { + uint8_t res = 0; + readRegister(XPOWERS_AXP2101_STATUS2, &res, 1); + return (res >> 5) == 0x00; +} + +uint8_t AXP2101_isVbusGood(void) { + return getRegisterBit(XPOWERS_AXP2101_STATUS1, 5); +} + +uint8_t AXP2101_isVbusIn(void) { + return getRegisterBit(XPOWERS_AXP2101_STATUS2, 3) == 0 && AXP2101_isVbusGood(); +} + uint32_t AXP2101_getIrqStatus(uint32_t* out_value) { HAL_StatusTypeDef status = HAL_OK; @@ -314,3 +370,30 @@ uint32_t AXP2101_getBatteryPercent(uint8_t* out_value) { return HAL_I2C_Mem_Read(&hi2c2, AXP2101_DEV_I2C_ID, XPOWERS_AXP2101_BAT_PERCENT_DATA, 1, out_value, 1, 60); } + +xpowers_chg_status_t AXP2101_getChargerStatus(void) { + uint8_t val = 0; + int8_t status = readRegister(XPOWERS_AXP2101_STATUS2, &val, 1); + if (status == -1) + return XPOWERS_AXP2101_CHG_STOP_STATE; + val &= 0x07; + return (xpowers_chg_status_t)val; +} + +uint16_t AXP2101_getBattVoltage(void) { + if (!AXP2101_isBatteryConnect()) { + return 0; + } + return readRegisterH5L8(XPOWERS_AXP2101_ADC_DATA_RELUST0, XPOWERS_AXP2101_ADC_DATA_RELUST1); +} + +uint16_t AXP2101_getVbusVoltage(void) { + if (!AXP2101_isVbusIn()) { + return 0; + } + return readRegisterH6L8(XPOWERS_AXP2101_ADC_DATA_RELUST4, XPOWERS_AXP2101_ADC_DATA_RELUST5); +} + +uint16_t AXP2101_getSystemVoltage(void) { + return readRegisterH6L8(XPOWERS_AXP2101_ADC_DATA_RELUST6, XPOWERS_AXP2101_ADC_DATA_RELUST7); +} diff --git a/Core/Src/keyboard.c b/Core/Src/keyboard.c index 4cbcc3d..0e26d47 100644 --- a/Core/Src/keyboard.c +++ b/Core/Src/keyboard.c @@ -103,7 +103,7 @@ static const struct gpio_pin btn_pins[12] = { 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 uint32_t last_process_time = 0; static uint8_t mods[MOD_LAST]; static uint8_t capslock_changed = 0; static uint8_t capslock = 0; @@ -281,7 +281,7 @@ static void next_item_state(struct list_item* const p_item, const uint8_t presse break; case KEY_STATE_PRESSED: - if (uptime_ms() > p_item->hold_start_time + hold_period) { + if (uptime_ms() - p_item->hold_start_time > hold_period) { transition_to(p_item, KEY_STATE_HOLD); p_item->last_repeat_time = uptime_ms(); } else if (!pressed) @@ -292,8 +292,8 @@ static void next_item_state(struct list_item* const p_item, const uint8_t presse if (!pressed) transition_to(p_item, KEY_STATE_RELEASED); else { - if (uptime_ms() > p_item->hold_start_time + hold_period) { - if(uptime_ms() > p_item->last_repeat_time + KEY_REPEAT_TIME) { + if (uptime_ms() - p_item->hold_start_time > hold_period) { + 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(); } @@ -314,7 +314,7 @@ static void next_item_state(struct list_item* const p_item, const uint8_t presse void keyboard_process(void) { js_bits = 0xFF; - if (uptime_ms() <= (last_process_time + reg_get_value(REG_ID_FRQ))) + if (uptime_ms() - last_process_time <= reg_get_value(REG_ID_FRQ)) return; // Scan for columns @@ -417,6 +417,7 @@ void keyboard_process(void) { } io_matrix[8] = 0xFF; - last_process_time = uptime_ms(); } + + last_process_time = uptime_ms(); } diff --git a/Core/Src/main.c b/Core/Src/main.c index 42e69f6..7b06ed5 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -66,9 +66,9 @@ static const uint8_t hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9 extern UART_HandleTypeDef huart3; #endif -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; +volatile uint32_t systicks_counter = 0; // 1 MHz systick counter +static uint32_t pmu_check_counter = 0; +static uint32_t i2cs_rearm_counter = 0; static uint8_t i2cs_r_buff[5]; static volatile uint8_t i2cs_r_idx = 0; @@ -89,12 +89,14 @@ static uint32_t pmu_online = 0; static void key_cb(char key, enum key_state state); static void hw_check_HP_presence(void); static void sync_bat(void); +#ifdef DEBUG +static void printPMU(void); +#endif static void check_pmu_int(void); extern void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim2) { systicks_counter += 1; - i2cs_rearm_counter += 1; } } @@ -111,7 +113,7 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect 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; + i2cs_rearm_counter = uptime_ms(); } } @@ -176,6 +178,10 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect i2cs_w_len = 10; } else if (reg == REG_ID_C64_JS) { i2cs_w_buff[1] = js_bits; + } else if (reg == REG_ID_RST) { + if (is_write) + reg_set_value(REG_ID_RST, 1); + i2cs_w_buff[1] = reg_get_value(REG_ID_RST); } else if (reg == REG_ID_RST_PICO) { if (is_write) reg_set_value(REG_ID_RST_PICO, 1); @@ -196,7 +202,7 @@ extern void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirect HAL_I2C_Slave_Sequential_Transmit_IT(hi2c, i2cs_w_buff, i2cs_w_len, I2C_FIRST_AND_LAST_FRAME); - i2cs_rearm_counter = 0; + i2cs_rearm_counter = uptime_ms(); } } } @@ -307,8 +313,6 @@ int main(void) { Error_Handler(); // 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) { @@ -402,7 +406,7 @@ int main(void) { LL_IWDG_ReloadCounter(IWDG); // Re-arm I2CS in case of lost master signal - if (i2cs_state != I2CS_STATE_IDLE && i2cs_rearm_counter > I2CS_REARM_TIMEOUT) + if (i2cs_state != I2CS_STATE_IDLE && ((uptime_ms() - i2cs_rearm_counter) > I2CS_REARM_TIMEOUT)) i2cs_state = I2CS_STATE_IDLE; reg_sync(); @@ -411,14 +415,23 @@ int main(void) { hw_check_HP_presence(); // Check internal status - if (reg_get_value(REG_ID_SHTDW) == 1) { + if (reg_get_value(REG_ID_SHTDW) == 1) { // Nominal full system shutdown as requested from I2C bus reg_set_value(REG_ID_SHTDW, 0); 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(); - } else if (reg_get_value(REG_ID_RST_PICO) == 1) { + } else if (reg_get_value(REG_ID_RST) == 1) { // Try to reset only the STM32 + reg_set_value(REG_ID_RST, 0); + HAL_Delay(200); // Wait for final I2C answer + if (HAL_I2C_DisableListen_IT(&hi2c1) != HAL_OK) + Error_Handler(); + LL_GPIO_ResetOutputPin(SP_AMP_EN_GPIO_Port, SP_AMP_EN_Pin); + LL_GPIO_ResetOutputPin(PICO_EN_GPIO_Port, PICO_EN_Pin); + + NVIC_SystemReset(); + } else if (reg_get_value(REG_ID_RST_PICO) == 1) { // Reset only the Pico reg_set_value(REG_ID_RST_PICO, 0); HAL_Delay(200); // Wait for final I2C answer if (HAL_I2C_DisableListen_IT(&hi2c1) != HAL_OK) @@ -538,6 +551,77 @@ __STATIC_INLINE void sync_bat(void) { reg_set_value(REG_ID_BAT, pcnt); } +#ifdef DEBUG +__STATIC_INLINE void printPMU(void) { + DEBUG_UART_MSG("PMU isCharging: "); + if (AXP2101_isCharging()) + DEBUG_UART_MSG("YES\n\r"); + else + DEBUG_UART_MSG( "NO\n\r"); + + DEBUG_UART_MSG("PMU isDischarge: "); + if (AXP2101_isDischarge()) + DEBUG_UART_MSG("YES\n\r"); + else + DEBUG_UART_MSG( "NO\n\r"); + + DEBUG_UART_MSG("PMU isStandby: "); + if (AXP2101_isStandby()) + DEBUG_UART_MSG("YES\n\r"); + else + DEBUG_UART_MSG( "NO\n\r"); + + DEBUG_UART_MSG("PMU isVbusIn: "); + if (AXP2101_isVbusIn()) + DEBUG_UART_MSG("YES\n\r"); + else + DEBUG_UART_MSG( "NO\n\r"); + + DEBUG_UART_MSG("PMU isVbusGood: "); + if (AXP2101_isVbusGood()) + DEBUG_UART_MSG("YES\n\r"); + else + DEBUG_UART_MSG( "NO\n\r"); + + DEBUG_UART_MSG("PMU getChargerStatus: "); + uint8_t charge_status = AXP2101_getChargerStatus(); + if (charge_status == XPOWERS_AXP2101_CHG_TRI_STATE) { + DEBUG_UART_MSG("tri_charge"); + } else if (charge_status == XPOWERS_AXP2101_CHG_PRE_STATE) { + DEBUG_UART_MSG("pre_charge"); + } else if (charge_status == XPOWERS_AXP2101_CHG_CC_STATE) { + DEBUG_UART_MSG("constant charge"); + } else if (charge_status == XPOWERS_AXP2101_CHG_CV_STATE) { + DEBUG_UART_MSG("constant voltage"); + } else if (charge_status == XPOWERS_AXP2101_CHG_DONE_STATE) { + DEBUG_UART_MSG("charge done"); + } else if (charge_status == XPOWERS_AXP2101_CHG_STOP_STATE) { + DEBUG_UART_MSG("not charging"); + } + + DEBUG_UART_MSG("PMU getBattVoltage: "); + DEBUG_UART_MSG2(AXP2101_getBattVoltage(), 2, 0); + DEBUG_UART_MSG("mV\n\r"); + DEBUG_UART_MSG("PMU getVbusVoltage: "); + DEBUG_UART_MSG2(AXP2101_getVbusVoltage(), 2, 0); + DEBUG_UART_MSG("mV\n\r"); + DEBUG_UART_MSG("PMU getSystemVoltage: "); + DEBUG_UART_MSG2(AXP2101_getSystemVoltage(), 2, 0); + DEBUG_UART_MSG("mV\n\r"); + + // The battery percentage may be inaccurate at first use, the PMU will + // automatically learn the battery curve and will automatically calibrate the + // battery percentage after a charge and discharge cycle + if (AXP2101_isBatteryConnect()) { + DEBUG_UART_MSG("PMU getBatteryPercent: "); + uint8_t pcnt = 0; + AXP2101_getBatteryPercent(&pcnt); + DEBUG_UART_MSG2(pcnt, 1, 0); + DEBUG_UART_MSG("%\n\r"); + } +} +#endif + __STATIC_INLINE void check_pmu_int(void) { if (!pmu_online) return; @@ -636,23 +720,26 @@ __STATIC_INLINE void check_pmu_int(void) { #endif stop_chg(); } + if (AXP2101_isPekeyShortPressIrq()) { +#ifdef DEBUG + DEBUG_UART_MSG("PMU: isPekeyShortPress\n\r"); - /*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(); - }*/ + uint8_t data[4] = {0}; + AXP2101_readDataBuffer(data, XPOWERS_AXP2101_DATA_BUFFER_SIZE); + DEBUG_UART_MSG("PMU data buffer:\n\r"); + DEBUG_UART_MSG2(data[0], 1, 0); + DEBUG_UART_MSG("\n\r"); + DEBUG_UART_MSG2(data[1], 1, 0); + DEBUG_UART_MSG("\n\r"); + DEBUG_UART_MSG2(data[2], 1, 0); + DEBUG_UART_MSG("\n\r"); + DEBUG_UART_MSG2(data[3], 1, 0); + DEBUG_UART_MSG("\n\r"); + printPMU(); +#endif + // enterPmuSleep(); //TODO: implement sleep mode, RTC, etc.? + } if (AXP2101_isPekeyLongPressIrq()) { #ifdef DEBUG DEBUG_UART_MSG("PMU: isPekeyLongPress\n\r"); diff --git a/Core/Src/regs.c b/Core/Src/regs.c index 4dacf6f..a14d383 100644 --- a/Core/Src/regs.c +++ b/Core/Src/regs.c @@ -110,7 +110,7 @@ uint32_t reg_check_and_save_eeprom(void) { void reg_sync(void) { // Save user registers in EEPROM if unsynced every 1.5s - if (uptime_ms() > (eeprom_refresh_counter + 1500)) { + if (uptime_ms() - eeprom_refresh_counter > 1500) { reg_check_and_save_eeprom(); eeprom_refresh_counter = uptime_ms(); } diff --git a/STM32F103XX_FLASH.ld b/STM32F103XX_FLASH.ld index 0833426..7355922 100644 --- a/STM32F103XX_FLASH.ld +++ b/STM32F103XX_FLASH.ld @@ -62,7 +62,8 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K -FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K +FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 62K +FEE (rw) : ORIGIN = 0x800F800, LENGTH = 2K } /* Define output sections */