Compare commits
5 Commits
56cd06876a
...
ee4a12c9c5
Author | SHA1 | Date |
---|---|---|
|
ee4a12c9c5 | |
|
0a940b692d | |
|
d6026c38ff | |
|
65a826bd46 | |
|
6b84019e0b |
|
@ -9,19 +9,27 @@
|
|||
* 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
|
||||
*
|
||||
* speed settings chosen are what the lowest speed device (the sub MCU) will
|
||||
* handle without faults.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <CH59x_common.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#define CYCLES_TO_HI 2
|
||||
#define CYCLES_TO_LO 0
|
||||
|
||||
|
@ -66,8 +74,6 @@
|
|||
|
||||
|
||||
static uint16_t delay_hi, delay_lo;
|
||||
|
||||
|
||||
static volatile uint16_t spin;
|
||||
|
||||
|
||||
|
@ -98,6 +104,7 @@ void i2cm_start()
|
|||
{
|
||||
SDA_IN_HI(); bit_delay_hi();
|
||||
SCL_IN_HI(); bit_delay_hi();
|
||||
while (!SCL_GET()); // clock stretch
|
||||
SDA_OUTLO(); bit_delay_lo();
|
||||
SCL_OUTLO(); bit_delay_lo();
|
||||
}
|
||||
|
@ -108,6 +115,7 @@ void i2cm_restart()
|
|||
SDA_IN_HI(); bit_delay_hi();
|
||||
SCL_IN_HI();
|
||||
while (!SCL_GET()); // clock stretch
|
||||
bit_delay_hi(); // attempt to fix corruption; unnecessary?
|
||||
i2cm_start();
|
||||
}
|
||||
|
||||
|
@ -116,9 +124,8 @@ void i2cm_stop()
|
|||
{
|
||||
SDA_OUTLO(); bit_delay_lo();
|
||||
SCL_IN_HI(); bit_delay_hi();
|
||||
while (!SCL_GET()); // clock stretch
|
||||
SDA_IN_HI(); bit_delay_hi();
|
||||
|
||||
// wait some extra time
|
||||
bit_delay_hi();
|
||||
bit_delay_hi();
|
||||
bit_delay_hi();
|
||||
|
|
|
@ -87,7 +87,7 @@ void ch32sub_process()
|
|||
for (i = 0; i < 8; i++) {
|
||||
if (flags & (1 << i)) {
|
||||
if (intr_cb[i]) {
|
||||
clear |= (i << 1);
|
||||
clear |= (1 << i);
|
||||
intr_cb[i]();
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -114,8 +114,8 @@ void menu_btn_use_std()
|
|||
btn[0].cb_push = &menu_btn_next;
|
||||
btn[0].cb_hold = &menu_btn_next;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 300 / HOLD_PERIOD;
|
||||
btn[0].repeat = 150 / HOLD_PERIOD;
|
||||
btn[0].hold = 400 / HOLD_PERIOD;
|
||||
btn[0].repeat = 180 / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &menu_btn_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
|
@ -132,8 +132,8 @@ void menu_btn_use_std()
|
|||
btn[3].cb_push = &menu_btn_prev;
|
||||
btn[3].cb_hold = &menu_btn_prev;
|
||||
btn[3].cb_release = 0;
|
||||
btn[3].hold = 300 / HOLD_PERIOD;
|
||||
btn[3].repeat = 150 / HOLD_PERIOD;
|
||||
btn[3].hold = btn[0].hold;
|
||||
btn[3].repeat = btn[0].repeat;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ void menu_btn_use_none()
|
|||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
for (i = 0; i < MENU_BTN_COUNT; i++) {
|
||||
btn[i].cb_push = &menu_btn_enter;
|
||||
btn[i].hold = 0;
|
||||
btn[i].repeat = 0;
|
||||
|
|
|
@ -287,8 +287,8 @@ void snek_btn_use()
|
|||
btn[0].cb_push = &snek_btn_prev;
|
||||
btn[0].cb_hold = &snek_btn_prev;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 200 / HOLD_PERIOD;
|
||||
btn[0].repeat = 40 / HOLD_PERIOD;
|
||||
btn[0].hold = 300 / HOLD_PERIOD;
|
||||
btn[0].repeat = 80 / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &snek_btn_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
|
|
|
@ -178,12 +178,16 @@ void menu_6_disp(uint8_t idx)
|
|||
case 3: {
|
||||
ssd1306fb_set_cursor(54, 4);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "LED", 1);
|
||||
ssd1306fb_set_cursor(54, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "9-12", 1);
|
||||
ssd1306fb_set_cursor(53, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "9", 1);
|
||||
ssd1306fb_set_cursor(58, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "-", 1);
|
||||
ssd1306fb_set_cursor(63, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "12", 1);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ssd1306fb_set_cursor(led_pos[i][0], led_pos[i][1]);
|
||||
sprintf(txt, "R%d h%03X", i + 5, hsv_out[rgb_map[i + 8]].h);
|
||||
sprintf(txt, "R%1X h%03X", i + 9, hsv_out[rgb_map[i + 8]].h);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "s%02X v%02X", hsv_out[rgb_map[i + 8]].s, hsv_out[rgb_map[i + 8]].v);
|
||||
oled.cursor_x = led_pos[i][0];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,29 @@
|
|||
* Created on: Jul 27, 2024
|
||||
*
|
||||
* generic button handler like I do on most of my projects
|
||||
*
|
||||
*
|
||||
* to use the buttons:
|
||||
*
|
||||
* - set REG_BTN_DEBOUNCE_TIME to desired debounce time in 5ms increments
|
||||
* (default is set in BTN_DEBOUNCE)
|
||||
*
|
||||
* - set button flags for the buttons you want the interrupt line to respond
|
||||
* to in REG_BTNx_INT_ENABLE for each button
|
||||
* flags are BTN_PUSH / BTN_HOLD / BTN_RELEASE bits
|
||||
*
|
||||
* - enable button line interrupt if you want the interrupt line to go high
|
||||
* when a button interrupt happens
|
||||
* this is INT_BTN bit in REG_INTR_FLAGS
|
||||
*
|
||||
* - if using interrupts, check flag; clear the flag by writing BTN_INT bit
|
||||
* to REG_INTR_FLAGS_CLEAR
|
||||
*
|
||||
* - read the button states from REG_BTN_PUSHED, REG_BTN_HELD, REG_BTN_RELEASED
|
||||
* these registers are clear-on-read so will always have fresh information
|
||||
*
|
||||
* - alternatively, work the mask directly by reading REG_BTNx_MASK
|
||||
*
|
||||
*/
|
||||
|
||||
#include "btn.h"
|
||||
|
@ -117,16 +140,18 @@ void btn_poll()
|
|||
|
||||
uint8_t pushed;
|
||||
|
||||
set_interrupt = 0;
|
||||
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
pushed = 0;
|
||||
|
||||
ignore = btn[i]._mask & BTN_IGNORE;
|
||||
r = BTN_PORT->INDR & (1 << (btn[i]._pintype & BTN_PIN_MASK));
|
||||
r = BTN_PORT->INDR & (1 << (btn[i]._pintype & BTN_PINTYPE_MASK));
|
||||
|
||||
// active low type buttons
|
||||
if (!r && (btn[i]._pintype & BTN_ACTIVE_LO)) pushed = 1;
|
||||
if (!r) pushed = 1;
|
||||
// active high type buttons
|
||||
if (r && !(btn[i]._pintype & BTN_ACTIVE_LO)) pushed = 1;
|
||||
// if (r && !(btn[i]._pintype & BTN_ACTIVE_LO)) pushed = 1;
|
||||
|
||||
if (pushed) {
|
||||
// hold counter
|
||||
|
@ -138,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);
|
||||
}
|
||||
|
||||
|
@ -165,18 +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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,37 +14,38 @@
|
|||
|
||||
|
||||
|
||||
#define BTN_COUNT 4
|
||||
#define BTN_DEBOUNCE (12 >> 1) // debounce time in 2ms
|
||||
#define BTN_COUNT 5
|
||||
#define BTN_DEBOUNCE (30 / 5) // debounce time in 5ms increments
|
||||
|
||||
#define BTN_PORT GPIOC
|
||||
#define BTN_MODE 0x80
|
||||
#define BTN_PORT GPIOC
|
||||
#define BTN_MODE 0x80
|
||||
|
||||
#define BTN_PULL_UP (1 << 0)
|
||||
#define BTN_PULL_DOWN (1 << 16)
|
||||
#define BTN_PULL_UP (1 << 0)
|
||||
#define BTN_PULL_DOWN (1 << 16)
|
||||
|
||||
#define BTN_ACTIVE_LO 0xf1
|
||||
#define BTN_PIN_MASK 0xf1
|
||||
#define BTN_ACTIVE_LO 0xf1
|
||||
#define BTN_PIN_MASK 0xf1
|
||||
#define BTN_PINTYPE_MASK 0x0f
|
||||
|
||||
#define BTN1_PIN 4
|
||||
#define BTN1_PUPD BTN_PULL_UP
|
||||
#define BTN1_PIN 4
|
||||
#define BTN1_PUPD BTN_PULL_UP
|
||||
|
||||
#define BTN2_PIN 5
|
||||
#define BTN2_PUPD BTN_PULL_UP
|
||||
#define BTN2_PIN 5
|
||||
#define BTN2_PUPD BTN_PULL_UP
|
||||
|
||||
#define BTN3_PIN 6
|
||||
#define BTN3_PUPD BTN_PULL_UP
|
||||
#define BTN3_PIN 6
|
||||
#define BTN3_PUPD BTN_PULL_UP
|
||||
|
||||
#define BTN4_PIN 7
|
||||
#define BTN4_PUPD BTN_PULL_UP
|
||||
#define BTN4_PIN 7
|
||||
#define BTN4_PUPD BTN_PULL_UP
|
||||
|
||||
#define BTN5_PIN 0 // side button
|
||||
#define BTN5_PUPD BTN_PULL_UP
|
||||
#define BTN5_PIN 0 // side button
|
||||
#define BTN5_PUPD BTN_PULL_UP
|
||||
|
||||
#define BTN_PUSH (1 << 0)
|
||||
#define BTN_HOLD (1 << 1)
|
||||
#define BTN_RELEASE (1 << 2)
|
||||
#define BTN_IGNORE (1 << 3)
|
||||
#define BTN_PUSH (1 << 0)
|
||||
#define BTN_HOLD (1 << 1)
|
||||
#define BTN_RELEASE (1 << 2)
|
||||
#define BTN_IGNORE (1 << 3)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -173,7 +170,7 @@ void i2cs_init(uint8_t address, uint8_t read_only_mode)
|
|||
// enable interrupts
|
||||
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN | I2C_CTLR2_ITERREN;
|
||||
|
||||
NVIC_SetPriority(I2C1_EV_IRQn, 1 << 6);
|
||||
NVIC_SetPriority(I2C1_EV_IRQn, 0 << 6);
|
||||
NVIC_EnableIRQ(I2C1_EV_IRQn); // Event interrupt
|
||||
NVIC_SetPriority(I2C1_ER_IRQn, 3 << 6);
|
||||
NVIC_EnableIRQ(I2C1_ER_IRQn); // Error interrupt
|
||||
|
@ -205,21 +202,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 +241,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 +256,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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -96,11 +96,12 @@ 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_SetPriority(SysTicK_IRQn, 1 << 6);
|
||||
NVIC_EnableIRQ(SysTicK_IRQn); // enable interrupt
|
||||
}
|
||||
|
||||
|
@ -121,9 +122,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 +145,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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue