Add picocalc stm32 i2c keyboard code

This commit is contained in:
cuu 2025-03-14 13:28:19 +08:00
parent 3bfa8164db
commit f4a1bef7dc
16 changed files with 1552 additions and 0 deletions

View File

@ -0,0 +1,5 @@
# picocalc keyboard
* STM32F103R8T6
## Libraries
* https://github.com/stm32duino/Arduino_Core_STM32/

View File

@ -0,0 +1,9 @@
#ifndef BACKLIGHT_H
#define BACKLIGHT_H
void lcd_backlight_update_reg();
void lcd_backlight_update(int);
void kbd_backlight_update(int);
#endif

View File

@ -0,0 +1,59 @@
#include "backlight.h"
#define KBD_BACKLIGHT_SEG 4
uint8_t kbd_backlight_segs[KBD_BACKLIGHT_SEG] = {20,40,80,110};
uint8_t kbd_backlight_offset = 0;
void lcd_backlight_update_reg() {
uint8_t val;
val = reg_get_value(REG_ID_BKL);
val = val & 0xff;
analogWriteFrequency(10000);
analogWrite(PA8, val);
}
void lcd_backlight_update(int v) {
int val;
val = reg_get_value(REG_ID_BKL);
val += v;
if(val < 0) val = 0;
if(val > 0xff) val = 0xff;
analogWriteFrequency(10000);
analogWrite(PA8, val);
reg_set_value(REG_ID_BKL,val);
}
void kbd_backlight_update(int v){
int val;
val = reg_get_value(REG_ID_BK2);
val += v;
if(val < 20 ) val = 0;
if(val > 0xff) val = 0;
analogWriteFrequency(10000);
analogWrite(PC8, val);
reg_set_value(REG_ID_BK2,val);
}
void kbd_backlight_update_offset(){
int val;
kbd_backlight_offset++;
kbd_backlight_offset = kbd_backlight_offset % KBD_BACKLIGHT_SEG;
val = reg_get_value(REG_ID_BK2);
val += kbd_backlight_segs[kbd_backlight_offset];
if(val < 20 ) val = 0;
if(val > 0xff) val = 0;
analogWriteFrequency(10000);
analogWrite(PC8, val);
reg_set_value(REG_ID_BK2,val);
}

View File

@ -0,0 +1,11 @@
#ifndef BATTERY_H
#define BATTERY_H
void show_bat_segs();
void low_bat();
void start_chg();
void stop_chg();
#endif

View File

@ -0,0 +1,74 @@
#include "battery.h"
void indicator_led_on(){
digitalWrite(PC13, LOW);
}
void indicator_led_off(){
digitalWrite(PC13, HIGH);
}
void flash_one_time(int ts,int restore_status) {
for(int i=0;i<ts;i++) {
indicator_led_on();
delay(400);
indicator_led_off();
delay(200);
}
digitalWrite(PC13,restore_status);
}
void show_bat_segs(){
if(!PMU.isBatteryConnect()) return;
int pcnt = PMU.getBatteryPercent();
int last_d201_status = digitalRead(PC13);
if(pcnt >0 && pcnt < 33) {
//show one time
flash_one_time(1,last_d201_status);
}else if(pcnt >= 33 && pcnt <66){
//show 2 times
flash_one_time(2,last_d201_status);
}else if(pcnt >=66 && pcnt <= 100){
//show 3 times
flash_one_time(3,last_d201_status);
}
if(PMU.isCharging()){
start_chg();
}
}
void low_bat(){
if(PMU.isBatteryConnect() && !PMU.isCharging()){
int pcnt = PMU.getBatteryPercent();
if(pcnt <= LOW_BAT_VAL){
//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.
indicator_led_off();
if(pcnt <= 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.
PMU.setChargingLedMode(XPOWERS_CHG_LED_BLINK_4HZ);
if(pcnt==0){//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.
PMU.shutdown();//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.
}
}else{
PMU.setChargingLedMode(XPOWERS_CHG_LED_ON);
}
}else{
indicator_led_on();
PMU.setChargingLedMode(XPOWERS_CHG_LED_OFF);
}
}
}
void start_chg(){
indicator_led_on();
PMU.setChargingLedMode(XPOWERS_CHG_LED_BLINK_1HZ);
}
void stop_chg(){
PMU.setChargingLedMode(XPOWERS_CHG_LED_OFF);
low_bat();
}

