mirror of
https://github.com/clockworkpi/PicoCalc.git
synced 2025-12-12 10:18:54 +01:00
If i2c master performs discovery via address read, stm32 has no data to send for the masterin requestEvent. Send the zero byte if we got requestEvent early, for example on i2c address read.
572 lines
16 KiB
C++
572 lines
16 KiB
C++
#include <Arduino.h>
|
|
|
|
#define XPOWERS_CHIP_AXP2101
|
|
|
|
#include <Wire.h>
|
|
|
|
#include "XPowersLib.h"
|
|
//---------------------------------------
|
|
#include "backlight.h"
|
|
#include "conf_app.h"
|
|
#include "fifo.h"
|
|
#include "keyboard.h"
|
|
#include "pins.h"
|
|
#include "port.h"
|
|
#include "reg.h"
|
|
#include "battery.h"
|
|
|
|
#define DEBUG_UART
|
|
TwoWire Wire2 = TwoWire(CONFIG_PMU_SDA, CONFIG_PMU_SCL);
|
|
bool pmu_flag = 0;
|
|
bool pmu_online = 0;
|
|
uint8_t pmu_status = 0;
|
|
uint8_t keycb_start = 0;
|
|
|
|
uint8_t head_phone_status=LOW;
|
|
|
|
XPowersPMU PMU;
|
|
|
|
const uint8_t i2c_sda = CONFIG_PMU_SDA;
|
|
const uint8_t i2c_scl = CONFIG_PMU_SCL;
|
|
const uint8_t pmu_irq_pin = CONFIG_PMU_IRQ;
|
|
|
|
unsigned long run_time;
|
|
|
|
void set_pmu_flag(void) { pmu_flag = true; }
|
|
|
|
HardwareSerial Serial1(PA10, PA9);
|
|
|
|
uint8_t write_buffer[10] = {0};
|
|
uint8_t write_buffer_len = 0;
|
|
|
|
uint8_t io_matrix[9];//for IO matrix,last bytye is the restore key(c64 only)
|
|
uint8_t js_bits=0xff;// c64 joystick bits
|
|
|
|
static int current_bat_pcnt = 0;
|
|
|
|
unsigned long time_uptime_ms() { return millis(); }
|
|
|
|
void lock_cb(bool caps_changed, bool num_changed) {
|
|
bool do_int = false;
|
|
|
|
if (caps_changed && reg_is_bit_set(REG_ID_CFG, CFG_CAPSLOCK_INT)) {
|
|
reg_set_bit(REG_ID_INT, INT_CAPSLOCK);
|
|
do_int = true;
|
|
}
|
|
|
|
if (num_changed && reg_is_bit_set(REG_ID_CFG, CFG_NUMLOCK_INT)) {
|
|
reg_set_bit(REG_ID_INT, INT_NUMLOCK);
|
|
do_int = true;
|
|
}
|
|
|
|
/* // 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) {
|
|
bool do_int = false;
|
|
|
|
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);
|
|
do_int = true;
|
|
}
|
|
|
|
#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));
|
|
#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.
|
|
do_int = true;
|
|
}
|
|
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_OVERFLOW_ON)) fifo_enqueue_force(item);
|
|
}
|
|
|
|
//Serial1.println(key);
|
|
}
|
|
|
|
void receiveEvent(int howMany) {
|
|
uint8_t rcv_data[2]; // max size 2, protocol defined
|
|
uint8_t rcv_idx;
|
|
|
|
if (Wire.available() < 1) return;
|
|
|
|
rcv_idx = 0;
|
|
while (Wire.available()) // loop through all but the last
|
|
{
|
|
uint8_t c = Wire.read(); // receive byte as a character
|
|
rcv_data[rcv_idx] = c;
|
|
rcv_idx++;
|
|
if (rcv_idx >= 2) {
|
|
rcv_idx = 0;
|
|
}
|
|
}
|
|
|
|
const bool is_write = (rcv_data[0] & WRITE_MASK);
|
|
const uint8_t reg = (rcv_data[0] & ~WRITE_MASK);
|
|
|
|
write_buffer[0] = 0;
|
|
write_buffer[1] = 0;
|
|
write_buffer_len = 2;
|
|
|
|
switch (reg) {
|
|
case REG_ID_FIF: {
|
|
const struct fifo_item item = fifo_dequeue();
|
|
write_buffer[0] = (uint8_t)item.state;
|
|
write_buffer[1] = (uint8_t)item.key;
|
|
} break;
|
|
case REG_ID_BKL: {
|
|
if (is_write) {
|
|
reg_set_value(REG_ID_BKL, rcv_data[1]);
|
|
lcd_backlight_update_reg();
|
|
}
|
|
write_buffer[0] = reg;
|
|
write_buffer[1] = reg_get_value(REG_ID_BKL);
|
|
} break;
|
|
case REG_ID_BK2: {
|
|
if (is_write) {
|
|
reg_set_value(REG_ID_BK2, rcv_data[1]);
|
|
// kbd_backlight_update will add 0 to the reg
|
|
// value that we just set, and apply the result
|
|
// to the backlight
|
|
kbd_backlight_update(0);
|
|
}
|
|
write_buffer[0] = reg;
|
|
write_buffer[1] = reg_get_value(REG_ID_BK2);
|
|
} break;
|
|
case REG_ID_BAT:{
|
|
//Serial1.print("REG_ID_BAT getBatteryPercent:");Serial1.print(current_bat_pcnt);Serial1.println("%");
|
|
write_buffer[0] = reg;
|
|
write_buffer[1] = (uint8_t)current_bat_pcnt;
|
|
|
|
}break;
|
|
case REG_ID_KEY: {
|
|
write_buffer[0] = fifo_count();
|
|
write_buffer[0] |= keyboard_get_numlock() ? KEY_NUMLOCK : 0x00;
|
|
write_buffer[0] |= keyboard_get_capslock() ? KEY_CAPSLOCK : 0x00;
|
|
|
|
}break;
|
|
case REG_ID_C64_MTX:{
|
|
write_buffer[0] = reg;
|
|
memcpy(write_buffer + 1, io_matrix, sizeof(io_matrix));
|
|
write_buffer_len = 10;
|
|
}break;
|
|
case REG_ID_C64_JS:{
|
|
write_buffer[0] = reg;
|
|
write_buffer[1] = js_bits;
|
|
write_buffer_len = 2;
|
|
}break;
|
|
default: {
|
|
write_buffer[0] = 0;
|
|
write_buffer[1] = 0;
|
|
|
|
write_buffer_len = 2;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
//-this is after receiveEvent-------------------------------
|
|
void requestEvent() {
|
|
if (write_buffer_len > 0 && write_buffer_len <= sizeof(write_buffer)) {
|
|
Wire.write(write_buffer,write_buffer_len );
|
|
} else {
|
|
Wire.write((uint8_t)0); // Send something minimal to avoid stalling
|
|
}
|
|
}
|
|
|
|
void report_bat(){
|
|
if (PMU.isBatteryConnect()) {
|
|
write_buffer[0] = REG_ID_BAT;
|
|
write_buffer[1] = PMU.getBatteryPercent();
|
|
|
|
write_buffer_len = 2;
|
|
requestEvent();
|
|
}
|
|
}
|
|
|
|
void printPMU() {
|
|
Serial1.print("isCharging:");
|
|
Serial1.println(PMU.isCharging() ? "YES" : "NO");
|
|
Serial1.print("isDischarge:");
|
|
Serial1.println(PMU.isDischarge() ? "YES" : "NO");
|
|
Serial1.print("isStandby:");
|
|
Serial1.println(PMU.isStandby() ? "YES" : "NO");
|
|
Serial1.print("isVbusIn:");
|
|
Serial1.println(PMU.isVbusIn() ? "YES" : "NO");
|
|
Serial1.print("isVbusGood:");
|
|
Serial1.println(PMU.isVbusGood() ? "YES" : "NO");
|
|
Serial1.print("getChargerStatus:");
|
|
uint8_t charge_status = PMU.getChargerStatus();
|
|
if (charge_status == XPOWERS_AXP2101_CHG_TRI_STATE) {
|
|
Serial1.println("tri_charge");
|
|
} else if (charge_status == XPOWERS_AXP2101_CHG_PRE_STATE) {
|
|
Serial1.println("pre_charge");
|
|
} else if (charge_status == XPOWERS_AXP2101_CHG_CC_STATE) {
|
|
Serial1.println("constant charge");
|
|
} else if (charge_status == XPOWERS_AXP2101_CHG_CV_STATE) {
|
|
Serial1.println("constant voltage");
|
|
} else if (charge_status == XPOWERS_AXP2101_CHG_DONE_STATE) {
|
|
Serial1.println("charge done");
|
|
} else if (charge_status == XPOWERS_AXP2101_CHG_STOP_STATE) {
|
|
Serial1.println("not chargin");
|
|
}
|
|
|
|
Serial1.print("getBattVoltage:");
|
|
Serial1.print(PMU.getBattVoltage());
|
|
Serial1.println("mV");
|
|
Serial1.print("getVbusVoltage:");
|
|
Serial1.print(PMU.getVbusVoltage());
|
|
Serial1.println("mV");
|
|
Serial1.print("getSystemVoltage:");
|
|
Serial1.print(PMU.getSystemVoltage());
|
|
Serial1.println("mV");
|
|
|
|
// 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 (PMU.isBatteryConnect()) {
|
|
Serial1.print("getBatteryPercent:");
|
|
Serial1.print(PMU.getBatteryPercent());
|
|
Serial1.println("%");
|
|
}
|
|
|
|
Serial1.println();
|
|
}
|
|
|
|
void check_pmu_int() {
|
|
int pcnt;
|
|
|
|
if (!pmu_online) return;
|
|
|
|
if (time_uptime_ms() - run_time > 20000) {
|
|
run_time = millis(); // reset time
|
|
pcnt = PMU.getBatteryPercent();
|
|
//Serial1.print("check_pmu_int: ");Serial1.print(pcnt);Serial1.println();
|
|
if (pcnt < 0) { // disconnect
|
|
pcnt = 0;
|
|
pmu_status = 0xff;
|
|
current_bat_pcnt = pcnt;
|
|
} else { // battery connected
|
|
current_bat_pcnt = pcnt;
|
|
if (PMU.isCharging()) {
|
|
pmu_status = bitSet(pcnt, 7);
|
|
} else {
|
|
pmu_status = pcnt;
|
|
}
|
|
low_bat();
|
|
}
|
|
}
|
|
|
|
if (pmu_flag) {
|
|
pmu_flag = false;
|
|
|
|
// Get PMU Interrupt Status Register
|
|
uint32_t status = PMU.getIrqStatus();
|
|
Serial1.print("STATUS => HEX:");
|
|
Serial1.print(status, HEX);
|
|
Serial1.print(" BIN:");
|
|
Serial1.println(status, BIN);
|
|
|
|
|
|
// 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 (PMU.isDropWarningLevel1Irq()) {
|
|
report_bat();
|
|
//
|
|
PMU.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 (PMU.isVbusRemoveIrq()) {
|
|
Serial1.println("isVbusRemove");
|
|
stop_chg();
|
|
}
|
|
if (PMU.isBatInsertIrq()) {
|
|
pcnt = PMU.getBatteryPercent();
|
|
if (pcnt < 0) { // disconnect
|
|
pcnt = 0;
|
|
pmu_status = 0xff;
|
|
} else {
|
|
pmu_status = pcnt;
|
|
}
|
|
current_bat_pcnt = pcnt;
|
|
Serial1.println("isBatInsert");
|
|
}
|
|
if (PMU.isBatRemoveIrq()) {
|
|
pmu_status = 0xff;
|
|
current_bat_pcnt = 0;
|
|
Serial1.println("isBatRemove");
|
|
stop_chg();
|
|
}
|
|
|
|
if (PMU.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 (PMU.isPekeyLongPressIrq()) {
|
|
Serial1.println("isPekeyLongPress");
|
|
//Serial1.println("write pmu data buffer .");
|
|
//uint8_t data[4] = {1, 2, 3, 4};
|
|
//PMU.writeDataBuffer(data, XPOWERS_AXP2101_DATA_BUFFER_SIZE);
|
|
digitalWrite(PA13, LOW);
|
|
digitalWrite(PA14, LOW);
|
|
PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
|
|
PMU.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 (PMU.isBatChagerDoneIrq()) {
|
|
pcnt = PMU.getBatteryPercent();
|
|
if (pcnt < 0) { // disconnect
|
|
pcnt = 0;
|
|
pmu_status = 0xff;
|
|
}
|
|
current_bat_pcnt = pcnt;
|
|
pmu_status = bitClear(pcnt, 7);
|
|
Serial1.println("isBatChagerDone");
|
|
stop_chg();
|
|
}
|
|
if (PMU.isBatChagerStartIrq()) {
|
|
pcnt = PMU.getBatteryPercent();
|
|
if (pcnt < 0) { // disconnect
|
|
pcnt = 0;
|
|
pmu_status = 0xff;
|
|
}
|
|
current_bat_pcnt = pcnt;
|
|
pmu_status = bitSet(pcnt, 7);
|
|
Serial1.println("isBatChagerStart");
|
|
if(PMU.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
|
|
PMU.clearIrqStatus();
|
|
}
|
|
|
|
reg_set_value(REG_ID_BAT, (uint8_t)pmu_status);
|
|
}
|
|
|
|
/*
|
|
* PA8 lcd_bl
|
|
* PC8 keyboard_bl
|
|
*/
|
|
void setup() {
|
|
pinMode(PA13, OUTPUT); // pico enable
|
|
digitalWrite(PA13, LOW);
|
|
reg_init();
|
|
|
|
delay(10);
|
|
|
|
Serial1.begin(115200);
|
|
|
|
Wire.setSDA(PB9);
|
|
Wire.setSCL(PB8);
|
|
Wire.begin(SLAVE_ADDRESS);
|
|
Wire.setClock(10000);//It is important to set to 10Khz
|
|
Wire.onReceive(receiveEvent); // register event
|
|
Wire.onRequest(requestEvent);
|
|
|
|
// no delay here
|
|
|
|
bool result = PMU.begin(Wire2, AXP2101_SLAVE_ADDRESS, i2c_sda, i2c_scl);
|
|
|
|
if (result == false) {
|
|
Serial1.println("PMU is not online...");
|
|
} else {
|
|
pmu_online = 1;
|
|
Serial1.printf("getID:0x%x\n", PMU.getChipID());
|
|
}
|
|
|
|
pinMode(PC12, INPUT); // HP_DET
|
|
|
|
pinMode(PC13, OUTPUT); // indicator led
|
|
|
|
digitalWrite(PC13, LOW);
|
|
|
|
pinMode(PA14, OUTPUT); // PA_EN
|
|
digitalWrite(PA14, HIGH);
|
|
|
|
int pin = PC8;
|
|
|
|
/*
|
|
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
|
|
// 重映射Timer3的部分映射到PC6-PC9
|
|
AFIO->MAPR &= ~AFIO_MAPR_TIM3_REMAP;
|
|
AFIO->MAPR |= AFIO_MAPR_TIM3_REMAP_PARTIALREMAP;
|
|
*/
|
|
/*
|
|
pinMode(pin,OUTPUT);
|
|
|
|
TIM_TypeDef *Instance = (TIM_TypeDef
|
|
*)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM); uint32_t channel =
|
|
STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
|
|
// Instantiate HardwareTimer object. Thanks to 'new' instantiation,
|
|
HardwareTimer is not destructed when setup() function is finished.
|
|
HardwareTimer *MyTim = new HardwareTimer(Instance);
|
|
|
|
// Configure and start PWM
|
|
// MyTim->setPWM(channel, pin, 5, 10, NULL, NULL); // No callback required, we
|
|
can simplify the function call MyTim->setPWM(channel, pin, 80000, 1); //
|
|
Hertz, dutycycle
|
|
*/
|
|
/*
|
|
* data records:
|
|
* 500,10 === nightlight watch level
|
|
*/
|
|
/*
|
|
analogWriteFrequency(80000);
|
|
analogWrite(pin, 10);
|
|
*/
|
|
|
|
pin = PA8;
|
|
analogWriteFrequency(10000);
|
|
analogWrite(pin, 100);
|
|
|
|
keyboard_init();
|
|
keyboard_set_key_callback(key_cb);
|
|
lcd_backlight_update(-223);
|
|
|
|
digitalWrite(PA13, HIGH);
|
|
|
|
|
|
// 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
|
|
PMU.setSysPowerDownVoltage(2800);
|
|
PMU.disableTSPinMeasure();
|
|
// PMU.enableTemperatureMeasure();
|
|
PMU.enableBattDetection();
|
|
PMU.enableVbusVoltageMeasure();
|
|
PMU.enableBattVoltageMeasure();
|
|
PMU.enableSystemVoltageMeasure();
|
|
|
|
PMU.setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG);
|
|
|
|
pinMode(pmu_irq_pin, INPUT_PULLUP);
|
|
attachInterrupt(pmu_irq_pin, set_pmu_flag, FALLING);
|
|
|
|
PMU.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
|
PMU.clearIrqStatus();
|
|
PMU.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
|
|
PMU.setLowBatWarnThreshold(5); // Set to trigger interrupt when reaching 5%
|
|
|
|
// 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
|
|
PMU.setLowBatShutdownThreshold(1); //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.
|
|
|
|
run_time = 0;
|
|
keycb_start = 1;
|
|
low_bat();
|
|
//printf("Start pico");
|
|
}
|
|
|
|
//hp headphone
|
|
void check_hp_det(){
|
|
int v = digitalRead(PC12);
|
|
if(v == HIGH) {
|
|
if( head_phone_status != v ) {
|
|
Serial1.println("HeadPhone detected");
|
|
}
|
|
digitalWrite(PA14,LOW);
|
|
}else{
|
|
digitalWrite(PA14,HIGH);
|
|
}
|
|
head_phone_status = v;
|
|
|
|
}
|
|
void loop() {
|
|
check_pmu_int();
|
|
keyboard_process();
|
|
check_hp_det();
|
|
delay(10);
|
|
}
|