PicoCalc/Code/picocalc_keyboard/picocalc_keyboard.ino

548 lines
15 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];
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
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: {
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_BAT:{
write_buffer[0] = reg;
if (PMU.isBatteryConnect()) {
write_buffer[1] = PMU.getBatteryPercent();
}else{
write_buffer[1] = 0x00;
}
}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() { Wire.write(write_buffer,write_buffer_len ); }
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() {
/// 40 secs check battery percent
int pcnt;
if (!pmu_online) return;
if (time_uptime_ms() - run_time > 40000) {
run_time = millis(); // reset time
pcnt = PMU.getBatteryPercent();
if (pcnt < 0) { // disconnect
pcnt = 0;
pmu_status = 0xff;
} else { // battery connected
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;
}
Serial1.println("isBatInsert");
}
if (PMU.isBatRemoveIrq()) {
pmu_status = 0xff;
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;
}
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;
}
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();
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);
}