Skip to content
Snippets Groups Projects
Commit 9877e58c authored by Woazboat's avatar Woazboat
Browse files

Handle button interrupts in epicardium + Add button callbacks to pycardium

parent 9325a212
No related branches found
No related tags found
No related merge requests found
Pipeline #3842 passed
......@@ -196,9 +196,11 @@ API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));
#define EPIC_INT_BHI160_GYROSCOPE 6
/** MAX30001 ECG. See :c:func:`epic_isr_max30001_ecg`. */
#define EPIC_INT_MAX30001_ECG 7
/** Button interrupt. See :c:func:`epic_isr_button`. */
#define EPIC_INT_BUTTON 8
/* Number of defined interrupts. */
#define EPIC_INT_NUM 8
#define EPIC_INT_NUM 9
/* clang-format on */
/*
......@@ -445,6 +447,12 @@ enum epic_button {
*/
API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask));
struct epic_isr_button_param {
unsigned int pb;
bool falling;
};
API_ISR(EPIC_INT_BUTTON, epic_isr_button);
/**
* Wristband GPIO
* ==============
......
......@@ -150,6 +150,18 @@ int main(void)
abort();
}
/* Buttons */
if (xTaskCreate(
vButtonTask,
(const char *)"Button",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY + 1,
NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "Button");
abort();
}
/*
* Initialize serial driver data structures.
*/
......
#include "epicardium.h"
#include "modules/modules.h"
#include "modules/log.h"
#include "api/interrupt-sender.h"
#include "api/dispatcher.h"
#include "portexpander.h"
#include "pb.h"
#include "MAX77650-Arduino-Library.h"
#include <stdint.h>
#include <string.h>
#define LOCK_WAIT pdMS_TO_TICKS(1000)
static TaskHandle_t button_task_id = NULL;
enum { BUTTON_NOTIFY_IRQ = 1,
};
static const uint8_t pin_mask[] = {
[BUTTON_LEFT_BOTTOM] = 1 << 5,
......@@ -13,6 +24,13 @@ static const uint8_t pin_mask[] = {
[BUTTON_RIGHT_TOP] = 1 << 6,
};
static const uint8_t pb_epic_mapping[] = {
BUTTON_LEFT_BOTTOM,
BUTTON_LEFT_TOP,
BUTTON_RIGHT_BOTTOM,
BUTTON_RIGHT_TOP,
};
uint8_t epic_buttons_read(uint8_t mask)
{
uint8_t ret = 0;
......@@ -43,3 +61,64 @@ uint8_t epic_buttons_read(uint8_t mask)
return ret;
}
void portexpander_interrupt_callback(void *_)
{
portexpander_ack_interrupt();
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (button_task_id != NULL) {
xTaskNotifyFromISR(
button_task_id,
BUTTON_NOTIFY_IRQ,
eSetBits,
&xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void portexpander_poll_interrupts()
{
while (hwlock_acquire(HWLOCK_I2C, LOCK_WAIT) < 0) {
LOG_WARN("portexpander", "Failed to acquire I2C. Retrying ...");
xTaskNotify(button_task_id, BUTTON_NOTIFY_IRQ, eSetBits);
return;
}
uint8_t pending = 0;
uint8_t levels = 0;
portexpander_get_pending_interrupts(&pending, &levels);
hwlock_release(HWLOCK_I2C);
portexpander_handle_pending_interrupts(&pending, &levels);
}
void button_callback_handler(unsigned int pb, bool falling)
{
// TODO: Proper parameter passing
struct epic_isr_button_param p = { pb_epic_mapping[pb - 1], falling };
memcpy(API_CALL_MEM->buffer + 0x20, &p, sizeof(p));
api_interrupt_trigger(EPIC_INT_BUTTON);
}
void vButtonTask(void *pvParameters)
{
button_task_id = xTaskGetCurrentTaskHandle();
LOG_INFO("buttons", "Creating button task");
portexpander_int_clr(0xFF);
PB_RegisterCallback(1, button_callback_handler);
PB_RegisterCallback(3, button_callback_handler);
PB_RegisterCallback(4, button_callback_handler);
while (1) {
uint32_t reason = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (reason & BUTTON_NOTIFY_IRQ) {
portexpander_poll_interrupts();
}
}
}
......@@ -111,4 +111,7 @@ void max30001_mutex_init(void);
#define MAX30001_MUTEX_WAIT_MS 50
extern gpio_cfg_t gpio_configs[];
/* ---------- BUTTONS ------------------------------------------------------ */
void vButtonTask(void *pvParameters);
#endif /* MODULES_H */
......@@ -170,9 +170,6 @@ int portexpander_config(const portexpander_cfg_t *cfg)
/* ************************************************************************** */
uint8_t portexpander_in_get(uint8_t mask)
{
// Reading the input port clears interrupts, so we need to check them here to avoid losing information
portexpander_poll();
uint8_t buf = 0xFF;
if (detected) {
......@@ -303,29 +300,35 @@ int portexpander_register_callback(
/* ************************************************************************** */
__attribute__((weak)) void portexpander_interrupt_callback(void *_)
{
portexpander_ack_interrupt();
}
void portexpander_ack_interrupt()
{
GPIO_IntDisable(&pe_int_pin);
GPIO_IntClr(&pe_int_pin);
interrupt_pending = true;
}
/* ************************************************************************** */
void portexpander_poll()
void portexpander_get_pending_interrupts(uint8_t *pending, uint8_t *levels)
{
if (detected && interrupt_pending) {
if (detected) {
interrupt_pending = false;
uint8_t caused_by = portexpander_int_status();
*pending = portexpander_int_status();
// Port read resets interrupts
uint8_t port_levels = portexpander_in_get(0xFF);
*levels = portexpander_in_get(0xFF);
GPIO_IntEnable(&pe_int_pin);
}
}
void portexpander_handle_pending_interrupts(uint8_t *pending, uint8_t *levels)
{
for (uint8_t pin = 0; pin < 8; ++pin) {
if ((caused_by & (1 << pin)) && callbacks[pin]) {
if ((*pending & (1 << pin)) && callbacks[pin]) {
gpio_int_pol_t edge_type =
(port_levels & (1 << pin) ?
GPIO_INT_RISING :
(*levels & (1 << pin) ? GPIO_INT_RISING :
GPIO_INT_FALLING);
if ((int_edge_config[pin] == GPIO_INT_BOTH) ||
(edge_type == int_edge_config[pin])) {
......@@ -334,4 +337,15 @@ void portexpander_poll()
}
}
}
/* ************************************************************************** */
void portexpander_poll()
{
if (detected && interrupt_pending) {
uint8_t pending;
uint8_t levels;
portexpander_get_pending_interrupts(&pending, &levels);
portexpander_handle_pending_interrupts(&pending, &levels);
}
}
......@@ -37,8 +37,11 @@ void portexpander_int_disable(uint8_t mask);
uint8_t portexpander_int_status();
void portexpander_int_clr(uint8_t mask);
int portexpander_register_callback(uint8_t mask, pe_callback callback, void *cbdata);
void portexpander_get_pending_interrupts(uint8_t *pending, uint8_t *levels);
void portexpander_handle_pending_interrupts(uint8_t *pending, uint8_t *levels);
void portexpander_poll();
void portexpander_interrupt_callback(void *_);
void portexpander_ack_interrupt();
#endif
......@@ -2,8 +2,50 @@
#include "py/objlist.h"
#include "py/runtime.h"
#include <stdio.h>
#include <string.h>
#include "epicardium.h"
#include "interrupt.h"
#include "api/caller.h"
// This should really be defined somewhere in a central location to make it reusable (+ use the better linux kernel version)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/* clang-format off */
static const uint8_t button_callback_mapping[] = {
BUTTON_LEFT_BOTTOM,
BUTTON_RIGHT_BOTTOM,
BUTTON_RIGHT_TOP,
};
/* clang-format on */
static mp_obj_t button_callbacks[] = {
[0 ... ARRAY_SIZE(button_callback_mapping) - 1] = mp_const_none
};
void epic_isr_button()
{
// TODO: Proper parameter passing
struct epic_isr_button_param p;
memcpy(&p, API_CALL_MEM->buffer + 0x20, sizeof(p));
mp_obj_t callback = mp_const_none;
for (uint8_t i = 0; i < ARRAY_SIZE(button_callback_mapping); ++i) {
if (p.pb == button_callback_mapping[i]) {
callback = button_callbacks[i];
break;
}
}
if (callback != mp_const_none) {
// This may drop some events if the queue is full
mp_sched_schedule(
callback, (p.falling ? mp_const_true : mp_const_false)
);
}
}
static mp_obj_t mp_buttons_read(mp_obj_t mask_in)
{
......@@ -13,9 +55,51 @@ static mp_obj_t mp_buttons_read(mp_obj_t mask_in)
}
static MP_DEFINE_CONST_FUN_OBJ_1(buttons_read_obj, mp_buttons_read);
static mp_obj_t mp_buttons_set_callback(mp_obj_t mask_in, mp_obj_t callback)
{
uint8_t mask = mp_obj_get_int(mask_in);
uint8_t param_check_mask = mask;
bool enable_interrupt = false;
for (uint8_t i = 0; i < ARRAY_SIZE(button_callback_mapping); ++i) {
param_check_mask &= ~button_callback_mapping[i];
}
if (param_check_mask) {
mp_raise_ValueError("Callbacks not supported for given button");
}
for (uint8_t i = 0; i < ARRAY_SIZE(button_callback_mapping); ++i) {
if (button_callback_mapping[i] & mask) {
button_callbacks[i] = callback;
}
}
for (uint8_t i = 0; i < ARRAY_SIZE(button_callbacks); ++i) {
if (button_callbacks[i] != mp_const_none) {
enable_interrupt = true;
}
}
mp_obj_t irq_id = MP_OBJ_NEW_SMALL_INT(EPIC_INT_BUTTON);
if (enable_interrupt) {
mp_interrupt_enable_callback(irq_id);
} else {
mp_interrupt_disable_callback(irq_id);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(
buttons_set_callback_obj, mp_buttons_set_callback
);
static const mp_rom_map_elem_t buttons_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_buttons) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&buttons_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_callback),
MP_ROM_PTR(&buttons_set_callback_obj) },
{ MP_ROM_QSTR(MP_QSTR_BOTTOM_LEFT),
MP_OBJ_NEW_SMALL_INT(BUTTON_LEFT_BOTTOM) },
{ MP_ROM_QSTR(MP_QSTR_BOTTOM_RIGHT),
......
......@@ -7,17 +7,12 @@
#include "py/obj.h"
#include "py/runtime.h"
// TODO: these should be intialized as mp_const_none
mp_obj_t callbacks[EPIC_INT_NUM] = {
0,
};
mp_obj_t callbacks[EPIC_INT_NUM] = { [0 ... EPIC_INT_NUM - 1] = mp_const_none };
void epic_isr_default_handler(api_int_id_t id)
{
// TODO: check if id is out of rante
// TOOD: check against mp_const_none
if (id < EPIC_INT_NUM) {
if (callbacks[id]) {
if (callbacks[id] && (callbacks[id] != mp_const_none)) {
mp_sched_schedule(
callbacks[id], MP_OBJ_NEW_SMALL_INT(id)
);
......@@ -93,6 +88,7 @@ static const mp_rom_map_elem_t interrupt_module_globals_table[] = {
MP_OBJ_NEW_SMALL_INT(EPIC_INT_BHI160_GYROSCOPE) },
{ MP_ROM_QSTR(MP_QSTR_MAX30001_ECG),
MP_OBJ_NEW_SMALL_INT(EPIC_INT_MAX30001_ECG) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON), MP_OBJ_NEW_SMALL_INT(EPIC_INT_BUTTON) },
};
static MP_DEFINE_CONST_DICT(
......
......@@ -29,6 +29,7 @@ Q(TOP_RIGHT)
/* buttons */
Q(buttons)
Q(read)
Q(set_callback)
Q(BOTTOM_LEFT)
Q(TOP_LEFT)
Q(BOTTOM_RIGHT)
......@@ -57,6 +58,7 @@ Q(set_unix_time)
Q(vibra)
Q(vibrate)
/* interrupt */
Q(set_callback)
Q(enable_callback)
Q(disable_callback)
......@@ -64,6 +66,7 @@ Q(BHI160_ACCELEROMETER)
Q(BHI160_ORIENTATION)
Q(BHI160_GYROSCOPE)
Q(RTC_ALARM)
Q(BUTTON)
/* bhi160 */
Q(sys_bhi160)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment