some button fixes

buttons are now somewhat working properly, but register 0x12 (button debounce value) keeps getting overwritten / corrupted. once this is fixed then buttons will likley work properly.
This commit is contained in:
true 2024-10-25 23:17:12 -07:00
parent d6026c38ff
commit 0a940b692d
10 changed files with 163 additions and 129 deletions

View File

@ -9,20 +9,25 @@
* even in interrupt mode would result in higher speeds and less
* power consumption, living without debug is worse.
*
* quirks and features:
* quirks, features and limitations:
* - this implementation is blocking.
* - clock stretching is supported, and somewhat tested
* - any slave stuck clock stretching will lock everything until released;
* there is no timeout or cancel operation implemented
*
* known good settings:
* known good settings at 32MHz:
* CYCLES_TO_HI = 5, CYCLES_TO_LOW = 2: ~400-500kHz
* CYCLES_TO_HI = 2, CYCLES_TO_LOW = 0: ~650KHz
* CYCLES_TO_HI = 1, bit_delay_lo = __nop: ~900KHz, but sub MCU fails to respond
*
*/
#include <CH59x_common.h>
#include <stdint.h>
#define CYCLES_TO_HI 2
#define CYCLES_TO_HI 3
#define CYCLES_TO_LO 0
//#define CYCLES_RD 2 // cycles spent in read routine
@ -66,8 +71,6 @@
static uint16_t delay_hi, delay_lo;
static volatile uint16_t spin;
@ -108,6 +111,8 @@ void i2cm_restart()
SDA_IN_HI(); bit_delay_hi();
SCL_IN_HI();
while (!SCL_GET()); // clock stretch
bit_delay_hi(); // wait a little bit
bit_delay_hi(); // sub MCU can corrupt data if we don't wait
i2cm_start();
}
@ -122,6 +127,8 @@ void i2cm_stop()
bit_delay_hi();
bit_delay_hi();
bit_delay_hi();
bit_delay_hi();
bit_delay_hi();
}
// returns: data byte

View File