View File

@ -0,0 +1,31 @@
#ifndef CONF_APP_H
#define CONF_APP_H
#define SLAVE_ADDRESS 0x1F
#define FIFO_SIZE 31
#define INT_DURATION_MS 1
#ifndef CONFIG_PMU_SDA
#define CONFIG_PMU_SDA PB11
#endif
#ifndef CONFIG_PMU_SCL
#define CONFIG_PMU_SCL PB10
#endif
#ifndef CONFIG_PMU_IRQ
#define CONFIG_PMU_IRQ PC9
#endif
#define LOW_BAT_VAL 20
#define LCD_BACKLIGHT_STEP 10
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1 << (bit)))
#define bitClear(value, bit) ((value) &= ~(1 << (bit)))
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet((value), (bit)) : bitClear((value), (bit) ))
#endif

View File

@ -0,0 +1,18 @@
#ifndef FIFO_H
#define FIFO_H
#include "keyboard.h"
struct fifo_item {
char key;
enum key_state state;
};
uint8_t fifo_count(void);
void fifo_flush(void);
bool fifo_enqueue(const struct fifo_item item);
void fifo_enqueue_force(const struct fifo_item item);
struct fifo_item fifo_dequeue(void);
#endif

View File

@ -0,0 +1,58 @@
#include "fifo.h"
static struct {
struct fifo_item fifo[FIFO_SIZE];
uint8_t count;
uint8_t read_idx;
uint8_t write_idx;
} fifo_self;
uint8_t fifo_count(void)
{
return fifo_self.count;
}
void fifo_flush(void)
{
fifo_self.write_idx = 0;
fifo_self.read_idx = 0;
fifo_self.count = 0;
}
bool fifo_enqueue(const struct fifo_item item)
{
if (fifo_self.count >= FIFO_SIZE)
return false;
fifo_self.fifo[fifo_self.write_idx++] = item;
fifo_self.write_idx %= FIFO_SIZE;
++fifo_self.count;
return true;
}
void fifo_enqueue_force(const struct fifo_item item)
{
if (fifo_enqueue(item))
return;
fifo_self.fifo[fifo_self.write_idx++] = item;
fifo_self.write_idx %= FIFO_SIZE;
fifo_self.read_idx++;
fifo_self.read_idx %= FIFO_SIZE;
}
struct fifo_item fifo_dequeue(void)
{
struct fifo_item item = { 0 };
if (fifo_self.count == 0)
return item;
item = fifo_self.fifo[fifo_self.read_idx++];
fifo_self.read_idx %= FIFO_SIZE;
--fifo_self.count;
return item;
}

View File

@ -0,0 +1,83 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
enum key_state
{
KEY_STATE_IDLE = 0,
KEY_STATE_PRESSED,
KEY_STATE_HOLD,
KEY_STATE_RELEASED,
};
#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)(bool, bool);
void keyboard_process(void);
void keyboard_set_key_callback(key_callback callback);
void keyboard_set_lock_callback(lock_callback callback);
bool keyboard_get_capslock(void);
bool keyboard_get_numlock(void);
void keyboard_init(void);
#define NUM_OF_COLS 8
#define NUM_OF_ROWS 7
#define NUM_OF_BTNS 12
#define KEY_POLL_TIME 16
#define KEY_LIST_SIZE 10
#define KEY_HOLD_TIME 300
#endif

View File

