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
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • debug-queue-handling
  • dualcore
  • fix-issue_71
  • freertos-btle
  • issue104-UIError-update-fail
  • master
  • msgctl/faultscreen
  • msgctl/gfx_rle
  • msgctl/textbuffer_api
  • rahix/bhi
  • rahix/bma
  • schleicher-test
  • schneider/bonding
  • schneider/bootloader-update-9a0d158
  • schneider/bsec
  • schneider/mp-for-old-bl
  • schneider/schleicher-test
  • unique-advert-id
  • v0.0
23 results
Show changes
Showing
with 1440 additions and 969 deletions
......@@ -2,79 +2,26 @@
#define MODULES_H
#include "FreeRTOS.h"
#include "semphr.h"
#include "gpio.h"
#include "os/mutex.h"
#include "epicardium.h"
#include <stdint.h>
#include <stdbool.h>
/* ---------- Dispatcher --------------------------------------------------- */
void vApiDispatcher(void *pvParameters);
void dispatcher_mutex_init(void);
extern SemaphoreHandle_t api_mutex;
extern TaskHandle_t dispatcher_task_id;
/* ---------- Hardware Init & Reset ---------------------------------------- */
int hardware_early_init(void);
int hardware_init(void);
int hardware_reset(void);
/* ---------- Lifecycle ---------------------------------------------------- */
void vLifecycleTask(void *pvParameters);
void return_to_menu(void);
/* ---------- Serial ------------------------------------------------------- */
#define SERIAL_READ_BUFFER_SIZE 128
#define SERIAL_WRITE_STREAM_BUFFER_SIZE 512
void serial_init();
void vSerialTask(void *pvParameters);
void serial_enqueue_char(char chr);
extern TaskHandle_t serial_task_id;
// For the eSetBit xTaskNotify task semaphore trigger
enum serial_notify{
SERIAL_WRITE_NOTIFY = 0x01,
SERIAL_READ_NOTIFY = 0x02,
};
/* ---------- LED Animation / Personal States ------------------------------ */
#define PERSONAL_STATE_LED 14
void vLedTask(void *pvParameters);
int personal_state_enabled();
/* ---------- PMIC --------------------------------------------------------- */
void vPmicTask(void *pvParameters);
/* ---------- Watchdog ----------------------------------------------------- */
void watchdog_init();
void watchdog_clearer_init();
/* Critical battery voltage */
#define BATTERY_CRITICAL 3.40f
enum pmic_amux_signal {
PMIC_AMUX_CHGIN_U = 0x1,
PMIC_AMUX_CHGIN_I = 0x2,
PMIC_AMUX_BATT_U = 0x3,
PMIC_AMUX_BATT_CHG_I = 0x4,
PMIC_AMUX_BATT_DIS_I = 0x5,
PMIC_AMUX_BATT_NULL_I = 0x6,
PMIC_AMUX_THM_U = 0x7,
PMIC_AMUX_TBIAS_U = 0x8,
PMIC_AMUX_AGND_U = 0x9,
PMIC_AMUX_SYS_U = 0xA,
_PMIC_AMUX_MAX,
};
/*
* Read a value from the PMIC's AMUX. The result is already converted into its
* proper unit. See the MAX77650 datasheet for details.
*/
int pmic_read_amux(enum pmic_amux_signal sig, float *result);
/* ---------- BLE ---------------------------------------------------------- */
void vBleTask(void *pvParameters);
bool ble_shall_start(void);
bool ble_is_enabled(void);
void ble_uart_write(uint8_t *pValue, uint8_t len);
/* ---------- Hardware (Peripheral) Locks ---------------------------------- */
......@@ -88,21 +35,8 @@ enum hwlock_periph {
_HWLOCK_MAX,
};
int hwlock_acquire(enum hwlock_periph p, TickType_t wait);
int hwlock_release(enum hwlock_periph p);
/* ---------- Display ------------------------------------------------------ */
/* Forces an unlock of the display. Only to be used in Epicardium */
void disp_forcelock();
/* ---------- BHI160 ------------------------------------------------------- */
#define BHI160_FIFO_SIZE 128
#define BHI160_MUTEX_WAIT_MS 50
void vBhi160Task(void *pvParameters);
/* ---------- MAX30001 ----------------------------------------------------- */
#define MAX30001_MUTEX_WAIT_MS 50
void vMAX30001Task(void *pvParameters);
void max30001_mutex_init(void);
void hwlock_acquire(enum hwlock_periph p);
int hwlock_acquire_nonblock(enum hwlock_periph p);
void hwlock_release(enum hwlock_periph p);
#endif /* MODULES_H */
......@@ -2,23 +2,39 @@
#include "leds.h"
#include "modules.h"
#include "os/work_queue.h"
#include "FreeRTOS.h"
#include "timers.h"
#include <math.h>
uint8_t _personal_state_enabled = 0;
uint8_t personal_state = STATE_NONE;
uint8_t personal_state_persistent = 0;
static uint8_t _personal_state_enabled = 0;
static uint8_t personal_state = STATE_NONE;
static uint8_t personal_state_persistent = 0;
static int led_animation_ticks = 0;
static int led_animation_state = 0;
int led_animation_ticks = 0;
int led_animation_state = 0;
static TimerHandle_t led_timer;
static StaticTimer_t led_timer_buffer;
static void worktick(void *data);
static const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
int personal_state_enabled()
{
return _personal_state_enabled;
}
static void tick(TimerHandle_t xTimer)
{
workqueue_schedule(worktick, NULL);
}
int epic_personal_state_set(uint8_t state, bool persistent)
{
if (state < STATE_NONE || state > STATE_CAMP)
if (state > STATE_CAMP)
return -EINVAL;
led_animation_state = 0;
......@@ -30,16 +46,31 @@ int epic_personal_state_set(uint8_t state, bool persistent)
_personal_state_enabled = (state != STATE_NONE);
personal_state_persistent = persistent;
if (was_enabled && !_personal_state_enabled) {
while (hwlock_acquire(HWLOCK_LED, pdMS_TO_TICKS(1)) < 0) {
vTaskDelay(pdMS_TO_TICKS(1));
if (!was_enabled && _personal_state_enabled) {
// Activate
if (!led_timer) {
led_timer = xTimerCreateStatic(
"personal state",
led_animation_rate / portTICK_PERIOD_MS,
pdTRUE,
NULL,
tick,
&led_timer_buffer
);
// since &poll_timer_buffer is not NULL, xTimerCreateStatic should allways succeed, so
// we don't need to check for poll_timer being NULL.
}
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
leds_update_power();
leds_update();
if (xTimerIsTimerActive(led_timer) == pdFALSE) {
xTimerStart(led_timer, 0);
}
hwlock_release(HWLOCK_LED);
} else if (was_enabled && !_personal_state_enabled) {
// Deactivate
xTimerStop(led_timer, 0);
// TODO: we might need a lock here to avoid a race condition
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
epic_leds_update();
}
return 0;
......@@ -55,24 +86,15 @@ int epic_personal_state_is_persistent()
return personal_state_persistent;
}
void vLedTask(void *pvParameters)
static void worktick(void *data)
{
const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
while (1) {
if (_personal_state_enabled) {
while (hwlock_acquire(HWLOCK_LED, pdMS_TO_TICKS(1)) <
0) {
vTaskDelay(pdMS_TO_TICKS(1));
}
led_animation_ticks++;
if (personal_state == STATE_NO_CONTACT) {
leds_prep(PERSONAL_STATE_LED, 255, 0, 0);
} else if (personal_state == STATE_CHAOS) {
if (led_animation_state == 0) {
leds_prep(
PERSONAL_STATE_LED, 0, 0, 255
);
leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
if (led_animation_ticks >
(200 / led_animation_rate)) {
led_animation_ticks = 0;
......@@ -86,9 +108,7 @@ void vLedTask(void *pvParameters)
led_animation_state = 2;
}
} else if (led_animation_state == 2) {
leds_prep(
PERSONAL_STATE_LED, 0, 0, 255
);
leds_prep(PERSONAL_STATE_LED, 0, 0, 255);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
......@@ -104,9 +124,7 @@ void vLedTask(void *pvParameters)
}
} else if (personal_state == STATE_COMMUNICATION) {
if (led_animation_state == 0) {
leds_prep(
PERSONAL_STATE_LED, 255, 255, 0
);
leds_prep(PERSONAL_STATE_LED, 255, 255, 0);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
......@@ -127,15 +145,9 @@ void vLedTask(void *pvParameters)
1.0f,
fabs(sin(
led_animation_ticks /
(float)(1000 /
led_animation_rate))));
}
leds_update_power();
leds_update();
hwlock_release(HWLOCK_LED);
(float)(1000 / led_animation_rate))));
}
vTaskDelay(led_animation_rate / portTICK_PERIOD_MS);
epic_leds_update();
}
}
#include "epicardium.h"
#include "modules/modules.h"
#include "modules/log.h"
#include "card10.h"
#include "pmic.h"
#include "MAX77650-Arduino-Library.h"
#include "max32665.h"
#include "mxc_sys.h"
#include "mxc_pins.h"
#include "adc.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include <stdio.h>
#include <string.h>
#define LOCK_WAIT pdMS_TO_TICKS(1000)
/* Task ID for the pmic handler */
static TaskHandle_t pmic_task_id = NULL;
enum {
/* An irq was received, probably the power button */
PMIC_NOTIFY_IRQ = 1,
/* The timer has ticked and we should check the battery voltage again */
PMIC_NOTIFY_MONITOR = 2,
};
void pmic_interrupt_callback(void *_)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (pmic_task_id != NULL) {
xTaskNotifyFromISR(
pmic_task_id,
PMIC_NOTIFY_IRQ,
eSetBits,
&xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
int pmic_read_amux(enum pmic_amux_signal sig, float *result)
{
int ret = 0;
int i2c_ret = 0;
if (sig > _PMIC_AMUX_MAX) {
return -EINVAL;
}
int adc_ret = hwlock_acquire(HWLOCK_ADC, LOCK_WAIT);
if (adc_ret < 0) {
ret = adc_ret;
goto done;
}
i2c_ret = hwlock_acquire(HWLOCK_I2C, LOCK_WAIT);
if (i2c_ret < 0) {
ret = i2c_ret;
goto done;
}
/* Select the correct channel for this measurement. */
MAX77650_setMUX_SEL(sig);
/*
* According to the datasheet, the voltage will stabilize within 0.3us.
* Just to be sure, we'll wait a little longer. In the meantime,
* release the I2C mutex.
*/
hwlock_release(HWLOCK_I2C);
vTaskDelay(pdMS_TO_TICKS(5));
i2c_ret = hwlock_acquire(HWLOCK_I2C, LOCK_WAIT);
if (i2c_ret < 0) {
ret = i2c_ret;
goto done;
}
uint16_t adc_data;
ADC_StartConvert(ADC_CH_0, 0, 0);
ADC_GetData(&adc_data);
/* Turn MUX back to neutral so it does not waste power. */
MAX77650_setMUX_SEL(sig);
/* Convert ADC measurement to SI Volts */
float adc_voltage = (float)adc_data / 1023.0f * 1.22f;
/*
* Convert value according to PMIC formulas (Table 7)
*/
switch (sig) {
case PMIC_AMUX_CHGIN_U:
*result = adc_voltage / 0.167f;
break;
case PMIC_AMUX_CHGIN_I:
*result = adc_voltage / 2.632f;
break;
case PMIC_AMUX_BATT_U:
*result = adc_voltage / 0.272f;
break;
case PMIC_AMUX_BATT_CHG_I:
*result = adc_voltage / 1.25f;
break;
case PMIC_AMUX_BATT_NULL_I:
case PMIC_AMUX_THM_U:
case PMIC_AMUX_TBIAS_U:
case PMIC_AMUX_AGND_U:
*result = adc_voltage;
break;
case PMIC_AMUX_SYS_U:
*result = adc_voltage / 0.26f;
break;
default:
ret = -EINVAL;
}
done:
if (i2c_ret == 0) {
hwlock_release(HWLOCK_I2C);
}
if (adc_ret == 0) {
hwlock_release(HWLOCK_ADC);
}
return ret;
}
/*
* Read the interrupt flag register and handle all interrupts which the PMIC has
* sent. In most cases this will be the buttons.
*/
static void
pmic_poll_interrupts(TickType_t *button_start_tick, TickType_t duration)
{
while (hwlock_acquire(HWLOCK_I2C, LOCK_WAIT) < 0) {
LOG_WARN("pmic", "Failed to acquire I2C. Retrying ...");
xTaskNotify(pmic_task_id, PMIC_NOTIFY_IRQ, eSetBits);
return;
}
uint8_t int_flag = MAX77650_getINT_GLBL();
hwlock_release(HWLOCK_I2C);
if (int_flag & MAX77650_INT_nEN_F) {
/* Button was pressed */
*button_start_tick = xTaskGetTickCount();
}
if (int_flag & MAX77650_INT_nEN_R) {
/* Button was released */
*button_start_tick = 0;
if (duration < pdMS_TO_TICKS(400)) {
return_to_menu();
} else {
LOG_WARN("pmic", "Resetting ...");
card10_reset();
}
}
/* TODO: Remove when all interrupts are handled */
if (int_flag & ~(MAX77650_INT_nEN_F | MAX77650_INT_nEN_R)) {
LOG_WARN("pmic", "Unhandled PMIC Interrupt: %x", int_flag);
}
}
__attribute__((noreturn)) static void pmic_die(float u_batt)
{
/* Stop core 1 */
core1_stop();
/* Grab the screen */
disp_forcelock();
/* Draw an error screen */
epic_disp_clear(0x0000);
epic_disp_print(0, 0, " Battery", 0xffff, 0x0000);
epic_disp_print(0, 20, " critical", 0xffff, 0x0000);
epic_disp_print(0, 40, " !!!!", 0xffff, 0x0000);
epic_disp_update();
/* Vibrate violently */
epic_vibra_set(true);
/* Wait a bit */
for (int i = 0; i < 50000000; i++)
__NOP();
LOG_WARN("pmic", "Poweroff");
MAX77650_setSFT_RST(0x2);
while (1)
__WFI();
}
/*
* Check the battery voltage. If it drops too low, turn card10 off.
*/
static void pmic_check_battery()
{
float u_batt;
int res;
res = pmic_read_amux(PMIC_AMUX_BATT_U, &u_batt);
if (res < 0) {
LOG_ERR("pmic",
"Failed reading battery voltage: %s (%d)",
strerror(-res),
res);
return;
}
LOG_DEBUG(
"pmic",
"Battery is at %d.%03d V",
(int)u_batt,
(int)(u_batt * 1000.0) % 1000
);
if (u_batt < BATTERY_CRITICAL) {
pmic_die(u_batt);
}
}
/*
* API-call for battery voltage
*/
int epic_read_battery_voltage(float *result)
{
return pmic_read_amux(PMIC_AMUX_BATT_U, result);
}
/*
* API-call for battery current
*/
int epic_read_battery_current(float *result)
{
return pmic_read_amux(PMIC_AMUX_BATT_CHG_I, result);
}
/*
* API-call for charge voltage
*/
int epic_read_chargein_voltage(float *result)
{
return pmic_read_amux(PMIC_AMUX_CHGIN_U, result);
}
/*
* API-call for charge voltage
*/
int epic_read_chargein_current(float *result)
{
return pmic_read_amux(PMIC_AMUX_BATT_CHG_I, result);
}
/*
* API-call for system voltage
*/
int epic_read_system_voltage(float *result)
{
return pmic_read_amux(PMIC_AMUX_SYS_U, result);
}
/*
* API-call for thermistor voltage
*
* Thermistor is as 10k at room temperature,
* voltage divided with another 10k.
* (50% V_bias at room temperature)
*/
int epic_read_thermistor_voltage(float *result)
{
return pmic_read_amux(PMIC_AMUX_THM_U, result);
}
static StaticTimer_t pmic_timer_data;
static void vPmicTimerCb(TimerHandle_t xTimer)
{
/*
* Tell the PMIC task to check the battery again.
*/
xTaskNotify(pmic_task_id, PMIC_NOTIFY_MONITOR, eSetBits);
}
void vPmicTask(void *pvParameters)
{
pmic_task_id = xTaskGetCurrentTaskHandle();
ADC_Init(0x9, NULL);
GPIO_Config(&gpio_cfg_adc0);
TickType_t button_start_tick = 0;
pmic_check_battery();
TimerHandle_t pmic_timer = xTimerCreateStatic(
"PMIC Timer",
pdMS_TO_TICKS(60 * 1000),
pdTRUE,
NULL,
vPmicTimerCb,
&pmic_timer_data
);
if (pmic_timer == NULL) {
LOG_CRIT("pmic", "Could not create timer.");
vTaskDelay(portMAX_DELAY);
}
xTimerStart(pmic_timer, 0);
while (1) {
uint32_t reason;
if (button_start_tick == 0) {
reason = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} else {
reason = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100));
}
TickType_t duration = xTaskGetTickCount() - button_start_tick;
if (button_start_tick != 0 && duration > pdMS_TO_TICKS(1000)) {
LOG_WARN("pmic", "Poweroff");
MAX77650_setSFT_RST(0x2);
}
if (reason & PMIC_NOTIFY_IRQ) {
pmic_poll_interrupts(&button_start_tick, duration);
}
if (reason & PMIC_NOTIFY_MONITOR) {
pmic_check_battery();
}
}
}
#include "epicardium.h"
#include "modules/log.h"
#include "api/interrupt-sender.h"
#include "FreeRTOS.h"
#include "task.h"
#include "rtc.h"
#include <stdint.h>
uint32_t epic_rtc_get_seconds(void)
{
uint32_t sec, subsec;
/*
* TODO: Find out what causes the weird behavior of this function. The
* time needed for this call seems to depend on the frequency at
* which it is called.
*/
while (RTC_GetTime(&sec, &subsec) == E_BUSY) {
vTaskDelay(pdMS_TO_TICKS(4));
}
return sec;
}
uint64_t epic_rtc_get_milliseconds(void)
{
uint32_t sec, subsec;
while (RTC_GetTime(&sec, &subsec) == E_BUSY) {
vTaskDelay(pdMS_TO_TICKS(4));
}
return subsec * 1000ULL / 4096 + sec * 1000ULL;
}
void epic_rtc_set_milliseconds(uint64_t milliseconds)
{
uint32_t sec, subsec;
sec = milliseconds / 1000;
subsec = (milliseconds % 1000);
subsec *= 4096;
subsec /= 1000;
while (RTC_Init(MXC_RTC, sec, subsec, NULL) == E_BUSY)
;
while (RTC_EnableRTCE(MXC_RTC) == E_BUSY)
;
}
void RTC_IRQHandler(void)
{
int flags = RTC_GetFlags();
if (flags & MXC_F_RTC_CTRL_ALDF) {
RTC_ClearFlags(MXC_F_RTC_CTRL_ALDF);
api_interrupt_trigger(EPIC_INT_RTC_ALARM);
} else {
LOG_WARN("rtc", "Unknown IRQ caught!");
/* Disable IRQ so it does not retrigger */
NVIC_DisableIRQ(RTC_IRQn);
}
}
int epic_rtc_schedule_alarm(uint32_t timestamp)
{
int res;
/*
* Check if the timestamp lies in the past and if so, trigger
* immediately.
*/
if (epic_rtc_get_seconds() >= timestamp) {
api_interrupt_trigger(EPIC_INT_RTC_ALARM);
return 0;
}
NVIC_EnableIRQ(RTC_IRQn);
while ((res = RTC_SetTimeofdayAlarm(MXC_RTC, timestamp)) == E_BUSY)
;
if (res != E_SUCCESS) {
return -EINVAL;
}
return 0;
}
#include "epicardium.h"
#include "api/interrupt-sender.h"
#include "modules/log.h"
#include "modules/modules.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
);
}
/*
* API-call to write a string. Output goes to both CDCACM and UART
*/
void epic_uart_write_str(const char *str, intptr_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
);
portYIELD();
}
} while (index < length);
}
}
/*
* 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;
}
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') {
epic_uart_write_str(&buf[last], i - last);
epic_uart_write_str("\r", 1);
last = i;
}
}
epic_uart_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 */
api_interrupt_trigger(EPIC_INT_CTRL_C);
}
if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) {
/* Queue overran, wait a bit */
vTaskDelay(portTICK_PERIOD_MS * 50);
}
api_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,
};
uint8_t rx_data[20];
size_t received_bytes;
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) {
do {
received_bytes = xStreamBufferReceive(
write_stream_buffer,
(void *)rx_data,
sizeof(rx_data),
0
);
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
);
ble_uart_write(
(uint8_t *)&rx_data, received_bytes
);
} while (received_bytes > 0);
}
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 <string.h>
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "epicardium.h"
#include "modules/log.h"
#include "os/core.h"
#include "modules/stream.h"
#include "os/mutex.h"
/* Internal buffer of registered streams */
static struct stream_info *stream_table[SD_MAX];
/* Lock for modifying the stream info table */
static StaticSemaphore_t stream_table_lock_data;
static SemaphoreHandle_t stream_table_lock;
static struct mutex stream_table_lock;
int stream_init()
{
memset(stream_table, 0x00, sizeof(stream_table));
stream_table_lock =
xSemaphoreCreateMutexStatic(&stream_table_lock_data);
mutex_create(&stream_table_lock);
return 0;
}
int stream_register(int sd, struct stream_info *stream)
{
int ret = 0;
if (xSemaphoreTake(stream_table_lock, STREAM_MUTEX_WAIT) != pdTRUE) {
LOG_WARN("stream", "Lock contention error");
ret = -EBUSY;
goto out;
}
mutex_lock(&stream_table_lock);
if (sd < 0 || sd >= SD_MAX) {
ret = -EINVAL;
goto out_release;
goto out;
}
if (stream_table[sd] != NULL) {
/* Stream already registered */
ret = -EACCES;
goto out_release;
goto out;
}
stream_table[sd] = stream;
out_release:
xSemaphoreGive(stream_table_lock);
out:
mutex_unlock(&stream_table_lock);
return ret;
}
int stream_deregister(int sd, struct stream_info *stream)
{
int ret = 0;
if (xSemaphoreTake(stream_table_lock, STREAM_MUTEX_WAIT) != pdTRUE) {
LOG_WARN("stream", "Lock contention error");
ret = -EBUSY;
goto out;
}
mutex_lock(&stream_table_lock);
if (sd < 0 || sd >= SD_MAX) {
ret = -EINVAL;
goto out_release;
goto out;
}
if (stream_table[sd] != stream) {
/* Stream registered by someone else */
ret = -EACCES;
goto out_release;
goto out;
}
stream_table[sd] = NULL;
out_release:
xSemaphoreGive(stream_table_lock);
out:
mutex_unlock(&stream_table_lock);
return ret;
}
......@@ -86,35 +77,31 @@ int epic_stream_read(int sd, void *buf, size_t count)
* simulaneously. I don't know what the most efficient implementation
* of this would look like.
*/
if (xSemaphoreTake(stream_table_lock, STREAM_MUTEX_WAIT) != pdTRUE) {
LOG_WARN("stream", "Lock contention error");
ret = -EBUSY;
goto out;
}
mutex_lock(&stream_table_lock);
if (sd < 0 || sd >= SD_MAX) {
ret = -EBADF;
goto out_release;
goto out;
}
struct stream_info *stream = stream_table[sd];
if (stream == NULL) {
ret = -ENODEV;
goto out_release;
goto out;
}
/* Poll the stream, if a poll_stream function exists */
if (stream->poll_stream != NULL) {
int ret = stream->poll_stream();
ret = stream->poll_stream();
if (ret < 0) {
goto out_release;
goto out;
}
}
/* Check buffer size is a multiple of the data packet size */
if (count % stream->item_size != 0) {
ret = -EINVAL;
goto out_release;
goto out;
}
size_t i;
......@@ -126,8 +113,7 @@ int epic_stream_read(int sd, void *buf, size_t count)
ret = i / stream->item_size;
out_release:
xSemaphoreGive(stream_table_lock);
out:
mutex_unlock(&stream_table_lock);
return ret;
}
......@@ -4,14 +4,16 @@
#include <stdint.h>
#ifndef __SPHINX_DOC
/* stddef.h is not recognized by hawkmoth for some odd reason */
/* Some headers are not recognized by hawkmoth for some odd reason */
#include <stddef.h>
#else
typedef unsigned int size_t;
#endif /* __SPHINX_DOC */
#include "FreeRTOS.h"
#include "queue.h"
#else
typedef unsigned int size_t;
typedef int bool;
typedef void *QueueHandle_t;
#endif /* __SPHINX_DOC */
/* Time to wait for the descriptor table lock to become available */
#define STREAM_MUTEX_WAIT pdMS_TO_TICKS(100)
......@@ -25,11 +27,17 @@ typedef unsigned int size_t;
* Please keep IDs in sequential order.
*/
enum stream_descriptor {
/** BHI160 */
/** BHI160 Accelerometer */
SD_BHI160_ACCELEROMETER,
/** BHI160 Magnetometer */
SD_BHI160_MAGNETOMETER,
/** BHI160 Orientation Sensor */
SD_BHI160_ORIENTATION,
/** BHI160 Gyroscope */
SD_BHI160_GYROSCOPE,
/** MAX30001 ECG */
SD_MAX30001_ECG,
SD_MAX86150,
/** Highest descriptor must always be ``SD_MAX``. */
SD_MAX,
};
......@@ -60,6 +68,12 @@ struct stream_info {
* The function registered here should never block for a longer time.
*/
int (*poll_stream)();
/**
* Set to true if the last write to ``queue`` failed because
* the queue was full.
*/
bool was_full;
};
/**
......
#include "epicardium.h"
#include "trng.h"
int epic_trng_read(uint8_t *dest, size_t size)
{
if (dest == NULL)
return -EFAULT;
TRNG_Read(MXC_TRNG, dest, size);
return 0;
}
#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 TimerHandle_t vibra_timer;
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);
epic_vibra_set(1);
vibra_timer =
xTimerCreate("vibratimer", ticks, pdFALSE, 0, vTimerCallback);
xTimerStart(vibra_timer, 0);
}
#include "os/core.h"
#include "os/config.h"
#include "epicardium.h"
#include <assert.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#define MAX_LINE_LENGTH 80
#define KEYS_PER_BLOCK 16
#define KEY_LENGTH 16
#define NOT_INT_MAGIC ((int)0x80000000)
// one key-value pair representing a line in the config
typedef struct {
char key[KEY_LENGTH];
// the value in the config file, if it's an integer.
// for strings it's set to NOT_INT_MAGIC
int value;
// the byte offset in the config file to read the value string
size_t value_offset;
} config_slot;
// a block of 16 config slots
// if more are needed, this becomes a linked list
typedef struct {
config_slot slots[KEYS_PER_BLOCK];
void *next;
} config_block;
static config_block *config_data = NULL;
// returns the config slot for a key name
static config_slot *find_config_slot(const char *key)
{
config_block *current = config_data;
while (current) {
for (int i = 0; i < KEYS_PER_BLOCK; i++) {
config_slot *k = &current->slots[i];
if (strcmp(k->key, key) == 0) {
// found what we're looking for
return k;
} else if (*k->key == '\0') {
// found the first empty key
return NULL;
}
}
current = current->next;
}
return NULL;
}
// returns the next available config slot, or allocates a new block if needed
static config_slot *allocate_config_slot()
{
config_block *current;
if (config_data == NULL) {
config_data = malloc(sizeof(config_block));
assert(config_data != NULL);
memset(config_data, 0, sizeof(config_block));
}
current = config_data;
while (true) {
for (int i = 0; i < KEYS_PER_BLOCK; i++) {
config_slot *k = &current->slots[i];
if (*k->key == '\0') {
return k;
}
}
// this block is full and there's no next allocated block
if (current->next == NULL) {
current->next = malloc(sizeof(config_block));
assert(current->next != NULL);
memset(current->next, 0, sizeof(config_block));
}
current = current->next;
}
}
// parses an int out of 'value' or returns NOT_INT_MAGIC
static int try_parse_int(const char *value)
{
char *endptr;
size_t len = strlen(value);
int v = strtol(value, &endptr, 0);
if (endptr != (value + len)) {
return NOT_INT_MAGIC;
}
return v;
}
// loads a key/value pair into a new config slot
static void add_config_pair(
const char *key, const char *value, int line_number, size_t value_offset
) {
if (strlen(key) > KEY_LENGTH - 1) {
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
line_number
);
return;
}
config_slot *slot = allocate_config_slot();
strncpy(slot->key, key, KEY_LENGTH);
slot->value = try_parse_int(value);
slot->value_offset = value_offset;
}
static void trim(char *str)
{
char *start = str;
while (*start && !isgraph(*start))
start++;
if (strlen(start) > 0) {
char *end = start + strlen(start) - 1;
while (*end && !isgraph(*end))
end--;
end[1] = 0;
}
memmove(str, start, strlen(start) + 1);
}
// parses one line of the config file
static void parse_line(char *line, int line_number, size_t line_offset)
{
char *line_start = line;
char *value_start = strchr(line, '=') + 1;
trim(line);
//printf(line);
if (*line == '#') {
//skip comments
return;
}
char *eq = strchr(line, '=');
if (!eq) {
if (*line) {
LOG_WARN(
"card10.cfg",
"line %d: syntax error",
line_number
);
}
return;
}
*eq = 0;
char *key = line;
trim(key);
if (*key == '\0') {
LOG_WARN("card10.cfg", "line %d: empty key", line_number);
return;
}
char *value = eq + 1;
trim(value);
if (*value == '\0') {
LOG_WARN(
"card10.cfg",
"line %d: empty value for option '%s'",
line_number,
key
);
return;
}
size_t value_offset = value_start - line_start + line_offset;
add_config_pair(key, value, line_number, value_offset);
}
typedef struct {
int line_number;
int file_offset;
int line_start;
char line[MAX_LINE_LENGTH + 1];
int line_length;
} parser_state;
int parse_character(char c, parser_state *s)
{
if (c != '\r' && c != '\n') {
if (s->line_length == MAX_LINE_LENGTH) {
LOG_WARN(
"card10.cfg",
"line:%d: too long - aborting",
s->line_number
);
return -1;
}
s->line[s->line_length++] = c;
} else {
s->line[s->line_length] = 0;
//printf("New line: %s (%d %d)\n", s->line, s->line_number, s->line_start);
parse_line(s->line, s->line_number, s->line_start);
s->line_length = 0;
s->line_start = s->file_offset + 1;
if (c == '\n') {
s->line_number++;
}
}
s->file_offset++;
return 0;
}
// parses the entire config file
void load_config(void)
{
LOG_DEBUG("card10.cfg", "loading...");
int fd = epic_file_open("card10.cfg", "r");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"loading failed: %s (%d)",
strerror(-fd),
fd
);
return;
}
/* Clear any existing configuration */
/* Don't free the blocks as we are most likely
* going to re-use all of them. */
config_block *current;
if (config_data != NULL) {
current = config_data;
while (true) {
memset(current->slots, 0, sizeof(current->slots));
if (current->next == NULL) {
break;
}
current = current->next;
}
}
char buf[128];
int nread;
parser_state s;
memset(&s, 0, sizeof(s));
s.line_number = 1;
do {
nread = epic_file_read(fd, buf, sizeof(buf));
int i;
for (i = 0; i < nread; i++) {
parse_character(buf[i], &s);
}
} while (nread == sizeof(buf));
parse_character('\n', &s);
epic_file_close(fd);
}
// opens the config file, seeks to seek_offset and reads buf_len bytes
// used for reading strings without storing them in memory
// since we don't need to optimize for that use case as much
static size_t read_config_offset(size_t seek_offset, char *buf, size_t buf_len)
{
int fd = epic_file_open("card10.cfg", "r");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"opening config failed: %s (%d)",
strerror(-fd),
fd
);
return 0;
}
int rc = epic_file_seek(fd, seek_offset, SEEK_SET);
if (rc < 0) {
LOG_ERR("card10.cfg", "seek2 failed (%d), aborting", rc);
return 0;
}
// one byte less to accommodate the 0 termination
int nread = epic_file_read(fd, buf, buf_len - 1);
buf[nread] = '\0';
epic_file_close(fd);
return nread;
}
// returns error if not found or invalid
int epic_config_get_integer(const char *key, int *value)
{
config_slot *slot = find_config_slot(key);
if (slot && slot->value != NOT_INT_MAGIC) {
*value = slot->value;
return 0;
}
return -ENOENT;
}
// returns default_value if not found or invalid
int config_get_integer_with_default(const char *key, int default_value)
{
int value;
int ret = epic_config_get_integer(key, &value);
if (ret) {
return default_value;
} else {
return value;
}
}
// returns error if not found
int epic_config_get_string(const char *key, char *buf, size_t buf_len)
{
config_slot *slot = find_config_slot(key);
if (!(slot && slot->value_offset)) {
return -ENOENT;
}
size_t nread = read_config_offset(slot->value_offset, buf, buf_len);
if (nread == 0) {
return -ENOENT;
}
char *end = buf;
while (*end && !iscntrl(*end))
end++;
*end = 0;
trim(buf);
return 0;
}
// returns dflt if not found, otherwise same pointer as buf
char *config_get_string_with_default(
const char *key, char *buf, size_t buf_len, char *dflt
) {
int ret = epic_config_get_string(key, buf, buf_len);
if (ret) {
return dflt;
} else {
return buf;
}
}
// returns error if not found or invalid
int epic_config_get_boolean(const char *key, bool *value)
{
int int_value;
int ret = epic_config_get_integer(key, &int_value);
if (ret == 0) {
*value = !!int_value;
return 0;
}
char buf[MAX_LINE_LENGTH];
ret = epic_config_get_string(key, buf, MAX_LINE_LENGTH);
if (ret < 0) {
return ret;
}
if (!strcasecmp(buf, "true")) {
*value = true;
return 0;
} else if (!strcasecmp(buf, "false")) {
*value = false;
return 0;
}
return -ERANGE;
}
// returns default_value if not found or invalid
bool config_get_boolean_with_default(const char *key, bool default_value)
{
bool value;
int ret = epic_config_get_boolean(key, &value);
if (ret) {
return default_value;
} else {
return value;
}
}
int epic_config_set_string(const char *key, const char *value_in)
{
char value[MAX_LINE_LENGTH + 1];
if (strlen(key) > MAX_LINE_LENGTH) {
return -EINVAL;
}
/* TODO: Change interface of trim to take the buffer and size directly */
if (strlen(value_in) > MAX_LINE_LENGTH) {
return -EINVAL;
}
strcpy(value, value_in);
trim(value);
if (snprintf(NULL, 0, "\n%s = %s\n", key, value) > MAX_LINE_LENGTH) {
return -EINVAL;
}
/* Check if key is sane. No control characters, spaces, equal signs or pounds allowed */
for (size_t i = 0; i < strlen(key); i++) {
char c = key[i];
if (!isgraph(c) || c == '=' || c == '#') {
return -EINVAL;
}
}
/* Check if value is sane. No control characters allowed */
for (size_t i = 0; i < strlen(value); i++) {
char c = value[i];
if (!isprint(c)) {
return -EINVAL;
}
}
config_slot *slot = find_config_slot(key);
bool present = slot && slot->value_offset;
int ret = 0;
if (!present) {
/* Easy case: We simply add the new option at the
* end of the file. */
char buf[MAX_LINE_LENGTH];
/* Leading new line because I'm lazy */
ret = snprintf(buf, sizeof(buf), "\n%s = %s\n", key, value);
if (ret < 0 || ret >= (int)sizeof(buf)) {
return -EINVAL;
}
int fd = epic_file_open("card10.cfg", "a");
if (fd < 0) {
LOG_DEBUG(
"card10.cfg",
"open for appending failed: %s (%d)",
strerror(-fd),
fd
);
return fd;
}
int write_ret = epic_file_write(fd, buf, strlen(buf));
if (write_ret < 0) {
LOG_DEBUG(
"card10.cfg",
"writing failed: %s (%d)",
strerror(-write_ret),
write_ret
);
}
if (write_ret < (int)strlen(buf)) {
LOG_DEBUG(
"card10.cfg",
"writing failed to write all bytes (%d of %d)",
write_ret,
strlen(buf)
);
}
ret = epic_file_close(fd);
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"close failed: %s (%d)",
strerror(-ret),
ret
);
}
if (write_ret < 0) {
ret = write_ret;
}
if (ret < 0) {
goto out;
}
if (write_ret < (int)strlen(buf)) {
LOG_DEBUG(
"card10.cfg",
"writing failed to write all bytes (%d of %d)",
write_ret,
strlen(buf)
);
ret = -EIO;
goto out;
}
} else {
/* Complex case: The value is already somewhere in the file.
* We do not want to lose existing formatting or comments.
* Solution: Copy parts of the file, insert new value, copy
* rest, rename.
*/
char buf[MAX_LINE_LENGTH + 1];
int fd1 = -1;
int fd2 = -1;
ret = epic_config_get_string(key, buf, sizeof(buf));
if (ret == 0 && strcmp(buf, value) == 0) {
/* Nothing to do: the values are the same. */
return 0;
}
size_t nread = read_config_offset(
slot->value_offset, buf, sizeof(buf)
);
if (nread == 0) {
LOG_DEBUG("card10.cfg", "could not read old value");
ret = -EIO;
goto complex_out;
}
char *end = buf;
while (*end && (!iscntrl(*end) || isblank(*end)))
end++;
*end = 0;
int old_len = strlen(buf);
fd1 = epic_file_open("card10.cfg", "r");
if (fd1 < 0) {
LOG_DEBUG(
"card10.cfg",
"open for read failed: %s (%d)",
strerror(-fd1),
fd1
);
ret = fd1;
goto complex_out;
}
fd2 = epic_file_open("card10.nfg", "w");
if (fd2 < 0) {
LOG_DEBUG(
"card10.nfg",
"open for writing failed: %s (%d)",
strerror(-fd2),
fd2
);
ret = fd2;
goto complex_out;
}
/* Copy over slot->value_offset bytes */
int i = slot->value_offset;
while (i > 0) {
int n = i > (int)sizeof(buf) ? (int)sizeof(buf) : i;
ret = epic_file_read(fd1, buf, n);
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"read failed: rc: %d",
ret
);
goto complex_out;
}
int ret2 = epic_file_write(fd2, buf, ret);
if (ret2 < 0) {
ret = ret2;
LOG_DEBUG(
"card10.nfg",
"write failed: rc: %d",
ret
);
goto complex_out;
}
i -= ret;
}
/* Insert new value into the new file */
ret = epic_file_write(fd2, value, strlen(value));
if (ret < 0) {
LOG_DEBUG("card10.nfg", "write failed: rc: %d", ret);
goto complex_out;
}
/* Skip the old value inside the old file */
epic_file_seek(fd1, old_len, SEEK_CUR);
/* Copy the rest of the old file to the new file */
while (true) {
int ret = epic_file_read(fd1, buf, sizeof(buf));
if (ret == 0) {
break;
}
if (ret < 0) {
LOG_DEBUG(
"card10.cfg",
"read failed: rc: %d",
ret
);
goto complex_out;
}
int ret2 = epic_file_write(fd2, buf, ret);
if (ret2 < 0) {
ret = ret2;
LOG_DEBUG(
"card10.nfg",
"write failed: rc: %d",
ret
);
goto complex_out;
}
if (ret < (int)sizeof(buf)) {
break;
}
}
complex_out:
if (fd1 >= 0) {
epic_file_close(fd1);
}
if (fd2 >= 0) {
int ret2 = epic_file_close(fd2);
if (ret >= 0) {
ret = ret2;
}
}
if (ret >= 0) {
epic_file_unlink("card10.cfg");
epic_file_rename("card10.nfg", "card10.cfg");
}
}
out:
/* Reload config so the new key or the changed value is available */
load_config();
return ret < 0 ? ret : 0;
}
#ifndef EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#define EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#include <stdbool.h>
#include <stddef.h>
//initialize configuration values and load card10.cfg
void load_config(void);
// returns default_value if not found or invalid
bool config_get_boolean_with_default(const char *key, bool default_value);
int config_get_integer_with_default(const char *key, int default_value);
// returns dflt if not found, otherwise same pointer as buf
char *config_get_string_with_default(const char *key, char *buf, size_t buf_len, char *dflt);
#endif//EPICARDIUM_MODULES_CONFIG_H_INCLUDED
#pragma once
/**
* Panic
* =====
* Panicking should be used in situations where no automatic recovery is
* possible of where a fatal bug was detected. Calling :c:func:`panic()` will
* show a message on the screen and serial console and then reboot the device.
*
* Keep in mind that screen space is limited and thus the message should be as
* concise as possible.
*/
/**
* Trigger a firmware panic.
*
* This function will not return but instead reboot the device. No
* synchronization of e.g. the filesystem is done so this could potentially lead
* to data loss.
*/
void panic(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));
/**
* Logging
* =======
* All logging macros take a "subsystem" and a log message. This subsystem
* argument should be used to uniquely identify where a message came from.
*
* **Example**:
*
* .. code-block:: cpp
*
* LOG_ERR("pmic",
* "Failed reading battery voltage: %s (%d)",
* strerror(-res),
* res);
*/
/* Whether to enable logging at all */
#ifndef LOG_ENABLE
#define LOG_ENABLE 1
#endif
/* Whether to enable even more verbose logging */
#ifndef LOG_ENABLE_DEBUG
#define LOG_ENABLE_DEBUG 0
#endif
/* Whether to enable colorful log */
#ifndef LOG_ENABLE_COLOR
#define LOG_ENABLE_COLOR 1
#endif
#define SEV_DEBUG "D"
#define SEV_INFO "I"
#define SEV_WARN "W"
#define SEV_ERR "E"
#define SEV_CRIT "C"
#if LOG_ENABLE && !defined(__SPHINX_DOC)
int log_msg(const char *subsys, const char *format, ...)
__attribute__((format(printf, 2, 3)));
#if LOG_ENABLE_DEBUG
#define LOG_DEBUG(subsys, format, ...) \
log_msg(subsys, SEV_DEBUG format, ##__VA_ARGS__)
#else /* LOG_ENABLE_DEBUG */
#define LOG_DEBUG(subsys, format, ...)
#endif /* LOG_ENABLE_DEBUG */
#define LOG_INFO(subsys, format, ...) \
log_msg(subsys, SEV_INFO format, ##__VA_ARGS__)
#define LOG_WARN(subsys, format, ...) \
log_msg(subsys, SEV_WARN format, ##__VA_ARGS__)
#define LOG_ERR(subsys, format, ...) \
log_msg(subsys, SEV_ERR format, ##__VA_ARGS__)
#define LOG_CRIT(subsys, format, ...) \
log_msg(subsys, SEV_CRIT format, ##__VA_ARGS__)
#else /* LOG_ENABLE */
/** */
#define LOG_INFO(subsys, format, ...) 0
/** */
#define LOG_WARN(subsys, format, ...) 0
/** */
#define LOG_ERR(subsys, format, ...) 0
/** */
#define LOG_CRIT(subsys, format, ...) 0
/** Only prints when debug logging is enabled in the meson configuration. */
#define LOG_DEBUG(subsys, format, ...) 0
inline __attribute__((format(printf, 2, 3))) int
log_msg(const char *subsys, const char *format, ...)
{
return 0;
}
#endif /* LOG_ENABLE */
#include "modules/log.h"
#include "os/core.h"
#include "FreeRTOS.h"
#include "task.h"
......
os_sources = files(
'config.c',
'log.c',
'mutex.c',
'panic.c',
'work_queue.c',
)
#include "os/mutex.h"
#include <assert.h>
void _mutex_create(struct mutex *m, const char *name)
{
/* Assert that the mutex has not been initialized already */
assert(m->name == NULL);
/*
* The name is just the parameter stringified which is almost always a
* pointer. If it is, skip over the '&' because it adds no value as
* part of the name.
*/
if (name[0] == '&') {
m->name = &name[1];
} else {
m->name = name;
}
m->_rtos_mutex = xSemaphoreCreateMutexStatic(&m->_rtos_mutex_data);
}
void mutex_lock(struct mutex *m)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return;
}
int ret = xSemaphoreTake(m->_rtos_mutex, portMAX_DELAY);
/* Ensure locking was actually successful */
assert(ret == pdTRUE);
}
bool mutex_trylock(struct mutex *m)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return true;
}
int ret = xSemaphoreTake(m->_rtos_mutex, 0);
return ret == pdTRUE;
}
void mutex_assert_locked(struct mutex *m)
{
assert(mutex_get_owner(m) == xTaskGetCurrentTaskHandle());
}
void mutex_unlock(struct mutex *m)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
return;
}
/* Ensure that only the owner can unlock a mutex */
mutex_assert_locked(m);
int ret = xSemaphoreGive(m->_rtos_mutex);
/*
* Ensure that unlocking was successful; that is, the mutex must have
* been acquired previously (no multiple unlocks).
*/
assert(ret == pdTRUE);
}
TaskHandle_t mutex_get_owner(struct mutex *m)
{
return xSemaphoreGetMutexHolder(m->_rtos_mutex);
}
#ifndef _MUTEX_H
#define _MUTEX_H
#ifndef __SPHINX_DOC
/* Some headers are not recognized by hawkmoth for some odd reason */
#include <stddef.h>
#include <stdbool.h>
#include "FreeRTOS.h"
#include "semphr.h"
#else
typedef unsigned int size_t;
typedef _Bool bool;
typedef void *TaskHandle_t;
typedef void *SemaphoreHandle_t;
typedef int StaticSemaphore_t;
#endif /* __SPHINX_DOC */
/**
* Mutex data type.
*/
struct mutex {
/* Name of this mutex, kept for debugging purposes. */
const char *name;
/* FreeRTOS mutex data structures. */
SemaphoreHandle_t _rtos_mutex;
StaticSemaphore_t _rtos_mutex_data;
};
/**
* Create a new mutex.
*
* Call this function as early as possible, in an obvious location so it is easy
* to find. Mutexes should be defined statically so they stay alive for the
* entire run-time of the firmware.
*/
#define mutex_create(mutex) _mutex_create(mutex, #mutex)
void _mutex_create(struct mutex *m, const char *name);
/**
* Lock a mutex.
*
* If the mutex is held by another task, :c:func:`mutex_lock` will block the
* current task until the mutex is unlocked.
*
* .. warning::
*
* This function is **not** safe to use in a timer!
*/
void mutex_lock(struct mutex *m);
/**
* Try locking a mutex.
*
* If the mutex is currently locked by another task, :c:func:`mutex_trylock`
* will return ``false`` immediately. If the attmept to lock was successful, it
* will return ``true``.
*
* This funciton is safe for use in timers.
*/
bool mutex_trylock(struct mutex *m);
/**
* Unlock a mutex.
*
* You **must** call this function from the same task which originally locked
* the mutex.
*/
void mutex_unlock(struct mutex *m);
/**
* Assert that the current task is holding a mutex lock.
*
* If a function requires a certain mutex to be held for safe operation,
* but does not take care of the locking itself, :c:func:`mutex_assert_locked`
* can be used to assert correct caller behavior.
*/
void mutex_assert_locked(struct mutex *m);
/**
* Get the current owner of the mutex.
*
* Returns the task-handle of the task currently holding the mutex. If the
* mutex is unlocked, ``NULL`` is returned.
*/
TaskHandle_t mutex_get_owner(struct mutex *m);
#endif /* _MUTEX_H */
/*
* Panic
* =====
*
* Under some conditions the firmware should crash and reboot automatically.
* This module provides the necessary facilities to do so.
*
* Note that a panic should indicate **only** logic-errors in the firmware or
* unrecoverable hardware conditions.
*/
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "drivers/display/epic_ctx.h"
#include "drivers/display/lcd.h"
#include "card10.h"
#include "card10-version.h"
#include "LCD_Driver.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
static void faultsplash(const char *msg);
void __attribute__((noreturn)) panic(const char *format, ...)
{
/* Turn off interrupts. We won't get back from here anyway. */
__asm volatile("cpsid i" ::: "memory");
/*
* Turn off asynchronous printing because that won't ever work from
* here ...
*/
serial_return_to_synchronous();
printf("\x1b[31;1m --- SYSTEM PANIC ---\n"
"\x1b[0;31m --- ---\n"
" --- ---\n"
"\x1b[0m A fatal error occured:\n \x1b[1m");
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
printf("\n"
"\x1b[0m\n"
" Firmware Version:\n"
"\x1b[35m %s\n",
CARD10_VERSION);
printf("\x1b[0m\n"
" Stack Trace:\n"
"\x1b[36m %p\n",
__builtin_return_address(0));
printf("\x1b[33m\n"
" Please report this error to the card10 firmware team!\n"
"\x1b[0m -> https://git.card10.badge.events.ccc.de/card10/firmware/issues/new <-\n"
"\x1b[31m --- ====== ===== ---\x1b[0m\n");
char faultsplash_buffer[14 * 4];
va_start(ap, format);
vsnprintf(faultsplash_buffer, sizeof(faultsplash_buffer), format, ap);
va_end(ap);
faultsplash(faultsplash_buffer);
for (int i = 0; i < 96000000; i++) {
__asm volatile("nop");
}
card10_reset();
}
void __attribute__((noreturn)) __assert_func(
const char *file, int line, const char *func, const char *failedexpr
) {
panic("Assertion failure:\n"
" \"%s\"\n"
" failed in \"%s:%d\",\n"
" function: %s()",
failedexpr,
file,
line,
func);
}
static const unsigned char faultsplash_rle[] = {
0x7f, 0x50, 0x83, 0x0f, 0x82, 0x7f, 0x0d, 0x83, 0x0f, 0x82, 0x7f, 0x1d,
0x82, 0x7f, 0x1f, 0x82, 0x7f, 0x1f, 0x82, 0x7f, 0x12, 0x82, 0x09, 0x82,
0x7f, 0x14, 0x82, 0x09, 0x82, 0x7f, 0x09, 0x82, 0x11, 0x83, 0x7f, 0x0b,
0x82, 0x11, 0x83, 0x7f, 0x0f, 0x82, 0x07, 0x82, 0x7f, 0x16, 0x82, 0x07,
0x82, 0x7f, 0x16, 0x82, 0x07, 0x82, 0x7f, 0x1a, 0x83, 0x7f, 0x1e, 0x83,
0x7f, 0x0a, 0x8b, 0x17, 0x82, 0x02, 0x82, 0x02, 0x83, 0x73, 0x8c, 0x16,
0x82, 0x02, 0x82, 0x02, 0x83, 0x72, 0x81, 0x0c, 0x84, 0x07, 0x85, 0x7f,
0x03, 0x82, 0x0c, 0x84, 0x07, 0x86, 0x7f, 0x02, 0x82, 0x0c, 0x84, 0x07,
0x86, 0x7f, 0x82, 0x12, 0x87, 0x7f, 0x06, 0x82, 0x12, 0x87, 0x7f, 0x06,
0x82, 0x24, 0x82, 0x78, 0x82, 0x24, 0x82, 0x78, 0x82, 0x24, 0x82, 0x78,
0x82, 0x16, 0x83, 0x04, 0x82, 0x07, 0x82, 0x76, 0x82, 0x16, 0x83, 0x04,
0x82, 0x07, 0x82, 0x70, 0x8f, 0x0d, 0x82, 0x12, 0x82, 0x6e, 0x8f, 0x0d,
0x82, 0x12, 0x82, 0x6e, 0x8f, 0x0b, 0x82, 0x09, 0x82, 0x0b, 0x82, 0x6c,
0x8f, 0x0b, 0x82, 0x09, 0x82, 0x0b, 0x82, 0x6c, 0x8f, 0x0b, 0x82, 0x09,
0x82, 0x0b, 0x82, 0x6c, 0x8f, 0x7f, 0x12, 0x8f, 0x7f, 0x0d, 0x98, 0x12,
0x82, 0x74, 0x98, 0x12, 0x82, 0x70, 0xa1, 0x7f, 0xa1, 0x7f, 0xa1, 0x7f,
0xa1, 0x7f, 0xa1, 0x7d, 0xa5, 0x7b, 0xa5, 0x7b, 0xa5, 0x7b, 0xa5, 0x7a,
0xa6, 0x78, 0x89, 0x02, 0x9f, 0x76, 0x89, 0x02, 0x9f, 0x76, 0xaa, 0x76,
0xaa, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02,
0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0x87, 0x02,
0xa1, 0x76, 0x87, 0x02, 0xa1, 0x76, 0xaa, 0x76, 0xaa, 0x76, 0xaa, 0x76,
0x89, 0x02, 0x9f, 0x76, 0x89, 0x02, 0x9f, 0x79, 0xa5, 0x7b, 0xa5, 0x7b,
0xa5, 0x7b, 0x8b, 0x02, 0x98, 0x7b, 0x8b, 0x02, 0x98, 0x7d, 0xa1, 0x7f,
0xa1, 0x7f, 0xa1, 0x7f, 0xa1, 0x7f, 0xa1, 0x7f, 0x04, 0x98, 0x7f, 0x09,
0x98, 0x7f, 0x0e, 0x8f, 0x7f, 0x12, 0x8f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x06
};
static void faultsplash(const char *msg)
{
LCD_SetBacklight(100);
size_t idx = 0;
for (size_t i = 0; i < sizeof(faultsplash_rle); i++) {
uint8_t color = (faultsplash_rle[i] & 0x80) ? 0xff : 0x00;
uint8_t length = faultsplash_rle[i] & 0x7f;
memset(&epicardium_ctx_fb[idx], color, length * 2);
idx += length * 2;
}
lcd_write_fb(epicardium_ctx_fb);
/* make sure we get a clean graphics ctx */
disp_ctx_reinit();
ctx_text_baseline(epicardium_ctx, CTX_TEXT_BASELINE_TOP);
ctx_font_size(epicardium_ctx, 20.0f);
ctx_move_to(epicardium_ctx, 80.0f, 8.0f);
ctx_rgba8(epicardium_ctx, 0xff, 0x00, 0x00, 0xff);
ctx_text(epicardium_ctx, "Panic");
ctx_font_size(epicardium_ctx, 12.0f);
ctx_rgba8(epicardium_ctx, 197, 197, 197, 0xff);
size_t len = strlen(msg);
char buf[15] = { 0 };
float offset = 34.0f;
for (size_t i = 0; i < len; i += 14) {
strncpy(buf, &msg[i], 14);
ctx_move_to(epicardium_ctx, 52.0f, offset);
ctx_text(epicardium_ctx, buf);
offset += 12.0f;
}
lcd_write_fb(epicardium_ctx_fb);
}
#include "epicardium.h"
#include "os/core.h"
#include "os/work_queue.h"
#include "FreeRTOS.h"
#include "queue.h"
struct work {
void (*func)(void *data);
void *data;
};
static QueueHandle_t work_queue;
static uint8_t buffer[sizeof(struct work) * WORK_QUEUE_SIZE];
static StaticQueue_t work_queue_data;
void workqueue_init(void)
{
work_queue = xQueueCreateStatic(
WORK_QUEUE_SIZE, sizeof(struct work), buffer, &work_queue_data
);
}
int workqueue_schedule(void (*func)(void *data), void *data)
{
struct work work = { func, data };
if (xQueueSend(work_queue, &work, 0) != pdTRUE) {
/* Likely full */
LOG_WARN("workqueue", "could not schedule %p(%p)", func, data);
return -EAGAIN;
}
return 0;
}
void vWorkQueueTask(void *pvParameters)
{
struct work work;
workqueue_init();
while (1) {
if (xQueueReceive(work_queue, &work, portMAX_DELAY) == pdTRUE) {
if (work.func) {
work.func(work.data);
}
}
}
}
#ifndef _WORK_QUEUE_H
#define _WORK_QUEUE_H
#define WORK_QUEUE_SIZE 20
void workqueue_init(void);
int workqueue_schedule(void (*func)(void *data), void *data);
void vWorkQueueTask(void *pvParameters);
#endif
......@@ -6,32 +6,146 @@
#include "task.h"
#include "api/dispatcher.h"
#include "user_core/user_core.h"
#include "os/core.h"
#include "drivers/drivers.h"
#include "modules/modules.h"
#include "modules/log.h"
#define BB_CLK_RATE_HZ 1600000
#include "usb.h"
#include "mxc_sys.h"
#include "wut.h"
#include "wsf_types.h"
#include "wsf_os.h"
#include "sch_api_ble.h"
#include "bb_drv.h"
#include "card10.h"
#define US_TO_BBTICKS(x) (((x) * (BB_CLK_RATE_HZ / 100000) + 9) / 10)
#define MIN(a, b) (a < b) ? a : b
#define MIN_SLEEP_TIME_MS 1
static int32_t ble_sleep_ticks(void)
{
uint32_t nextDbbEventDue;
bool_t dueValid = SchBleGetNextDueTime(&nextDbbEventDue);
int sleep_ticks = nextDbbEventDue - BbDrvGetCurrentTime();
if (dueValid) {
if (sleep_ticks > 0) {
uint32_t bb_idle = BB_TICKS_TO_US(sleep_ticks) / 1000;
return bb_idle;
} else {
return 0;
}
} else {
return -1;
}
}
/*
* This hook is called before FreeRTOS enters tickless idle.
*/
void pre_idle_sleep(TickType_t xExpectedIdleTime)
{
if (xExpectedIdleTime > 0) {
if (xExpectedIdleTime > 0 &&
(CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
/*
* WFE because the other core should be able to notify
* epicardium if it wants to issue an API call.
*/
/*
* TODO: Ensure this is actually correct and does not have any
* race conditions.
*/
if ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0) {
/* If the other core is waiting for a call to return and we are able
* to sleep, reduce the system clock to save energy. */
if (xExpectedIdleTime >= pdMS_TO_TICKS(MIN_SLEEP_TIME_MS) &&
api_dispatcher_call_pending() && wsfOsReadyToSleep()) {
uint32_t ms = xExpectedIdleTime;
if (ble_is_enabled()) {
int32_t ble_idle = ble_sleep_ticks();
if (ble_idle >= 0) {
ms =
MIN(xExpectedIdleTime,
(uint32_t)ble_idle);
}
}
// Us the WUT only if we can sleep a significant amount of time
if (ms >= MIN_SLEEP_TIME_MS) {
/* Initialize Wakeup timer */
WUT_Init(WUT_PRES_1);
wut_cfg_t wut_cfg;
wut_cfg.mode = WUT_MODE_COMPARE;
wut_cfg.cmp_cnt = 0xFFFFFFFF;
WUT_Config(&wut_cfg);
WUT_Enable();
/* Enable WUT as a wakup source */
NVIC_EnableIRQ(WUT_IRQn);
uint32_t targetTick;
targetTick = WUT_GetCount();
targetTick +=
((uint64_t)(ms)*SYS_WUT_GetFreq() /
1000);
WUT_SetCompare(targetTick);
/* Stop SysTick */
uint32_t val = SysTick->VAL;
SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);
if (usb_get_status() & MAXUSB_STATUS_VBUS_ON) {
/* Need to stay on 96 MHz. USB serial becomes
* unstable otherwise. Don't know why. */
//SYS_Clock_Select(SYS_CLOCK_HIRC, NULL);
} else {
MXC_GCR->clkcn |=
MXC_S_GCR_CLKCN_PSC_DIV4;
SYS_Clock_Select(SYS_CLOCK_HIRC, NULL);
SYS_ClockSourceDisable(
SYS_CLOCK_HIRC96
);
}
disp_update_backlight_clock();
__asm volatile("dsb" ::: "memory");
__asm volatile("wfe");
__asm volatile("isb");
MXC_GCR->clkcn &= ~(MXC_S_GCR_CLKCN_PSC_DIV4);
SYS_Clock_Select(SYS_CLOCK_HIRC96, NULL);
#if 0
/* Allow to serve high priority interrupts before
* doing the lengthy xTaskIncrementTick loop. */
portDISABLE_INTERRUPTS();
__set_PRIMASK(0);
__set_PRIMASK(1);
portENABLE_INTERRUPTS();
#endif
disp_update_backlight_clock();
SYS_ClockSourceDisable(SYS_CLOCK_HIRC);
SysTick->LOAD = val;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
ms = (WUT_GetCount() * 1000) /
SYS_WUT_GetFreq();
for (uint32_t t = 0; t < ms; t++) {
xTaskIncrementTick();
}
return;
}
}
/* Fall back to light sleep with clocks on if we can't
* sleep long enough for the WUT */
__asm volatile("dsb" ::: "memory");
__asm volatile("wfe");
__asm volatile("isb");
}
}
/*
......@@ -117,5 +231,5 @@ void vApplicationGetTimerTaskMemory(
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
{
LOG_CRIT("rtos", "Task \"%s\" overflowed stack!", pcTaskName);
panic("Task \"%s\" overflowed stack!", pcTaskName);
}