#include #include "libs/shark_libs.h" #include "spi.h" #include "ml5238.h" #include "libs/logger.h" static int ml5238_read(uint8_t regaddr, uint8_t *data); static void ml5238_clear_bits(uint8_t regaddr, uint8_t bit); static void ml5238_set_bits(uint8_t regaddr, uint8_t bit); static void irq_hander_in_timer(shark_timer_t *timer); static ml5238_notify_hander _handler; static shark_timer_t irq_task = {.handler = irq_hander_in_timer}; void ml5238_init(void){ spi0_init(); ml5238_softreset(); ml5238_irq_enable(1); } uint8_t ml5238_noop_register_rw(uint8_t data){ uint8_t value = data; ml5238_write(ML5238_NOOP, value); value = 0xFF; ml5238_read(ML5238_NOOP, &value); return value; } void ml5238_register_notify_handler(ml5238_notify_hander handler){ _handler = handler; } //小电流打开等效discharger mos打开 int ml5238_charger_is_disconnect(int small_current_on){ uint8_t value = 0; uint8_t fet = 0; ml5238_read(ML5238_FET, &fet); ml5238_read(ML5238_PSENSE, &value); if ((fet & FET_DF) || small_current_on){ return (value & PSENSE_PSL); } return (value & PSENSE_PSH); } int ml5238_enable_load_detect(int enable){ ml5238_clear_bits(ML5238_RSENSE, RSENSE_RRS); if (enable){ ml5238_set_bits(ML5238_RSENSE, RSENSE_ERS); }else { ml5238_clear_bits(ML5238_RSENSE, RSENSE_ERS | RSENSE_IRS); } return 0; } int ml5238_is_load_disconnect(void){ uint8_t value = 0; ml5238_read(ML5238_RSENSE, &value); return (value & RSENSE_RS); } #define IRS_IRQ 1 //load disconnect中断 #define IPSL_IRQ 2 //charger over current #define ICS_IRQ 3 //短路中断 int ml5238_enable_irq(int enable, int irq){ if (irq == IRS_IRQ){ ml5238_clear_bits(ML5238_RSENSE, RSENSE_RRS); if (enable){ ml5238_set_bits(ML5238_RSENSE, RSENSE_IRS); }else { ml5238_clear_bits(ML5238_RSENSE, RSENSE_IRS); } } if (irq == IPSL_IRQ){ ml5238_clear_bits(ML5238_PSENSE, PSENSE_RPSL); if (enable){ ml5238_set_bits(ML5238_PSENSE, PSENSE_IPSL); }else { ml5238_clear_bits(ML5238_PSENSE, PSENSE_IPSL); } } if (irq == ICS_IRQ){ ml5238_clear_bits(ML5238_RSENSE, RSENSE_RSC); if (enable){ ml5238_set_bits(ML5238_RSENSE, RSENSE_ISC); }else { ml5238_clear_bits(ML5238_RSENSE, RSENSE_ISC); } } return 0; } //小电流打开等效discharger mos打开 int ml5238_enable_charger_detect(int small_current_on, int enable){ uint8_t fet = 0; ml5238_read(ML5238_FET, &fet); if ((fet & FET_DF) || small_current_on){ //discharger is on, used to detect charger over current ml5238_clear_bits(ML5238_PSENSE, PSENSE_RPSL); if (enable){ ml5238_set_bits(ML5238_PSENSE, PSENSE_EPSL); }else { ml5238_clear_bits(ML5238_PSENSE, PSENSE_EPSL); } }else { //discharger if off, used when powerdown, charger is insert ml5238_clear_bits(ML5238_PSENSE, PSENSE_RPSH); if (enable){ ml5238_set_bits(ML5238_PSENSE, PSENSE_EPSH); }else { ml5238_clear_bits(ML5238_PSENSE, PSENSE_EPSH); } } return 0; } static int __inline__ _charger_mosfet_is_open(void){ uint8_t data; ml5238_read(ML5238_FET, &data); return (data & FET_CF) != 0; } static int __inline__ _discharger_mosfet_is_open(void){ uint8_t data; ml5238_read(ML5238_FET, &data); return (data & FET_DF) != 0; } int ml5238_is_charging(void){ return _charger_mosfet_is_open(); } int ml5238_is_discharging(void){ return _discharger_mosfet_is_open(); } void ml5238_cell_start_balance(uint16_t balance_mask){ ml5238_write(ML5238_CBALH, (balance_mask >> 8) & 0xFF); ml5238_write(ML5238_CBALL, balance_mask & 0xFF); } int ml5238_enable_discharger_mosfet(int enable){ uint8_t data; if (ml5238_read(ML5238_FET, &data) == 0){ if ((data & FET_DF) == enable){ return 0; //alread enable/disabled } data &= ~(FET_DF); if (enable){ data |= (FET_DF | FET_DRV); }else { if ((data & FET_CF) == 0){ data &= ~(FET_DRV); } } return ml5238_write(ML5238_FET, data); } return -1; } /* when enable charger the discharger mosfet also must be enabled for charging */ int ml5238_enable_charger_mosfet(int enable){ uint8_t data; if (ml5238_read(ML5238_FET, &data) == 0){ if (((data & FET_CF) >> 1) == enable){ return 0; //alread enable/disabled } data &= ~(FET_CF); if (enable){ data |= (FET_CF | FET_DRV); }else { if ((data & FET_DF) == 0){ data &= ~(FET_DRV); } } return ml5238_write(ML5238_FET, data); } return -1; } int ml5238_short_current_detect(int mode){ uint8_t rsense = 0; if (mode >= SHORT_CURRENT_MODE_50A_100A){ if (ml5238_read(ML5238_RSENSE, &rsense) == 0){ if (ml5238_write(ML5238_SETSC, mode) == 0){ rsense |= (RSENSE_ESC | RSENSE_ISC);//enable short current detect && irq rsense &= ~RSENSE_RSC; return ml5238_write(ML5238_RSENSE, rsense); } } }else { if (ml5238_read(ML5238_RSENSE, &rsense) == 0){ rsense &= ~(RSENSE_ESC|RSENSE_ISC|RSENSE_RSC); return ml5238_write(ML5238_RSENSE, rsense); } } return -1; } int ml5238_is_short_current_enabled(int mode){ uint8_t value = 0; if (ml5238_read(ML5238_SETSC, &value) < 0){ return 0; } if (value != mode) { return 0; } value = 0; if (ml5238_read(ML5238_RSENSE, &value) < 0){ return 0; } if ((value & (RSENSE_ESC | RSENSE_ISC)) != (RSENSE_ESC | RSENSE_ISC)){ return 0; } if (value & RSENSE_RSC){ return 0; } return 1; } void ml5238_softreset(void) { for(unsigned char i = 0u; i < 0x0Au; i++){ ml5238_write((uint8_t)(ML5238_VMON + i), 0x00u); } } void ml5238_reg_log(void){ uint8_t data = 0xFF; for(unsigned char i = 0u; i < 0x0Au; i++){ ml5238_read((uint8_t)(ML5238_VMON + i), &data); sys_debug("Reg %d:0x%x\n", (ML5238_VMON + i), data); } } void ml5238_power_down(void){ do { ml5238_write(ML5238_PSENSE, PSENSE_EPSH|PSENSE_IPSH); //before power down, we must enable charger detect ml5238_write(ML5238_POWER, POWER_PDWN); }while(1); } void ml5238_power_save(int save){ if (save) { ml5238_write(ML5238_PSENSE, 0); ML5238_VMON_DISABLE(); ML5238_IMON_DISABLE(); ml5238_write(ML5238_POWER, POWER_PSV); ml5238_irq_enable(1); //enable charger detect irq, to wakeup bms when charger insert spi0_deinit(); }else { spi0_init(); ml5238_write(ML5238_POWER, 0); } } static void __inline__ call_handler(int event){ if (_handler) { _handler(event); } } static void irq_hander_in_timer(shark_timer_t *timer){ uint8_t status = 0; ml5238_read(ML5238_STATUS, &status); if (status & STATUS_RPSL){//chargering over current sys_error("charger over current\n"); ml5238_enable_charger_detect(0, 0); ml5238_enable_charger_detect(1, 0); call_handler(ML5238_Event_Charger_Over_Current); } if (status & STATUS_RSC) { //short current detect, close charger/discharger mosfet sys_error("short current\n"); if (_charger_mosfet_is_open()) { ml5238_enable_charger_mosfet(0); } if (_discharger_mosfet_is_open()) { ml5238_enable_discharger_mosfet(0); } ml5238_short_current_detect(SHORT_CURRENT_MODE_DISABLE); call_handler(ML5238_Event_Short_Current); } if (status & STATUS_RRS) {//load disconnect, if short detect, we must wait load disconnected, and then can open discharger ml5238_enable_irq(0, IRS_IRQ); call_handler(ML5238_Event_Load_Disconnect); } } void ml5238_irq_handler(void){ shark_timer_post(&irq_task, 0); } static void ml5238_set_bits(uint8_t regaddr, uint8_t bit) { uint8_t value; ml5238_read(regaddr, &value); ml5238_write(regaddr, value|bit); } static void ml5238_clear_bits(uint8_t regaddr, uint8_t bit) { uint8_t value; ml5238_read(regaddr, &value); ml5238_write(regaddr, value&(~bit)); } int ml5238_write(uint8_t regaddr, uint8_t data){ uint16_t send_data=(((uint16_t)regaddr)<<(0x09))|((uint16_t)data); ml5238_cs(0); int ret = spi0_send_uint16(send_data, NULL); ml5238_cs(1); return ret; } static int ml5238_read(uint8_t regaddr, uint8_t *data){ uint16_t send_data=((((uint16_t)regaddr)<<(0x09))|0x0100u)|((uint16_t)0x00u); ml5238_cs(0); int ret = spi0_send_uint16(send_data, data); ml5238_cs(1); return ret; }