@ -33,6 +33,7 @@
#include "led/rgbled.h"
#include "misc/accel.h"
#include "misc/tinymt.h"
#include "ui/menu.h"
#include "ui/oled.h"
@ -43,18 +44,17 @@
// global settings
#define OLED_UPDATE_RATE 13 // framerate of OLED; { (256*(8/8)) / OLED_UPDATE_RATE }; 13 = 19FPS
#define OLED_UPDATE_RATE 13 // framerate of OLED; { (256*(8/8)) / OLED_UPDATE_RATE }; 13 = 19FPS, 15 = 17FPS
// flags
#define FLAG_OLED_UPDATE (1 << 0)
#define FLAG_RGBLED_RUN_PROG (1 << 1)
#define FLAG_PROG_TICK_INTR (1 << 7)
#define FLAG_RESET_SYSTICK (1 << 6)
#define PROG_TICK_RATE ((32768 / 256) - 1)
#define FLAG_RGBLED_RUN (1 << 1)
#define FLAG_ACCEL_POLL (1 << 2)
#define FLAG_RGBLED_SEND (1 << 5)
#define PROG_TICK_RATE ((32768-8192-4096) / 256) // not sure why this value can't be 32768/256
// this was checked with a stopwatch and is close enough
// this value IS FRAMERATE DEPENDENT for some reason... figure it out later
const uint8_t vers[] = "241015a";
@ -73,7 +73,7 @@ uint32_t idle_time_still;
uint8_t idle_go_sleep;
static volatile uint8_t flags_lo = 0;
static uint8_t flags_hi = FLAG_RESET_SYSTICK;
//static uint8_t flags_hi = FLAG_RESET_SYSTICK;
static uint8_t st_tick = 0; // program tick counter
static uint8_t oled_tick = 0; // oled framerate counter
@ -83,7 +83,7 @@ static uint8_t oled_tick = 0; // oled framerate counter
void ch59x_xtal_conf()
{
HSECFG_Current(HSE_RCur_125);
HSECFG_Capacitance(HSECap_14p);
HSECFG_Capacitance(HSECap_12p);
}
/*
@ -117,6 +117,8 @@ void rtcisr_init()
Calibration_LSI(Level_128); // calibrate LSI fromm HSE
rtc_reset_trig();
PFIC_SetPriority(RTC_IRQn, 0x00); // rtc is highest priority interrupt
PFIC_EnableIRQ(RTC_IRQn);
}
@ -183,7 +185,7 @@ void oled_update_done()
menu_tick();
// calculate CPU usage
cpu_pct = (cpu_use * 100) / cpu_max;
cpu_pct = ((cpu_use * 100) / OLED_UPDATE_RATE) / cpu_max;
if (cpu_pct > cpu_pct_max) cpu_pct_max = cpu_pct;
}
@ -202,11 +204,23 @@ void lowprio_task() {
// can corrupt transactions
SYS_DisableAllIrq(&interrupt_flags);
// temporary: enable sub interrupts
// temporary: re-send sub interrupts, sub button holds, enable rgb_hwen
ch32sub_intr_defaults();
btn_commit_hold();
ch32sub_rgb_hwen(1);
// process sub MCU interrupt, if pending
ch32sub_process();
if (flags_lo & FLAG_RGBLED_SEND) {
flags_lo &= ~FLAG_RGBLED_SEND;
// rgbled_send();
}
if (flags_lo & FLAG_ACCEL_POLL) {
flags_lo &= ~FLAG_ACCEL_POLL;
// accel_poll();
}
// send the last oled frame data
if (flags_lo & FLAG_OLED_UPDATE) {
flags_lo &= ~FLAG_OLED_UPDATE;
@ -227,15 +241,7 @@ void lowprio_task() {
}
ssd1306_update();
}
}
// send the RGBLED data
// todo
// capture CPU use
if (flags_lo & FLAG_PROG_TICK_INTR) {
flags_lo &= ~FLAG_PROG_TICK_INTR;
flags_hi |= FLAG_RESET_SYSTICK;
cpu_use = SysTick->CNT;
}
@ -243,8 +249,9 @@ void lowprio_task() {
SYS_RecoverIrq(interrupt_flags);
// render new rgbled frame
if (flags_lo & FLAG_RGBLED_RUN_PROG) {
flags_lo &= ~FLAG_RGBLED_RUN_PROG;
// this is not included in cpu calcs :/
if (flags_lo & FLAG_RGBLED_RUN) {
flags_lo &= ~FLAG_RGBLED_RUN;
rgbled_runprog(st_tick);
}
@ -261,36 +268,36 @@ int main()
SetSysClock(SYSCLK_FREQ_USEI2C);
// note that system clock speed is decreased after every use of I2C.
PWR_DCDCCfg(ENABLE); // enable DC-DC converter; brings significant power saving
PWR_DCDCCfg(ENABLE); // enable DC-DC converter; brings significant power saving
i2c_init(); // get i2c initialized since most stuff will need it
i2c_init(); // get i2c initialized since most stuff will need it
uconf_load(); // read the user config
uconf_load(); // read the user config
ch32sub_init(); // initialize aux MCU
ch32sub_rgb_hwen(1); // and enable RGBLED controller hardware pin so the controller can wake up
ch32sub_init(); // initialize aux MCU
ch32sub_rgb_hwen(1); // and enable RGBLED controller hardware pin so the controller can wake up
accel_init(); // initialize accelerometer
rgbled_init(); // initialize RGBLED controller
tinymt32_init(&tinymt32_s, 1337); // let's get random
gat_gpio_init(); // configure GAT aux GPIOs, get gat ID
// todo: implement // configure GAT i2c slave
accel_init(); // initialize accelerometer
rgbled_init(); // initialize RGBLED controller
ssd1306fb_set_target(&oled); // start up the OLED and menu system
ssd1306_init(1); // we'll try to init later too, since sometimes OLED fails to init
menu_stop(0); // lol. yes, this "starts" the "menu" in nametag mode
gat_gpio_init(); // configure GAT aux GPIOs, get gat ID
// todo: implement // configure GAT i2c slave
systick_init(); // start up the system tick, used for CPU percentage calculation
ssd1306fb_set_target(&oled); // start up the OLED and menu system
ssd1306_init(1); // we'll try to init later too, since sometimes OLED fails to init
menu_stop(0); // lol. yes, this "starts" the "menu" in nametag mode
systick_init(); // start up the system tick, used for CPU percentage calculation
cpu_max = GetSysClock() / PROG_TICK_RATE;
port_intr_init(); // configure port-based interrupts (used for ch32sub interrupt)
ch32sub_intr_defaults(); // configure interrupt sources on sub MCU - buttons, etc
port_intr_init(); // configure port-based interrupts (used for ch32sub interrupt)
ch32sub_intr_defaults(); // configure interrupt sources on sub MCU - buttons, etc
// configure main program tick interrupt
rtcisr_init();
rtcisr_init(); // configure main program tick interrupt
// start the low priority task loop
lowprio_task();
lowprio_task(); // start the low priority task loop
}
@ -298,19 +305,12 @@ __INTERRUPT
__HIGH_CODE
void RTC_IRQHandler(void)
{
// clear systick for CPU usage counting
if (flags_hi & FLAG_RESET_SYSTICK) {
SysTick->CNT = 0;
flags_hi &= ~FLAG_RESET_SYSTICK;
flags_lo |= FLAG_PROG_TICK_INTR;
}
// clear interrupt flag and reset RTC
rtc_reset_trig();
// speed up
SetSysClock(SYSCLK_FREQ_NORMAL);
// clear interrupt flag and reset RTC
rtc_reset_trig();
// manual uptime counter
st_tick++;
if (!st_tick) {
@ -320,6 +320,13 @@ void RTC_IRQHandler(void)
uptime_sec = (uint8_t)((uptime ) % 60);
}
oled_tick++;
if (oled_tick >= OLED_UPDATE_RATE) {
oled_tick = 0;
SysTick->CNT = 0;
flags_lo |= FLAG_OLED_UPDATE;
}
// operations
switch (st_tick & 0x7) {
case 0:
@ -329,22 +336,14 @@ void RTC_IRQHandler(void)
uconf.ledprog_rgb_idx = 0;
}
// send any rendered data now
rgbled_send();
// defer rendering
flags_lo |= FLAG_RGBLED_RUN_PROG;
// send RGBLEDs, render new program
flags_lo |= FLAG_RGBLED_SEND;
flags_lo |= FLAG_RGBLED_RUN;
break;
}
case 1: {
accel_poll();
flags_lo |= FLAG_ACCEL_POLL;
// no break
}
}
oled_tick++;
if (oled_tick >= OLED_UPDATE_RATE) {
oled_tick = 0;
flags_lo |= FLAG_OLED_UPDATE;
}
}

View File

@ -43,8 +43,11 @@ void port_intr_cb_register(uint8_t port, uint8_t idx, void (*fn)(void))
void port_intr_init()
{
// clear state then enable port interrupt
// clear state
R16_PB_INT_IF = 0xff;
// then enable interrupt as low priority
PFIC_SetPriority(RTC_IRQn, 0x30);
PFIC_EnableIRQ(GPIO_B_IRQn);
}

View File

@ -21,11 +21,17 @@ uint8_t btn_held;
static uint8_t btn_map[BTN_COUNT] = {2, 1, 3, 0, 4}; // bot left, top left, bot right, top right
__HIGH_CODE
void btn_push_cb(uint8_t idx)
{
btn_pushed |= (1 << idx);
if (btn[idx].cb_push) btn[idx].cb_push(idx);
if (!(btn_pushed & (1 << idx))) {
btn_pushed |= (1 << idx);
if (btn[idx].cb_push) btn[idx].cb_push(idx);
}
}
__HIGH_CODE
@ -52,8 +58,8 @@ void btn_commit_hold()
x = 0;
for (i = 0; i < BTN_COUNT; i++) {
val[x + 0x00] = btn[i].hold >> 8;
val[x + 0x01] = btn[i].hold & 0xff;
val[x + 0x00] = btn[btn_map[i]].hold >> 8;
val[x + 0x01] = btn[btn_map[i]].hold & 0xff;
x += 2;
}
@ -61,8 +67,8 @@ void btn_commit_hold()
x = 0;
for (i = 0; i < BTN_COUNT; i++) {
val[x + 0x00] = btn[i].repeat >> 8;
val[x + 0x01] = btn[i].repeat & 0xff;
val[x + 0x00] = btn[btn_map[i]].repeat >> 8;
val[x + 0x01] = btn[btn_map[i]].repeat & 0xff;
x += 2;
}
@ -87,13 +93,13 @@ void btn_intr()
// process callbacks for new events
for (i = 0; i < BTN_COUNT; i++) {
if (btn_state[1] & (1 << i)) {
btn_push_cb(i);
btn_push_cb(btn_map[i]);
}
if (btn_state[2] & (1 << i)) {
btn_hold_cb(i);
btn_hold_cb(btn_map[i]);
}
if (btn_state[3] & (1 << i)) {
btn_release_cb(i);
btn_release_cb(btn_map[i]);
}
}
}

View File

@ -13,7 +13,8 @@
#define BTN_COUNT 5
#define BTN_COUNT 5 // total buttons in system
#define MENU_BTN_COUNT 4 // buttons used for menu, must be the first buttons
#define HOLD_PERIOD 5 // period in milliseconds for each "hold" tick internally

View File

@ -15,19 +15,24 @@
void NMI_Handler(void); //__attribute__((interrupt("WCH-Interrupt-fast")));
void HardFault_Handler(void); //__attribute__((interrupt("WCH-Interrupt-fast")));
#define SYSTICK_COOLDOWN 3
uint8_t systick_timeout;
__attribute__((interrupt()))
void NMI_Handler(void)
{
while (1);
}
__attribute__((interrupt()))
void HardFault_Handler(void)
{
// hopefully we're running well enough to isolate the i2c bus
// then reboot, because we shouldn't really be here
while (1);
}
@ -36,31 +41,31 @@ void SysTick_Handler(void)
{
// ---- PROCESS UI AND COMMS
// button handler
btn_poll();
btn_poll(); // button handler
irda_process(); // irda timeout and retry
// irda timeout and retry
irda_process();
// clear comparison flag
SysTick->SR = 0;
SysTick->SR = 0; // clear comparison flag
// ---- DISABLE INTERRUPT
// see if any buttons are currently pushed
if ((BTN_PORT->INDR & BTN_PIN_MASK) != BTN_PIN_MASK) {
// yup, something is pushed, so keep the interrupt on
// something is pushed, so keep the tick interrupt enabled
systick_timeout = SYSTICK_COOLDOWN;
return;
}
// see if irda is active
// nothing is active, so shut down this interrupt
NVIC_DisableIRQ(SysTicK_IRQn);
// nothing is active, so shut down this interrupt source
systick_timeout--;
if (!systick_timeout) {
NVIC_DisableIRQ(SysTicK_IRQn);
}
}
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((interrupt()))
void EXTI7_0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line3)) {
@ -75,6 +80,7 @@ void EXTI7_0_IRQHandler(void)
// enable main tick handler on button push.
// tick handler will sense when it isn't needed and shut itself off.
SysTick->SR = 0;
systick_timeout = SYSTICK_COOLDOWN;
NVIC_EnableIRQ(SysTicK_IRQn);
EXTI_ClearITPendingBit(EXTI_Line0 | EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7);
}

View File

@ -140,6 +140,8 @@ void btn_poll()
uint8_t pushed;
set_interrupt = 0;
for (i = 0; i < BTN_COUNT; i++) {
pushed = 0;
@ -161,20 +163,21 @@ void btn_poll()
// first push?
if (!(btn[i]._mask & BTN_PUSH)) {
btn[i]._mask = BTN_PUSH | ignore;
if (btn[i].cb_push && !ignore) {
btn[i].cb_push(i);
if (!ignore) {
if (btn[i].cb_push) btn[i].cb_push(i);
btn[i]._mask |= (BTN_PUSH << 4);
}
} else if (btn[i]._count >= btn[i].hold) {
// held to count limit
} else
// held to count limit
if (btn[i].hold && (btn[i]._count >= btn[i].hold)) {
// if button is not repeatable, do not retrigger
if ((btn[i]._mask & BTN_HOLD) && !btn[i].repeat) continue;
btn[i]._mask |= BTN_HOLD;
// call callback only if not in ignore state
if (btn[i].cb_hold && !ignore) {
btn[i].cb_hold(i);
if (!ignore) {
if (btn[i].cb_hold) btn[i].cb_hold(i);
btn[i]._mask |= (BTN_HOLD << 4);
}
@ -188,19 +191,18 @@ void btn_poll()
if (!(btn[i]._mask & BTN_RELEASE)) {
// note: release will remove ignore status
btn[i]._mask = BTN_RELEASE;
btn[i]._count = 0;
// call callback only if not in ignore state
if (btn[i].cb_release && !ignore) {
btn[i].cb_release(i);
btn[i]._mask |= (BTN_RELEASE << 4);
}
}
btn[i]._count = 0;
}
}
// any button event with enabled interrupt flags will set button interrupt flag
if (set_interrupt) {
i2cs_append_flag(INT_BTN);
set_interrupt = 0;
}
}

View File

@ -63,7 +63,7 @@ static void i2cs_int_check()
void i2cs_write_cb(uint16_t initial_reg, uint16_t last_reg)
{
uint8_t i;
uint8_t trig = 0;
uint8_t trig;
uint16_t w;
// nothing written?
@ -73,7 +73,7 @@ void i2cs_write_cb(uint16_t initial_reg, uint16_t last_reg)
for (i = 0; i < sizeof(write_cb_trig); i++) {
if ((initial_reg <= write_cb_trig[i]) && (last_reg >= write_cb_trig[i])) {
trig = write_cb_trig[i];
}
} else continue;
switch (trig) {
case REG_INTR_FLAGS_CLEAR: {
@ -151,9 +151,6 @@ void i2cs_init(uint8_t address, uint8_t read_only_mode)
{
I2C_InitTypeDef i2c = {0};
i2cs_state.first_write = 1;
i2cs_state.offset = 0;
i2cs_state.position = 0;
i2cs_state.reg_file = i2cs_reg;
i2cs_state.reg_len = sizeof(i2cs_reg);
i2cs_state.write_callback = i2cs_write_cb;
@ -161,7 +158,7 @@ void i2cs_init(uint8_t address, uint8_t read_only_mode)
i2cs_state.read_callback = i2cs_read_cb;
i2cs_state.read_only = read_only_mode;
i2c.I2C_ClockSpeed = 500000;
i2c.I2C_ClockSpeed = 600000;
i2c.I2C_Mode = I2C_Mode_I2C;
i2c.I2C_DutyCycle = I2C_DutyCycle_2;
i2c.I2C_OwnAddress1 = address;
@ -195,6 +192,7 @@ void i2cs_poll()
}
//__attribute__((section(".ramfunc")))
__attribute__((interrupt("WCH-Interrupt-fast")))
void I2C1_EV_IRQHandler(void)
{
@ -205,21 +203,23 @@ void I2C1_EV_IRQHandler(void)
STAR2 = I2C1->STAR2;
if (STAR1 & I2C_STAR1_ADDR) { // Start event
i2cs_state.first_write = 1; // Next write will be the offset
i2cs_state.position = i2cs_state.offset; // Reset position
i2cs_state.addr_write = 1; // Next write will be the 16-bit register address
i2cs_state.addr_req = 0; // reset this address
i2cs_state.writing = 0; // not yet writing data from master
}
if (STAR1 & I2C_STAR1_RXNE) { // Write from master event
if (i2cs_state.first_write == 2) { // low address byte written; set the offset
i2cs_state.offset = i2cs_state.offset | I2C1->DATAR;
i2cs_state.position = i2cs_state.offset;
i2cs_state.first_write = 0;
i2cs_state.writing = 0;
} else if (i2cs_state.first_write == 1) { // high address byte written
i2cs_state.offset = I2C1->DATAR << 8;
i2cs_state.first_write++;
if (i2cs_state.addr_write == 2) { // low address byte written
i2cs_state.addr_req |= I2C1->DATAR;
i2cs_state.position = i2cs_state.addr_req; // position set to requested index
i2cs_state.addr_write = 0; // not writing the address anymore
} else
{ // Normal register write
if (i2cs_state.addr_write == 1) { // high address byte written
i2cs_state.addr_req = I2C1->DATAR << 8;
i2cs_state.addr_write++;
} else
{ // Normal register write
i2cs_state.writing = 1;
if (i2cs_state.position < i2cs_state.reg_len && !i2cs_state.read_only) {
@ -242,7 +242,6 @@ void I2C1_EV_IRQHandler(void)
}
if (STAR1 & I2C_STAR1_TXE) { // Read to master event
i2cs_state.writing = 0;
if (i2cs_state.position < i2cs_state.reg_len) {
I2C1->DATAR = i2cs_state.reg_file[i2cs_state.position];
if (i2cs_state.read_callback != 0) {
@ -258,25 +257,33 @@ void I2C1_EV_IRQHandler(void)
I2C1->CTLR1 &= ~(I2C_CTLR1_STOP); // Clear stop
if (i2cs_state.write_callback != 0 && i2cs_state.writing) {
i2cs_state.write_callback(i2cs_state.offset, i2cs_state.position - 1);
i2cs_state.write_callback(i2cs_state.addr_req, i2cs_state.position - 1);
}
}
}
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((interrupt()))
void I2C1_ER_IRQHandler(void)
{
uint8_t err = 0;
uint16_t STAR1 = I2C1->STAR1;
if (STAR1 & I2C_STAR1_BERR) { // Bus error
err++;
I2C1->STAR1 &= ~(I2C_STAR1_BERR); // Clear error
}
if (STAR1 & I2C_STAR1_ARLO) { // Arbitration lost error
err++;
I2C1->STAR1 &= ~(I2C_STAR1_ARLO); // Clear error
}
if (STAR1 & I2C_STAR1_AF) { // Acknowledge failure
err++;
I2C1->STAR1 &= ~(I2C_STAR1_AF); // Clear error
}
if (err) {
I2C1->CTLR1 &= ~(I2C_CTLR1_STOP); // Clear stop
}
}

View File

@ -87,9 +87,9 @@ typedef void (*i2c_werr_callback_t)(uint16_t reg, uint8_t data, uint8_t error);
typedef void (*i2c_read_callback_t)(uint16_t reg);
typedef struct i2c_slave_state {
uint8_t first_write; // first 2 bytes address the register file
uint16_t offset; // offset within the register file as sent by the master
uint16_t position; // current read/write position within the register file
uint8_t addr_write; // first 2 bytes address the register file
uint16_t addr_req; // requested read/write index as sent by the master
uint16_t position; // current read/write index within the register file
volatile uint8_t* volatile reg_file; // register file pointer
uint16_t reg_len; // register file max length
i2c_write_callback_t write_callback;

View File

@ -96,9 +96,9 @@ static inline void exti_nvic_init()
void systick_init(void)
{
SysTick->CMP = (SystemCoreClock / 200 / 8) - 1; // we want a 200Hz interrupt
SysTick->CMP = (SystemCoreClock / 200) - 1; // we want a 200Hz interrupt
SysTick->CNT = 0; // clear counter
SysTick->CTLR = 0xB; // start counter in /8 mode, enable interrupts, auto-reset counter
SysTick->CTLR = 0xF; // start counter in /1 mode, enable interrupts, auto-reset counter
SysTick->SR = 0; // clear count comparison flag
NVIC_EnableIRQ(SysTicK_IRQn); // enable interrupt
@ -121,9 +121,10 @@ int main(void)
// enable peripheral clocks used in this program
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO |
RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC |
RCC_APB2Periph_GPIOD, ENABLE);
// configure gpio
@ -143,6 +144,8 @@ int main(void)
systick_init();
while (1) {
__WFI();
// todo: determine why we HardFault with user button interrupts
// randomly but only with __WFI being used
// __WFI();
}
}