Compare commits

...

5 Commits

Author SHA1 Message Date
true ee4a12c9c5 finally fixed the last known bit of i2c corruption
needed to implement clock stretching on start/stop routines too.

program tick timer on sub MCU doesn't really need to be highest priority so made i2c higher priority.
2024-10-26 00:13:53 -07:00
true 0a940b692d 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.
2024-10-25 23:17:12 -07:00
true d6026c38ff update menus 2024-10-25 23:15:08 -07:00
true 65a826bd46 fix set sub MCU interrupt clear 2024-10-25 23:14:39 -07:00
true 6b84019e0b fixed button handler not reporting buttons correctly 2024-10-25 18:40:55 -07:00
15 changed files with 230 additions and 167 deletions

View File

@ -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();

View File

@ -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 {

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

@ -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;

View File

@ -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;

View File

@ -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];

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

@ -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;
}
}

View File

@ -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)

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;
@ -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
}
}

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,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();
}
}