Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • add_menu_vibration
  • blinkisync-as-preload
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • ch3/splashscreen
  • dualcore
  • dx/flatten-config-module
  • dx/meh-bdf-to-stm
  • freertos-btle
  • genofire/ble-follow-py
  • koalo/bhi160-works-but-dirty
  • koalo/factory-reset
  • koalo/wip/i2c-for-python
  • master
  • msgctl/faultscreen
  • msgctl/textbuffer_api
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bluetooth-app-favorite
  • rahix/bma
  • rahix/user-space-ctx
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/212-reset-hardware-when-entering-repl
  • schneider/ancs
  • schneider/ble-buffers
  • schneider/ble-central
  • schneider/ble-ecg-stream-visu
  • schneider/ble-fixes-2020-3
  • schneider/ble-mini-demo
  • schneider/ble-stability
  • schneider/ble-stability-new-phy
  • schneider/bonding
  • schneider/bonding-fail-if-full
  • schneider/bootloader-update-9a0d158
  • schneider/deepsleep
  • schneider/deepsleep2
  • schneider/deepsleep4
  • schneider/default-main
  • schneider/freertos-list-debug
  • schneider/fundamental-test
  • schneider/iaq-python
  • schneider/ir
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-exception-print
  • schneider/mp-for-old-bl
  • schneider/png
  • schneider/schleicher-test
  • schneider/sdk-0.2.1-11
  • schneider/sdk-0.2.1-7
  • schneider/sleep-display
  • schneider/spo2-playground
  • schneider/stream-locks
  • schneider/v1.17-changelog
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.10
  • v1.11
  • v1.12
  • v1.13
  • v1.14
  • v1.15
  • v1.16
  • v1.17
  • v1.18
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
82 results

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Select Git revision
  • dualcore
  • fix-nix
  • master
  • rahix/freertos10
  • rahix/new-build
  • unjailbreak
6 results
Show changes
Showing
with 3203 additions and 492 deletions
#include "epicardium.h"
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "user_core/interrupts.h"
#include "max32665.h"
#include "usb/cdcacm.h"
#include "uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stream_buffer.h"
#include <stdint.h>
#include <stdio.h>
/* The serial console in use (UART0) */
extern mxc_uart_regs_t *ConsoleUart;
/* Task ID for the serial handler */
TaskHandle_t serial_task_id = NULL;
/* Read queue, filled by both UART and CDCACM */
static QueueHandle_t read_queue;
/* Stream Buffer for handling all writes to serial */
static StreamBufferHandle_t write_stream_buffer = NULL;
void serial_init()
{
/* Setup read queue */
static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE];
static StaticQueue_t read_queue_data;
read_queue = xQueueCreateStatic(
SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data
);
/* Setup write queue */
static uint8_t ucWrite_stream_buffer[SERIAL_WRITE_STREAM_BUFFER_SIZE];
static StaticStreamBuffer_t xStream_buffer_struct;
write_stream_buffer = xStreamBufferCreateStatic(
sizeof(ucWrite_stream_buffer),
1,
ucWrite_stream_buffer,
&xStream_buffer_struct
);
}
void serial_return_to_synchronous()
{
write_stream_buffer = NULL;
}
static void write_str_(const char *str, size_t length)
{
if (length == 0) {
return;
}
/*
* Check if the stream buffer is even initialized yet
*/
if (write_stream_buffer == NULL) {
UART_Write(ConsoleUart, (uint8_t *)str, length);
cdcacm_write((uint8_t *)str, length);
return;
}
if (xPortIsInsideInterrupt()) {
BaseType_t resched1 = pdFALSE;
BaseType_t resched2 = pdFALSE;
/*
* Enter a critial section so no other task can write to the
* stream buffer.
*/
uint32_t basepri = __get_BASEPRI();
taskENTER_CRITICAL_FROM_ISR();
xStreamBufferSendFromISR(
write_stream_buffer, str, length, &resched1
);
taskEXIT_CRITICAL_FROM_ISR(basepri);
if (serial_task_id != NULL) {
xTaskNotifyFromISR(
serial_task_id,
SERIAL_WRITE_NOTIFY,
eSetBits,
&resched2
);
}
/* Yield if this write woke up a higher priority task */
portYIELD_FROM_ISR(resched1 || resched2);
} else {
size_t bytes_sent = 0;
size_t index = 0;
do {
taskENTER_CRITICAL();
/*
* Wait time needs to be zero, because we are in a
* critical section.
*/
bytes_sent = xStreamBufferSend(
write_stream_buffer,
&str[index],
length - index,
0
);
index += bytes_sent;
taskEXIT_CRITICAL();
if (serial_task_id != NULL) {
xTaskNotify(
serial_task_id,
SERIAL_WRITE_NOTIFY,
eSetBits
);
if (bytes_sent == 0) {
vTaskDelay(1);
} else {
portYIELD();
}
}
} while (index < length);
}
}
/*
* API-call to write a string. Output goes to CDCACM, UART and BLE
*
* This is user data from core 1.
*/
void epic_uart_write_str(const char *str, size_t length)
{
/* Make sure that we are not in an interrupt when talking to BLE.
* Should not be the case if this is called from core 1
* anyways. */
if (!xPortIsInsideInterrupt()) {
ble_uart_write((uint8_t *)str, length);
}
write_str_(str, length);
}
static void serial_flush_from_isr(void)
{
uint8_t rx_data[32];
size_t received_bytes;
BaseType_t resched = pdFALSE;
BaseType_t woken = pdFALSE;
uint32_t basepri = __get_BASEPRI();
taskENTER_CRITICAL_FROM_ISR();
do {
received_bytes = xStreamBufferReceiveFromISR(
write_stream_buffer,
(void *)rx_data,
sizeof(rx_data),
&woken
);
resched |= woken;
if (received_bytes == 0) {
break;
}
/*
* The SDK-driver for UART is not reentrant
* which means we need to perform UART writes
* in a critical section.
*/
UART_Write(ConsoleUart, (uint8_t *)&rx_data, received_bytes);
} while (received_bytes > 0);
taskEXIT_CRITICAL_FROM_ISR(basepri);
portYIELD_FROM_ISR(resched);
}
static void serial_flush_from_thread(void)
{
uint8_t rx_data[32];
size_t received_bytes;
do {
taskENTER_CRITICAL();
received_bytes = xStreamBufferReceive(
write_stream_buffer,
(void *)rx_data,
sizeof(rx_data),
0
);
taskEXIT_CRITICAL();
if (received_bytes == 0) {
break;
}
/*
* The SDK-driver for UART is not reentrant
* which means we need to perform UART writes
* in a critical section.
*/
taskENTER_CRITICAL();
UART_Write(ConsoleUart, (uint8_t *)&rx_data, received_bytes);
taskEXIT_CRITICAL();
cdcacm_write((uint8_t *)&rx_data, received_bytes);
} while (received_bytes > 0);
}
/*
* Flush all characters which are currently waiting to be printed.
*
* If this function is called from an ISR, it will only flush to hardware UART
* while a call from thread mode will flush to UART, CDC-ACM, and BLE Serial.
*/
void serial_flush(void)
{
if (xPortIsInsideInterrupt()) {
serial_flush_from_isr();
} else {
serial_flush_from_thread();
}
}
/*
* API-call to read a character from the queue.
*/
int epic_uart_read_char(void)
{
char chr;
if (xQueueReceive(read_queue, &chr, 0) == pdTRUE) {
return (int)chr;
}
return (-1);
}
/*
* API-call to read data from the queue.
*/
int epic_uart_read_str(char *buf, size_t cnt)
{
size_t i = 0;
for (i = 0; i < cnt; i++) {
if (xQueueReceive(read_queue, &buf[i], 0) != pdTRUE) {
break;
}
}
return i;
}
/*
* Write a string from epicardium. Output goes to CDCACM and UART
*
* This mainly log data from epicardium, not user date from core 1.
*/
long _write_epicardium(int fd, const char *buf, size_t cnt)
{
/*
* Only print one line at a time. Insert `\r` between lines so they are
* properly displayed on the serial console.
*/
size_t i, last = 0;
for (i = 0; i < cnt; i++) {
if (buf[i] == '\n') {
write_str_(&buf[last], i - last);
write_str_("\r", 1);
last = i;
}
}
write_str_(&buf[last], cnt - last);
return cnt;
}
/* Interrupt handler needed for SDK UART implementation */
void UART0_IRQHandler(void)
{
UART_Handler(ConsoleUart);
}
static void uart_callback(uart_req_t *req, int error)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(
serial_task_id,
SERIAL_READ_NOTIFY,
eSetBits,
&xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void serial_enqueue_char(char chr)
{
if (chr == 0x3) {
/* Control-C */
interrupt_trigger(EPIC_INT_CTRL_C);
}
if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) {
/* Queue overran, wait a bit */
vTaskDelay(portTICK_PERIOD_MS * 50);
}
interrupt_trigger(EPIC_INT_UART_RX);
}
void vSerialTask(void *pvParameters)
{
serial_task_id = xTaskGetCurrentTaskHandle();
/* Setup UART interrupt */
NVIC_ClearPendingIRQ(UART0_IRQn);
NVIC_DisableIRQ(UART0_IRQn);
NVIC_SetPriority(UART0_IRQn, 6);
NVIC_EnableIRQ(UART0_IRQn);
unsigned char data;
uart_req_t read_req = {
.data = &data,
.len = 1,
.callback = uart_callback,
};
while (1) {
int ret = UART_ReadAsync(ConsoleUart, &read_req);
if (ret != E_NO_ERROR && ret != E_BUSY) {
LOG_ERR("serial", "error reading uart: %d", ret);
vTaskDelay(portMAX_DELAY);
}
ret = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (ret & SERIAL_WRITE_NOTIFY) {
serial_flush_from_thread();
}
if (ret & SERIAL_READ_NOTIFY) {
if (read_req.num > 0) {
serial_enqueue_char(*read_req.data);
}
while (UART_NumReadAvail(ConsoleUart) > 0) {
serial_enqueue_char(UART_ReadByte(ConsoleUart));
}
while (cdcacm_num_read_avail() > 0) {
serial_enqueue_char(cdcacm_read());
}
}
}
}
#include "epicardium.h"
#include "modules/modules.h"
#include "os/core.h"
#include "card10.h"
#include "simo.h"
#include "lp.h"
#include "max86150.h"
#include "MAX77650-Arduino-Library.h"
#include "bb_drv.h"
#include "max32665.h"
#include "mxc_sys.h"
#include "mxc_pins.h"
#include <stdint.h>
#include <limits.h>
/* Most code is taken and adapted rom EvKitExamples/LP/main.c */
static uint32_t old_clkcn;
static uint32_t old_perckcn0, old_perckcn1;
void GPIOWAKE_IRQHandler(void)
{
/* Nothing to do here */
}
static void switchToHIRC(void)
{
MXC_GCR->clkcn &= ~(MXC_S_GCR_CLKCN_PSC_DIV128);
MXC_GCR->clkcn |= MXC_S_GCR_CLKCN_PSC_DIV4;
MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC_EN;
MXC_SETFIELD(
MXC_GCR->clkcn,
MXC_F_GCR_CLKCN_CLKSEL,
MXC_S_GCR_CLKCN_CLKSEL_HIRC
);
while (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CKRDY))
; /* Wait for the clock switch to occur */
/* Disable the now unused 96 MHz clock */
MXC_GCR->clkcn &= ~(MXC_F_GCR_CLKCN_HIRC96M_EN);
SystemCoreClockUpdate();
}
static void deepsleep(void)
{
SIMO_setVregO_B(810); /* Reduce VCOREB to 0.81 V */
LP_FastWakeupEnable();
LP_EnterDeepSleepMode();
SIMO_setVregO_B(1000); /* Restore VCOREB to 1 V */
}
static void turnOffClocks(void)
{
old_perckcn0 = MXC_GCR->perckcn0;
old_perckcn1 = MXC_GCR->perckcn1;
/* Allow the USB Switch to be turned off in deepsleep and backup modes. */
/* TODO: should this be the default setting? */
LP_USBSWLPDisable();
/* Disable all peripheral clocks except GPIO0 (needed for interrupt wakeup) */
MXC_GCR->perckcn0 = 0xFFFFFFFE;
MXC_GCR->perckcn1 = 0xFFFFFFFF;
}
static void turnOnClocks(void)
{
MXC_GCR->perckcn0 = old_perckcn0;
MXC_GCR->perckcn1 = old_perckcn1;
}
/*
* Move most GPIOs into a special low power state with the fact
* in mind that the external 3.3 V are switched off.
*
* E.g. this means that the SD card pins need to be pulled low
* to preven them from backfeeding into the 3.3 V rail via their
* external pull-up resistors.
*
* Pins needed to talk to the PMIC are left untouched.
* ECG AOUT and 32 kHz out as well.
*/
static void gpio_low_power(void)
{
/* clang-format off */
const gpio_cfg_t pins_low_power[] = {
{ PORT_0, PIN_0, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // FLash
{ PORT_0, PIN_1, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // Flash
{ PORT_0, PIN_2, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // Flash
{ PORT_0, PIN_3, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // Flash
{ PORT_0, PIN_4, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // Flash
{ PORT_0, PIN_5, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // Flash
{ PORT_0, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // I2C 3.3V
{ PORT_0, PIN_7, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // I2C 3.3V
{ PORT_0, PIN_8, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // Motor
{ PORT_0, PIN_9, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // UART TX
{ PORT_0, PIN_10, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // UART RX
{ PORT_0, PIN_11, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // BMA400 interrupt
// 0, 12: PMIC IRQ
{ PORT_0, PIN_13, GPIO_FUNC_IN, GPIO_PAD_NONE }, // BHI160 interrupt, not sure if PP
// 0, 14: PMIC I2C
// 0, 15: PMIC I2C
{ PORT_0, PIN_16, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // PMIC AMUX
{ PORT_0, PIN_17, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // SOA GPIO
// 0, 18: ECG AOUT
// 0, 19: 32 kHz
{ PORT_0, PIN_20, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // Wristband 1
{ PORT_0, PIN_21, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // Wristband 2
{ PORT_0, PIN_22, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // Wristband 3
{ PORT_0, PIN_23, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // IR-LED
{ PORT_0, PIN_24, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // display SS
{ PORT_0, PIN_25, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // display MOSI
{ PORT_0, PIN_26, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // SOA GPIO
{ PORT_0, PIN_27, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // display SCK
{ PORT_0, PIN_28, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // Backlight
{ PORT_0, PIN_29, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN }, // Wristband 4
// 0, 30: PMIC power hold
{ PORT_0, PIN_31, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // ECG switch
{ PORT_1, PIN_0, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // SDHC
{ PORT_1, PIN_1, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // SDHC
{ PORT_1, PIN_2, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // SDHC
{ PORT_1, PIN_3, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // SDHC
{ PORT_1, PIN_4, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // SDHC
{ PORT_1, PIN_5, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // SDHC
{ PORT_1, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // display RS
{ PORT_1, PIN_7, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // Portexpander interrupt
{ PORT_1, PIN_8, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG CS TODO: better OUT high
{ PORT_1, PIN_9, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // ECG SDI
{ PORT_1, PIN_10, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG SDO
{ PORT_1, PIN_11, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG SCK
{ PORT_1, PIN_11, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG INT
{ PORT_1, PIN_13, GPIO_FUNC_IN, GPIO_PAD_NONE }, // PPG Interrupt
{ PORT_1, PIN_14, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // RGB LEDs
{ PORT_1, PIN_15, GPIO_FUNC_OUT, GPIO_PAD_NONE }, // RGB LEDs
};
/* clang-format on */
const unsigned int num_pins =
(sizeof(pins_low_power) / sizeof(gpio_cfg_t));
for (size_t i = 0; i < num_pins; i++) {
GPIO_OutClr(&pins_low_power[i]);
GPIO_Config(&pins_low_power[i]);
}
}
/*
* Go to deepsleep, turning off peripherals.
*
* This functions returns after waking up again.
* Currently the only wakeup source is an interrupt
* from the PMIC.
*
* Some peripherals and GPIOs are moved into a low
* power state before going to sleep. There is no guarantee
* that all peripherals will be moved to a low power state
*
* TODO: Move BHI160, BMA400, BME680, MAX30001 into a low
* power state.
*
* The state of the GPIOs and generally all peripherials
* on board is not restored after a wakeup.
*/
void sleep_deepsleep(void)
{
LOG_WARN("pmic", "Powersave");
epic_disp_backlight(0);
epic_leds_set_rocket(0, 0);
epic_leds_set_rocket(1, 0);
epic_leds_set_rocket(2, 0);
#if 1
/* This will fail if there is no
* harmonic board attached */
max86150_begin();
max86150_get_int1();
max86150_get_int2();
max86150_shut_down();
#endif
epic_bhi160_disable_all_sensors();
epic_bme680_deinit();
epic_max30001_disable_sensor();
MAX77650_setEN_SBB2(0b100);
MAX77650_setSBIA_LPM(true);
core1_stop();
MAX77650_getINT_GLBL();
gpio_low_power();
if (ble_is_enabled()) {
BbDrvDisable();
}
turnOffClocks();
SYS_ClockSourceEnable(SYS_CLOCK_HIRC);
old_clkcn = MXC_GCR->clkcn;
switchToHIRC();
deepsleep();
/* Now wait for an interrupt to wake us up */
turnOnClocks();
MXC_GCR->clkcn = old_clkcn;
while (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CKRDY))
; /* Wait for the clock switch to occur */
SystemCoreClockUpdate();
MAX77650_setEN_SBB2(0b110);
}
int epic_sleep(uint32_t ms)
{
/* Allow the interrupt module to break us out of a call to
* epic_sleep() */
uint32_t count = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(ms));
if (count == 0) {
return 0;
} else {
return INT_MAX;
}
}
/**
*
* Implementation of public epic_usb_ interface
*
* Configuration and FLASH-specific implementation of USB mass storage device
* Configuration of USB CDCACM device
*
* USB descriptors for both are defined here.
*
*/
#include "epicardium.h"
#include "fs/filesystem.h"
#include "os/config.h"
#include "usb/cdcacm.h"
#include "usb/mass_storage.h"
#include "usb/descriptors.h"
#include "usb/epc_usb.h"
#include "os/core.h"
#include "mx25lba.h"
#include "msc.h"
#include "mxc_sys.h"
#include "wdt.h"
/* memory access callbacks for the mass storage (FLASH) device */
static int mscmem_init();
static uint32_t mscmem_size(void);
static int mscmem_read(uint32_t lba, uint8_t *buffer);
static int mscmem_write(uint32_t lba, uint8_t *buffer);
static int mscmem_start();
static int mscmem_stop();
static int mscmem_ready();
/* USB descriptors, definition at the end of this file */
static usb_device_descriptor_t device_descriptor_cdcacm
__attribute__((aligned(4)));
static struct config_descriptor_cdcacm config_descriptor_cdcacm
__attribute__((aligned(4)));
static usb_device_descriptor_t device_descriptor_msc
__attribute__((aligned(4)));
static struct config_descriptor_msc config_descriptor_msc
__attribute__((aligned(4)));
/* definitions of config structs */
static const msc_mem_t s_mscCallbacks = {
mscmem_init, mscmem_start, mscmem_stop, mscmem_ready,
mscmem_size, mscmem_read, mscmem_write,
};
static struct esb_device_descriptors s_dd_cdcacm = {
.device = &device_descriptor_cdcacm, .cdcacm = &config_descriptor_cdcacm
};
static struct esb_device_descriptors s_dd_msc = {
.device = &device_descriptor_msc, .msc = &config_descriptor_msc
};
static struct esb_config s_cfg_cdcacm = {
.name = "cdcacm",
.init = esb_cdc_init,
.configure = esb_cdc_configure,
.deconfigure = esb_cdc_deconfigure,
.deinit = NULL,
.descriptors = &s_dd_cdcacm,
.deviceData = NULL,
};
static struct esb_config s_cfg_msc = {
.name = "msc FLASH",
.init = esb_msc_init,
.configure = esb_msc_configure,
.deconfigure = esb_msc_deconfigure,
.deinit = NULL,
.descriptors = &s_dd_msc,
.deviceData = (void *)&s_mscCallbacks,
};
/* function implementations */
static int dirty = 0;
static int mscmem_init()
{
LOG_DEBUG("msc", "mem.init");
fatfs_detach();
return mx25_init();
}
static uint32_t mscmem_size(void)
{
return mx25_size();
}
static int mscmem_read(uint32_t lba, uint8_t *buffer)
{
/* Reset the watchdog as this interrupt might be
* firing back to back for a few seconds. */
WDT_ResetTimer(MXC_WDT0);
return mx25_read(lba, buffer);
}
static int mscmem_write(uint32_t lba, uint8_t *buffer)
{
if (dirty == 0) {
//bootloader_dirty();
}
dirty = 2;
/* Reset the watchdog as this interrupt might be
* firing back to back for a few seconds. */
WDT_ResetTimer(MXC_WDT0);
return mx25_write(lba, buffer);
}
static int mscmem_start()
{
return mx25_start();
}
static int mscmem_stop()
{
LOG_DEBUG("msc", "mem.stop");
int ret = mx25_stop();
fatfs_schedule_attach();
return ret;
}
static int mscmem_ready()
{
if (dirty) {
dirty--;
if (dirty == 0) {
LOG_DEBUG("msc", "sync");
mx25_sync();
//bootloader_clean();
}
}
return mx25_ready();
}
static bool s_fsDetached = false;
int epic_usb_shutdown(void)
{
esb_deinit();
if (s_fsDetached) {
fatfs_attach();
load_config();
}
return 0;
}
int epic_usb_storage(void)
{
esb_deinit();
s_fsDetached = true;
return esb_init(&s_cfg_msc);
}
int epic_usb_cdcacm(void)
{
esb_deinit();
if (s_fsDetached) {
fatfs_attach();
load_config();
}
return esb_init(&s_cfg_cdcacm);
}
/* clang-format off */
static usb_device_descriptor_t device_descriptor_cdcacm = {
.bLength = 0x12,
.bDescriptorType = DT_DEVICE,
.bcdUSB = 0x0110,
.bDeviceClass = CLS_COMM,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize = 0x40,
.idVendor = VNDR_MAXIM,
.idProduct = 0x003C,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x00,
.bNumConfigurations = 0x01
};
static struct config_descriptor_cdcacm config_descriptor_cdcacm = {
.config = {
.bLength = 0x09,
.bDescriptorType = DT_CONFIG,
.wTotalLength = 0x0043, /* sizeof(struct config_descriptor_cdcacm) */
.bNumInterfaces = 0x02,
.bConfigurationValue= 0x01,
.iConfiguration = 0x00,
.bmAttributes = 0xE0, /* (self-powered, remote wakeup) */
.bMaxPower = 0x01, /* MaxPower is 2ma (units are 2ma/bit) */
},
.comm_interface = { /* First Interface Descriptor For Comm Class Interface */
.bLength = 0x09,
.bDescriptorType = DT_INTERFACE,
.bInterfaceNumber = 0x00,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x01,
.bInterfaceClass = CLS_COMM,
.bInterfaceSubClass = SCLS_ACM,
.bInterfaceProtocol = 0x00,
.iInterface = 0x00,
},
.header_functional = {
0x05, /* bFunctionalLength = 5 */
DT_FUNCTIONAL,/* bDescriptorType */
0x00, /* bDescriptorSubtype */
0x10, 0x01, /* bcdCDC */
},
.call_management = {
0x05, /* bFunctionalLength = 5 */
DT_FUNCTIONAL,/* bDescriptorType */
0x01, /* bDescriptorSubtype */
0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */
0x01, /* bmDataInterface */
},
.acm_functional = {
0x04, /* bFunctionalLength = 4 */
DT_FUNCTIONAL,/* bDescriptorType */
0x02, /* bDescriptorSubtype */
0x02, /* bmCapabilities */
},
.union_functional = {
0x05, /* bFunctionalLength = 5 */
DT_FUNCTIONAL,/* bDescriptorType */
0x06, /* bDescriptorSubtype */
0x00, /* bmMasterInterface */
0x01, /* bmSlaveInterface0 */
},
.endpoint_notify = {
.bLength = 0x07,
.bDescriptorType = DT_ENDPOINT,
.bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_NOTIFY,
.bmAttributes = ATTR_INTERRUPT,
.wMaxPacketSize = 0x0040,
.bInterval = 0xff,
},
.data_interface = {
.bLength = 0x09,
.bDescriptorType = DT_INTERFACE,
.bInterfaceNumber = 0x01,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x02,
.bInterfaceClass = CLS_DATA,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.iInterface = 0x00, //not 1?
},
.endpoint_out = {
.bLength = 0x07,
.bDescriptorType = DT_ENDPOINT,
.bEndpointAddress = ESB_ENDPOINT_CDCACM_OUT,
.bmAttributes = ATTR_BULK,
.wMaxPacketSize = 0x0040,
.bInterval = 0x00,
},
.endpoint_in = {
.bLength = 0x07,
.bDescriptorType = DT_ENDPOINT,
.bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_IN,
.bmAttributes = ATTR_BULK,
.wMaxPacketSize = 0x0040,
.bInterval = 0x00
}
};
static usb_device_descriptor_t device_descriptor_msc = {
.bLength = 0x12,
.bDescriptorType = DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = CLS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize = 0x40,
.idVendor = VNDR_MAXIM,
.idProduct = 0x4402,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
static struct config_descriptor_msc config_descriptor_msc =
{
.config = {
.bLength = 0x09,
.bDescriptorType = DT_CONFIG,
.wTotalLength = 0x0020,
.bNumInterfaces = 0x01,
.bConfigurationValue= 0x01,
.iConfiguration = 0x00,
.bmAttributes = 0xC0, /* (self-powered, no remote wakeup) */
.bMaxPower = 0x32,
},
.msc_interface = {
.bLength = 0x09,
.bDescriptorType = DT_INTERFACE,
.bInterfaceNumber = 0x00,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x02,
.bInterfaceClass = CLS_MASS_STOR,
.bInterfaceSubClass = SCLS_SCSI_CMDS,
.bInterfaceProtocol = PROT_BULK_TRANS,
.iInterface = 0x00,
},
.endpoint_out = {
.bLength = 0x07,
.bDescriptorType = DT_ENDPOINT,
.bEndpointAddress = ESB_ENDPOINT_MSC_OUT,
.bmAttributes = ATTR_BULK,
.wMaxPacketSize = 0x0040,
.bInterval = 0x00,
},
.endpoint_in = {
.bLength = 0x07,
.bDescriptorType = DT_ENDPOINT,
.bEndpointAddress = 0x80 | ESB_ENDPOINT_MSC_IN,
.bmAttributes = ATTR_BULK,
.wMaxPacketSize = 0x0040,
.bInterval = 0x00
}
};
/* clang-format on */
#include "gpio.h"
#include "FreeRTOS.h"
#include "timers.h"
static const gpio_cfg_t motor_pin = {
PORT_0, PIN_8, GPIO_FUNC_OUT, GPIO_PAD_NONE
};
static StaticTimer_t vibra_timer_data;
static TimerHandle_t vibra_timer = NULL;
void epic_vibra_set(int status)
{
if (status) {
GPIO_OutSet(&motor_pin);
} else {
GPIO_OutClr(&motor_pin);
}
}
void vTimerCallback()
{
epic_vibra_set(0);
}
void epic_vibra_vibrate(int millis)
{
int ticks = millis * (configTICK_RATE_HZ / 1000);
if (vibra_timer == NULL) {
vibra_timer = xTimerCreateStatic(
"vibratimer",
1,
pdFALSE, /* one-shot */
0,
vTimerCallback,
&vibra_timer_data);
}
/* Make sure the duration is valid */
if (ticks < 1) {
/* Disable a potentially running motor / timer */
epic_vibra_set(0);
xTimerStop(vibra_timer, 0);
return;
}
if (vibra_timer != NULL) {
epic_vibra_set(1);
xTimerChangePeriod(vibra_timer, ticks, 0);
}
}
#include "os/core.h"
#include "modules/modules.h"
#include "timers.h"
#include "mxc_sys.h"
#include "wdt.h"
static TimerHandle_t clearer_timer;
static StaticTimer_t clearer_timer_buffer;
#define CLEAR_PERIOD pdMS_TO_TICKS(2000)
static void watchdog_clearer_callback()
{
WDT_ResetTimer(MXC_WDT0);
}
void watchdog_init()
{
/*
* Don't enable the the watchdog when a debugger is connected.
*/
if ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) != 0) {
return;
}
sys_cfg_wdt_t wdt_cfg = NULL;
WDT_Init(MXC_WDT0, wdt_cfg);
if (WDT_GetResetFlag(MXC_WDT0)) {
WDT_ClearResetFlag(MXC_WDT0);
LOG_INFO("watchdog", "Last reset was due to watchdog timeout");
}
WDT_Enable(MXC_WDT0, 1);
WDT_SetResetPeriod(
MXC_WDT0,
WDT_PERIOD_2_28); /* Clocked by PCLK at 50MHz, reset at 2^28 ticks = 5.4 seconds */
WDT_EnableReset(MXC_WDT0, 1);
}
void watchdog_clearer_init()
{
WDT_ResetTimer(MXC_WDT0);
clearer_timer = xTimerCreateStatic(
"watchdog_clearer_timer",
CLEAR_PERIOD,
pdTRUE,
NULL,
watchdog_clearer_callback,
&clearer_timer_buffer
);
xTimerStart(clearer_timer, 0);
}
#include "leds.h"
#include "pmic.h"
#include "FreeRTOS.h"
#include "task.h"
#include "epicardium.h"
#include "max32665.h"
#include "gpio.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include <stdbool.h>
#define OVERHEAD 33
#define EPIC_WS2812_ZERO_HIGH_TICKS (34 - OVERHEAD)
#define EPIC_WS2812_ZERO_LOW_TICKS (86 - OVERHEAD)
#define EPIC_WS2812_ONE_HIGH_TICKS (86 - OVERHEAD)
#define EPIC_WS2812_ONE_LOW_TICKS (34 - OVERHEAD)
#define EPIC_WS2812_RESET_TCKS (4800 - OVERHEAD)
static volatile uint32_t counter = 0;
static inline __attribute__((always_inline)) void
epic_ws2812_delay_ticks(uint32_t ticks)
{
counter = ticks;
while (--counter) {
};
}
static inline __attribute__((always_inline)) void
epic_ws2812_transmit_bit(gpio_cfg_t *pin, uint8_t bit)
{
if (bit != 0) {
GPIO_OutSet(pin);
epic_ws2812_delay_ticks(EPIC_WS2812_ONE_HIGH_TICKS);
GPIO_OutClr(pin);
epic_ws2812_delay_ticks(EPIC_WS2812_ONE_LOW_TICKS);
} else {
GPIO_OutSet(pin);
epic_ws2812_delay_ticks(EPIC_WS2812_ZERO_HIGH_TICKS);
GPIO_OutClr(pin);
epic_ws2812_delay_ticks(EPIC_WS2812_ZERO_LOW_TICKS);
}
}
static inline __attribute__((always_inline)) void
epic_ws2812_transmit_byte(gpio_cfg_t *pin, uint8_t byte)
{
epic_ws2812_transmit_bit(pin, byte & 0b10000000);
epic_ws2812_transmit_bit(pin, byte & 0b01000000);
epic_ws2812_transmit_bit(pin, byte & 0b00100000);
epic_ws2812_transmit_bit(pin, byte & 0b00010000);
epic_ws2812_transmit_bit(pin, byte & 0b00001000);
epic_ws2812_transmit_bit(pin, byte & 0b00000100);
epic_ws2812_transmit_bit(pin, byte & 0b00000010);
epic_ws2812_transmit_bit(pin, byte & 0b00000001);
}
void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes)
{
uint8_t *pixels_end = pixels + n_bytes;
gpio_cfg_t *pin_cfg = &gpio_configs[pin];
taskENTER_CRITICAL();
/*
* If the pin was not previously configured as an output, the
* `epic_gpio_set_pin_mode()` call will pull it down which the first
* neopixel interprets as the color `0x008000`. To fix this, wait a bit
* after mode-setting and then write the new values.
*/
if ((epic_gpio_get_pin_mode(pin) & EPIC_GPIO_MODE_OUT) == 0) {
epic_gpio_set_pin_mode(pin, EPIC_GPIO_MODE_OUT);
}
GPIO_OutClr(pin_cfg);
epic_ws2812_delay_ticks(EPIC_WS2812_RESET_TCKS);
do {
epic_ws2812_transmit_byte(pin_cfg, *pixels);
} while (++pixels != pixels_end);
taskEXIT_CRITICAL();
}
#include <math.h>
static const uint8_t pride_colors[6][3] = {
{ 0xe4, 0x02, 0x02 }, { 0xff, 0x8c, 0x00 }, { 0xff, 0xed, 0x00 },
{ 0x00, 0x80, 0x26 }, { 0x00, 0x4d, 0xff }, { 0x75, 0x06, 0x87 },
};
static void epic_frame(Ctx *epicardium_ctx, float frame)
{
int num_colors = sizeof(pride_colors) / sizeof(pride_colors[0]);
for (int color = 0; color < num_colors; color++) {
ctx_rgba8_stroke(
epicardium_ctx,
pride_colors[color][0],
pride_colors[color][1],
pride_colors[color][2],
0xff
);
ctx_line_width(epicardium_ctx, 10.0);
ctx_line_cap(epicardium_ctx, CTX_CAP_ROUND);
float width =
expf(-pow(frame / 2.0 - 5 + color / 2.0, 2)) * 55.0 +
5.0;
float ypos = color * 10.0 + 40.0 - (num_colors - 1) * 5.0;
ctx_move_to(epicardium_ctx, 80.0 - width, ypos);
ctx_line_to(epicardium_ctx, 80.0 + width, ypos);
ctx_stroke(epicardium_ctx);
}
ctx_save(epicardium_ctx);
ctx_font_size(epicardium_ctx, 20.0f);
ctx_text_baseline(epicardium_ctx, CTX_TEXT_BASELINE_BOTTOM);
ctx_rgba8(epicardium_ctx, 0xff, 0xff, 0xff, 0xff);
ctx_text_align(epicardium_ctx, CTX_TEXT_ALIGN_CENTER);
ctx_move_to(epicardium_ctx, 80.0f, 58.0f);
ctx_text(epicardium_ctx, "Epicardium");
if (strcmp(CARD10_VERSION, "v1.18") != 0) {
ctx_text_align(epicardium_ctx, CTX_TEXT_ALIGN_LEFT);
ctx_move_to(epicardium_ctx, 2.0f, 78.0f);
ctx_text(epicardium_ctx, CARD10_VERSION);
} else {
ctx_move_to(epicardium_ctx, 80.0f, 78.0f);
ctx_text(epicardium_ctx, "Queer Quinoa");
}
ctx_restore(epicardium_ctx);
}
...@@ -33,9 +33,11 @@ typedef _Bool bool; ...@@ -33,9 +33,11 @@ typedef _Bool bool;
#define API_SYSTEM_EXEC 0x2 #define API_SYSTEM_EXEC 0x2
#define API_SYSTEM_RESET 0x3 #define API_SYSTEM_RESET 0x3
#define API_BATTERY_VOLTAGE 0x4 #define API_BATTERY_VOLTAGE 0x4
#define API_SLEEP 0x5
#define API_INTERRUPT_ENABLE 0xA #define API_INTERRUPT_ENABLE 0xA
#define API_INTERRUPT_DISABLE 0xB #define API_INTERRUPT_DISABLE 0xB
#define API_INTERRUPT_IS_ENABLED 0xC
#define API_UART_WRITE_STR 0x10 #define API_UART_WRITE_STR 0x10
#define API_UART_READ_CHAR 0x11 #define API_UART_READ_CHAR 0x11
...@@ -53,6 +55,16 @@ typedef _Bool bool; ...@@ -53,6 +55,16 @@ typedef _Bool bool;
#define API_DISP_CIRC 0x27 #define API_DISP_CIRC 0x27
#define API_DISP_PIXEL 0x28 #define API_DISP_PIXEL 0x28
#define API_DISP_FRAMEBUFFER 0x29 #define API_DISP_FRAMEBUFFER 0x29
#define API_DISP_BACKLIGHT 0x2a
#define API_DISP_PRINT_ADV 0x2b
#define API_DISP_BLIT 0x2d
/* API_BATTERY_VOLTAGE 0x30 */
#define API_BATTERY_CURRENT 0x31
#define API_CHARGEIN_VOLTAGE 0x32
#define API_CHARGEIN_CURRENT 0x33
#define API_SYSTEM_VOLTAGE 0x34
#define API_THERMISTOR_VOLTAGE 0x35
#define API_FILE_OPEN 0x40 #define API_FILE_OPEN 0x40
#define API_FILE_CLOSE 0x41 #define API_FILE_CLOSE 0x41
...@@ -67,10 +79,14 @@ typedef _Bool bool; ...@@ -67,10 +79,14 @@ typedef _Bool bool;
#define API_FILE_UNLINK 0x4b #define API_FILE_UNLINK 0x4b
#define API_FILE_RENAME 0x4c #define API_FILE_RENAME 0x4c
#define API_FILE_MKDIR 0x4d #define API_FILE_MKDIR 0x4d
#define API_FILE_FS_ATTACHED 0x4e
#define API_RTC_GET_SECONDS 0x50 #define API_RTC_GET_SECONDS 0x50
#define API_RTC_SCHEDULE_ALARM 0x51 #define API_RTC_SCHEDULE_ALARM 0x51
#define API_RTC_SET_MILLISECONDS 0x52 #define API_RTC_SET_MILLISECONDS 0x52
#define API_RTC_GET_MILLISECONDS 0x53
#define API_RTC_GET_MONOTONIC_SECONDS 0x54
#define API_RTC_GET_MONOTONIC_MILLISECONDS 0x55
#define API_LEDS_SET 0x60 #define API_LEDS_SET 0x60
#define API_LEDS_SET_HSV 0x61 #define API_LEDS_SET_HSV 0x61
...@@ -86,6 +102,9 @@ typedef _Bool bool; ...@@ -86,6 +102,9 @@ typedef _Bool bool;
#define API_LEDS_SET_ALL_HSV 0x6b #define API_LEDS_SET_ALL_HSV 0x6b
#define API_LEDS_SET_GAMMA_TABLE 0x6c #define API_LEDS_SET_GAMMA_TABLE 0x6c
#define API_LEDS_CLEAR_ALL 0x6d #define API_LEDS_CLEAR_ALL 0x6d
#define API_LEDS_GET_ROCKET 0x6e
#define API_LEDS_GET 0x6f
#define API_LEDS_FLASH_ROCKET 0x72
#define API_VIBRA_SET 0x70 #define API_VIBRA_SET 0x70
#define API_VIBRA_VIBRATE 0x71 #define API_VIBRA_VIBRATE 0x71
...@@ -93,6 +112,7 @@ typedef _Bool bool; ...@@ -93,6 +112,7 @@ typedef _Bool bool;
#define API_LIGHT_SENSOR_RUN 0x80 #define API_LIGHT_SENSOR_RUN 0x80
#define API_LIGHT_SENSOR_GET 0x81 #define API_LIGHT_SENSOR_GET 0x81
#define API_LIGHT_SENSOR_STOP 0x82 #define API_LIGHT_SENSOR_STOP 0x82
#define API_LIGHT_SENSOR_READ 0x83
#define API_BUTTONS_READ 0x90 #define API_BUTTONS_READ 0x90
...@@ -102,11 +122,78 @@ typedef _Bool bool; ...@@ -102,11 +122,78 @@ typedef _Bool bool;
#define API_GPIO_READ_PIN 0xA3 #define API_GPIO_READ_PIN 0xA3
#define API_TRNG_READ 0xB0 #define API_TRNG_READ 0xB0
#define API_CSPRNG_READ 0XB1
#define API_PERSONAL_STATE_SET 0xc0 #define API_PERSONAL_STATE_SET 0xc0
#define API_PERSONAL_STATE_GET 0xc1 #define API_PERSONAL_STATE_GET 0xc1
#define API_PERSONAL_STATE_IS_PERSISTENT 0xc2 #define API_PERSONAL_STATE_IS_PERSISTENT 0xc2
#define API_BME680_INIT 0xD0
#define API_BME680_DEINIT 0xD1
#define API_BME680_GET_DATA 0xD2
#define API_BSEC_GET_DATA 0xD3
#define API_BHI160_ENABLE 0xe0
#define API_BHI160_DISABLE 0xe1
#define API_BHI160_DISABLE_ALL 0xe2
#define API_MAX30001_ENABLE 0xf0
#define API_MAX30001_DISABLE 0xf1
#define API_MAX86150_ENABLE 0x0100
#define API_MAX86150_DISABLE 0x0101
#define API_USB_SHUTDOWN 0x110
#define API_USB_STORAGE 0x111
#define API_USB_CDCACM 0x112
#define API_WS2812_WRITE 0x0120
#define API_CONFIG_GET_STRING 0x130
#define API_CONFIG_GET_INTEGER 0x131
#define API_CONFIG_GET_BOOLEAN 0x132
#define API_CONFIG_SET_STRING 0x133
#define API_BLE_GET_COMPARE_VALUE 0x140
#define API_BLE_COMPARE_RESPONSE 0x141
#define API_BLE_SET_MODE 0x142
#define API_BLE_GET_EVENT 0x143
#define API_BLE_GET_SCAN_REPORT 0x144
#define API_BLE_GET_LAST_PAIRING_NAME 0x145
#define API_BLE_GET_PEER_DEVICE_NAME 0x146
#define API_BLE_FREE_EVENT 0x147
#define API_BLE_HID_SEND_REPORT 0x150
#define API_BLE_ATTS_DYN_CREATE_GROUP 0x160
#define API_BLE_ATTS_DYN_DELETE_GROUP 0x161
#define API_BLE_ATTS_DYN_DELETE_GROUPS 0x169
#define API_BLE_ATTS_DYN_ADD_CHARACTERISTIC 0x16B
#define API_BLE_ATTS_DYN_ADD_DESCRIPTOR 0x163
#define API_BLE_ATTS_SET_BUFFER 0x16A
#define API_BLE_ATTS_SEND_SERVICE_CHANGED_IND 0x168
#define API_BLE_ATTS_SET_ATTR 0x170
#define API_BLE_ATTS_HANDLE_VALUE_NTF 0x171
#define API_BLE_ATTS_HANDLE_VALUE_IND 0x172
#define API_BLE_CLOSE_CONNECTION 0x180
#define API_BLE_IS_CONNECTION_OPEN 0x181
#define API_BLE_SET_DEVICE_NAME 0x182
#define API_BLE_GET_DEVICE_NAME 0x183
#define API_BLE_GET_ADDRESS 0x184
#define API_BLE_ADVERTISE 0x185
#define API_BLE_ADVERTISE_STOP 0x186
#define API_BLE_DISCOVER_PRIMARY_SERVICES 0x187
#define API_BLE_DISCOVER_CHARACTERISTICS 0x188
#define API_BLE_DISCOVER_DESCRIPTORS 0x189
#define API_BLE_ATTC_READ 0x18A
#define API_BLE_ATTC_WRITE_NO_RSP 0x18B
#define API_BLE_ATTC_WRITE 0x18C
#define API_BLE_INIT 0x190
#define API_BLE_DEINIT 0x191
/* clang-format on */ /* clang-format on */
typedef uint32_t api_int_id_t; typedef uint32_t api_int_id_t;
...@@ -118,6 +205,17 @@ typedef uint32_t api_int_id_t; ...@@ -118,6 +205,17 @@ typedef uint32_t api_int_id_t;
* the other direction. These interrupts can be enabled/disabled * the other direction. These interrupts can be enabled/disabled
* (masked/unmasked) using :c:func:`epic_interrupt_enable` and * (masked/unmasked) using :c:func:`epic_interrupt_enable` and
* :c:func:`epic_interrupt_disable`. * :c:func:`epic_interrupt_disable`.
*
* These interrupts work similar to hardware interrupts: You will only get a
* single interrupt, even if multiple events occured since the ISR last ran
* (*this behavior is new since version 1.16*).
*
* .. warning::
*
* Never attempt to call the API from inside an ISR. This might trigger an
* assertion if a call is already being made from thread context. We plan to
* lift this restriction at some point, but for the time being, this is how
* it is.
*/ */
/** /**
...@@ -134,6 +232,19 @@ API(API_INTERRUPT_ENABLE, int epic_interrupt_enable(api_int_id_t int_id)); ...@@ -134,6 +232,19 @@ API(API_INTERRUPT_ENABLE, int epic_interrupt_enable(api_int_id_t int_id));
*/ */
API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id)); API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));
/**
* Check if an API interrupt is enabled.
*
* :param int int_id: The interrupt to be checked
* :param bool* enabled: ``true`` will be stored here if the interrupt is enabled.
* ``false`` otherwise.
*
* :return: 0 on success, ``-EINVAL`` if the interrupt is unknown.
*
* .. versionadded:: 1.16
*/
API(API_INTERRUPT_IS_ENABLED, int epic_interrupt_is_enabled(api_int_id_t int_id, bool *enabled));
/** /**
* The following interrupts are defined: * The following interrupts are defined:
*/ */
...@@ -145,11 +256,25 @@ API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id)); ...@@ -145,11 +256,25 @@ API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));
#define EPIC_INT_CTRL_C 1 #define EPIC_INT_CTRL_C 1
/** UART Receive interrupt. See :c:func:`epic_isr_uart_rx`. */ /** UART Receive interrupt. See :c:func:`epic_isr_uart_rx`. */
#define EPIC_INT_UART_RX 2 #define EPIC_INT_UART_RX 2
/** RTC Alarm interrupt. See :c:func:`epic_isr_rtc_alarm` */ /** RTC Alarm interrupt. See :c:func:`epic_isr_rtc_alarm`. */
#define EPIC_INT_RTC_ALARM 3 #define EPIC_INT_RTC_ALARM 3
/** BHI160 Accelerometer. See :c:func:`epic_isr_bhi160_accelerometer`. */
#define EPIC_INT_BHI160_ACCELEROMETER 4
/** BHI160 Orientation Sensor. See :c:func:`epic_isr_bhi160_orientation`. */
#define EPIC_INT_BHI160_ORIENTATION 5
/** BHI160 Gyroscope. See :c:func:`epic_isr_bhi160_gyroscope`. */
#define EPIC_INT_BHI160_GYROSCOPE 6
/** MAX30001 ECG. See :c:func:`epic_isr_max30001_ecg`. */
#define EPIC_INT_MAX30001_ECG 7
/** BHI160 Magnetometer. See :c:func:`epic_isr_bhi160_magnetometer`. */
#define EPIC_INT_BHI160_MAGNETOMETER 8
/** MAX86150 ECG and PPG sensor. See :c:func:`epic_isr_max86150`. */
#define EPIC_INT_MAX86150 9
/** Bluetooth Low Energy event. See :c:func:`epic_isr_ble`. */
#define EPIC_INT_BLE 10
#define EPIC_INT_MAX86150_PROX 11
/* Number of defined interrupts. */ /* Number of defined interrupts. */
#define EPIC_INT_NUM 4 #define EPIC_INT_NUM 12
/* clang-format on */ /* clang-format on */
/* /*
...@@ -212,15 +337,61 @@ API(API_SYSTEM_EXEC, int __epic_exec(char *name)); ...@@ -212,15 +337,61 @@ API(API_SYSTEM_EXEC, int __epic_exec(char *name));
API(API_SYSTEM_RESET, void epic_system_reset(void)); API(API_SYSTEM_RESET, void epic_system_reset(void));
/** /**
* Battery Voltage * Sleep for the specified amount of time.
*
* This call will block for at most the specified amount of time. It allows epicardium to
* reduce clock speed of the system until this call is finished.
*
* This call returns early if an interrupt is signaled from epicardium.
*
* The clock source of epicardium has a limited amount of accuracy. Tolerances
* of +- 10% have been observed. This means that the sleep time also has a
* tolarance of at least +- 10%. The exact amount varies from device to device and
* also with temperature. You should take this into consideration when selecting
* the time you want to sleep.
*
* :param ms: Time to wait in milliseconds
* :returns: 0 if no interrupt happened, ``INT_MAX`` if an interrupt happened and the sleep ended early.
*/
API(API_SLEEP, int epic_sleep(uint32_t ms));
/**
* PMIC API
* =============== * ===============
*/ */
/** /**
* Read the current battery voltage. * Read the current battery voltage.
*/ */
API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result)); API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result));
/**
* Read the current battery current.
*/
API(API_BATTERY_CURRENT, int epic_read_battery_current(float *result));
/**
* Read the current charge voltage.
*/
API(API_CHARGEIN_VOLTAGE, int epic_read_chargein_voltage(float *result));
/**
* Read the current charge current.
*/
API(API_CHARGEIN_CURRENT, int epic_read_chargein_current(float *result));
/**
* Read the current system voltage.
*/
API(API_SYSTEM_VOLTAGE, int epic_read_system_voltage(float *result));
/**
* Read the current thermistor voltage.
*/
API(API_THERMISTOR_VOLTAGE, int epic_read_thermistor_voltage(float *result));
/** /**
* UART/Serial Interface * UART/Serial Interface
* ===================== * =====================
...@@ -237,8 +408,7 @@ API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result)); ...@@ -237,8 +408,7 @@ API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result));
* :param length: Amount of bytes to print. * :param length: Amount of bytes to print.
*/ */
API(API_UART_WRITE_STR, void epic_uart_write_str( API(API_UART_WRITE_STR, void epic_uart_write_str(
const char *str, const char *str, size_t length
intptr_t length
)); ));
/** /**
...@@ -264,7 +434,7 @@ API(API_UART_READ_CHAR, int epic_uart_read_char(void)); ...@@ -264,7 +434,7 @@ API(API_UART_READ_CHAR, int epic_uart_read_char(void));
API(API_UART_READ_STR, int epic_uart_read_str(char *buf, size_t cnt)); API(API_UART_READ_STR, int epic_uart_read_str(char *buf, size_t cnt));
/** /**
* **Interrupt Service Routine** * **Interrupt Service Routine** for :c:data:`EPIC_INT_UART_RX`
* *
* UART receive interrupt. This interrupt is triggered whenever a new character * UART receive interrupt. This interrupt is triggered whenever a new character
* becomes available on any connected UART device. This function is weakly * becomes available on any connected UART device. This function is weakly
...@@ -294,7 +464,7 @@ API(API_UART_READ_STR, int epic_uart_read_str(char *buf, size_t cnt)); ...@@ -294,7 +464,7 @@ API(API_UART_READ_STR, int epic_uart_read_str(char *buf, size_t cnt));
API_ISR(EPIC_INT_UART_RX, epic_isr_uart_rx); API_ISR(EPIC_INT_UART_RX, epic_isr_uart_rx);
/** /**
* **Interrupt Service Routine** * **Interrupt Service Routine** for :c:data:`EPIC_INT_CTRL_C`
* *
* A user-defineable ISR which is triggered when a ``^C`` (``0x04``) is received * A user-defineable ISR which is triggered when a ``^C`` (``0x04``) is received
* on any serial input device. This function is weakly aliased to * on any serial input device. This function is weakly aliased to
...@@ -378,26 +548,27 @@ API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask)); ...@@ -378,26 +548,27 @@ API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask));
/** GPIO pins IDs */ /** GPIO pins IDs */
enum gpio_pin { enum gpio_pin {
/** ``1``, Wristband connector 1 */ /** ``1``, Wristband connector 1 */
GPIO_WRISTBAND_1 = 1, EPIC_GPIO_WRISTBAND_1 = 1,
/** ``2``, Wristband connector 2 */ /** ``2``, Wristband connector 2 */
GPIO_WRISTBAND_2 = 2, EPIC_GPIO_WRISTBAND_2 = 2,
/** ``3``, Wristband connector 3 */ /** ``3``, Wristband connector 3 */
GPIO_WRISTBAND_3 = 3, EPIC_GPIO_WRISTBAND_3 = 3,
/** ``4``, Wristband connector 4 */ /** ``4``, Wristband connector 4 */
GPIO_WRISTBAND_4 = 4, EPIC_GPIO_WRISTBAND_4 = 4,
}; };
/** GPIO pin modes */ /** GPIO pin modes */
enum gpio_mode { enum gpio_mode {
/** Configure the pin as input */ /** Configure the pin as input */
GPIO_MODE_IN = (1<<0), EPIC_GPIO_MODE_IN = (1<<0),
/** Configure the pin as output */ /** Configure the pin as output */
GPIO_MODE_OUT = (1<<1), EPIC_GPIO_MODE_OUT = (1<<1),
EPIC_GPIO_MODE_ADC = (1<<2),
/** Enable the internal pull-up resistor */ /** Enable the internal pull-up resistor */
GPIO_PULL_UP = (1<<6), EPIC_GPIO_PULL_UP = (1<<6),
/** Enable the internal pull-down resistor */ /** Enable the internal pull-down resistor */
GPIO_PULL_DOWN = (1<<7), EPIC_GPIO_PULL_DOWN = (1<<7),
}; };
/** /**
...@@ -421,7 +592,9 @@ enum gpio_mode { ...@@ -421,7 +592,9 @@ enum gpio_mode {
* :param uint8_t mode: Mode to be configured. Use a combination of the :c:type:`gpio_mode` flags. * :param uint8_t mode: Mode to be configured. Use a combination of the :c:type:`gpio_mode` flags.
* :returns: ``0`` if the mode was set, ``-EINVAL`` if ``pin`` is not valid or the mode could not be set. * :returns: ``0`` if the mode was set, ``-EINVAL`` if ``pin`` is not valid or the mode could not be set.
*/ */
API(API_GPIO_SET_PIN_MODE, int epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode)); API(API_GPIO_SET_PIN_MODE, int epic_gpio_set_pin_mode(
uint8_t pin, uint8_t mode
));
/** /**
* Get the mode of a card10 GPIO pin. * Get the mode of a card10 GPIO pin.
...@@ -494,7 +667,7 @@ API(API_GPIO_WRITE_PIN, int epic_gpio_write_pin(uint8_t pin, bool on)); ...@@ -494,7 +667,7 @@ API(API_GPIO_WRITE_PIN, int epic_gpio_write_pin(uint8_t pin, bool on));
* :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`. * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
* :returns: ``-EINVAL`` if ``pin`` is not valid, an integer value otherwise. * :returns: ``-EINVAL`` if ``pin`` is not valid, an integer value otherwise.
*/ */
API(API_GPIO_READ_PIN, uint32_t epic_gpio_read_pin(uint8_t pin)); API(API_GPIO_READ_PIN, int epic_gpio_read_pin(uint8_t pin));
/** /**
* LEDs * LEDs
...@@ -516,6 +689,34 @@ API(API_GPIO_READ_PIN, uint32_t epic_gpio_read_pin(uint8_t pin)); ...@@ -516,6 +689,34 @@ API(API_GPIO_READ_PIN, uint32_t epic_gpio_read_pin(uint8_t pin));
*/ */
API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)); API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b));
/**
* Get one of card10's RGB LEDs in format of RGB.
*
* :c:func:`epic_leds_get_rgb` will get the value of a RGB LED described by ``led``.
*
* :param int led: Which LED to get. 0-10 are the LEDs on the top and 11-14
* are the 4 "ambient" LEDs.
* :param uint8_t * rgb: need tree byte array to get the value of red, green and blue.
* :returns: ``0`` on success or ``-EPERM`` if the LED is blocked by personal-state.
*
* .. versionadded:: 1.10
*/
API(API_LEDS_GET, int epic_leds_get_rgb(int led, uint8_t * rgb));
/**
* Set one of the rockets to flash for a certain time.
*
* :c:func:`epic_leds_flash_rocket` will set a timer for the flash of a rocket.
*
* :param int led: Number of the rocket that sould flash
* :param uint8_t value: brightness of the 'on'-state of this rocket ( 0 < value < 32)
* :param int millis: time in milliseconds defining the duration of the flash (i.e. how long is the rocket 'on')
*
* .. versionadded:: 1.16
*/
API(API_LEDS_FLASH_ROCKET, void epic_leds_flash_rocket(int led, uint8_t valiue, int millis));
/** /**
* Set one of card10's RGB LEDs to a certain color in HSV format. * Set one of card10's RGB LEDs to a certain color in HSV format.
* *
...@@ -528,7 +729,9 @@ API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)); ...@@ -528,7 +729,9 @@ API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b));
* :param float s: Saturation component of the color. (0 <= s <= 1) * :param float s: Saturation component of the color. (0 <= s <= 1)
* :param float v: Value/Brightness component of the color. (0 <= v <= 0) * :param float v: Value/Brightness component of the color. (0 <= v <= 0)
*/ */
API(API_LEDS_SET_HSV, void epic_leds_set_hsv(int led, float h, float s, float v)); API(API_LEDS_SET_HSV, void epic_leds_set_hsv(
int led, float h, float s, float v
));
/** /**
* Set multiple of card10's RGB LEDs to a certain color in RGB format. * Set multiple of card10's RGB LEDs to a certain color in RGB format.
...@@ -551,7 +754,9 @@ API(API_LEDS_SET_ALL, void epic_leds_set_all(uint8_t *pattern, uint8_t len)); ...@@ -551,7 +754,9 @@ API(API_LEDS_SET_ALL, void epic_leds_set_all(uint8_t *pattern, uint8_t len));
* LEDs. (0 <= h < 360, 0 <= s <= 1, 0 <= v <= 1) * LEDs. (0 <= h < 360, 0 <= s <= 1, 0 <= v <= 1)
* :param uint8_t len: Length of 1st dimension of ``pattern``, see above. * :param uint8_t len: Length of 1st dimension of ``pattern``, see above.
*/ */
API(API_LEDS_SET_ALL_HSV, void epic_leds_set_all_hsv(float *pattern, uint8_t len)); API(API_LEDS_SET_ALL_HSV, void epic_leds_set_all_hsv(
float *pattern, uint8_t len
));
/** /**
* Prepare one of card10's RGB LEDs to be set to a certain color in RGB format. * Prepare one of card10's RGB LEDs to be set to a certain color in RGB format.
...@@ -564,7 +769,9 @@ API(API_LEDS_SET_ALL_HSV, void epic_leds_set_all_hsv(float *pattern, uint8_t len ...@@ -564,7 +769,9 @@ API(API_LEDS_SET_ALL_HSV, void epic_leds_set_all_hsv(float *pattern, uint8_t len
* :param uint8_t g: Green component of the color. * :param uint8_t g: Green component of the color.
* :param uint8_t b: Blue component of the color. * :param uint8_t b: Blue component of the color.
*/ */
API(API_LEDS_PREP, void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b)); API(API_LEDS_PREP, void epic_leds_prep(
int led, uint8_t r, uint8_t g, uint8_t b
));
/** /**
* Prepare one of card10's RGB LEDs to be set to a certain color in HSV format. * Prepare one of card10's RGB LEDs to be set to a certain color in HSV format.
...@@ -577,7 +784,9 @@ API(API_LEDS_PREP, void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b) ...@@ -577,7 +784,9 @@ API(API_LEDS_PREP, void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b)
* :param uint8_t s: Saturation component of the color. (float, 0 <= s <= 1) * :param uint8_t s: Saturation component of the color. (float, 0 <= s <= 1)
* :param uint8_t v: Value/Brightness component of the color. (float, 0 <= v <= 0) * :param uint8_t v: Value/Brightness component of the color. (float, 0 <= v <= 0)
*/ */
API(API_LEDS_PREP_HSV, void epic_leds_prep_hsv(int led, float h, float s, float v)); API(API_LEDS_PREP_HSV, void epic_leds_prep_hsv(
int led, float h, float s, float v
));
/** /**
* Set global brightness for top RGB LEDs. * Set global brightness for top RGB LEDs.
...@@ -641,6 +850,26 @@ API(API_LEDS_UPDATE, void epic_leds_update(void)); ...@@ -641,6 +850,26 @@ API(API_LEDS_UPDATE, void epic_leds_update(void));
*/ */
API(API_LEDS_SET_ROCKET, void epic_leds_set_rocket(int led, uint8_t value)); API(API_LEDS_SET_ROCKET, void epic_leds_set_rocket(int led, uint8_t value));
/**
* Get the brightness of one of the rocket LEDs.
*
* :param int led: Which LED to get.
*
* +-------+--------+----------+
* | ID | Color | Location |
* +=======+========+==========+
* | ``0`` | Blue | Left |
* +-------+--------+----------+
* | ``1`` | Yellow | Top |
* +-------+--------+----------+
* | ``2`` | Green | Right |
* +-------+--------+----------+
* :returns value: Brightness of LED (value between 0 and 31) or ``-EINVAL`` if the LED/rocket does not exists.
*
* .. versionadded:: 1.10
*/
API(API_LEDS_GET_ROCKET, int epic_leds_get_rocket(int led));
/** /**
* Turn on the bright side LED which can serve as a flashlight if worn on the left wrist or as a rad tattoo illuminator if worn on the right wrist. * Turn on the bright side LED which can serve as a flashlight if worn on the left wrist or as a rad tattoo illuminator if worn on the right wrist.
* *
...@@ -660,8 +889,7 @@ API(API_LEDS_SET_FLASHLIGHT, void epic_set_flashlight(bool power)); ...@@ -660,8 +889,7 @@ API(API_LEDS_SET_FLASHLIGHT, void epic_set_flashlight(bool power));
* :param uint8_t[256] gamma_table: Gamma lookup table. (default = 4th order power function rounded up) * :param uint8_t[256] gamma_table: Gamma lookup table. (default = 4th order power function rounded up)
*/ */
API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table( API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table(
uint8_t rgb_channel, uint8_t rgb_channel, uint8_t *gamma_table
uint8_t *gamma_table
)); ));
/** /**
...@@ -671,7 +899,294 @@ API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table( ...@@ -671,7 +899,294 @@ API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table(
* :param uint8_t g: Value for the green color channel. * :param uint8_t g: Value for the green color channel.
* :param uint8_t b: Value for the blue color channel. * :param uint8_t b: Value for the blue color channel.
*/ */
API(API_LEDS_CLEAR_ALL, void epic_leds_clear_all(uint8_t r, uint8_t g, uint8_t b)); API(API_LEDS_CLEAR_ALL, void epic_leds_clear_all(
uint8_t r, uint8_t g, uint8_t b
));
/**
* BME680
* ======
*
* .. versionadded:: 1.4
*/
/**
* BME680 Sensor Data
*/
struct bme680_sensor_data {
/** Temperature in degree celsius */
float temperature;
/** Humidity in % relative humidity */
float humidity;
/** Pressure in hPa */
float pressure;
/** Gas resistance in Ohms */
float gas_resistance;
};
/**
* Initialize the BM680 sensor.
*
* .. versionadded:: 1.4
*
* :return: 0 on success or ``-Exxx`` on error. The following
* errors might occur:
*
* - ``-EFAULT``: On NULL-pointer.
* - ``-EINVAL``: Invalid configuration.
* - ``-EIO``: Communication with the device failed.
* - ``-ENODEV``: Device was not found.
*/
API(API_BME680_INIT, int epic_bme680_init());
/**
* De-Initialize the BM680 sensor.
*
* .. versionadded:: 1.4
*
* :return: 0 on success or ``-Exxx`` on error. The following
* errors might occur:
*
* - ``-EFAULT``: On NULL-pointer.
* - ``-EINVAL``: Invalid configuration.
* - ``-EIO``: Communication with the device failed.
* - ``-ENODEV``: Device was not found.
*/
API(API_BME680_DEINIT, int epic_bme680_deinit());
/**
* Get the current BME680 data.
*
* .. versionadded:: 1.4
*
* :param data: Where to store the environmental data.
* :return: 0 on success or ``-Exxx`` on error. The following
* errors might occur:
*
* - ``-EFAULT``: On NULL-pointer.
* - ``-EINVAL``: Sensor not initialized.
* - ``-EIO``: Communication with the device failed.
* - ``-ENODEV``: Device was not found.
*/
API(API_BME680_GET_DATA, int epic_bme680_read_sensors(
struct bme680_sensor_data *data
));
/**
* .. _bsec_api:
*
* BSEC
* ----
* The Bosch Sensortec Environmental Cluster (BSEC) library
* allows to estimate an indoor air qualtiy (IAQ) metric as
* well as CO2 and VOC content equivalents using the gas sensor
* of the BME680.
*
* As it is a proprietary binary blob, it has to be enabled using
* the ``bsec_enable`` configuration option (see :ref:`card10_cfg`).
*
* Please also have a look at the BME680 datasheet and some of
* the BSEC documentation:
*
* https://git.card10.badge.events.ccc.de/card10/hardware/-/blob/master/datasheets/bosch/BST-BME680-DS001.pdf
*
* https://git.card10.badge.events.ccc.de/card10/firmware/-/blob/master/lib/vendor/Bosch/BSEC/integration_guide/BST-BME680-Integration-Guide-AN008-48.pdf
*/
/**
* BSEC Sensor Data
*/
struct bsec_sensor_data {
/** Compensated temperature in degree celsius */
float temperature;
/** Compensated humidity in % relative humidity */
float humidity;
/** Pressure in hPa */
float pressure;
/** Gas resistance in Ohms */
float gas_resistance;
/** Timestamp in of the measurement in UNIX time (seconds since
* 1970-01-01 00:00:00 UTC)*/
uint32_t timestamp;
/** Accuracy of IAQ, CO2 equivalent and breath VOC equivalent:
*
* 0: Stabilization / run-in ongoing:
* This means that the sensor still needs to warm up. Takes about
* 5 min after activation of BSEC / reboot.
*
* 1: Low accuracy:
* The sensor has not yet been calibrated. BSEC needs to collect
* more data to calibrate the sensor. This can take multiple
* hours.
*
* BSEC documentation: To reach high accuracy(3) please expose
* sensor once to good air (e.g. outdoor air) and bad air (e.g.
* box with exhaled breath) for auto-trimming
*
* 2: Medium accuracy: auto-trimming ongoing
* BSEC has detected that it needs to recalibrate the sensor.
* This is an automatic process and usally finishes after tens
* of minutes. Can happen every now and then.
*
* 3: High accuracy:
* The sensor has warmed up and is calibrated.
*
* From BSEC documentation:
* IAQ accuracy indicator will notify the user when they should
* initiate a calibration process. Calibration is performed automatically
* in the background if the sensor is exposed to clean and polluted air
* for approximately 30 minutes each.
*
* See also:
* https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BME680-IAQ-accuracy-definition/m-p/5931/highlight/true#M10
*/
uint8_t accuracy;
/** Indoor Air Quality with range 0 to 500
*
* Statement from the Bosch BSEC library:
*
* Indoor-air-quality (IAQ) gives an indication of the relative change
* in ambient TVOCs detected by BME680.
*
* The IAQ scale ranges from 0 (clean air) to 500 (heavily polluted air).
* During operation, algorithms automatically calibrate and adapt
* themselves to the typical environments where the sensor is operated
* (e.g., home, workplace, inside a car, etc.).This automatic background
* calibration ensures that users experience consistent IAQ performance.
* The calibration process considers the recent measurement history (typ.
* up to four days) to ensure that IAQ=25 corresponds to typical good air
* and IAQ=250 indicates typical polluted air.
*
* Please also consult the BME680 datsheet (pages 9 and 21) as well:
* https://git.card10.badge.events.ccc.de/card10/hardware/-/blob/master/datasheets/bosch/BST-BME680-DS001.pdf
*
*/
int32_t indoor_air_quality;
/** Unscaled IAQ value.
*
* See this post for details:
* https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BME680-strange-IAQ-and-CO2-values/m-p/9667/highlight/true#M1505
*/
int32_t static_indoor_air_quality;
/** Estimation of equivalant CO2 content in the air in ppm. */
float co2_equivalent;
/** Estimation of equivalant breath VOC content in the air in ppm. */
float breath_voc_equivalent;
};
/**
*
* Get the current BME680 data filtered by Bosch BSEC library
*
* As it is a proprietary binary blob, it has to be enabled using
* the ``bsec_enable`` configuration option (see :ref:`card10_cfg`).
*
* The sample rate is currently fixed to one sample every 3 seconds.
* Querying the sensor more often will return cached data.
*
* After the libary has been activated it starts to calibrate the
* sensor. This can take multiple hours.
* After a reset/power on it takes about 5 minutes to stabilize
* the sensor if it was calibrated before.
*
* The BSEC library regularly recalibrates the sensor during operation.
* The ``accuracy`` field of the return data indicates the calibration
* status of the sensor. Please take it into consideration when
* using / displaying the IAQ.
*
* Please refer to the description of :c:type:`bsec_sensor_data` for more
* information about how to interpret its content.
*
* .. versionadded:: 1.x
*
* :param data: Where to store the environmental data.
* :return: 0 on success or ``-Exxx`` on error. The following
* errors might occur:
*
* - ``-EFAULT``: On NULL-pointer.
* - ``-EINVAL``: No data available from the sensor.
* - ``-ENODEV``: BSEC libray is not running.
*/
API(API_BSEC_GET_DATA, int epic_bsec_read_sensors(
struct bsec_sensor_data *data
));
/**
* MAX86150
* ========
*/
/**
* Configuration for a MAX86150 sensor.
*
* This struct is used when enabling a sensor using
* :c:func:`epic_max86150_enable_sensor`.
*/
struct max86150_sensor_config {
/**
* Number of samples Epicardium should keep for this sensor. Do not set
* this number too high as the sample buffer will eat RAM.
*/
size_t sample_buffer_len;
/**
* Sample rate for PPG from the sensor in Hz. Maximum data rate is limited
* to 200 Hz for all sensors though some might be limited at a lower
* rate.
*
* Possible values are 10, 20, 50, 84, 100, 200.
*/
uint16_t ppg_sample_rate;
};
/**
* MAX86150 Sensor Data
*/
struct max86150_sensor_data {
/** Red LED data */
uint32_t red;
/** IR LED data */
uint32_t ir;
/** ECG data */
int32_t ecg;
};
/**
* Enable a MAX86150 PPG and ECG sensor.
*
* Calling this function will instruct the MAX86150 to collect a
* data from the sensor. You can then retrieve the samples using
* :c:func:`epic_stream_read`.
*
* :param max86150_sensor_config* config: Configuration for this sensor.
* :param size_t config_size: Size of ``config``.
* :returns: A sensor descriptor which can be used with
* :c:func:`epic_stream_read` or a negative error value:
*
* - ``-ENOMEM``: The MAX86150 driver failed to create a stream queue.
* - ``-ENODEV``: The MAX86150 driver failed due to physical connectivity problem
* (broken wire, unpowered, etc).
* - ``-EINVAL``: config->ppg_sample_rate is not one of 10, 20, 50, 84, 100, 200
* or config_size is not size of config.
*
* .. versionadded:: 1.16
*/
API(API_MAX86150_ENABLE, int epic_max86150_enable_sensor(struct max86150_sensor_config *config, size_t config_size));
/**
* Disable the MAX86150 sensor.
*
* :returns: 0 in case of success or forward negative error value from stream_deregister.
*
* .. versionadded:: 1.16
*/
API(API_MAX86150_DISABLE, int epic_max86150_disable_sensor());
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_MAX86150`
*
* :c:func:`epic_isr_max86150` is called whenever the MAX86150
* PPG sensor has new data available.
*/
API_ISR(EPIC_INT_MAX86150, epic_isr_max86150);
/** /**
* Personal State * Personal State
...@@ -697,10 +1212,12 @@ enum personal_state { ...@@ -697,10 +1212,12 @@ enum personal_state {
STATE_NO_CONTACT = 1, STATE_NO_CONTACT = 1,
/** ``2``, "chaos" - Adventure time - blue led, short blink, long blink. */ /** ``2``, "chaos" - Adventure time - blue led, short blink, long blink. */
STATE_CHAOS = 2, STATE_CHAOS = 2,
/** ``3``, "communication" - want to learn something or have a nice conversation - green led, long blinks. */ /** ``3``, "communication" - want to learn something or have a nice conversation - yellow led, long blinks. */
STATE_COMMUNICATION = 3, STATE_COMMUNICATION = 3,
/** ``4``, "camp" - I am focussed on self-, camp-, or community maintenance - yellow led, fade on and off. */ /** ``4``, "camp" - I am focussed on self-, camp-, or community maintenance - green led, fade on and off. */
STATE_CAMP = 4, STATE_CAMP = 4,
/** STATE_MAX gives latest value and count of possible STATEs**/
STATE_MAX = 5,
}; };
/** /**
...@@ -712,8 +1229,9 @@ enum personal_state { ...@@ -712,8 +1229,9 @@ enum personal_state {
* :param bool persistent: Indicates whether the configured personal state will remain set and active on pycardium application restart/change. * :param bool persistent: Indicates whether the configured personal state will remain set and active on pycardium application restart/change.
* :returns: ``0`` on success, ``-EINVAL`` if an invalid state was requested. * :returns: ``0`` on success, ``-EINVAL`` if an invalid state was requested.
*/ */
API(API_PERSONAL_STATE_SET, int epic_personal_state_set(uint8_t state, API(API_PERSONAL_STATE_SET, int epic_personal_state_set(
bool persistent)); uint8_t state, bool persistent
));
/** /**
* Get the users personal state. * Get the users personal state.
...@@ -796,69 +1314,287 @@ API(API_PERSONAL_STATE_IS_PERSISTENT, int epic_personal_state_is_persistent()); ...@@ -796,69 +1314,287 @@ API(API_PERSONAL_STATE_IS_PERSISTENT, int epic_personal_state_is_persistent());
API(API_STREAM_READ, int epic_stream_read(int sd, void *buf, size_t count)); API(API_STREAM_READ, int epic_stream_read(int sd, void *buf, size_t count));
/** /**
* Vibration Motor * BHI160 Sensor Fusion
* =============== * ====================
* card10 has a BHI160 onboard which is used as an IMU. BHI160 exposes a few
* different sensors which can be accessed using Epicardium API.
*
* .. versionadded:: 1.4
*
* **Example**:
*
* .. code-block:: cpp
*
* #include "epicardium.h"
*
* // Configure a sensor & enable it
* struct bhi160_sensor_config cfg = {0};
* cfg.sample_buffer_len = 40;
* cfg.sample_rate = 4; // Hz
* cfg.dynamic_range = 2; // g
*
* int sd = epic_bhi160_enable_sensor(BHI160_ACCELEROMETER, &cfg);
*
* // Read sensor data
* while (1) {
* struct bhi160_data_vector buf[10];
*
* int n = epic_stream_read(sd, buf, sizeof(buf));
*
* for (int i = 0; i < n; i++) {
* printf("X: %6d Y: %6d Z: %6d\n",
* buf[i].x,
* buf[i].y,
* buf[i].z);
* }
* }
*
* // Disable the sensor
* epic_bhi160_disable_sensor(BHI160_ACCELEROMETER);
*/ */
/** /**
* Turn vibration motor on or off * BHI160 Sensor Types
* * -------------------
* :param status: 1 to turn on, 0 to turn off.
*/ */
API(API_VIBRA_SET, void epic_vibra_set(int status));
/** /**
* Turn vibration motor on for a given time * BHI160 virtual sensor type.
*
* :param millis: number of milliseconds to run the vibration motor.
*/ */
API(API_VIBRA_VIBRATE, void epic_vibra_vibrate(int millis)); enum bhi160_sensor_type {
/** /**
* Display * Accelerometer
* =======
* The card10 has an LCD screen that can be accessed from user code.
* *
* There are two ways to access the display: * - Data type: :c:type:`bhi160_data_vector`
* - Dynamic range: g's (1x Earth Gravity, ~9.81m*s^-2)
*/
BHI160_ACCELEROMETER = 0,
/**
* Magnetometer
* *
* - *immediate mode*, where you ask Epicardium to draw shapes and text for * - Data type: :c:type:`bhi160_data_vector`
* you. Most functions in this subsection are related to *immediate mode*. * - Dynamic range: -1000 to 1000 microtesla
* - *framebuffer mode*, where you provide Epicardium with a memory range where
* you already drew graphics whichever way you wanted and Epicardium will
* copy them to the display. To use *framebuffer mode*, use the
* :c:func:`epic_disp_framebuffer` function.
*/ */
BHI160_MAGNETOMETER = 1,
/** Orientation */
BHI160_ORIENTATION = 2,
/** Gyroscope */
BHI160_GYROSCOPE = 3,
/** Gravity (**Unimplemented**) */
BHI160_GRAVITY = 4,
/** Linear acceleration (**Unimplemented**) */
BHI160_LINEAR_ACCELERATION = 5,
/** Rotation vector (**Unimplemented**) */
BHI160_ROTATION_VECTOR = 6,
/** Uncalibrated magnetometer (**Unimplemented**) */
BHI160_UNCALIBRATED_MAGNETOMETER = 7,
/** Game rotation vector (whatever that is supposed to be) */
BHI160_GAME_ROTATION_VECTOR = 8,
/** Uncalibrated gyroscrope (**Unimplemented**) */
BHI160_UNCALIBRATED_GYROSCOPE = 9,
/** Geomagnetic rotation vector (**Unimplemented**) */
BHI160_GEOMAGNETIC_ROTATION_VECTOR = 10,
};
/** Line-Style */ enum bhi160_data_type {
enum disp_linestyle { BHI160_DATA_TYPE_VECTOR
/** */
LINESTYLE_FULL = 0,
/** */
LINESTYLE_DOTTED = 1
}; };
/** Fill-Style */ /**
enum disp_fillstyle { * BHI160 Sensor Data Types
/** */ * ------------------------
FILLSTYLE_EMPTY = 0, */
/** */
FILLSTYLE_FILLED = 1 /**
* Vector Data. The scaling of these values is dependent on the chosen dynamic
* range. See the individual sensor's documentation for details.
*/
struct bhi160_data_vector {
enum bhi160_data_type data_type;
/** X */
int16_t x;
/** Y */
int16_t y;
/** Z */
int16_t z;
/** Status */
uint8_t status;
}; };
/** Width of display in pixels */ /**
#define DISP_WIDTH 160 * BHI160 API
* ----------
*/
/** Height of display in pixels */ /**
#define DISP_HEIGHT 80 * Configuration for a BHI160 sensor.
*
* This struct is used when enabling a sensor using
* :c:func:`epic_bhi160_enable_sensor`.
*/
struct bhi160_sensor_config {
/**
* Number of samples Epicardium should keep for this sensor. Do not set
* this number too high as the sample buffer will eat RAM.
*/
size_t sample_buffer_len;
/**
* Sample rate for the sensor in Hz. Maximum data rate is limited
* to 200 Hz for all sensors though some might be limited at a lower
* rate.
*/
uint16_t sample_rate;
/**
* Dynamic range. Interpretation of this value depends on
* the sensor type. Please refer to the specific sensor in
* :c:type:`bhi160_sensor_type` for details.
*/
uint16_t dynamic_range;
/** Always zero. Reserved for future parameters. */
uint8_t _padding[8];
};
/** /**
* Framebuffer * Enable a BHI160 virtual sensor. Calling this function will instruct the
* BHI160 to collect data for this specific virtual sensor. You can then
* retrieve the samples using :c:func:`epic_stream_read`.
* *
* The frambuffer stores pixels as RGB565, but byte swapped. That is, for every ``(x, y)`` coordinate, there are two ``uint8_t``\ s storing 16 bits of pixel data. * :param bhi160_sensor_type sensor_type: Which sensor to enable.
* :param bhi160_sensor_config* config: Configuration for this sensor.
* :returns: A sensor descriptor which can be used with
* :c:func:`epic_stream_read` or a negative error value:
* *
* .. todo:: * - ``-EBUSY``: The BHI160 driver is currently busy with other tasks and
* could not be acquired for enabling a sensor.
* *
* Document (x, y) in relation to chirality. * .. versionadded:: 1.4
*/
API(API_BHI160_ENABLE, int epic_bhi160_enable_sensor(
enum bhi160_sensor_type sensor_type,
struct bhi160_sensor_config *config
));
/**
* Disable a BHI160 sensor.
*
* :param bhi160_sensor_type sensor_type: Which sensor to disable.
*
* .. versionadded:: 1.4
*/
API(API_BHI160_DISABLE, int epic_bhi160_disable_sensor(
enum bhi160_sensor_type sensor_type
));
/**
* Disable all BHI160 sensors.
*
* .. versionadded:: 1.4
*/
API(API_BHI160_DISABLE_ALL, void epic_bhi160_disable_all_sensors());
/**
* BHI160 Interrupt Handlers
* -------------------------
*/
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_ACCELEROMETER`
*
* :c:func:`epic_isr_bhi160_accelerometer` is called whenever the BHI160
* accelerometer has new data available.
*/
API_ISR(EPIC_INT_BHI160_ACCELEROMETER, epic_isr_bhi160_accelerometer);
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_MAGNETOMETER`
*
* :c:func:`epic_isr_bhi160_magnetometer` is called whenever the BHI160
* magnetometer has new data available.
*/
API_ISR(EPIC_INT_BHI160_MAGNETOMETER, epic_isr_bhi160_magnetometer);
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_ORIENTATION`
*
* :c:func:`epic_isr_bhi160_orientation` is called whenever the BHI160
* orientation sensor has new data available.
*/
API_ISR(EPIC_INT_BHI160_ORIENTATION, epic_isr_bhi160_orientation);
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_BHI160_GYROSCOPE`
*
* :c:func:`epic_isr_bhi160_orientation` is called whenever the BHI160
* gyroscrope has new data available.
*/
API_ISR(EPIC_INT_BHI160_GYROSCOPE, epic_isr_bhi160_gyroscope);
/**
* Vibration Motor
* ===============
*/
/**
* Turn vibration motor on or off
*
* :param status: 1 to turn on, 0 to turn off.
*/
API(API_VIBRA_SET, void epic_vibra_set(int status));
/**
* Turn vibration motor on for a given time
*
* :param millis: number of milliseconds to run the vibration motor.
*/
API(API_VIBRA_VIBRATE, void epic_vibra_vibrate(int millis));
/**
* Display
* =======
* The card10 has an LCD screen that can be accessed from user code.
*
* There are two ways to access the display:
*
* - *immediate mode*, where you ask Epicardium to draw shapes and text for
* you. Most functions in this subsection are related to *immediate mode*.
* - *framebuffer mode*, where you provide Epicardium with a memory range where
* you already drew graphics whichever way you wanted and Epicardium will
* copy them to the display. To use *framebuffer mode*, use the
* :c:func:`epic_disp_framebuffer` function.
*/
/** Line-Style */
enum disp_linestyle {
/** */
LINESTYLE_FULL = 0,
/** */
LINESTYLE_DOTTED = 1
};
/** Fill-Style */
enum disp_fillstyle {
/** */
FILLSTYLE_EMPTY = 0,
/** */
FILLSTYLE_FILLED = 1
};
/** Width of display in pixels */
#define DISP_WIDTH 160
/** Height of display in pixels */
#define DISP_HEIGHT 80
/**
* Framebuffer
*
* The frambuffer stores pixels as RGB565, but byte swapped. That is, for every ``(x, y)`` coordinate, there are two ``uint8_t``\ s storing 16 bits of pixel data.
*
* .. todo::
*
* Document (x, y) in relation to chirality.
* *
* **Example**: Fill framebuffer with red * **Example**: Fill framebuffer with red
* *
...@@ -911,8 +1647,8 @@ API(API_DISP_UPDATE, int epic_disp_update()); ...@@ -911,8 +1647,8 @@ API(API_DISP_UPDATE, int epic_disp_update());
/** /**
* Prints a string into the display framebuffer * Prints a string into the display framebuffer
* *
* :param posx: x position to print to. 0 <= x <= 160 * :param posx: x position to print to.
* :param posy: y position to print to. 0 <= y <= 80 * :param posy: y position to print to.
* :param pString: string to print * :param pString: string to print
* :param fg: foreground color in rgb565 * :param fg: foreground color in rgb565
* :param bg: background color in rgb565 * :param bg: background color in rgb565
...@@ -922,13 +1658,56 @@ API(API_DISP_UPDATE, int epic_disp_update()); ...@@ -922,13 +1658,56 @@ API(API_DISP_UPDATE, int epic_disp_update());
*/ */
API(API_DISP_PRINT, API(API_DISP_PRINT,
int epic_disp_print( int epic_disp_print(
uint16_t posx, int16_t posx,
uint16_t posy, int16_t posy,
const char *pString, const char *pString,
uint16_t fg, uint16_t fg,
uint16_t bg) uint16_t bg)
); );
/*
* Font Selection
*/
enum disp_font_name {
DISP_FONT8 = 0,
DISP_FONT12 = 1,
DISP_FONT16 = 2,
DISP_FONT20 = 3,
DISP_FONT24 = 4,
};
/*
* Image data type
*/
enum epic_rgb_format {
EPIC_RGB8 = 0,
EPIC_RGBA8 = 1,
EPIC_RGB565 = 2,
EPIC_RGBA5551 = 3,
};
/**
* Prints a string into the display framebuffer with font type selectable
*
* :param fontName: number of font, use FontName enum
* :param posx: x position to print to.
* :param posy: y position to print to.
* :param pString: string to print
* :param fg: foreground color in rgb565
* :param bg: background color in rgb565, no background is drawn if bg==fg
* :return: ``0`` on success or a negative value in case of an error:
*
* - ``-EBUSY``: Display was already locked from another task.
*/
API(API_DISP_PRINT_ADV, int epic_disp_print_adv(
uint8_t font,
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
));
/** /**
* Fills the whole screen with one color * Fills the whole screen with one color
* *
...@@ -942,27 +1721,43 @@ API(API_DISP_CLEAR, int epic_disp_clear(uint16_t color)); ...@@ -942,27 +1721,43 @@ API(API_DISP_CLEAR, int epic_disp_clear(uint16_t color));
/** /**
* Draws a pixel on the display * Draws a pixel on the display
* *
* :param x: x position; 0 <= x <= 160 * :param x: x position;
* :param y: y position; 0 <= y <= 80 * :param y: y position;
* :param color: pixel color in rgb565 * :param color: pixel color in rgb565
* :return: ``0`` on success or a negative value in case of an error: * :return: ``0`` on success or a negative value in case of an error:
* *
* - ``-EBUSY``: Display was already locked from another task. * - ``-EBUSY``: Display was already locked from another task.
*/ */
API(API_DISP_PIXEL, API(API_DISP_PIXEL, int epic_disp_pixel(
int epic_disp_pixel( int16_t x, int16_t y, uint16_t color
uint16_t x, ));
uint16_t y,
uint16_t color) /**
); * Blits an image buffer to the display
*
* :param x: x position
* :param y: y position
* :param w: Image width
* :param h: Image height
* :param img: Image data
* :param format: Format of the image data. One of :c:type:`epic_rgb_format`.
*/
API(API_DISP_BLIT, int epic_disp_blit(
int16_t x,
int16_t y,
int16_t w,
int16_t h,
void *img,
enum epic_rgb_format format
));
/** /**
* Draws a line on the display * Draws a line on the display
* *
* :param xstart: x starting position; 0 <= x <= 160 * :param xstart: x starting position
* :param ystart: y starting position; 0 <= y <= 80 * :param ystart: y starting position
* :param xend: x ending position; 0 <= x <= 160 * :param xend: x ending position
* :param yend: y ending position; 0 <= y <= 80 * :param yend: y ending position
* :param color: line color in rgb565 * :param color: line color in rgb565
* :param linestyle: 0 for solid, 1 for dottet (almost no visual difference) * :param linestyle: 0 for solid, 1 for dottet (almost no visual difference)
* :param pixelsize: thickness of the line; 1 <= pixelsize <= 8 * :param pixelsize: thickness of the line; 1 <= pixelsize <= 8
...@@ -970,24 +1765,23 @@ API(API_DISP_PIXEL, ...@@ -970,24 +1765,23 @@ API(API_DISP_PIXEL,
* *
* - ``-EBUSY``: Display was already locked from another task. * - ``-EBUSY``: Display was already locked from another task.
*/ */
API(API_DISP_LINE, API(API_DISP_LINE, int epic_disp_line(
int epic_disp_line( int16_t xstart,
uint16_t xstart, int16_t ystart,
uint16_t ystart, int16_t xend,
uint16_t xend, int16_t yend,
uint16_t yend,
uint16_t color, uint16_t color,
enum disp_linestyle linestyle, enum disp_linestyle linestyle,
uint16_t pixelsize) uint16_t pixelsize
); ));
/** /**
* Draws a rectangle on the display * Draws a rectangle on the display
* *
* :param xstart: x coordinate of top left corner; 0 <= x <= 160 * :param xstart: x coordinate of top left corner
* :param ystart: y coordinate of top left corner; 0 <= y <= 80 * :param ystart: y coordinate of top left corner
* :param xend: x coordinate of bottom right corner; 0 <= x <= 160 * :param xend: x coordinate of bottom right corner
* :param yend: y coordinate of bottom right corner; 0 <= y <= 80 * :param yend: y coordinate of bottom right corner
* :param color: line color in rgb565 * :param color: line color in rgb565
* :param fillstyle: 0 for empty, 1 for filled * :param fillstyle: 0 for empty, 1 for filled
* :param pixelsize: thickness of the rectangle outline; 1 <= pixelsize <= 8 * :param pixelsize: thickness of the rectangle outline; 1 <= pixelsize <= 8
...@@ -995,16 +1789,15 @@ API(API_DISP_LINE, ...@@ -995,16 +1789,15 @@ API(API_DISP_LINE,
* *
* - ``-EBUSY``: Display was already locked from another task. * - ``-EBUSY``: Display was already locked from another task.
*/ */
API(API_DISP_RECT, API(API_DISP_RECT, int epic_disp_rect(
int epic_disp_rect( int16_t xstart,
uint16_t xstart, int16_t ystart,
uint16_t ystart, int16_t xend,
uint16_t xend, int16_t yend,
uint16_t yend,
uint16_t color, uint16_t color,
enum disp_fillstyle fillstyle, enum disp_fillstyle fillstyle,
uint16_t pixelsize) uint16_t pixelsize
); ));
/** /**
* Draws a circle on the display * Draws a circle on the display
...@@ -1019,15 +1812,14 @@ API(API_DISP_RECT, ...@@ -1019,15 +1812,14 @@ API(API_DISP_RECT,
* *
* - ``-EBUSY``: Display was already locked from another task. * - ``-EBUSY``: Display was already locked from another task.
*/ */
API(API_DISP_CIRC, API(API_DISP_CIRC, int epic_disp_circ(
int epic_disp_circ( int16_t x,
uint16_t x, int16_t y,
uint16_t y,
uint16_t rad, uint16_t rad,
uint16_t color, uint16_t color,
enum disp_fillstyle fillstyle, enum disp_fillstyle fillstyle,
uint16_t pixelsize) uint16_t pixelsize
); ));
/** /**
* Immediately send the contents of a framebuffer to the display. This overrides * Immediately send the contents of a framebuffer to the display. This overrides
...@@ -1038,17 +1830,36 @@ API(API_DISP_CIRC, ...@@ -1038,17 +1830,36 @@ API(API_DISP_CIRC,
* *
* - ``-EBUSY``: Display was already locked from another task. * - ``-EBUSY``: Display was already locked from another task.
*/ */
API(API_DISP_FRAMEBUFFER, int epic_disp_framebuffer(union disp_framebuffer *fb)); API(API_DISP_FRAMEBUFFER, int epic_disp_framebuffer(
union disp_framebuffer *fb
));
/**
* Light Sensor
* ============
*/
/**
* Set the backlight brightness.
*
* Note that this function does not require acquiring the display.
*
* :param brightness: brightness from 0 - 100
* :return: ``0`` on success or negative value in case of an error
*/
API(API_DISP_BACKLIGHT, int epic_disp_backlight(uint16_t brightness));
/** /**
* Start continuous readout of the light sensor. Will read light level * Start continuous readout of the light sensor. Will read light level
* at preconfigured interval and make it available via `epic_light_sensor_get()`. * at preconfigured interval and make it available via :c:func:`epic_light_sensor_get`.
* *
* If the continuous readout was already running, this function will silently pass. * If the continuous readout was already running, this function will silently pass.
* *
* *
* :return: `0` if the start was successful or a negative error value * :return: ``0`` if the start was successful or a negative error value
* if an error occured. Possible errors: * if an error occured. Possible errors:
* *
* - ``-EBUSY``: The timer could not be scheduled. * - ``-EBUSY``: The timer could not be scheduled.
...@@ -1059,7 +1870,7 @@ API(API_LIGHT_SENSOR_RUN, int epic_light_sensor_run()); ...@@ -1059,7 +1870,7 @@ API(API_LIGHT_SENSOR_RUN, int epic_light_sensor_run());
* Get the last light level measured by the continuous readout. * Get the last light level measured by the continuous readout.
* *
* :param uint16_t* value: where the last light level should be written. * :param uint16_t* value: where the last light level should be written.
* :return: `0` if the readout was successful or a negative error * :return: ``0`` if the readout was successful or a negative error
* value. Possible errors: * value. Possible errors:
* *
* - ``-ENODATA``: Continuous readout not currently running. * - ``-ENODATA``: Continuous readout not currently running.
...@@ -1072,13 +1883,27 @@ API(API_LIGHT_SENSOR_GET, int epic_light_sensor_get(uint16_t* value)); ...@@ -1072,13 +1883,27 @@ API(API_LIGHT_SENSOR_GET, int epic_light_sensor_get(uint16_t* value));
* *
* If the continuous readout wasn't running, this function will silently pass. * If the continuous readout wasn't running, this function will silently pass.
* *
* :return: `0` if the stop was sucessful or a negative error value * :return: ``0`` if the stop was sucessful or a negative error value
* if an error occured. Possible errors: * if an error occured. Possible errors:
* *
* - ``-EBUSY``: The timer stop could not be scheduled. * - ``-EBUSY``: The timer stop could not be scheduled.
*/ */
API(API_LIGHT_SENSOR_STOP, int epic_light_sensor_stop()); API(API_LIGHT_SENSOR_STOP, int epic_light_sensor_stop());
/**
* Get the light level directly.
*
* Each call has an intrinsic delay of about 240us, I recommend another
* 100-300us delay between calls. Whether or not the IR LED is fast enough is
* another issue.
*
* :return: Light level
*
* .. versionadded:: 1.8
*/
API(API_LIGHT_SENSOR_READ, uint16_t epic_light_sensor_read(void));
/** /**
* File * File
* ==== * ====
...@@ -1114,10 +1939,9 @@ API(API_FILE_READ, int epic_file_read(int fd, void* buf, size_t nbytes)); ...@@ -1114,10 +1939,9 @@ API(API_FILE_READ, int epic_file_read(int fd, void* buf, size_t nbytes));
* :return: ``< 0`` on error, ``nbytes`` on success. (Partial writes don't occur on success!) * :return: ``< 0`` on error, ``nbytes`` on success. (Partial writes don't occur on success!)
* *
*/ */
API( API(API_FILE_WRITE, int epic_file_write(
API_FILE_WRITE, int fd, const void* buf, size_t nbytes
int epic_file_write(int fd, const void* buf, size_t nbytes) ));
);
/** */ /** */
API(API_FILE_FLUSH, int epic_file_flush(int fd)); API(API_FILE_FLUSH, int epic_file_flush(int fd));
...@@ -1261,11 +2085,42 @@ API(API_FILE_RENAME, int epic_file_rename(const char *oldp, const char* newp)); ...@@ -1261,11 +2085,42 @@ API(API_FILE_RENAME, int epic_file_rename(const char *oldp, const char* newp));
*/ */
API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname)); API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname));
/**
* Check whether the filesystem is currently attached and a call like
* :c:func:`epic_file_open` has a chance to succeed.
*
* :return: ``true`` if the filesystem is attached and ``false`` if the
* filesystem is not attached.
*/
API(API_FILE_FS_ATTACHED, bool epic_fs_is_attached(void));
/** /**
* RTC * RTC
* === * ===
*/ */
/**
* Get the monotonic time in seconds.
*
* :return: monotonic time in seconds
*
* .. versionadded:: 1.11
*/
API(API_RTC_GET_MONOTONIC_SECONDS,
uint32_t epic_rtc_get_monotonic_seconds(void)
);
/**
* Get the monotonic time in ms.
*
* :return: monotonic time in milliseconds
*
* .. versionadded:: 1.11
*/
API(API_RTC_GET_MONOTONIC_MILLISECONDS,
uint64_t epic_rtc_get_monotonic_milliseconds(void)
);
/** /**
* Read the current RTC value. * Read the current RTC value.
* *
...@@ -1273,16 +2128,25 @@ API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname)); ...@@ -1273,16 +2128,25 @@ API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname));
*/ */
API(API_RTC_GET_SECONDS, uint32_t epic_rtc_get_seconds(void)); API(API_RTC_GET_SECONDS, uint32_t epic_rtc_get_seconds(void));
/**
* Read the current RTC value in ms.
*
* :return: Unix time in milliseconds
*/
API(API_RTC_GET_MILLISECONDS, uint64_t epic_rtc_get_milliseconds(void));
/** /**
* Sets the current RTC time in milliseconds * Sets the current RTC time in milliseconds
*/ */
API(API_RTC_SET_MILLISECONDS, void epic_rtc_set_milliseconds(uint64_t milliseconds)); API(API_RTC_SET_MILLISECONDS, void epic_rtc_set_milliseconds(
uint64_t milliseconds
));
/** /**
* Schedule the RTC alarm for the given timestamp. * Schedule the RTC alarm for the given timestamp.
* *
* :param uint32_t timestamp: When to schedule the IRQ * :param uint32_t timestamp: When to schedule the IRQ
* :return: `0` on success or a negative value if an error occured. Possible * :return: ``0`` on success or a negative value if an error occured. Possible
* errors: * errors:
* *
* - ``-EINVAL``: RTC is in a bad state * - ``-EINVAL``: RTC is in a bad state
...@@ -1290,7 +2154,7 @@ API(API_RTC_SET_MILLISECONDS, void epic_rtc_set_milliseconds(uint64_t millisecon ...@@ -1290,7 +2154,7 @@ API(API_RTC_SET_MILLISECONDS, void epic_rtc_set_milliseconds(uint64_t millisecon
API(API_RTC_SCHEDULE_ALARM, int epic_rtc_schedule_alarm(uint32_t timestamp)); API(API_RTC_SCHEDULE_ALARM, int epic_rtc_schedule_alarm(uint32_t timestamp));
/** /**
* **Interrupt Service Routine** * **Interrupt Service Routine** for :c:data:`EPIC_INT_RTC_ALARM`
* *
* ``epic_isr_rtc_alarm()`` is called when the RTC alarm triggers. The RTC alarm * ``epic_isr_rtc_alarm()`` is called when the RTC alarm triggers. The RTC alarm
* can be scheduled using :c:func:`epic_rtc_schedule_alarm`. * can be scheduled using :c:func:`epic_rtc_schedule_alarm`.
...@@ -1298,20 +2162,605 @@ API(API_RTC_SCHEDULE_ALARM, int epic_rtc_schedule_alarm(uint32_t timestamp)); ...@@ -1298,20 +2162,605 @@ API(API_RTC_SCHEDULE_ALARM, int epic_rtc_schedule_alarm(uint32_t timestamp));
API_ISR(EPIC_INT_RTC_ALARM, epic_isr_rtc_alarm); API_ISR(EPIC_INT_RTC_ALARM, epic_isr_rtc_alarm);
/** /**
* TRNG * RNG
* ==== * ====
*/ */
/** /**
* Read random bytes from the TRNG. * Read random bytes from the TRNG.
* *
* Be aware that this function returns raw unprocessed bytes from
* the TRNG. They might be biased or have other kinds of imperfections.
*
* Use :c:func:`epic_csprng_read` for cryptographically safe random
* numbers instead.
*
* .. warning::
*
* The exact behaviour of the TRNG is not well understood. Its
* distribution and other parameters are unknown. Only use this
* function if you really want the unmodified values from the
* hardware TRNG to experiment with it.
*
* :param uint8_t * dest: Destination buffer * :param uint8_t * dest: Destination buffer
* :param size: Number of bytes to read. * :param size: Number of bytes to read.
* :return: `0` on success or a negative value if an error occured. Possible * :return: ``0`` on success or a negative value if an error occured. Possible
* errors: * errors:
* *
* - ``-EFAULT``: Invalid destination address. * - ``-EFAULT``: Invalid destination address.
*/ */
API(API_TRNG_READ, int epic_trng_read(uint8_t *dest, size_t size)); API(API_TRNG_READ, int epic_trng_read(uint8_t *dest, size_t size));
/**
* Read random bytes from the CSPRNG.
*
* The random bytes returned are safe to be used for cryptography.
*
* :param uint8_t * dest: Destination buffer
* :param size: Number of bytes to read.
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-EFAULT``: Invalid destination address.
*/
API(API_CSPRNG_READ, int epic_csprng_read(uint8_t *dest, size_t size));
/**
* MAX30001
* ========
*/
/**
* Configuration for a MAX30001 sensor.
*
* This struct is used when enabling the sensor using
* :c:func:`epic_max30001_enable_sensor`.
*/
struct max30001_sensor_config {
/**
* Number of samples Epicardium should keep for this sensor. Do not set
* this number too high as the sample buffer will eat RAM.
*/
size_t sample_buffer_len;
/**
* Sample rate for the sensor in Hz.
*/
uint16_t sample_rate;
/**
* Set to true if the second lead comes from USB-C
*/
bool usb;
/**
* Set to true if the interal lead bias of the MAX30001 is to be used.
*/
bool bias;
/** Always zero. Reserved for future parameters. */
uint8_t _padding[8];
};
/**
* Enable a MAX30001 ECG sensor.
*
* Calling this function will instruct the MAX30001 to collect data for this
* sensor. You can then retrieve the samples using :c:func:`epic_stream_read`.
*
* :param max30001_sensor_config* config: Configuration for this sensor.
* :returns: A sensor descriptor which can be used with
* :c:func:`epic_stream_read` or a negative error value:
*
* - ``-EBUSY``: The MAX30001 driver is currently busy with other tasks and
* could not be acquired for enabling a sensor.
*
* .. versionadded:: 1.6
*/
API(API_MAX30001_ENABLE, int epic_max30001_enable_sensor(
struct max30001_sensor_config *config
));
/**
* Disable MAX30001
*
* .. versionadded:: 1.6
*/
API(API_MAX30001_DISABLE, int epic_max30001_disable_sensor());
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_MAX30001_ECG`
*
* This interrupt handler is called whenever the MAX30001 ECG has new data
* available.
*/
API_ISR(EPIC_INT_MAX30001_ECG, epic_isr_max30001_ecg);
/**
* USB
* ===
*/
/**
* De-initialize the currently configured USB device (if any)
*
*/
API(API_USB_SHUTDOWN, int epic_usb_shutdown(void));
/**
* Configure the USB peripheral to export the internal FLASH
* as a Mass Storage device.
*/
API(API_USB_STORAGE, int epic_usb_storage(void));
/**
* Configure the USB peripheral to provide card10's stdin/stdout
* on a USB CDC-ACM device.
*/
API(API_USB_CDCACM, int epic_usb_cdcacm(void));
/**
* WS2812
* ======
*/
/**
* Takes a gpio pin specified with the gpio module and transmits
* the led data. The format ``GG:RR:BB`` is expected.
*
* :param uint8_t pin: The gpio pin to be used for data.
* :param uint8_t * pixels: The buffer, in which the pixel data is stored.
* :param uint32_t n_bytes: The size of the buffer.
*
* .. versionadded:: 1.10
*/
API(API_WS2812_WRITE, void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes));
/**
* Configuration
* =============
*/
/**
* Read an integer from the configuration file
*
* :param char* key: Name of the option to read
* :param int* value: Place to read the value into
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-ENOENT``: Value can not be read
*
* .. versionadded:: 1.13
*/
API(API_CONFIG_GET_INTEGER, int epic_config_get_integer(const char *key, int *value));
/**
* Read a boolean from the configuration file
*
* :param char* key: Name of the option to read
* :param bool* value: Place to read the value into
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-ENOENT``: Value can not be read
*
* .. versionadded:: 1.13
*/
API(API_CONFIG_GET_BOOLEAN, int epic_config_get_boolean(const char *key, bool *value));
/**
* Read a string from the configuration file.
*
* If the buffer supplied is too small for the config option,
* no error is reported and the first ``buf_len - 1`` characters
* are returned (0 terminated).
*
* :param char* key: Name of the option to read
* :param char* buf: Place to read the string into
* :param size_t buf_len: Size of the provided buffer
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-ENOENT``: Value can not be read
*
* .. versionadded:: 1.13
*/
API(API_CONFIG_GET_STRING, int epic_config_get_string(const char *key, char *buf, size_t buf_len));
/**
* Write a string to the configuration file.
*
* :param char* key: Name of the option to write
* :param char* value: The value to write
* :return: ``0`` on success or a negative value if an error occured. Possivle
* errors:
*
* - ``-EINVAL``: Parameters out of range
* - ``-ENOENT``: Key already exists but can not be read
* - ``-EIO`` : Unspecified I/O error
* - Any fopen/fread/fwrite/fclose related error code
*
* .. versionadded:: 1.16
*/
API(API_CONFIG_SET_STRING, int epic_config_set_string(const char *key, const char *value));
/**
* Bluetooth Low Energy (BLE)
* ==========================
*/
/**
* BLE event type
*/
enum epic_ble_event_type {
/** No event pending */
BLE_EVENT_NONE = 0,
/** Numeric comparison requested */
BLE_EVENT_HANDLE_NUMERIC_COMPARISON = 1,
/** A pairing procedure has failed */
BLE_EVENT_PAIRING_FAILED = 2,
/** A pairing procedure has successfully completed */
BLE_EVENT_PAIRING_COMPLETE = 3,
/** New scan data is available */
BLE_EVENT_SCAN_REPORT = 4,
BLE_EVENT_ATT_EVENT = 5,
BLE_EVENT_ATT_WRITE = 6,
BLE_EVENT_DM_EVENT = 7,
};
/**
* MicroPython Bluetooth support data types. Please
* do not use them until they are stabilized.
*/
typedef uint8_t bdAddr_t[6];
struct epic_wsf_header
{
/** General purpose parameter passed to event handler */
uint16_t param;
/** General purpose event value passed to event handler */
uint8_t event;
/** General purpose status value passed to event handler */
uint8_t status;
};
struct epic_att_event
{
/** Header structure */
struct epic_wsf_header hdr;
/** Value */
uint8_t *pValue;
/** Value length */
uint16_t valueLen;
/** Attribute handle */
uint16_t handle;
/** TRUE if more response packets expected */
uint8_t continuing;
/** Negotiated MTU value */
uint16_t mtu;
};
struct epic_hciLeConnCmpl_event
{ /** Event header */
struct epic_wsf_header hdr;
/** Status. */
uint8_t status;
/** Connection handle. */
uint16_t handle;
/** Local connection role. */
uint8_t role;
/** Peer address type. */
uint8_t addrType;
/** Peer address. */
bdAddr_t peerAddr;
/** Connection interval */
uint16_t connInterval;
/** Connection latency. */
uint16_t connLatency;
/** Supervision timeout. */
uint16_t supTimeout;
/** Clock accuracy. */
uint8_t clockAccuracy;
/** enhanced fields */
/** Local RPA. */
bdAddr_t localRpa;
/** Peer RPA. */
bdAddr_t peerRpa;
};
/*! \brief Disconnect complete event */
struct epic_hciDisconnectCmpl_event
{
/** Event header */
struct epic_wsf_header hdr;
/** Disconnect complete status. */
uint8_t status;
/** Connect handle. */
uint16_t handle;
/** Reason. */
uint8_t reason;
};
struct epic_dm_event
{
union {
/** LE connection complete. */
struct epic_hciLeConnCmpl_event leConnCmpl;
/** Disconnect complete. */
struct epic_hciDisconnectCmpl_event disconnectCmpl;
};
};
struct epic_att_write
{
/** Header structure */
struct epic_wsf_header hdr;
/** Value length */
uint16_t valueLen;
/** Attribute handle */
uint16_t handle;
uint8_t operation;
uint16_t offset;
void *buffer;
};
struct epic_ble_event {
enum epic_ble_event_type type;
union {
void *data;
struct epic_att_event *att_event;
struct epic_dm_event *dm_event;
struct epic_att_write *att_write;
};
};
/**
* Scan report data. Based on ``hciLeAdvReportEvt_t`` from BLE stack.
*
* TODO: 64 bytes for data is an arbitrary number ATM */
struct epic_scan_report
{
/** advertising or scan response data. */
uint8_t data[64];
/** length of advertising or scan response data. */
uint8_t len;
/** RSSI. */
int8_t rssi;
/** Advertising event type. */
uint8_t eventType;
/** Address type. */
uint8_t addrType;
/** Device address. */
uint8_t addr[6];
/** direct fields */
/** Direct advertising address type. */
uint8_t directAddrType;
/** Direct advertising address. */
uint8_t directAddr[6];
};
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_BLE`
*
* :c:func:`epic_isr_ble` is called when the BLE stack wants to signal an
* event to the application. You can use :c:func:`epic_ble_get_event` to obtain
* the event which triggered this interrupt.
*
* Currently supported events:
*
* :c:data:`BLE_EVENT_HANDLE_NUMERIC_COMPARISON`:
* An ongoing pairing procedure requires a numeric comparison to complete.
* The compare value can be retreived using :c:func:`epic_ble_get_compare_value`.
*
* :c:data:`BLE_EVENT_PAIRING_FAILED`:
* A pairing procedure failed. The stack automatically went back advertising
* and accepting new pairings.
*
* :c:data:`BLE_EVENT_PAIRING_COMPLETE`:
* A pairing procedure has completed sucessfully.
* The stack automatically persists the pairing information, creating a bond.
*
* .. versionadded:: 1.16
*/
API_ISR(EPIC_INT_BLE, epic_isr_ble);
/**
* Retreive the event which triggered :c:func:`epic_isr_ble`
*
* .. versionadded:: 1.16
* .. versionchanged:: 1.17
*/
API(API_BLE_GET_EVENT, int epic_ble_get_event(struct epic_ble_event *e));
/**
* Retrieve the compare value of an ongoing pairing procedure.
*
* If no pairing procedure is ongoing, the returned value is undefined.
*
* :return: 6 digit long compare value
*
* .. versionadded:: 1.16
*/
API(API_BLE_GET_COMPARE_VALUE, uint32_t epic_ble_get_compare_value(void));
/**
* Retrieve the (file) name of the last pairing which was successful.
*
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-ENOENT``: There was no successful pairing yet.
*
* .. versionadded:: 1.16
*/
API(API_BLE_GET_LAST_PAIRING_NAME, int epic_ble_get_last_pairing_name(char *buf, size_t buf_size));
/**
* Retrieve the name of the peer to which we are connected
*
* The name might be empty if the peer device does not expose it or
* if it has not yet been read from it.
*
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-ENOENT``: There is no active connection at the moment.
*
* .. versionadded:: 1.16
*/
API(API_BLE_GET_PEER_DEVICE_NAME, int epic_ble_get_peer_device_name(char *buf, size_t buf_size));
/**
* Indicate wether the user confirmed the compare value.
*
* If a pariring procedure involving a compare value is ongoing and this
* function is called with confirmed set to ``true``, it will try to
* proceed and complete the pairing process. If called with ``false``, the
* pairing procedure will be aborted.
*
* :param bool confirmed: ``true`` if the user confirmed the compare value.
*
* .. versionadded:: 1.16
*/
API(API_BLE_COMPARE_RESPONSE, void epic_ble_compare_response(bool confirmed));
/**
* Set the desired mode of the BLE stack.
*
* There are three allowed modes:
*
* - Peripheral which is not bondable (bondable = ``false``, scanner = ``false``).
* - Peripheral which is bondable (bondable = ``true``, scanner = ``false``).
* - Observer which scans for advertisements (bondable = ``false``, scanner = ``true``).
*
* By default the card10 will not allow new bondings to be made. New
* bondings have to explicitly allowed by calling this function.
*
* While bondable the card10 will change its advertisements to
* indicate to scanning hosts that it is available for discovery.
*
* When scanning is active, :c:data:`BLE_EVENT_SCAN_REPORT` events will be sent
* and the scan reports can be fetched using :c:func:`epic_ble_get_scan_report`.
*
* When switching applications new bondings are automatically
* disallowed and scanning is stopped.
*
* :param bool bondable: ``true`` if new bondings should be allowed.
* :param bool scanner: ``true`` if scanning should be turned on.
*
* .. versionadded:: 1.16
*/
API(API_BLE_SET_MODE, void epic_ble_set_mode(bool bondable, bool scanner));
/**
* Retrieve a scan report from the queue of scan reports.
*
* :param struct\ epic_scan_report* rpt: Pointer where the report will be stored.
*
* :return: ``0`` on success or a negative value if an error occured. Possible
* errors:
*
* - ``-ENOENT``: No scan report available
*
*/
API(API_BLE_GET_SCAN_REPORT, int epic_ble_get_scan_report(struct epic_scan_report *rpt));
/**
* Send an input report to the host.
*
* :param uint8_t report_id: The id of the report to use. 1: keyboard, 2: mouse, 3: consumer control
* :param uint8_t* data: Data to be reported.
* :param uint8_t len: Length in bytes of the data to be reported. Maximum length is 8 bytes.
*
* :return: ``0`` on success, ``1`` if the report is queued or a negative value
* if an error occured. Possible errors:
*
* - ``-EIO``: There is no host device connected or BLE HID is not enabled.
* - ``-EAGAIN``: There is no space in the queue available. Try again later.
* - ``-EINVAL``: Either the report_id is out of range or the data is too long.
*
*/
API(API_BLE_HID_SEND_REPORT, int epic_ble_hid_send_report(uint8_t report_id, uint8_t *data, uint8_t len));
/**
* MicroPython BLE Support API
* ---------------------------
* The following API calls are to be used for MicroPython BLE support.
*
* .. warning::
*
* The following epic-calls are **not** part of the stable public API and
* thus **no** guarantee about stability or behavior is made. Do not use
* these outside of Pycardium unless you can live with sudden breakage!!
*
* They are only documented here for completeness and as a reference for
* firmware hackers, not for common usage.
*/
/** Private API call for Pycardium BLE support. */
API(API_BLE_INIT, int epic_ble_init(void));
/** Private API call for Pycardium BLE support. */
API(API_BLE_DEINIT, int epic_ble_deinit(void));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_DYN_CREATE_GROUP, int epic_atts_dyn_create_service(const uint8_t *uuid, uint8_t uuid_len, uint16_t group_size, void **pSvcHandle));
//API(API_BLE_ATTS_DYN_DELETE_GROUP, void AttsDynDeleteGroup(void *pSvcHandle));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_DYN_DELETE_GROUPS, int epic_ble_atts_dyn_delete_groups(void));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_DYN_ADD_CHARACTERISTIC, int epic_atts_dyn_add_characteristic(void *pSvcHandle, const uint8_t *uuid, uint8_t uuid_len, uint8_t flags, uint16_t maxLen, uint16_t *value_handle));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_DYN_ADD_DESCRIPTOR, int epic_ble_atts_dyn_add_descriptor(void *pSvcHandle, const uint8_t *uuid, uint8_t uuid_len, uint8_t flags, const uint8_t *value, uint16_t value_len, uint16_t maxLen, uint16_t *descriptor_handle));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_SEND_SERVICE_CHANGED_IND, int epic_atts_dyn_send_service_changed_ind(void));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_SET_ATTR, int epic_ble_atts_set_attr(uint16_t handle, const uint8_t *value, uint16_t value_len));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_HANDLE_VALUE_NTF, int epic_ble_atts_handle_value_ntf(uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_HANDLE_VALUE_IND, int epic_ble_atts_handle_value_ind(uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTS_SET_BUFFER, int epic_ble_atts_set_buffer(uint16_t value_handle, size_t len, bool append));
/** Private API call for Pycardium BLE support. */
API(API_BLE_FREE_EVENT, int epic_ble_free_event(struct epic_ble_event *e));
/** Private API call for Pycardium BLE support. */
API(API_BLE_CLOSE_CONNECTION, void epic_ble_close_connection(uint8_t connId));
/** Private API call for Pycardium BLE support. */
API(API_BLE_IS_CONNECTION_OPEN, int epic_ble_is_connection_open(void));
/** Private API call for Pycardium BLE support. */
API(API_BLE_SET_DEVICE_NAME, int epic_ble_set_device_name(const uint8_t *buf, uint16_t len));
/** Private API call for Pycardium BLE support. */
API(API_BLE_GET_DEVICE_NAME, int epic_ble_get_device_name(uint8_t **buf, uint16_t *len));
/** Private API call for Pycardium BLE support. */
API(API_BLE_GET_ADDRESS, void epic_ble_get_address(uint8_t *addr));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ADVERTISE, int epic_ble_advertise(int interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len, bool connectable));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ADVERTISE_STOP, int epic_ble_advertise_stop(void));
/** Private API call for Pycardium BLE support. */
API(API_BLE_DISCOVER_PRIMARY_SERVICES, int epic_ble_attc_discover_primary_services(uint8_t connId, const uint8_t *uuid, uint8_t uuid_len));
/** Private API call for Pycardium BLE support. */
API(API_BLE_DISCOVER_CHARACTERISTICS, int epic_ble_attc_discover_characteristics(uint8_t connId, uint16_t start_handle, uint16_t end_handle));
/** Private API call for Pycardium BLE support. */
API(API_BLE_DISCOVER_DESCRIPTORS, int epic_ble_attc_discover_descriptors(uint8_t connId, uint16_t start_handle, uint16_t end_handle));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTC_READ, int epic_ble_attc_read(uint8_t connId, uint16_t value_handle));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTC_WRITE_NO_RSP, int epic_ble_attc_write_no_rsp(uint8_t connId, uint16_t value_handle, const uint8_t *value, uint16_t value_len));
/** Private API call for Pycardium BLE support. */
API(API_BLE_ATTC_WRITE, int epic_ble_attc_write(uint8_t connId, uint16_t value_handle, const uint8_t *value, uint16_t value_len));
#endif /* _EPICARDIUM_H */ #endif /* _EPICARDIUM_H */
...@@ -29,6 +29,17 @@ int epic_file_open(const char *filename, const char *mode) ...@@ -29,6 +29,17 @@ int epic_file_open(const char *filename, const char *mode)
return res; return res;
} }
bool epic_fs_is_attached(void)
{
EpicFileSystem *fs;
if (efs_lock_global(&fs) == 0) {
efs_unlock_global(fs);
return true;
} else {
return false;
}
}
int epic_file_close(int fd) int epic_file_close(int fd)
{ {
EpicFileSystem *fs; EpicFileSystem *fs;
......
#ifndef EPICARDIUM_MODULE_FILESYSTEM_INCLUDED
#define EPICARDIUM_MODULE_FILESYSTEM_INCLUDED
/* ---------- FAT fs ------------------------------------------------------ */
#include <stdbool.h>
#include "epicardium.h"
/**
* module initialization - to be called once at startup before any FreeRTOS tasks
* have been started
*
* calls fatfs_attach
*/
void fatfs_init(void);
/**
* initialize and mount the FLASH storage
*
* NOTE: not safe to be called from an ISR
*/
int fatfs_attach(void);
/**
* asynchronously attach the FLASH storage
*
* safe to be called from an ISR
*/
void fatfs_schedule_attach(void);
/** close all opened FDs, sync and deinitialize FLASH layer */
void fatfs_detach(void);
/** close all onpened FDs
* TODO: add ability to close FDs opened by core0/core1 only
*/
#define EPICARDIUM_COREMASK_0 0x01
#define EPICARDIUM_COREMASK_1 0x02
#define EPICARDIUM_COREMASK_BOTH 0x03
void fatfs_close_all(int coreMask);
#endif//EPICARDIUM_MODULE_FILESYSTEM_INCLUDED
...@@ -16,21 +16,22 @@ ...@@ -16,21 +16,22 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <semphr.h> #include <semphr.h>
#include <timers.h>
#include "fs/internal.h" #include "fs/internal.h"
#include "modules/filesystem.h" #include "fs/filesystem.h"
#include "user_core/user_core.h"
#include "epicardium.h" #include "epicardium.h"
#include "card10.h" #include "card10.h"
#include "modules/log.h" #include "os/core.h"
#include "modules/modules.h"
#include "api/common.h"
#include "os/mutex.h"
#define SSLOG_DEBUG(...) LOG_DEBUG("fatfs", __VA_ARGS__) #define SSLOG_DEBUG(...) LOG_DEBUG("fatfs", __VA_ARGS__)
#define SSLOG_INFO(...) LOG_INFO("fatfs", __VA_ARGS__) #define SSLOG_INFO(...) LOG_INFO("fatfs", __VA_ARGS__)
#define SSLOG_ERR(...) LOG_ERR("fatfs", __VA_ARGS__) #define SSLOG_ERR(...) LOG_ERR("fatfs", __VA_ARGS__)
#ifndef EPIC_FAT_STATIC_SEMAPHORE
#define EPIC_FAT_STATIC_SEMAPHORE 0
#endif
/* clang-format off */ /* clang-format off */
#define EPIC_FAT_MAX_OPENED (1 << (EPIC_FAT_FD_INDEX_BITS)) #define EPIC_FAT_MAX_OPENED (1 << (EPIC_FAT_FD_INDEX_BITS))
#define EPIC_FAT_FD_GENERATION_BITS (31 - (EPIC_FAT_FD_INDEX_BITS)) #define EPIC_FAT_FD_GENERATION_BITS (31 - (EPIC_FAT_FD_INDEX_BITS))
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
struct FatObject { struct FatObject {
uint32_t generation; uint32_t generation;
int coreMask;
enum epic_stat_type type; enum epic_stat_type type;
union { union {
FIL file; FIL file;
...@@ -54,17 +56,16 @@ struct FatObject { ...@@ -54,17 +56,16 @@ struct FatObject {
struct EpicFileSystem { struct EpicFileSystem {
struct FatObject pool[EPIC_FAT_MAX_OPENED]; struct FatObject pool[EPIC_FAT_MAX_OPENED];
uint32_t generationCount; uint32_t generationCount;
bool initialized; bool attached;
FATFS FatFs; FATFS FatFs;
int lockCoreMask;
}; };
// this table converts from FRESULT to POSIX errno // this table converts from FRESULT to POSIX errno
static const int s_libffToErrno[20]; static const int s_libffToErrno[20];
static const char *f_get_rc_string(FRESULT rc); static const char *f_get_rc_string(FRESULT rc);
static bool globalLockAccquire(); static void efs_close_all(EpicFileSystem *fs, int coreMask);
static void globalLockRelease();
static void efs_close_all(EpicFileSystem *fs);
/** /**
* if EPICSTAT_NONE is passed to `expected`, the type is not checked. * if EPICSTAT_NONE is passed to `expected`, the type is not checked.
...@@ -92,11 +93,12 @@ static void efs_init_stat(struct epic_stat *stat, FILINFO *finfo); ...@@ -92,11 +93,12 @@ static void efs_init_stat(struct epic_stat *stat, FILINFO *finfo);
static EpicFileSystem s_globalFileSystem; static EpicFileSystem s_globalFileSystem;
#if (EPIC_FAT_STATIC_SEMAPHORE == 1) static struct mutex fatfs_lock = { 0 };
static StaticSemaphore_t s_globalLockBuffer;
#endif
static SemaphoreHandle_t s_globalLock = NULL; static void cb_attachTimer(void *a, uint32_t b)
{
fatfs_attach();
}
void fatfs_init() void fatfs_init()
{ {
...@@ -108,11 +110,8 @@ void fatfs_init() ...@@ -108,11 +110,8 @@ void fatfs_init()
assert(!s_initCalled); assert(!s_initCalled);
s_initCalled = true; s_initCalled = true;
#if (EPIC_FAT_STATIC_SEMAPHORE == 1) mutex_create(&fatfs_lock);
s_globalLock = xSemaphoreCreateMutexStatic(&s_globalLockBuffer);
#else
s_globalLock = xSemaphoreCreateMutex();
#endif
s_globalFileSystem.generationCount = 1; s_globalFileSystem.generationCount = 1;
fatfs_attach(); fatfs_attach();
} }
...@@ -131,27 +130,36 @@ int fatfs_attach() ...@@ -131,27 +130,36 @@ int fatfs_attach()
{ {
FRESULT ff_res; FRESULT ff_res;
int rc = 0; int rc = 0;
if (globalLockAccquire()) {
mutex_lock(&fatfs_lock);
EpicFileSystem *fs = &s_globalFileSystem; EpicFileSystem *fs = &s_globalFileSystem;
if (!fs->initialized) { if (!fs->attached) {
ff_res = f_mount(&fs->FatFs, "/", 0); ff_res = f_mount(&fs->FatFs, "/", 0);
if (ff_res == FR_OK) { if (ff_res == FR_OK) {
fs->initialized = true; fs->attached = true;
SSLOG_DEBUG("FatFs mounted\n"); SSLOG_INFO("attached\n");
} else { } else {
SSLOG_ERR( SSLOG_ERR(
"f_mount error %s\n", "f_mount error %s\n", f_get_rc_string(ff_res)
f_get_rc_string(ff_res)
); );
rc = -s_libffToErrno[ff_res]; rc = -s_libffToErrno[ff_res];
} }
} }
globalLockRelease(); mutex_unlock(&fatfs_lock);
return rc;
}
void fatfs_schedule_attach(void)
{
//if we're running in thread context, cont't call the *FromISR version
if (xPortIsInsideInterrupt()) {
xTimerPendFunctionCallFromISR(cb_attachTimer, NULL, 0, NULL);
} else { } else {
SSLOG_ERR("Failed to lock\n"); xTimerPendFunctionCall(
cb_attachTimer, NULL, 0, 1); //wait 1 tick
} }
return rc;
} }
void fatfs_detach() void fatfs_detach()
...@@ -159,7 +167,8 @@ void fatfs_detach() ...@@ -159,7 +167,8 @@ void fatfs_detach()
FRESULT ff_res; FRESULT ff_res;
EpicFileSystem *fs; EpicFileSystem *fs;
if (efs_lock_global(&fs) == 0) { if (efs_lock_global(&fs) == 0) {
efs_close_all(fs); if (fs->attached) {
efs_close_all(fs, EPICARDIUM_COREMASK_BOTH);
//unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj
ff_res = f_mount(NULL, "/", 0); ff_res = f_mount(NULL, "/", 0);
...@@ -170,13 +179,22 @@ void fatfs_detach() ...@@ -170,13 +179,22 @@ void fatfs_detach()
); );
} }
fs->initialized = false; fs->attached = false;
disk_deinitialize(); disk_deinitialize();
SSLOG_INFO("detached\n"); SSLOG_INFO("detached\n");
}
efs_unlock_global(fs); efs_unlock_global(fs);
} }
} }
void fatfs_close_all(int coreMask)
{
EpicFileSystem *fs;
if (efs_lock_global(&fs) == 0) {
efs_close_all(fs, coreMask);
efs_unlock_global(fs);
}
}
static const char *f_get_rc_string(FRESULT rc) static const char *f_get_rc_string(FRESULT rc)
{ {
static const TCHAR *rcstrings = static const TCHAR *rcstrings =
...@@ -195,34 +213,27 @@ static const char *f_get_rc_string(FRESULT rc) ...@@ -195,34 +213,27 @@ static const char *f_get_rc_string(FRESULT rc)
return p; return p;
} }
static bool globalLockAccquire()
{
return (int)(xSemaphoreTake(s_globalLock, FF_FS_TIMEOUT) == pdTRUE);
}
static void globalLockRelease()
{
xSemaphoreGive(s_globalLock);
}
int efs_lock_global(EpicFileSystem **fs) int efs_lock_global(EpicFileSystem **fs)
{ {
*fs = NULL; *fs = NULL;
if (!globalLockAccquire()) { mutex_lock(&fatfs_lock);
return -EBUSY; if (!s_globalFileSystem.attached) {
} mutex_unlock(&fatfs_lock);
if (!s_globalFileSystem.initialized) {
globalLockRelease();
return -ENODEV; return -ENODEV;
} }
*fs = &s_globalFileSystem; *fs = &s_globalFileSystem;
if (xTaskGetCurrentTaskHandle() == dispatcher_task_id) {
s_globalFileSystem.lockCoreMask = EPICARDIUM_COREMASK_1;
} else {
s_globalFileSystem.lockCoreMask = EPICARDIUM_COREMASK_0;
}
return 0; return 0;
} }
void efs_unlock_global(EpicFileSystem *fs) void efs_unlock_global(EpicFileSystem *fs)
{ {
(void)fs; (void)fs;
globalLockRelease(); mutex_unlock(&fatfs_lock);
} }
static bool efs_get_opened( static bool efs_get_opened(
...@@ -275,6 +286,7 @@ efs_get_new(EpicFileSystem *fs, uint32_t *idx, struct FatObject **obj, int *rc) ...@@ -275,6 +286,7 @@ efs_get_new(EpicFileSystem *fs, uint32_t *idx, struct FatObject **obj, int *rc)
} }
*obj = &fs->pool[index]; *obj = &fs->pool[index];
*idx = index;
return true; return true;
} }
...@@ -292,6 +304,7 @@ static int efs_obj_init( ...@@ -292,6 +304,7 @@ static int efs_obj_init(
} }
obj->type = type; obj->type = type;
obj->generation = generation; obj->generation = generation;
obj->coreMask = fs->lockCoreMask;
return EPIC_FAT_FD(index, generation); return EPIC_FAT_FD(index, generation);
} }
...@@ -300,6 +313,7 @@ static void efs_obj_deinit(EpicFileSystem *fs, struct FatObject *obj) ...@@ -300,6 +313,7 @@ static void efs_obj_deinit(EpicFileSystem *fs, struct FatObject *obj)
{ {
obj->type = EPICSTAT_NONE; obj->type = EPICSTAT_NONE;
obj->generation = 0; obj->generation = 0;
obj->coreMask = 0;
} }
/* here we're trying to mirror glibc's behaviour: /* here we're trying to mirror glibc's behaviour:
...@@ -392,9 +406,13 @@ int efs_close(EpicFileSystem *fs, int fd) ...@@ -392,9 +406,13 @@ int efs_close(EpicFileSystem *fs, int fd)
return res; return res;
} }
void efs_close_all(EpicFileSystem *fs) void efs_close_all(EpicFileSystem *fs, int coreMask)
{ {
assert(coreMask != 0);
for (int i = 0; i < EPIC_FAT_MAX_OPENED; ++i) { for (int i = 0; i < EPIC_FAT_MAX_OPENED; ++i) {
if (!(fs->pool[i].coreMask & coreMask)) {
continue;
}
switch (fs->pool[i].type) { switch (fs->pool[i].type) {
case EPICSTAT_FILE: case EPICSTAT_FILE:
f_close(&fs->pool[i].file); f_close(&fs->pool[i].file);
......
#include "fs_util.h"
#include "epicardium.h"
#include <stdint.h>
#include <string.h>
int fs_read_file(char *filename, void *data, int len)
{
int fd = epic_file_open(filename, "r");
if (fd < 0) {
return fd;
}
int res = epic_file_read(fd, data, len);
epic_file_close(fd);
return res;
}
int fs_read_text_file(char *filename, char *data, int len)
{
int readbytes;
if (len < 1)
return -1;
readbytes = fs_read_file(filename, data, len - 1);
if (readbytes < 0) {
data[0] = 0;
return readbytes;
};
data[readbytes] = 0;
while (readbytes > 0 && data[readbytes - 1] < 0x20) {
data[--readbytes] = 0;
};
return readbytes;
}
int fs_write_file(char *filename, const void *data, int len)
{
int fd = epic_file_open(filename, "w");
if (fd < 0) {
return fd;
}
int res = epic_file_write(fd, data, len);
epic_file_close(fd);
return res;
}
int fs_get_file_size(char *filename)
{
struct epic_stat stat;
int res = epic_file_stat(filename, &stat);
return res < 0 ? res : (int)stat.size;
}
#if 0
#include "ff.h"
FATFS FatFs; /* File system object for logical drive */
FS_USAGE FsUsage;
/* TODO: Port functions from r0ket/rad10 libs */
int fs_info(FATFS *fs)
{
memcpy(fs, &FatFs, sizeof(FATFS));
return 0;
}
int fs_usage(FATFS *fs, FS_USAGE *fs_usage)
{
FRESULT res;
DWORD tot_clust, fre_clust, sec_size;
res = f_getfree("/", &fre_clust, &fs);
if(res != FR_OK)
return -res;
// sectore size = sectors per cluster * sector size
#if FF_MAX_SS == FF_MIN_SS
sec_size = fs->csize * FF_MAX_SS;
#else
sec_size = fs->csize * fs.ssize;
#endif
// total/free sectors * sectore size
tot_clust = fs->n_fatent - 2;
fs_usage->total = tot_clust * sec_size; //FatFs.ssize;
fs_usage->free = fre_clust * sec_size; //FatFs.ssize;
return 0;
}
#endif
File moved
fs_sources = files(
'fileops.c',
'filesystem_fat.c',
'fs_util.c',
)
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include "epicardium.h" #include "epicardium.h"
#include "l0der/elf.h" #include "l0der/elf.h"
#include "modules/log.h" #include "os/core.h"
/* /*
* l0der is, in reality, a boneless operating-system style ELF loader. * l0der is, in reality, a boneless operating-system style ELF loader.
...@@ -119,7 +119,7 @@ static int _seek_and_read(int fd, uint32_t address, void *data, size_t count) ...@@ -119,7 +119,7 @@ static int _seek_and_read(int fd, uint32_t address, void *data, size_t count)
return res; return res;
} }
if ((res = epic_file_read(fd, data, count)) != count) { if ((size_t)(res = epic_file_read(fd, data, count)) != count) {
LOG_ERR("l0der", "_seek_and_read: could not read: %d", res); LOG_ERR("l0der", "_seek_and_read: could not read: %d", res);
return res; return res;
} }
...@@ -149,7 +149,7 @@ static int _read_section_header(int fd, uint32_t shdr_addr, Elf32_Shdr *shdr) ...@@ -149,7 +149,7 @@ static int _read_section_header(int fd, uint32_t shdr_addr, Elf32_Shdr *shdr)
* This function ensures basic memory sanity of a program header / segment. * This function ensures basic memory sanity of a program header / segment.
* It ensures that it points to a file region that is contained within the file fully. * It ensures that it points to a file region that is contained within the file fully.
*/ */
static int _check_program_header(int fd, int size, Elf32_Phdr *phdr) static int _check_program_header(int fd, size_t size, Elf32_Phdr *phdr)
{ {
// Check file size/offset. // Check file size/offset.
uint32_t file_start = phdr->p_offset; uint32_t file_start = phdr->p_offset;
...@@ -191,7 +191,7 @@ static int _check_program_header(int fd, int size, Elf32_Phdr *phdr) ...@@ -191,7 +191,7 @@ static int _check_program_header(int fd, int size, Elf32_Phdr *phdr)
* This function ensures basic memory sanity of a section header. * This function ensures basic memory sanity of a section header.
* It ensures that it points to a file region that is contained within the file fully. * It ensures that it points to a file region that is contained within the file fully.
*/ */
static int _check_section_header(int fd, int size, Elf32_Shdr *shdr) static int _check_section_header(int fd, size_t size, Elf32_Shdr *shdr)
{ {
// Check file size/offset. // Check file size/offset.
uint32_t file_start = shdr->sh_offset; uint32_t file_start = shdr->sh_offset;
...@@ -329,7 +329,7 @@ static int _load_segment(int fd, struct _pie_load_info *li, Elf32_Phdr *phdr) ...@@ -329,7 +329,7 @@ static int _load_segment(int fd, struct _pie_load_info *li, Elf32_Phdr *phdr)
* Parse dynamic symbol sections. * Parse dynamic symbol sections.
*/ */
static int _parse_dynamic_symbols( static int _parse_dynamic_symbols(
int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr int fd, size_t size, struct _pie_load_info *li, Elf32_Ehdr *hdr
) { ) {
int res; int res;
Elf32_Shdr shdr; Elf32_Shdr shdr;
...@@ -366,7 +366,7 @@ static int _parse_dynamic_symbols( ...@@ -366,7 +366,7 @@ static int _parse_dynamic_symbols(
return res; return res;
} }
for (int j = 0; j < sym_count; j++) { for (uint32_t j = 0; j < sym_count; j++) {
if ((res = epic_file_read( if ((res = epic_file_read(
fd, &sym, sizeof(Elf32_Sym))) != fd, &sym, sizeof(Elf32_Sym))) !=
sizeof(Elf32_Sym)) { sizeof(Elf32_Sym)) {
...@@ -402,9 +402,9 @@ static int _parse_dynamic_symbols( ...@@ -402,9 +402,9 @@ static int _parse_dynamic_symbols(
* the only one used when making 'standard' PIE binaries on RAM. However, other * the only one used when making 'standard' PIE binaries on RAM. However, other
* kinds might have to be implemented in the future. * kinds might have to be implemented in the future.
*/ */
static int static int _run_relocations(
_run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr) int fd, size_t size, struct _pie_load_info *li, Elf32_Ehdr *hdr
{ ) {
int res; int res;
Elf32_Shdr shdr; Elf32_Shdr shdr;
Elf32_Rel rel; Elf32_Rel rel;
...@@ -447,7 +447,7 @@ _run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr) ...@@ -447,7 +447,7 @@ _run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr)
return res; return res;
} }
for (int j = 0; j < reloc_count; j++) { for (uint32_t j = 0; j < reloc_count; j++) {
if ((res = epic_file_read( if ((res = epic_file_read(
fd, &rel, sizeof(Elf32_Rel))) != fd, &rel, sizeof(Elf32_Rel))) !=
sizeof(Elf32_Rel)) { sizeof(Elf32_Rel)) {
...@@ -464,7 +464,7 @@ _run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr) ...@@ -464,7 +464,7 @@ _run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr)
// (ie., do not resolve relocation - they default to a safe NULL) // (ie., do not resolve relocation - they default to a safe NULL)
uint8_t skip = 0; uint8_t skip = 0;
if (sym != 0) { if (sym != 0) {
for (int k = 0; k < li->weak_symbol_count; for (uint32_t k = 0; k < li->weak_symbol_count;
k++) { k++) {
if (li->weak_symbols[k] == sym) { if (li->weak_symbols[k] == sym) {
skip = 1; skip = 1;
...@@ -513,7 +513,7 @@ _run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr) ...@@ -513,7 +513,7 @@ _run_relocations(int fd, int size, struct _pie_load_info *li, Elf32_Ehdr *hdr)
* Load a l0dable PIE binary. * Load a l0dable PIE binary.
*/ */
static int static int
_load_pie(int fd, int size, Elf32_Ehdr *hdr, struct l0dable_info *info) _load_pie(int fd, size_t size, Elf32_Ehdr *hdr, struct l0dable_info *info)
{ {
int res; int res;
struct _pie_load_info li = { 0 }; struct _pie_load_info li = { 0 };
...@@ -545,7 +545,8 @@ _load_pie(int fd, int size, Elf32_Ehdr *hdr, struct l0dable_info *info) ...@@ -545,7 +545,8 @@ _load_pie(int fd, int size, Elf32_Ehdr *hdr, struct l0dable_info *info)
if (phdr.p_type == PT_LOAD) { if (phdr.p_type == PT_LOAD) {
// Check alignment request. // Check alignment request.
if ((phdr.p_vaddr % phdr.p_align) != 0) { if ((phdr.p_offset % phdr.p_align) !=
(phdr.p_vaddr % phdr.p_align)) {
LOG_ERR("l0der", LOG_ERR("l0der",
"_load_pie: phdr %d alignment too strict", "_load_pie: phdr %d alignment too strict",
i); i);
...@@ -646,7 +647,10 @@ int l0der_load_path(const char *path, struct l0dable_info *info) ...@@ -646,7 +647,10 @@ int l0der_load_path(const char *path, struct l0dable_info *info)
return res; return res;
} }
int size = epic_file_tell(fd); if ((res = epic_file_tell(fd)) < 0) {
return res;
}
size_t size = res;
if ((res = epic_file_seek(fd, 0, SEEK_SET)) != 0) { if ((res = epic_file_seek(fd, 0, SEEK_SET)) != 0) {
return res; return res;
......
#include "modules/modules.h" #include "modules/modules.h"
#include "modules/log.h" #include "os/core.h"
#include "modules/filesystem.h" #include "os/work_queue.h"
#include "fs/filesystem.h"
#include "drivers/drivers.h"
#include "user_core/user_core.h"
#include "os/config.h"
#include "card10-version.h" #include "card10-version.h"
#include "user_core/interrupts.h"
#include "drivers/display/epic_ctx.h"
#include "leds.h"
#include "version-splash.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"
...@@ -9,20 +18,89 @@ ...@@ -9,20 +18,89 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <machine/endian.h>
#include "epic_boot.c"
int main(void) int main(void)
{ {
watchdog_init();
LOG_INFO("startup", "Epicardium startup ..."); LOG_INFO("startup", "Epicardium startup ...");
LOG_INFO("startup", "Version " CARD10_VERSION); LOG_INFO("startup", "Version " CARD10_VERSION);
LOG_DEBUG("startup", "Initializing hardware ..."); LOG_DEBUG("startup", "Initializing hardware ...");
hardware_early_init(); hardware_early_init();
char *version_buf = CARD10_VERSION; load_config();
epic_disp_print(0, 5, "epicardium:", 0xffff, 0x0000);
epic_disp_print(0, 24, version_buf, 0xffff, 0x0000); migration_delete_app_launchers();
//LED feedback in case of dead display
epic_leds_set(11, 0, 0, 1);
epic_leds_set(12, 0, 0, 1);
epic_leds_set(13, 0, 0, 1);
epic_leds_set(14, 0, 0, 1);
epic_disp_clear(0x0000);
#if 0 /* reenable for future releases */
epic_leds_set_rocket(0, 31);
epic_leds_set_rocket(1, 31);
epic_leds_set_rocket(2, 31);
// TODO: Use blit function here
#if BYTE_ORDER == LITTLE_ENDIAN
for (size_t i = 0; i < sizeof(epicardium_ctx_fb); i += 2) {
epicardium_ctx_fb[i] = version_splash[i + 1];
epicardium_ctx_fb[i + 1] = version_splash[i];
}
#else
memcpy(epicardium_ctx_fb, version_splash, sizeof(epicardium_ctx_fb));
#endif
if (strcmp(CARD10_VERSION, "v1.17") != 0) {
ctx_font_size(epicardium_ctx, 20.0f);
ctx_text_baseline(epicardium_ctx, CTX_TEXT_BASELINE_BOTTOM);
ctx_rgba8(epicardium_ctx, 0xff, 0xc6, 0x00, 0xff);
ctx_text_align(epicardium_ctx, CTX_TEXT_ALIGN_CENTER);
ctx_move_to(epicardium_ctx, 80.0f, 58.0f);
ctx_text(epicardium_ctx, "Epicardium");
ctx_text_align(epicardium_ctx, CTX_TEXT_ALIGN_LEFT);
ctx_move_to(epicardium_ctx, 2.0f, 78.0f);
ctx_text(epicardium_ctx, CARD10_VERSION);
/* re-init for the first app */
disp_ctx_reinit();
}
epic_disp_update(); epic_disp_update();
mxc_delay(2000000); mxc_delay(2000000);
#else
for (uint32_t frame = 0; frame < 15; frame++) {
switch (frame) {
case 0:
epic_leds_set_rocket(0, 31);
break;
case 5:
epic_leds_set_rocket(1, 31);
break;
case 10:
epic_leds_set_rocket(2, 31);
break;
}
epic_disp_clear(0x0000);
epic_frame(epicardium_ctx, frame);
epic_disp_update();
}
/* re-init for the first app */
disp_ctx_reinit();
#endif
epic_leds_clear_all(0, 0, 0);
LOG_DEBUG("startup", "Initializing tasks ..."); LOG_DEBUG("startup", "Initializing tasks ...");
...@@ -32,23 +110,70 @@ int main(void) ...@@ -32,23 +110,70 @@ int main(void)
(const char *)"Serial", (const char *)"Serial",
configMINIMAL_STACK_SIZE * 2, configMINIMAL_STACK_SIZE * 2,
NULL, NULL,
tskIDLE_PRIORITY + 1, tskIDLE_PRIORITY + 3,
NULL) != pdPASS) { NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "Serial"); panic("Failed to create %s task!", "Serial");
abort();
} }
/* PMIC */ /* PMIC */
if (xTaskCreate( if (xTaskCreate(
vPmicTask, vPmicTask,
(const char *)"PMIC", (const char *)"PMIC",
configMINIMAL_STACK_SIZE, configMINIMAL_STACK_SIZE * 3,
NULL, NULL,
tskIDLE_PRIORITY + 4, tskIDLE_PRIORITY + 4,
NULL) != pdPASS) { NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "PMIC"); panic("Failed to create %s task!", "PMIC");
}
/* BHI160 */
if (xTaskCreate(
vBhi160Task,
(const char *)"BHI160 Driver",
configMINIMAL_STACK_SIZE * 2,
NULL,
tskIDLE_PRIORITY + 1,
NULL) != pdPASS) {
panic("Failed to create %s task!", "BHI160");
}
/* MAX30001 */
if (xTaskCreate(
vMAX30001Task,
(const char *)"MAX30001 Driver",
configMINIMAL_STACK_SIZE * 2,
NULL,
tskIDLE_PRIORITY + 1,
NULL) != pdPASS) {
panic("Failed to create %s task!", "MAX30001");
}
/* MAX86150 */
if (xTaskCreate(
vMAX86150Task,
(const char *)"MAX86150 Driver",
configMINIMAL_STACK_SIZE * 2,
NULL,
tskIDLE_PRIORITY + 1,
NULL) != pdPASS) {
panic("Failed to create %s task!", "MAX86150");
}
/* BSEC */
if (bsec_activate() == 0) {
if (xTaskCreate(
vBSECTask,
(const char *)"BSEC",
configMINIMAL_STACK_SIZE * 5,
NULL,
tskIDLE_PRIORITY + 1,
NULL) != pdPASS) {
LOG_CRIT(
"startup", "Failed to create %s task!", "BSEC"
);
abort(); abort();
} }
}
/* API */ /* API */
if (xTaskCreate( if (xTaskCreate(
...@@ -58,55 +183,62 @@ int main(void) ...@@ -58,55 +183,62 @@ int main(void)
NULL, NULL,
tskIDLE_PRIORITY + 2, tskIDLE_PRIORITY + 2,
&dispatcher_task_id) != pdPASS) { &dispatcher_task_id) != pdPASS) {
LOG_CRIT( panic("Failed to create %s task!", "API Dispatcher");
"startup", }
"Failed to create %s task!", /* Interrupts */
"API Dispatcher" if (xTaskCreate(
); vInterruptsTask,
abort(); (const char *)"Interrupt Dispatcher",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY + 2,
NULL) != pdPASS) {
panic("Failed to create %s task!", "Interrupt Dispatcher");
} }
/* BLE */ /* BLE */
if (ble_shall_start()) { if (ble_is_enabled()) {
if (xTaskCreate( if (xTaskCreate(
vBleTask, vBleTask,
(const char *)"BLE", (const char *)"BLE",
configMINIMAL_STACK_SIZE * 10, configMINIMAL_STACK_SIZE * 10,
NULL, NULL,
tskIDLE_PRIORITY + 1, tskIDLE_PRIORITY + 3,
NULL) != pdPASS) { NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "BLE"); panic("Failed to create %s task!", "BLE");
abort();
} }
} }
/* LEDs */ /* Lifecycle */
if (xTaskCreate( if (xTaskCreate(
vLedTask, vLifecycleTask,
(const char *)"LED", (const char *)"Lifecycle",
configMINIMAL_STACK_SIZE, configMINIMAL_STACK_SIZE * 4,
NULL, NULL,
tskIDLE_PRIORITY + 1, tskIDLE_PRIORITY + 3,
NULL) != pdPASS) { NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "LED"); panic("Failed to create %s task!", "Lifecycle");
abort();
} }
/* Lifecycle */ /* Work Queue */
if (xTaskCreate( if (xTaskCreate(
vLifecycleTask, vWorkQueueTask,
(const char *)"Lifecycle", (const char *)"Work Queue",
configMINIMAL_STACK_SIZE * 4, configMINIMAL_STACK_SIZE * 4,
NULL, NULL,
tskIDLE_PRIORITY + 3, tskIDLE_PRIORITY + 1,
NULL) != pdPASS) { NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "Lifecycle"); panic("Failed to create %s task!", "Work Queue");
abort();
} }
workqueue_init();
/*
* Initialize serial driver data structures.
*/
serial_init();
LOG_DEBUG("startup", "Starting FreeRTOS ..."); LOG_DEBUG("startup", "Starting FreeRTOS ...");
vTaskStartScheduler(); vTaskStartScheduler();
LOG_CRIT("startup", "FreeRTOS did not start due to unknown error!"); panic("FreeRTOS did not start due to unknown error!");
abort();
} }
...@@ -66,21 +66,50 @@ freertos = static_library( ...@@ -66,21 +66,50 @@ freertos = static_library(
########################################################################## ##########################################################################
subdir('modules/') subdir('modules/')
subdir('drivers/')
subdir('user_core/')
subdir('ble/') subdir('ble/')
subdir('os/')
subdir('fs/')
subdir('l0der/') subdir('l0der/')
epicardium_cargs = ['-D_POSIX_C_SOURCE=200809']
if get_option('jailbreak_card10')
epicardium_cargs += [
'-DJAILBREAK_CARD10=1',
]
endif
version_screen = custom_target(
'version-splash.h',
input: 'version-splash.png',
output: 'version-splash.h',
command: [
python3,
meson.current_source_dir() + '../tools/version-image.py',
'@INPUT@',
'@OUTPUT@',
],
)
elf = executable( elf = executable(
name + '.elf', name + '.elf',
'cdcacm.c', 'usb/epc_usb.c',
'usb/cdcacm.c',
'usb/mass_storage.c',
'main.c', 'main.c',
'support.c', 'support.c',
'fs/filesystem_fat.c',
module_sources, module_sources,
os_sources,
user_core_sources,
driver_sources,
fs_sources,
l0der_sources, l0der_sources,
ble_sources, ble_sources,
version_hdr, version_hdr,
dependencies: [libcard10, max32665_startup_core0, maxusb, libff13, ble], version_screen,
dependencies: [libcard10, max32665_startup_core0, maxusb, libff13, ble, bhy1, libcrypto, bsec, libctx],
link_with: [api_dispatcher_lib, freertos], link_with: [api_dispatcher_lib, freertos],
link_whole: [max32665_startup_core0_lib, board_card10_lib, newlib_heap_lib], link_whole: [max32665_startup_core0_lib, board_card10_lib, newlib_heap_lib],
include_directories: [freertos_includes], include_directories: [freertos_includes],
...@@ -88,6 +117,7 @@ elf = executable( ...@@ -88,6 +117,7 @@ elf = executable(
'-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map', '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map',
'-Wl,--defsym=_write=_write_epicardium', '-Wl,--defsym=_write=_write_epicardium',
], ],
c_args: epicardium_cargs,
) )
epicardium_bin = custom_target( epicardium_bin = custom_target(
......
#include "modules/log.h"
#include "api/dispatcher.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#define TIMEOUT pdMS_TO_TICKS(2000)
TaskHandle_t dispatcher_task_id;
static StaticSemaphore_t api_mutex_data;
SemaphoreHandle_t api_mutex = NULL;
/*
* API dispatcher task. This task will sleep until an API call is issued and
* then wake up to dispatch it.
*/
void vApiDispatcher(void *pvParameters)
{
api_mutex = xSemaphoreCreateMutexStatic(&api_mutex_data);
LOG_DEBUG("dispatcher", "Ready.");
while (1) {
if (api_dispatcher_poll()) {
if (xSemaphoreTake(api_mutex, TIMEOUT) != pdTRUE) {
LOG_ERR("dispatcher", "API mutex blocked");
continue;
}
api_dispatcher_exec();
xSemaphoreGive(api_mutex);
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
}
#include "display.h"
#include "Fonts/fonts.h"
#include "FreeRTOS.h"
#include "LCD_Driver.h"
#include "epicardium.h"
#include "gfx.h"
#include "gpio.h"
#include "task.h"
#include "tmr.h"
#include "tmr_utils.h"
static TaskHandle_t lock = NULL;
static int check_lock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (task != lock) {
return -EBUSY;
} else {
return 0;
}
}
int epic_disp_print(
uint16_t posx,
uint16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
gfx_puts(&Font20, &display_screen, posx, posy, pString, fg, bg);
return 0;
}
}
int epic_disp_clear(uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
gfx_clear_to_color(&display_screen, color);
return 0;
}
}
int epic_disp_pixel(uint16_t x, uint16_t y, uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
gfx_setpixel(&display_screen, x, y, color);
return 0;
}
}
int epic_disp_line(
uint16_t xstart,
uint16_t ystart,
uint16_t xend,
uint16_t yend,
uint16_t color,
enum disp_linestyle linestyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0) {
return cl;
} else {
/* TODO add linestyle support to gfx code */
gfx_line(
&display_screen,
xstart,
ystart,
xend,
yend,
pixelsize,
color
);
return 0;
}
}
int epic_disp_rect(
uint16_t xstart,
uint16_t ystart,
uint16_t xend,
uint16_t yend,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
switch (fillstyle) {
case FILLSTYLE_EMPTY:
gfx_rectangle(
&display_screen,
xstart,
ystart,
xend - xstart,
yend - ystart,
pixelsize,
color
);
break;
case FILLSTYLE_FILLED:
gfx_rectangle_fill(
&display_screen,
xstart,
ystart,
xend - xstart,
yend - ystart,
color
);
break;
}
return 0;
}
int epic_disp_circ(
uint16_t x,
uint16_t y,
uint16_t rad,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
switch (fillstyle) {
case FILLSTYLE_EMPTY:
gfx_circle(&display_screen, x, y, rad, pixelsize, color);
break;
case FILLSTYLE_FILLED:
gfx_circle_fill(&display_screen, x, y, rad, color);
break;
}
return 0;
}
int epic_disp_update()
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
gfx_update(&display_screen);
return 0;
}
int epic_disp_framebuffer(union disp_framebuffer *fb)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
LCD_Set(fb->raw, sizeof(fb->raw));
return 0;
}
int epic_disp_open()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (lock == task) {
return 0;
} else if (lock == NULL) {
lock = task;
return 0;
} else {
return -EBUSY;
}
}
int epic_disp_close()
{
if (check_lock() < 0 && lock != NULL) {
return -EBUSY;
} else {
lock = NULL;
return 0;
}
}
void disp_forcelock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
lock = task;
}