@ -0,0 +1,453 @@
#include "port.h"
#include "keyboard.h"
enum mod
{
MOD_NONE = 0,
MOD_SYM,
MOD_ALT,
MOD_SHL,
MOD_SHR,
MOD_CTRL,
MOD_LAST,
};
struct entry
{
char chr;
char symb;
enum mod mod;
};
struct list_item
{
const struct entry *p_entry;
uint32_t hold_start_time;
uint32_t last_repeat_time;
enum key_state state;
bool mods[MOD_LAST];
};
static const struct entry kbd_entries[][NUM_OF_COLS] =
{
{{KEY_F5,KEY_F10}, {KEY_F4,KEY_F9}, {KEY_F3,KEY_F8},{KEY_F2,KEY_F7}, {KEY_F1,KEY_F6}, {'`','~'},{'3','#'}, {'2','@'}},
{{KEY_BACKSPACE}, {KEY_DEL,KEY_END},{KEY_CAPS_LOCK},{KEY_TAB,KEY_HOME},{KEY_ESC,KEY_BREAK},{'4','$'},{'E'}, {'W'}},
{{'P'}, {'=','+'}, {'-','_'}, {'\\','|'}, {'/','?'}, {'R'}, {'S'}, {'1','!'}},
{{KEY_ENTER,KEY_INSERT},{'8','*'}, {'7','&'}, {'6','^'}, {'5','%'}, {'F'}, {'X'}, {'Q'}},
{{'.','>'}, {'I'}, {'U'}, {'Y'}, {'T'}, {'V'}, {';',':'}, {'A'}},
{{'L'}, {'K'}, {'J'}, {'H'}, {'G'}, {'C'}, {'\'','"'},{'Z'}},
{{'O'}, {',','<'}, {'M'}, {'N'}, {'B'}, {'D'}, {' '}, { }},
};
static const struct entry btn_entries[NUM_OF_BTNS] =
{
{.mod = MOD_ALT},
{.mod = MOD_CTRL},
{.mod = MOD_SHL},
{.mod = MOD_SHR},
{'0',')'},
{'9','('},
{']','}'},
{'[','{'},
{KEY_RIGHT},
{KEY_UP,KEY_PAGE_UP},
{KEY_DOWN,KEY_PAGE_DOWN},
{KEY_LEFT}
};
static struct {
lock_callback _lock_callback;
key_callback _key_callback;
struct list_item list[KEY_LIST_SIZE];
uint32_t last_process_time;
bool mods[MOD_LAST];
bool capslock_changed;
bool capslock;
bool numlock_changed;
bool numlock;
} self;
void output_string(char*str){
if (!self._key_callback) return;
while(*str){
self._key_callback(*str, KEY_STATE_PRESSED);
str++;
}
}
static void transition_to(struct list_item * const p_item, const enum key_state next_state)
{
bool output = true;
const struct entry * const p_entry = p_item->p_entry;
p_item->state = next_state;
if (!self._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 ){
if(self.capslock == true){
self.capslock = false;
}else{
self.capslock = true;
}
self.capslock_changed = true;
}
if (reg_is_bit_set(REG_ID_CFG, CFG_USE_MODS)) {
const bool shift = (self.mods[MOD_SHL] || self.mods[MOD_SHR]);
const bool alt = self.mods[MOD_ALT] | self.numlock;
const bool ctrl = self.mods[MOD_CTRL];//shortcuts control
if (shift && (chr <'A' || chr >'Z')) {
chr = p_entry->symb;
}else if(self.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 = false;
}
if(chr == 'I'){
output = true;
chr = KEY_INSERT;
}
}
if( next_state == KEY_STATE_RELEASED ) {
if(chr == ',' || chr == '.' || chr == ' ' || chr == 'B'){
output = false;
}
if(chr == 'I'){
output = true;
chr = KEY_INSERT;
}
}
if(next_state == KEY_STATE_RELEASED) {
if(chr ==','){
lcd_backlight_update(-LCD_BACKLIGHT_STEP);
}else if(chr =='.'){
lcd_backlight_update(LCD_BACKLIGHT_STEP);
}else if(chr == ' '){
//loop update keyboard backlight
kbd_backlight_update_offset();
}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==true) {
if(next_state == KEY_STATE_HOLD){
if( (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 ) {
self._key_callback(chr, KEY_STATE_PRESSED);
}else{
self._key_callback(chr, next_state);
}
}else{
self._key_callback(chr, next_state);
}
}
}
static void next_item_state(struct list_item * const p_item, const bool pressed)
{
switch (p_item->state) {
case KEY_STATE_IDLE:
if (pressed) {
if (p_item->p_entry->mod != MOD_NONE)
self.mods[p_item->p_entry->mod] = true;
if (!self.capslock_changed && self.mods[MOD_SHR] && self.mods[MOD_ALT]) {
self.capslock = true;
self.capslock_changed = true;
}
if (!self.numlock_changed && self.mods[MOD_SHL] && self.mods[MOD_ALT]) {
self.numlock = true;
self.numlock_changed = true;
}
if (!self.capslock_changed && (self.mods[MOD_SHL] || self.mods[MOD_SHR])) {
self.capslock = false;
self.capslock_changed = true;
}
if (!self.numlock_changed && (self.mods[MOD_SHL] || self.mods[MOD_SHR])) {
self.numlock = false;
self.numlock_changed = true;
}
if (!self.mods[MOD_ALT]) {
self.capslock_changed = false;
self.numlock_changed = false;
}
if (self._lock_callback && (self.capslock_changed || self.numlock_changed))
self._lock_callback(self.capslock_changed, self.numlock_changed);
transition_to(p_item, KEY_STATE_PRESSED);
p_item->hold_start_time = time_uptime_ms();
p_item->last_repeat_time = 0;
}
break;
case KEY_STATE_PRESSED:
if ((time_uptime_ms() - p_item->hold_start_time) > KEY_HOLD_TIME) {
transition_to(p_item, KEY_STATE_HOLD);
} 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 ((time_uptime_ms() - p_item->hold_start_time) > KEY_HOLD_TIME) {
if(time_uptime_ms() - p_item->last_repeat_time > 100) {
transition_to(p_item, KEY_STATE_HOLD);
p_item->last_repeat_time = time_uptime_ms();
}
}
}
break;
case KEY_STATE_RELEASED:
{
if (p_item->p_entry->mod != MOD_NONE)
self.mods[p_item->p_entry->mod] = false;
p_item->p_entry = NULL;
transition_to(p_item, KEY_STATE_IDLE);
break;
}
}
}
void keyboard_process(void)
{
struct port_config port_init;
js_bits = 0xff;
if ((time_uptime_ms() - self.last_process_time) <= KEY_POLL_TIME)
return;
port_get_config_defaults(&port_init);
for (uint8_t c = 0; c < NUM_OF_COLS; ++c) {
uint8_t col_value = 0;
port_init.direction = PORT_PIN_DIR_OUTPUT;
port_pin_set_config(col_pins[c], &port_init);
port_pin_set_output_level(col_pins[c], 0);
for (uint8_t r = 0; r < NUM_OF_ROWS; ++r) {
uint8_t pin_value = port_pin_get_input_level(row_pins[r]);
const bool pressed = (pin_value == 0);
uint8_t row_bit = (1<<r);
if(pressed){
if(c == 1 && r == 4){//enter key as fire
js_bits &= ~row_bit;
}
col_value &= ~row_bit;
}else{
if(c == 1 && r == 4){//enter key as fire
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 (self.list[i].p_entry != &((const struct entry*)kbd_entries)[key_idx])
continue;
list_idx = i;
break;
}
if (list_idx > -1) {
next_item_state(&self.list[list_idx], pressed);
continue;
}
if (!pressed)
continue;
for (uint32_t i = 0 ; i < KEY_LIST_SIZE; ++i) {
if (self.list[i].p_entry != NULL)
continue;
self.list[i].p_entry = &((const struct entry*)kbd_entries)[key_idx];
self.list[i].state = KEY_STATE_IDLE;
next_item_state(&self.list[i], pressed);
break;
}
}
io_matrix[c] = col_value;
port_pin_set_output_level(col_pins[c], 1);
port_init.direction = PORT_PIN_DIR_INPUT;
port_init.input_pull = PORT_PIN_PULL_NONE;
port_pin_set_config(col_pins[c], &port_init);
}
#if NUM_OF_BTNS > 0
for (uint8_t b = 0; b < NUM_OF_BTNS; ++b) {
uint8_t pin_value = port_pin_get_input_level(btn_pins[b]);
const bool pressed = (pin_value == 0);
if( b < 8 ) {// read BTN1->BTN8
if(pressed){
io_matrix[b] &= ~(1 << 7);
}else{
io_matrix[b] |= ( 1 << 7);
}
}else{//c64 joystick arrow keys
//B12=left,, B11=down,B10 = up,B9 = right
uint8_t btn_bts = b-8;
if(pressed){
js_bits &= ~(1<<btn_bts);
}else{
js_bits |= (1<<btn_bts);
}
}
int8_t list_idx = -1;
for (int8_t i = 0; i < KEY_LIST_SIZE; ++i) {
if (self.list[i].p_entry != &((const struct entry*)btn_entries)[b])
continue;
list_idx = i;
break;
}
if (list_idx > -1) {
next_item_state(&self.list[list_idx], pressed);
continue;
}
if (!pressed)
continue;
for (uint8_t i = 0 ; i < KEY_LIST_SIZE; ++i) {
if (self.list[i].p_entry != NULL)
continue;
self.list[i].p_entry = &((const struct entry*)btn_entries)[b];
self.list[i].state = KEY_STATE_IDLE;
next_item_state(&self.list[i], pressed);
break;
}
}
#endif
io_matrix[8] = 0xFF;
self.last_process_time = time_uptime_ms();
}
void keyboard_set_key_callback(key_callback callback)
{
self._key_callback = callback;
}
void keyboard_set_lock_callback(lock_callback callback)
{
self._lock_callback = callback;
}
bool keyboard_get_capslock(void)
{
return self.capslock;
}
bool keyboard_get_numlock(void)
{
return self.numlock;
}
void keyboard_init(void)
{
struct port_config port_init;
port_get_config_defaults(&port_init);
for (int i = 0; i < MOD_LAST; ++i)
self.mods[i] = false;
// Rows
port_init.direction = PORT_PIN_DIR_INPUT;
port_init.input_pull = PORT_PIN_PULL_UP;
for (uint32_t i = 0; i < NUM_OF_ROWS; ++i)
port_pin_set_config(row_pins[i], &port_init);
// Cols
port_init.direction = PORT_PIN_DIR_INPUT;
port_init.input_pull = PORT_PIN_PULL_NONE;
for(uint32_t i = 0; i < NUM_OF_COLS; ++i)
port_pin_set_config(col_pins[i], &port_init);
// Btns
#if NUM_OF_BTNS > 0
port_init.direction = PORT_PIN_DIR_INPUT;
port_init.input_pull = PORT_PIN_PULL_UP;
for(uint32_t i = 0; i < NUM_OF_BTNS; ++i)
port_pin_set_config(btn_pins[i], &port_init);
#endif
}

View File

@ -0,0 +1,547 @@
#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);
}

View File

@ -0,0 +1,21 @@
#ifndef PINS_H
#define PINS_H
#include "keyboard.h"
const uint8_t row_pins[NUM_OF_ROWS] =
{
PA0,PA1,PA2,PA3,PA4,PA5,PA6
};
const uint8_t col_pins[NUM_OF_COLS] =
{
PC0,PC1,PC2,PC3,PC4,PC5,PC6,PC7
};
const uint8_t btn_pins[NUM_OF_BTNS] =
{
PB0,PB1,PB2,PB3,PB4,PB5,PB6,PB7,PB12,PB13,PB14,PB15
};
#endif

View File

@ -0,0 +1,44 @@
#ifndef PORT_H
#define PORT_H
enum port_pin_dir {
/** The pin's input buffer should be enabled, so that the pin state can
* be read */
PORT_PIN_DIR_INPUT = INPUT,
/** The pin's output buffer should be enabled, so that the pin state can
* be set */
PORT_PIN_DIR_OUTPUT = OUTPUT,
};
/**
* \brief Port pin input pull configuration enum.
*
* Enum for the possible pin pull settings of the port pin configuration
* structure, to indicate the type of logic level pull the pin should use.
*/
enum port_pin_pull {
/** No logical pull should be applied to the pin */
PORT_PIN_PULL_NONE = INPUT_FLOATING,
/** Pin should be pulled up when idle */
PORT_PIN_PULL_UP = INPUT_PULLUP,
/** Pin should be pulled down when idle */
PORT_PIN_PULL_DOWN = INPUT_PULLDOWN,
};
struct port_config {
/** Port buffer input/output direction */
enum port_pin_dir direction;
/** Port pull-up/pull-down for input pins */
enum port_pin_pull input_pull;
};
void port_get_config_defaults(struct port_config *const config);
void port_pin_set_config(const uint8_t gpio_pin,const struct port_config *const config);
void port_pin_set_output_level(const uint8_t gpio_pin, const bool level);
bool port_pin_get_input_level(const uint8_t gpio_pin);
#endif

View File

@ -0,0 +1,39 @@
#include "port.h"
void port_get_config_defaults(struct port_config *const config)
{
/* Default configuration values */
config->direction = PORT_PIN_DIR_INPUT;
config->input_pull = PORT_PIN_PULL_UP;
}
void port_pin_set_config(const uint8_t gpio_pin,const struct port_config *const config)
{
if(config->direction == PORT_PIN_DIR_OUTPUT){
pinMode(gpio_pin,OUTPUT);
return;
}
if(config->direction == PORT_PIN_DIR_INPUT){
if(config->input_pull != PORT_PIN_PULL_NONE){
pinMode(gpio_pin,config->input_pull);
}else{
pinMode(gpio_pin,config->direction);
}
}
}
void port_pin_set_output_level(const uint8_t gpio_pin, const bool level)
{
digitalWrite(gpio_pin,level);
}
bool port_pin_get_input_level(const uint8_t gpio_pin){
return digitalRead(gpio_pin);
}

View File

@ -0,0 +1,55 @@
#ifndef REG_H
#define REG_H
#include <stdbool.h>
#include <stdint.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
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
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))
#define WRITE_MASK (1<<7)
uint8_t reg_get_value(enum reg_id reg);
void reg_set_value(enum reg_id reg, uint8_t value);
bool 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);
#endif

View File

@ -0,0 +1,45 @@
#include "reg.h"
static uint8_t regs[REG_ID_LAST];
uint8_t reg_get_value(enum reg_id reg)
{
return regs[reg];
}
void reg_set_value(enum reg_id reg, uint8_t value)
{
regs[reg] = value;
}
bool reg_is_bit_set(enum reg_id reg, uint8_t bit)
{
return regs[reg] & bit;
}
void reg_set_bit(enum reg_id reg, uint8_t bit)
{
regs[reg] |= bit;
}
/*
* | 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)
{
regs[REG_ID_CFG] = CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS|CFG_REPORT_MODS ;
regs[REG_ID_DEB] = 10;
regs[REG_ID_FRQ] = 5;
regs[REG_ID_BKL] = 255;//100%duty
regs[REG_ID_BK2] = 0;
regs[REG_ID_BAT] = 0xff; //default .no battery
}