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
  • blinkisync-as-preload
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • ch3/splashscreen
  • drawcall_clipping
  • dualcore
  • faulty_unsigned_comparisons
  • freertos-btle
  • fuchsi-ecg-app
  • genofire/ble-follow-py
  • genofire/haule-ble-fs-deactive
  • genofire/leds_rgb_get_state
  • genofire/rockets-state
  • gpio_in_adc_fix
  • ios-workarounds
  • koalo/bhi160-works-but-dirty
  • koalo/factory-reset
  • koalo/wip/i2c-for-python
  • master
  • msgctl/faultscreen
  • msgctl/gfx_rle
  • msgctl/textbuffer_api
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bma
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/ble-buffers
  • schneider/bonding
  • schneider/bootloader-update-9a0d158
  • schneider/bsec
  • schneider/fundamental-test
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-for-old-bl
  • schneider/schleicher-test
  • schneider/stream-locks
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
55 results
Show changes
Showing
with 3077 additions and 735 deletions
......@@ -24,13 +24,17 @@
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "card10-version.h"
#include "wsf_types.h"
#include "att_api.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#define DIS_MAXSIZE_FWR_ATT 32
#include "svc_dis.h"
#include "svc_cfg.h"
#include <string.h>
/**************************************************************************************************
Macros
......@@ -60,10 +64,10 @@
#define DIS_DEFAULT_SERIAL_NUM_LEN 1
/*! Default firmware revision */
#define DIS_DEFAULT_FW_REV "<git hash>"
#define DIS_DEFAULT_FW_REV CARD10_VERSION
/*! Length of default firmware revision */
#define DIS_DEFAULT_FW_REV_LEN 10
#define DIS_DEFAULT_FW_REV_LEN strlen(CARD10_VERSION)
/*! Default hardware revision */
#define DIS_DEFAULT_HW_REV "1"
......@@ -128,7 +132,7 @@ static const uint16_t disLenFwrCh = sizeof(disValFwrCh);
/* Firmware revision string */
static const uint8_t disUuFwr[] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
static uint8_t disValFwr[DIS_MAXSIZE_FWR_ATT] = DIS_DEFAULT_FW_REV;
static uint16_t disLenFwr = DIS_DEFAULT_FW_REV_LEN;
static uint16_t disLenFwr;
/* Hardware revision string characteristic */
static const uint8_t disValHwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_HWR_HDL), UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
......@@ -319,6 +323,7 @@ WSF_CT_ASSERT(((sizeof(disList) / sizeof(disList[0])) == DIS_END_HDL - DIS_START
/*************************************************************************************************/
void SvcDisAddGroup(void)
{
disLenFwr = DIS_DEFAULT_FW_REV_LEN;
AttsAddGroup(&svcDisGroup);
}
......
#include "uart.h"
#include "cccd.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "att_api.h"
#include "dm_api.h"
#include "app_api.h"
#include "FreeRTOS.h"
#include "timers.h"
......@@ -11,85 +17,91 @@
#include <string.h>
#include <stdbool.h>
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/**************************************************************************************************
Handles
**************************************************************************************************/
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
/**@}*/
/* clang-format off */
static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E};
static const uint16_t UARTSvc_len = sizeof(UARTSvc);
static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint16_t uartRxCh_len = sizeof(uartRxCh);
static const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
/* clang-format on */
static const uint16_t uartTxCh_len = sizeof(uartTxCh);
static const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
static void *SvcUARTAddGroupDyn(void)
{
void *pSHdl;
uint8_t initCcc[] = { UINT16_TO_BYTES(0x0000) };
static uint8_t uartValTxChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t uartLenTxChCcc = sizeof(uartValTxChCcc);
/* Create the service */
pSHdl = AttsDynCreateGroup(UART_START_HDL, UART_END_HDL);
static uint8_t ble_uart_tx_buf[20];
static uint16_t ble_uart_buf_tx_fill = 0;
/* clang-format on */
if (pSHdl != NULL) {
/* clang-format off */
/* Attribute list for uriCfg group */
static const attsAttr_t uartAttrCfgList[] = {
/* Primary service */
AttsDynAddAttrConst( pSHdl, attPrimSvcUuid, UARTSvc, sizeof(UARTSvc),
0, ATTS_PERMIT_READ);
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UARTSvc,
.pLen = (uint16_t *)&UARTSvc_len,
.maxLen = sizeof(UARTSvc),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartRxCh, sizeof(uartRxCh),
0, ATTS_PERMIT_READ);
// XXX: attUartRxChUuid is 16 bytes but nothing says so....
{
.pUuid = attChUuid,
.pValue = (uint8_t *)uartRxCh,
.pLen = (uint16_t *)&uartRxCh_len,
.maxLen = sizeof(uartRxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx value */
// XXX: not sure if max value of 128 is fine...
AttsDynAddAttr( pSHdl, attUartRxChUuid, NULL, 0, 128,
ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, ATTS_PERMIT_WRITE);
{
.pUuid = attUartRxChUuid,
.pValue = NULL,
.pLen = NULL,
.maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
/* UART tx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartTxCh, sizeof(uartTxCh),
0, ATTS_PERMIT_READ);
{
.pUuid = attChUuid,
.pValue = (uint8_t *)uartTxCh,
.pLen = (uint16_t *)&uartTxCh_len,
.maxLen = sizeof(uartTxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART tx value */
/* TODO: do we need ATTS_SET_READ_CBACK ? */
AttsDynAddAttr( pSHdl, attUartTxChUuid, NULL, 0, sizeof(uint8_t),
ATTS_SET_READ_CBACK, ATTS_PERMIT_READ);
{
.pUuid = attUartTxChUuid,
.pValue = ble_uart_tx_buf,
.pLen = &ble_uart_buf_tx_fill,
.maxLen = sizeof(ble_uart_tx_buf),
.settings = 0,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* UART tx CCC descriptor */
AttsDynAddAttr( pSHdl, attCliChCfgUuid, initCcc, sizeof(uint16_t), sizeof(uint16_t),
ATTS_SET_CCC, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
/* clang-format on */
}
return pSHdl;
}
dmConnId_t active_connection = 0;
{
.pUuid = attCliChCfgUuid,
.pValue = uartValTxChCcc,
.pLen = (uint16_t *)&uartLenTxChCcc,
.maxLen = sizeof(uartValTxChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
static uint8_t UARTReadCback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
printf("read callback\n");
return ATT_SUCCESS;
}
};
static uint8_t UARTWriteCback(
dmConnId_t connId,
......@@ -100,76 +112,107 @@ static uint8_t UARTWriteCback(
uint8_t *pValue,
attsAttr_t *pAttr
) {
active_connection = connId;
static bool was_r = false;
//printf("UARTWriteCback %d: ", len);
int i;
for (i = 0; i < len; i++) {
//printf("%c", pValue[i]);
if (pValue[i] == '\n' && !was_r) {
serial_enqueue_char('\r');
}
was_r = pValue[i] == '\r';
serial_enqueue_char(pValue[i]);
}
serial_enqueue_char('\r');
//printf("\n");
#if 0
AttsSetAttr(UART_TX_HDL, len, pValue);
AttsHandleValueNtf(connId, UART_TX_HDL, len, pValue);
#endif
return ATT_SUCCESS;
}
uint8_t ble_uart_tx_buf[129];
uint8_t ble_uart_buf_tx_fill;
int ble_uart_lasttick = 0;
static bool done;
static bool again;
void ble_uart_write(uint8_t *pValue, uint8_t len)
static void ble_uart_flush(void)
{
int i;
for (i = 0; i < len; i++) {
if (pValue[i] >= 0x20 && pValue[i] < 0x7f) {
ble_uart_tx_buf[ble_uart_buf_tx_fill] = pValue[i];
ble_uart_buf_tx_fill++;
} else if (pValue[i] == '\r' || pValue[i] == '\n') {
if (ble_uart_buf_tx_fill > 0) {
AttsSetAttr(
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
if (active_connection) {
int x = xTaskGetTickCount() -
ble_uart_lasttick;
if (x < 100) {
// Ugly hack if we already send something recently.
// TODO: figure out how fast we can send or use indications
vTaskDelay(100 - x);
}
//printf("notify: ");
//int j;
//for(j=0;j<ble_uart_buf_tx_fill;j++) {
// printf("%02x ", ble_uart_tx_buf[j]);
//}
//printf("\n");
if (ble_uart_buf_tx_fill == 0) {
return;
}
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE) {
if (AttsCccEnabled(connId, UART_TX_CH_CCC_IDX)) {
done = false;
again = true;
int t0 = xTaskGetTickCount();
while (!done && ((xTaskGetTickCount() - t0) < 1000)) {
if (again) {
again = false;
AttsHandleValueNtf(
active_connection,
connId,
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
ble_uart_lasttick = xTaskGetTickCount();
}
/* This function is supposed to only be called
* from the API scheduler with lowest priority.
*
* If that is not the case anymore, use a delay
* instead of the yield. Ideally refactor to avoid
* the delay.
*/
//vTaskDelay(5);
taskYIELD();
}
}
}
ble_uart_buf_tx_fill = 0;
}
static void ble_uart_write_char(uint8_t c)
{
ble_uart_tx_buf[ble_uart_buf_tx_fill] = c;
ble_uart_buf_tx_fill++;
// TODO: increase buffer if configured MTU allows it
if (ble_uart_buf_tx_fill == sizeof(ble_uart_tx_buf)) {
ble_uart_flush();
}
}
void ble_uart_write(uint8_t *pValue, uint8_t len)
{
for (int i = 0; i < len; i++) {
ble_uart_write_char(pValue[i]);
}
// TODO schedule timer in a few ms to flush the buffer
ble_uart_flush();
}
static attsGroup_t uartCfgGroup = {
.pAttr = (attsAttr_t *)uartAttrCfgList,
.writeCback = UARTWriteCback,
.startHandle = UART_START_HDL,
.endHandle = UART_END_HDL,
};
void bleuart_init(void)
{
/* Add the UART service dynamically */
void *pSHdl;
pSHdl = SvcUARTAddGroupDyn();
AttsDynRegister(pSHdl, UARTReadCback, UARTWriteCback);
//AttsDynRegister(pSHdl, NULL, UARTWriteCback);
/* Add the UART service */
AttsAddGroup(&uartCfgGroup);
}
void UartProcMsg(bleMsg_t *pMsg)
{
if (pMsg->hdr.event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->att.handle == UART_TX_HDL) {
if (pMsg->hdr.status == ATT_SUCCESS) {
done = true;
} else if (pMsg->hdr.status == ATT_ERR_OVERFLOW) {
again = true;
}
}
}
if (pMsg->hdr.event == DM_CONN_OPEN_IND) {
ble_uart_buf_tx_fill = 0;
}
}
#pragma once
#include "ble_api.h"
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
void UartProcMsg(bleMsg_t *pMsg);
/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*
* $Id: main.c 32120 2017-11-28 23:51:11Z lorne.smith $
*
*******************************************************************************
*/
/**
* @file main.c
* @brief USB CDC-ACM example
* @details This project creates a virtual COM port, which loops back data sent to it.
* Load the project, connect a cable from the PC to the USB connector
* on the Evaluation Kit, and observe that the PC now recognizes a new COM port.
* A driver for the COM port, if needed, is located in the Driver/ subdirectory.
*
*/
#include <stdio.h>
#include <stddef.h>
#include "mxc_config.h"
#include "mxc_sys.h"
#include "mxc_delay.h"
#include "board.h"
#include "led.h"
#include "usb.h"
#include "usb_event.h"
#include "enumerate.h"
#include "cdc_acm.h"
#include "descriptors.h"
#include "modules/log.h"
#include <errno.h>
/***** Definitions *****/
#define EVENT_ENUM_COMP MAXUSB_NUM_EVENTS
#define BUFFER_SIZE 64
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
/***** Global Data *****/
//SWYM: rename to CDC_xy or put into struct CDC_state
volatile int configured; //SWYM: actually unused...
volatile int suspended;
volatile unsigned int event_flags;
int remote_wake_en;
/***** Function Prototypes *****/
static int setconfig_callback(usb_setup_pkt *sud, void *cbdata);
static int setfeature_callback(usb_setup_pkt *sud, void *cbdata);
static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata);
static int event_callback(maxusb_event_t evt, void *data);
static void usb_app_sleep(void);
static void usb_app_wakeup(void);
static int usb_read_callback(void);
//static void echo_usb(void);
/***** File Scope Variables *****/
/* This EP assignment must match the Configuration Descriptor */
static const acm_cfg_t acm_cfg = {
1, /* EP OUT */
MXC_USBHS_MAX_PACKET, /* OUT max packet size */
2, /* EP IN */
MXC_USBHS_MAX_PACKET, /* IN max packet size */
3, /* EP Notify */
MXC_USBHS_MAX_PACKET, /* Notify max packet size */
};
static volatile int usb_read_complete;
int usb_startup_cb()
{
const sys_cfg_usbhs_t sys_usbhs_cfg = NULL;
return SYS_USBHS_Init(&sys_usbhs_cfg);
}
int usb_shutdown_cb()
{
return SYS_USBHS_Shutdown();
}
/* User-supplied function to delay usec micro-seconds */
void delay_us(unsigned int usec)
{
/* mxc_delay() takes unsigned long, so can't use it directly */
mxc_delay(usec);
}
/******************************************************************************/
int cdcacm_init(void)
{
maxusb_cfg_options_t usb_opts;
/* Initialize state */
configured = 0;
suspended = 0;
event_flags = 0;
remote_wake_en = 0;
/* Start out in full speed */
usb_opts.enable_hs = 0;
usb_opts.delay_us =
delay_us; /* Function which will be used for delays */
usb_opts.init_callback = usb_startup_cb;
usb_opts.shutdown_callback = usb_shutdown_cb;
/* Initialize the usb module */
if (usb_init(&usb_opts) != 0) {
LOG_ERR("cdcacm", "usb_init() failed");
return -EIO;
}
/* Initialize the enumeration module */
if (enum_init() != 0) {
LOG_ERR("cdcacm", "enum_init() failed");
return -EIO;
}
/* Register enumeration data */
enum_register_descriptor(
ENUM_DESC_DEVICE, (uint8_t *)&device_descriptor, 0
);
enum_register_descriptor(
ENUM_DESC_CONFIG, (uint8_t *)&config_descriptor, 0
);
enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0);
enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1);
enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2);
/* Handle configuration */
enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL);
/* Handle feature set/clear */
enum_register_callback(ENUM_SETFEATURE, setfeature_callback, NULL);
enum_register_callback(ENUM_CLRFEATURE, clrfeature_callback, NULL);
/* Initialize the class driver */
if (acm_init(&config_descriptor.comm_interface_descriptor) != 0) {
LOG_ERR("cdcacm", "acm_init() failed");
return -EIO;
}
/* Register callbacks */
usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL);
usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL);
acm_register_callback(ACM_CB_READ_READY, usb_read_callback);
usb_read_complete = 0;
/* Start with USB in low power mode */
usb_app_sleep();
/* TODO: Fix priority */
NVIC_SetPriority(USB_IRQn, 6);
NVIC_EnableIRQ(USB_IRQn);
return 0;
}
int cdcacm_num_read_avail(void)
{
return acm_canread();
}
uint8_t cdcacm_read(void)
{
while (acm_canread() <= 0) {
}
uint8_t buf;
acm_read(&buf, 1);
return buf;
}
void cdcacm_write(uint8_t *data, int len)
{
static int lockup_disable = 0;
if (acm_present() && !lockup_disable) {
int ret = acm_write(data, len);
if (ret < 0) {
LOG_ERR("cdcacm", "fifo lockup detected");
lockup_disable = 1;
} else if (ret != len) {
LOG_WARN(
"cdcacm", "write length mismatch, got %d", ret
);
}
}
}
/******************************************************************************/
#if 0
static void echo_usb(void)
{
int chars;
uint8_t buffer[BUFFER_SIZE];
if ((chars = acm_canread()) > 0) {
if (chars > BUFFER_SIZE) {
chars = BUFFER_SIZE;
}
// Read the data from USB
if (acm_read(buffer, chars) != chars) {
printf("acm_read() failed\n");
return;
}
// Echo it back
if (acm_present()) {
if (acm_write(buffer, chars) != chars) {
printf("acm_write() failed\n");
}
}
}
}
#endif
/******************************************************************************/
static int setconfig_callback(usb_setup_pkt *sud, void *cbdata)
{
/* Confirm the configuration value */
if (sud->wValue ==
config_descriptor.config_descriptor.bConfigurationValue) {
configured = 1;
MXC_SETBIT(&event_flags, EVENT_ENUM_COMP);
return acm_configure(&acm_cfg); /* Configure the device class */
} else if (sud->wValue == 0) {
configured = 0;
return acm_deconfigure();
}
return -1;
}
/******************************************************************************/
static int setfeature_callback(usb_setup_pkt *sud, void *cbdata)
{
if (sud->wValue == FEAT_REMOTE_WAKE) {
remote_wake_en = 1;
} else {
// Unknown callback
return -1;
}
return 0;
}
/******************************************************************************/
static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata)
{
if (sud->wValue == FEAT_REMOTE_WAKE) {
remote_wake_en = 0;
} else {
// Unknown callback
return -1;
}
return 0;
}
/******************************************************************************/
static void usb_app_sleep(void)
{
/* TODO: Place low-power code here */
suspended = 1;
}
/******************************************************************************/
static void usb_app_wakeup(void)
{
/* TODO: Place low-power code here */
suspended = 0;
}
/******************************************************************************/
static int event_callback(maxusb_event_t evt, void *data)
{
/* Set event flag */
MXC_SETBIT(&event_flags, evt);
switch (evt) {
case MAXUSB_EVENT_NOVBUS:
usb_event_disable(MAXUSB_EVENT_BRST);
usb_event_disable(MAXUSB_EVENT_SUSP);
usb_event_disable(MAXUSB_EVENT_DPACT);
usb_disconnect();
configured = 0;
enum_clearconfig();
acm_deconfigure();
usb_app_sleep();
break;
case MAXUSB_EVENT_VBUS:
usb_event_clear(MAXUSB_EVENT_BRST);
usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL);
usb_event_clear(MAXUSB_EVENT_SUSP);
usb_event_enable(MAXUSB_EVENT_SUSP, event_callback, NULL);
usb_connect();
usb_app_sleep();
break;
case MAXUSB_EVENT_BRST:
usb_app_wakeup();
enum_clearconfig();
acm_deconfigure();
configured = 0;
suspended = 0;
break;
case MAXUSB_EVENT_SUSP:
usb_app_sleep();
break;
case MAXUSB_EVENT_DPACT:
usb_app_wakeup();
break;
default:
break;
}
return 0;
}
/******************************************************************************/
static int usb_read_callback(void)
{
usb_read_complete = 1;
return 0;
}
/******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
extern TaskHandle_t serial_task_id;
void USB_IRQHandler(void)
{
usb_event_handler();
if (serial_task_id != NULL) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(
serial_task_id, &xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/******************************************************************************/
/* TODO: We probably need to fix something related to this */
#if 0
void SysTick_Handler(void)
{
mxc_delay_handler();
}
#endif /* 0 */
#ifndef CDCACM_H
#define CDCACM_H
#include <stdint.h>
int cdcacm_init(void);
int cdcacm_num_read_avail(void);
uint8_t cdcacm_read(void);
void cdcacm_write(uint8_t *data, int len);
#endif
/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*
* Description: Communications Device Class ACM (Serial Port) over USB
* $Id: descriptors.h 36682 2018-08-06 21:14:03Z michael.bayern $
*
*******************************************************************************
*/
#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_
#include <stdint.h>
#include "usb.h"
#include "hid_kbd.h"
usb_device_descriptor_t __attribute__((aligned(4))) device_descriptor = {
0x12, /* bLength = 18 */
0x01, /* bDescriptorType = Device */
0x0110, /* bcdUSB USB spec rev (BCD) */
0x02, /* bDeviceClass = comm class (2) */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
0x40, /* bMaxPacketSize0 is 64 bytes */
0x0B6A, /* idVendor (Maxim Integrated) */
0x003C, /* idProduct */
0x0100, /* bcdDevice */
0x01, /* iManufacturer Descriptor ID */
0x02, /* iProduct Descriptor ID */
0x00, /* iSerialNumber = (0) No string */
0x01 /* bNumConfigurations */
};
__attribute__((aligned(4)))
struct __attribute__((packed)) {
usb_configuration_descriptor_t config_descriptor;
usb_interface_descriptor_t comm_interface_descriptor;
uint8_t header_functional_descriptor[5];
uint8_t call_management_descriptor[5];
uint8_t acm_functional_descriptor[4];
uint8_t union_functional_descriptor[5];
usb_endpoint_descriptor_t endpoint_descriptor_3;
usb_interface_descriptor_t data_interface_descriptor;
usb_endpoint_descriptor_t endpoint_descriptor_1;
usb_endpoint_descriptor_t endpoint_descriptor_2;
} config_descriptor =
{
{
0x09, /* bLength = 9 */
0x02, /* bDescriptorType = Config (2) */
0x0043, /* wTotalLength(L/H) */
0x02, /* bNumInterfaces */
0x01, /* bConfigValue */
0x00, /* iConfiguration */
0xE0, /* bmAttributes (self-powered, remote wakeup) */
0x01, /* MaxPower is 2ma (units are 2ma/bit) */
},
{ /* First Interface Descriptor For Comm Class Interface */
0x09, /* bLength = 9 */
0x04, /* bDescriptorType = Interface (4) */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x01, /* bNumEndpoints (one for OUT) */
0x02, /* bInterfaceClass = Communications Interface Class (2) */
0x02, /* bInterfaceSubClass = Abstract Control Model (2) */
0x01, /* bInterfaceProtocol = Common "AT" commands (1), no class specific protocol (0) */
0x00, /* iInterface */
},
{ /* Header Functional Descriptor */
0x05, /* bFunctionalLength = 5 */
0x24, /* bDescriptorType */
0x00, /* bDescriptorSubtype */
0x10, 0x01, /* bcdCDC */
},
{ /* Call Management Descriptor */
0x05, /* bFunctionalLength = 5 */
0x24, /* bDescriptorType */
0x01, /* bDescriptorSubtype */
0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */
0x01, /* bmDataInterface */
},
{ /* Abstract Control Management Functional Descriptor */
0x04, /* bFunctionalLength = 4 */
0x24, /* bDescriptorType */
0x02, /* bDescriptorSubtype */
0x02, /* bmCapabilities */
},
{ /* Union Functional Descriptor */
0x05, /* bFunctionalLength = 5 */
0x24, /* bDescriptorType */
0x06, /* bDescriptorSubtype */
0x00, /* bmMasterInterface */
0x01, /* bmSlaveInterface0 */
},
{ /* IN Endpoint 3 (Descriptor #1) */
0x07, /* bLength */
0x05, /* bDescriptorType (Endpoint) */
0x83, /* bEndpointAddress (EP3-IN) */
0x03, /* bmAttributes (interrupt) */
0x0040, /* wMaxPacketSize */
0xff, /* bInterval (milliseconds) */
},
{ /* Second Interface Descriptor For Data Interface */
0x09, /* bLength */
0x04, /* bDescriptorType (Interface) */
0x01, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x02, /* bNumEndpoints */
0x0a, /* bInterfaceClass = Data Interface (10) */
0x00, /* bInterfaceSubClass = none (0) */
0x00, /* bInterfaceProtocol = No class specific protocol (0) */
0x00, /* biInterface = No Text String (0) */
},
{ /* OUT Endpoint 1 (Descriptor #2) */
0x07, /* bLength */
0x05, /* bDescriptorType (Endpoint) */
0x01, /* bEndpointAddress (EP1-OUT) */
0x02, /* bmAttributes (bulk) */
0x0040, /* wMaxPacketSize */
0x00, /* bInterval (N/A) */
},
{ /* IN Endpoint 2 (Descriptor #3) */
0x07, /* bLength */
0x05, /* bDescriptorType (Endpoint) */
0x82, /* bEndpointAddress (EP2-IN) */
0x02, /* bmAttributes (bulk) */
0x0040, /* wMaxPacketSize */
0x00 /* bInterval (N/A) */
}
};
__attribute__((aligned(4)))
uint8_t lang_id_desc[] = {
0x04, /* bLength */
0x03, /* bDescriptorType */
0x09, 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */
};
__attribute__((aligned(4)))
uint8_t mfg_id_desc[] = {
0x22, /* bLength */
0x03, /* bDescriptorType */
'M', 0,
'a', 0,
'x', 0,
'i', 0,
'm', 0,
' ', 0,
'I', 0,
'n', 0,
't', 0,
'e', 0,
'g', 0,
'r', 0,
'a', 0,
't', 0,
'e', 0,
'd', 0,
};
__attribute__((aligned(4)))
uint8_t prod_id_desc[] = {
0x22, /* bLength */
0x03, /* bDescriptorType */
'M', 0,
'A', 0,
'X', 0,
'3', 0,
'2', 0,
'6', 0,
'6', 0,
'5', 0,
' ', 0,
'C', 0,
'D', 0,
'C', 0,
'-', 0,
'A', 0,
'C', 0,
'M', 0,
};
/* Not currently used (see device descriptor), but could be enabled if desired */
__attribute__((aligned(4)))
uint8_t serial_id_desc[] = {
0x14, /* bLength */
0x03, /* bDescriptorType */
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'1', 0
};
#endif /* _DESCRIPTORS_H_ */
/* clang-format off */
/*******************************************************************************
* Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
#ifndef _MAX30003_H_
#define _MAX30003_H_
///MAX30003 Registers
enum Registers_e
{
NO_OP = 0x00,
STATUS = 0x01,
EN_INT = 0x02,
EN_INT2 = 0x03,
MNGR_INT = 0x04,
MNGR_DYN = 0x05,
SW_RST = 0x08,
SYNCH = 0x09,
FIFO_RST = 0x0A,
INFO = 0x0F,
CNFG_GEN = 0x10,
CNFG_ALL = 0x12,
CNFG_EMUX = 0x14,
CNFG_ECG = 0x15,
CNFG_RTOR1 = 0x1D,
CNFG_RTOR2 = 0x1E,
ECG_FIFO_BURST = 0x20,
ECG_FIFO = 0x21,
RTOR = 0x25,
NO_OP2 = 0x7F
};
///Status register bits
union Status_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t loff_nl : 1;
uint32_t loff_nh : 1;
uint32_t loff_pl : 1;
uint32_t loff_ph : 1;
uint32_t reserved1 : 4;
uint32_t pllint : 1;
uint32_t samp : 1;
uint32_t rrint : 1;
uint32_t lonint : 1;
uint32_t reserved2 : 8;
uint32_t dcloffint : 1;
uint32_t fstint : 1;
uint32_t eovf : 1;
uint32_t eint : 1;
uint32_t reserved3 : 8;
}bits;
};
///Enable Interrupt registers bits
union EnableInterrupts_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t intb_type : 2;
uint32_t reserved1 : 6;
uint32_t en_pllint : 1;
uint32_t en_samp : 1;
uint32_t en_rrint : 1;
uint32_t en_loint : 1;
uint32_t reserved2 : 8;
uint32_t en_dcloffint : 1;
uint32_t en_fstint : 1;
uint32_t en_eovf : 1;
uint32_t en_eint : 1;
uint32_t reserved3 : 8;
}bits;
};
///Manage Interrupt register bits
union ManageInterrupts_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t samp_it : 4;
uint32_t clr_samp : 1;
uint32_t reserved1 : 1;
uint32_t clr_rrint : 2;
uint32_t clr_fast : 1;
uint32_t reserved2 : 12;
uint32_t efit : 5;
uint32_t reserved3 : 8;
}bits;
};
///Manage Dynamic Modes register bits
union ManageDynamicModes_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 16;
uint32_t fast_th : 6;
uint32_t fast : 2;
uint32_t reserved2 : 8;
}bits;
};
///General Configuration bits
union GeneralConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t rbiasn : 1;
uint32_t rbiasp : 1;
uint32_t rbiasv : 2;
uint32_t en_rbias : 2;
uint32_t vth : 2;
uint32_t imag : 3;
uint32_t ipol : 1;
uint32_t en_dcloff : 2;
uint32_t reserved1 : 5;
uint32_t en_ecg : 1;
uint32_t fmstr : 2;
uint32_t en_ulp_lon : 2;
uint32_t reserved2 : 8;
}bits;
};
///Cal Configuration bits
union CalConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t thigh : 11;
uint32_t fifty : 1;
uint32_t fcal : 3;
uint32_t reserved1 : 5;
uint32_t vmag : 1;
uint32_t vmode : 1;
uint32_t en_vcal : 1;
uint32_t reserved2 : 9;
}bits;
};
///Mux Configuration bits
union MuxConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 16;
uint32_t caln_sel : 2;
uint32_t calp_sel : 2;
uint32_t openn : 1;
uint32_t openp : 1;
uint32_t reserved2 : 1;
uint32_t pol : 1;
uint32_t reserved3 : 8;
}bits;
};
///ECG Configuration bits
union ECGConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 12;
uint32_t dlpf : 2;
uint32_t dhpf : 1;
uint32_t reserved2 : 1;
uint32_t gain : 2;
uint32_t reserved3 : 4;
uint32_t rate : 2;
uint32_t reserved4 : 8;
}bits;
};
///RtoR1 Configuration bits
union RtoR1Configuration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 8;
uint32_t ptsf : 4;
uint32_t pavg : 2;
uint32_t reserved2 : 1;
uint32_t en_rtor : 1;
uint32_t rgain : 4;
uint32_t wndw : 4;
uint32_t reserved3 : 8;
}bits;
};
///RtoR2 Configuration bits
union RtoR2Configuration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 8;
uint32_t rhsf : 3;
uint32_t reserved2 : 1;
uint32_t ravg : 2;
uint32_t reserved3 : 2;
uint32_t hoff : 6;
uint32_t reserved4 : 10;
}bits;
};
#endif /* _MAX30003_H_ */
/* clang-format on */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "gpio.h"
#include "bhy_uc_driver.h"
#include "bhy.h"
#include "pmic.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "epicardium.h"
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "modules/stream.h"
#include "user_core/interrupts.h"
/* BHI160 Firmware Blob. Contents are defined in libcard10. */
extern uint8_t bhy1_fw[];
/* Interrupt Pin */
static const gpio_cfg_t bhi160_interrupt_pin = {
PORT_0, PIN_13, GPIO_FUNC_IN, GPIO_PAD_PULL_UP
};
/* clang-format off */
/* Axis remapping matrices */
static const int8_t bhi160_mapping_matrix[3 * 3] = {
0, -1, 0,
1, 0, 0,
0, 0, 1,
};
static const int8_t bmm150_mapping_matrix[3 * 3] = {
-1, 0, 0,
0, 1, 0,
0, 0, -1,
};
/*
* From the official docs:
*
* The sic matrix should be calculated for customer platform by logging
* uncalibrated magnetometer data. The sic matrix here is only an example
* array (identity matrix). Customer should generate their own matrix. This
* affects magnetometer fusion performance.
*
* TODO: Get data for card10
*/
static const float bhi160_sic_array[3 * 3] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
};
/* clang-format on */
/* BHI160 Task ID */
static TaskHandle_t bhi160_task_id = NULL;
/* BHI160 Mutex */
static struct mutex bhi160_mutex = { 0 };
/* Streams */
static struct stream_info bhi160_streams[10];
/* Active */
static bool bhi160_sensor_active[10] = { 0 };
/*
* Driver State: A flag that is set when an unrecoverable error occurred.
* Effectively, this means the sensor will be disabled until next reboot and any
* API calls will fail immediately.
*/
static bool bhi160_driver_b0rked = false;
/* -- Utilities -------------------------------------------------------- {{{ */
/*
* Retrieve the data size for a sensor. This value is needed for the creation
* of the sensor's sample queue.
*/
static size_t bhi160_lookup_data_size(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
case BHI160_MAGNETOMETER:
case BHI160_ORIENTATION:
case BHI160_GYROSCOPE:
return sizeof(struct bhi160_data_vector);
default:
return 0;
}
}
/*
* Map a sensor type to the virtual sensor ID used by BHy1.
*/
static bhy_virtual_sensor_t bhi160_lookup_vs_id(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
return VS_ID_ACCELEROMETER;
case BHI160_MAGNETOMETER:
return VS_ID_MAGNETOMETER;
case BHI160_ORIENTATION:
return VS_ID_ORIENTATION;
case BHI160_GYROSCOPE:
return VS_ID_GYROSCOPE;
default:
return -1;
}
}
/*
* Map a sensor type to its stream descriptor.
*/
static int bhi160_lookup_sd(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
return SD_BHI160_ACCELEROMETER;
case BHI160_MAGNETOMETER:
return SD_BHI160_MAGNETOMETER;
case BHI160_ORIENTATION:
return SD_BHI160_ORIENTATION;
case BHI160_GYROSCOPE:
return SD_BHI160_GYROSCOPE;
default:
return -1;
}
}
/* }}} */
/* -- API -------------------------------------------------------------- {{{ */
int epic_bhi160_enable_sensor(
enum bhi160_sensor_type sensor_type,
struct bhi160_sensor_config *config
) {
int result = 0;
bhy_virtual_sensor_t vs_id = bhi160_lookup_vs_id(sensor_type);
if (vs_id == (bhy_virtual_sensor_t)-1) {
return -ENODEV;
}
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
if (bhi160_driver_b0rked) {
result = -ENODEV;
goto out_free;
}
struct stream_info *stream = &bhi160_streams[sensor_type];
stream->item_size = bhi160_lookup_data_size(sensor_type);
/* TODO: Sanity check length */
stream->queue =
xQueueCreate(config->sample_buffer_len, stream->item_size);
if (stream->queue == NULL) {
result = -ENOMEM;
goto out_free;
}
result = stream_register(bhi160_lookup_sd(sensor_type), stream);
if (result < 0) {
goto out_free;
}
result = bhy_enable_virtual_sensor(
vs_id,
VS_WAKEUP,
config->sample_rate,
0,
VS_FLUSH_NONE,
0,
config->dynamic_range /* dynamic range is sensor dependent */
);
if (result != BHY_SUCCESS) {
goto out_free;
}
bhi160_sensor_active[sensor_type] = true;
/* Return the sensor stream descriptor */
result = bhi160_lookup_sd(sensor_type);
out_free:
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
return result;
}
int epic_bhi160_disable_sensor(enum bhi160_sensor_type sensor_type)
{
int result = 0;
bhy_virtual_sensor_t vs_id = bhi160_lookup_vs_id(sensor_type);
if (vs_id == (bhy_virtual_sensor_t)-1) {
return -ENODEV;
}
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
if (bhi160_driver_b0rked) {
result = -ENODEV;
goto out_free;
}
struct stream_info *stream = &bhi160_streams[sensor_type];
result = stream_deregister(bhi160_lookup_sd(sensor_type), stream);
if (result < 0) {
goto out_free;
}
vQueueDelete(stream->queue);
stream->queue = NULL;
result = bhy_disable_virtual_sensor(vs_id, VS_WAKEUP);
if (result < 0) {
goto out_free;
}
bhi160_sensor_active[sensor_type] = false;
result = 0;
out_free:
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
return result;
}
void epic_bhi160_disable_all_sensors()
{
for (size_t i = 0; i < sizeof(bhi160_sensor_active); i++) {
if (bhi160_sensor_active[i]) {
epic_bhi160_disable_sensor(i);
}
}
}
/* }}} */
/* -- Driver ----------------------------------------------------------- {{{ */
/*
* Handle a single packet from the FIFO. For most sensors this means pushing
* the sample into its sample queue.
*/
static void
bhi160_handle_packet(bhy_data_type_t data_type, bhy_data_generic_t *sensor_data)
{
uint8_t sensor_id = sensor_data->data_vector.sensor_id;
struct bhi160_data_vector data_vector;
/*
* Timestamp of the next samples, counting at 32 kHz.
* Currently unused.
*/
static uint32_t timestamp = 0;
enum bhi160_sensor_type sensor_type = 0;
int epic_int = 0;
bool wakeup = false;
switch (sensor_id) {
case VS_ID_TIMESTAMP_MSW_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_TIMESTAMP_MSW:
assert(data_type == BHY_DATA_TYPE_SCALAR_U16);
timestamp = sensor_data->data_scalar_u16.data << 16;
break;
case VS_ID_TIMESTAMP_LSW_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_TIMESTAMP_LSW:
assert(data_type == BHY_DATA_TYPE_SCALAR_U16);
timestamp = (timestamp & 0xFFFF0000) |
sensor_data->data_scalar_u16.data;
break;
case VS_ID_ACCELEROMETER_WAKEUP:
case VS_ID_MAGNETOMETER_WAKEUP:
case VS_ID_ORIENTATION_WAKEUP:
case VS_ID_GYROSCOPE_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_ACCELEROMETER:
case VS_ID_MAGNETOMETER:
case VS_ID_ORIENTATION:
case VS_ID_GYROSCOPE:
switch (sensor_id) {
case VS_ID_ACCELEROMETER_WAKEUP:
case VS_ID_ACCELEROMETER:
sensor_type = BHI160_ACCELEROMETER;
epic_int = EPIC_INT_BHI160_ACCELEROMETER;
break;
case VS_ID_MAGNETOMETER_WAKEUP:
case VS_ID_MAGNETOMETER:
sensor_type = BHI160_MAGNETOMETER;
epic_int = EPIC_INT_BHI160_MAGNETOMETER;
break;
case VS_ID_ORIENTATION_WAKEUP:
case VS_ID_ORIENTATION:
sensor_type = BHI160_ORIENTATION;
epic_int = EPIC_INT_BHI160_ORIENTATION;
break;
case VS_ID_GYROSCOPE_WAKEUP:
case VS_ID_GYROSCOPE:
sensor_type = BHI160_GYROSCOPE;
epic_int = EPIC_INT_BHI160_GYROSCOPE;
break;
}
assert(data_type == BHY_DATA_TYPE_VECTOR);
if (bhi160_streams[sensor_type].queue == NULL) {
break;
}
data_vector.data_type = BHI160_DATA_TYPE_VECTOR;
data_vector.x = sensor_data->data_vector.x;
data_vector.y = sensor_data->data_vector.y;
data_vector.z = sensor_data->data_vector.z;
data_vector.status = sensor_data->data_vector.status;
/* Discard overflow. See discussion in !316. */
if (xQueueSend(
bhi160_streams[sensor_type].queue,
&data_vector,
0) != pdTRUE) {
if (!bhi160_streams[sensor_type].was_full) {
LOG_WARN(
"bhi160",
"queue full for %d",
sensor_type
);
}
bhi160_streams[sensor_type].was_full = true;
} else {
bhi160_streams[sensor_type].was_full = false;
}
if (wakeup) {
interrupt_trigger(epic_int);
}
break;
default:
break;
}
}
/*
* Fetch all data available from BHI160's FIFO buffer and handle all packets
* contained in it.
*/
static void bhi160_fetch_fifo(void)
{
/*
* Warning: The code from the BHy1 docs has some issues. This
* implementation looks similar, but has a few important differences.
* You'll probably be best of leaving it as it is ...
*/
/*
* FIFO buffer. Access to this static variable is safe because this
* function is guarded by the bhi160_mutex.
*/
static uint8_t bhi160_fifo[BHI160_FIFO_SIZE];
static size_t start_index = 0;
BHY_RETURN_FUNCTION_TYPE result = 0;
/* Number of bytes left in BHI160's FIFO buffer */
uint16_t bytes_left_in_fifo = 1;
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
while (bytes_left_in_fifo) {
/* Fill local FIFO buffer with as many bytes as possible */
uint16_t bytes_read;
result = bhy_read_fifo(
&bhi160_fifo[start_index],
BHI160_FIFO_SIZE - start_index,
&bytes_read,
&bytes_left_in_fifo
);
if (result != BHY_SUCCESS) {
/*
* Honestly not sure how we should handle these errors.
*
* Needs more digging to find out how bhy_read_fifo()
* behaves in error situations (a quick glance at the
* code shows that the function might have written
* _somethings_ into our buffer so we can't just retry
* blindly ...)
*
* For now, just abort everything and disable the
* sensor. Won't cause havoc at least ...
*/
LOG_ERR("bhi160",
"Error while reading fifo: %d. Disabling.",
result);
bhi160_driver_b0rked = true;
break;
}
/* Add the bytes left from the last transfer on top */
bytes_read += start_index;
/* Handle all full packets received in this transfer */
uint8_t *fifo_ptr = bhi160_fifo;
uint16_t bytes_left = bytes_read;
while (bytes_left > 0) {
bhy_data_generic_t sensor_data;
bhy_data_type_t data_type;
result = bhy_parse_next_fifo_packet(
&fifo_ptr,
&bytes_left,
&sensor_data,
&data_type
);
if (result == BHY_SUCCESS) {
bhi160_handle_packet(data_type, &sensor_data);
} else {
LOG_WARN(
"bhi160",
"Error in fifo packet: %d. Ignoring.",
result
);
break;
}
}
/* Shift the remaining bytes to the beginning */
for (int i = 0; i < bytes_left; i++) {
bhi160_fifo[i] =
bhi160_fifo[bytes_read - bytes_left + i];
}
start_index = bytes_left;
}
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
}
/*
* Callback for the BHI160 interrupt pin. This callback is called from the
* SDK's GPIO interrupt driver, in interrupt context.
*/
static void bhi160_interrupt_callback(void *_)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (bhi160_task_id != NULL && !bhi160_driver_b0rked) {
vTaskNotifyGiveFromISR(
bhi160_task_id, &xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/* }}} */
void vBhi160Task(void *pvParameters)
{
int ret;
bhi160_task_id = xTaskGetCurrentTaskHandle();
mutex_create(&bhi160_mutex);
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
memset(bhi160_streams, 0x00, sizeof(bhi160_streams));
/*
* The BHI160, coming out of power-on-reset will hold its interrupt line
* high until a firmware image is loaded. Once that firmware is loaded
* and running, the interrupt line is deasserted and from then on,
* interrupts are signaled using a rising edge.
*
* So, initially we need to configure the IRQ for a falling edge, load
* the firmware and then reconfigure for a rising edge.
*/
GPIO_Config(&bhi160_interrupt_pin);
GPIO_RegisterCallback(
&bhi160_interrupt_pin, bhi160_interrupt_callback, NULL
);
GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_FALLING);
GPIO_IntEnable(&bhi160_interrupt_pin);
NVIC_SetPriority(
(IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port), 2
);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port));
/* Upload firmware */
ret = bhy_driver_init(bhy1_fw);
if (ret) {
LOG_CRIT("bhi160", "BHy1 init failed! Disabling.");
/* Disable BHI160 until next reboot */
bhi160_driver_b0rked = true;
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
vTaskDelete(NULL);
}
/* Wait for first interrupt, a falling edge */
hwlock_release(HWLOCK_I2C);
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000)) == 0) {
LOG_CRIT(
"bhi160",
"Sensor firmware was not loaded correctly. Disabling."
);
/* Disable BHI160 until next reboot */
bhi160_driver_b0rked = true;
mutex_unlock(&bhi160_mutex);
vTaskDelete(NULL);
}
hwlock_acquire(HWLOCK_I2C);
/*
* The firmware is now loaded; as stated above, we now need to
* reconfigure the IRQ for a rising edge.
*/
GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_RISING);
/*
* Remap axes to match card10 layout.
*
* TODO: We set the matrix for the accelerometer twice because on some
* badges, the axis mapping is not applied properly the first time. We
* should fix this properly at some point.
*/
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_ACC, (int8_t *)bhi160_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_ACC, (int8_t *)bhi160_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_MAG, (int8_t *)bmm150_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_GYRO, (int8_t *)bhi160_mapping_matrix
);
/* Set "SIC" matrix. TODO: Find out what this is about */
bhy_set_sic_matrix((float *)bhi160_sic_array);
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
/* ----------------------------------------- */
while (1) {
bhi160_fetch_fifo();
/*
* Wait for interrupt. After two seconds, fetch FIFO anyway in
* case there are any diagnostics or errors.
*
* In the future, reads using epic_stream_read() might also
* trigger a FIFO fetch, from outside this task.
*/
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000));
}
}
#include "epicardium.h"
#include "modules/modules.h"
#include "os/core.h"
#include "drivers/drivers.h"
#include "card10.h"
#include "bme680.h"
#include "bosch.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#define HEATR_TEMP 320
#define HEATR_DUR 150
static bool initialized;
static struct bme680_dev bme;
static int convert_error(int8_t error)
{
switch (error) {
case BME680_OK:
return 0;
case BME680_E_NULL_PTR:
return EFAULT;
case BME680_E_COM_FAIL:
return EIO;
case BME680_E_DEV_NOT_FOUND:
return ENODEV;
case BME680_E_INVALID_LENGTH:
return EINVAL;
default:
return 1;
}
}
static int8_t
i2c_write(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_write(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static int8_t i2c_read(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_read(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static void delay(uint32_t msec)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
card10_bosch_delay(msec);
} else {
vTaskDelay(pdMS_TO_TICKS(msec));
}
}
int epic_bme680_init()
{
int8_t result = BME680_OK;
if (bsec_active()) {
/* If the proprietary Bosch BSEC libary is in use
* we redirect calls to that. It always runs
* in the background
*/
return 0;
}
if (initialized) {
return 0;
}
bme.dev_id = BME680_I2C_ADDR_PRIMARY;
bme.intf = BME680_I2C_INTF;
bme.read = i2c_read;
bme.write = i2c_write;
bme.delay_ms = delay;
/*
* amb_temp can be set to 25 prior to configuring the gas sensor
* or by performing a few temperature readings without operating
* the gas sensor.
*/
bme.amb_temp = 25;
result = bme680_init(&bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_init error: %d\n", result);
return -convert_error(result);
}
/*
* Select the power mode. Must be set before writing the sensor
* configuration
*/
bme.power_mode = BME680_FORCED_MODE;
/* Set the temperature, pressure and humidity settings */
bme.tph_sett.os_hum = BME680_OS_2X;
bme.tph_sett.os_pres = BME680_OS_4X;
bme.tph_sett.os_temp = BME680_OS_8X;
bme.tph_sett.filter = BME680_FILTER_SIZE_0;
/* Set the remaining gas sensor settings and link the heating profile */
bme.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
/* Create a ramp heat waveform in 3 steps */
bme.gas_sett.heatr_temp = HEATR_TEMP; /* degree Celsius */
bme.gas_sett.heatr_dur = HEATR_DUR; /* milliseconds */
/* Set the required sensor settings needed */
uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL |
BME680_OSH_SEL | BME680_FILTER_SEL |
BME680_GAS_SENSOR_SEL;
result = bme680_set_sensor_settings(settings_sel, &bme);
if (result != BME680_OK) {
LOG_ERR("bme680",
"bme680_set_sensor_settings error: %d\n",
result);
return -convert_error(result);
}
initialized = true;
return 0;
}
int epic_bme680_deinit()
{
/* This is an intentional NO OP to keep the BME680 always initialized.
*
* If it configured to foreced mode, there is no energy consumption
* penalty.
*/
if (bsec_active()) {
return 0;
}
#if 0
if (!initialized) {
return 0;
}
int8_t result = bme680_soft_reset(&bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_soft_reset error: %d\n", result);
}
initialized = false;
#endif
return 0;
}
int epic_bme680_read_sensors(struct bme680_sensor_data *data)
{
int8_t result = BME680_OK;
if (bsec_active()) {
return bsec_read_bme680(data);
}
if (!initialized) {
LOG_ERR("bme680", "bme680 sensor not initialized");
return -EINVAL;
}
if (data == NULL) {
return -EFAULT;
}
uint16_t profile_dur = 0;
bme680_get_profile_dur(&profile_dur, &bme);
result = bme680_set_sensor_mode(&bme); /* Trigger a measurement */
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_set_sensor_mode error: %d\n", result);
return -convert_error(result);
}
/*
* Wait for the measurement to complete. Release the I2C lock in the
* meantime.
*/
vTaskDelay(pdMS_TO_TICKS(profile_dur));
struct bme680_field_data raw_data;
result = bme680_get_sensor_data(&raw_data, &bme);
if (result != BME680_OK) {
LOG_ERR("bme680", "bme680_get_sensor_data error: %d\n", result);
return -convert_error(result);
}
data->temperature = (float)raw_data.temperature / 100.0f;
data->humidity = raw_data.humidity / 1000.0f;
data->pressure = raw_data.pressure / 100.0f;
data->gas_resistance = raw_data.gas_resistance;
return 0;
}
/* Adapted from bsec_iot_example.c and bsec_iot_ulp_plus_example.c */
#include "card10.h"
#include "bosch.h"
#include "bsec_integration.h"
#include "ble/ess.h"
#include "epicardium.h"
#include "modules/modules.h"
#include "os/config.h"
#include "os/core.h"
#include "FreeRTOS.h"
#include "task.h"
#include "max32665.h"
#include "gcr_regs.h"
#include <string.h>
#include <stdio.h>
TaskHandle_t bsec_task_id;
static int64_t last_bme680_timestamp;
static bool bsec_task_active;
static bool debug;
static struct bsec_sensor_data last_bsec_data;
#define ULP 0
// From generic_18v_3s_4d/bsec_serialized_configurations_iaq.c
static const uint8_t bsec_config_generic_18v_3s_4d[454] = {
0, 8, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1,
0, 0, 48, 0, 1, 0, 0, 192, 168, 71, 64, 49, 119, 76,
0, 0, 225, 68, 137, 65, 0, 191, 205, 204, 204, 190, 0, 0,
64, 191, 225, 122, 148, 190, 0, 0, 0, 0, 216, 85, 0, 100,
0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 2, 0, 0, 244,
1, 225, 0, 25, 0, 0, 128, 64, 0, 0, 32, 65, 144, 1,
0, 0, 112, 65, 0, 0, 0, 63, 16, 0, 3, 0, 10, 215,
163, 60, 10, 215, 35, 59, 10, 215, 35, 59, 9, 0, 5, 0,
0, 0, 0, 0, 1, 88, 0, 9, 0, 7, 240, 150, 61, 0,
0, 0, 0, 0, 0, 0, 0, 28, 124, 225, 61, 52, 128, 215,
63, 0, 0, 160, 64, 0, 0, 0, 0, 0, 0, 0, 0, 205,
204, 12, 62, 103, 213, 39, 62, 230, 63, 76, 192, 0, 0, 0,
0, 0, 0, 0, 0, 145, 237, 60, 191, 251, 58, 64, 63, 177,
80, 131, 64, 0, 0, 0, 0, 0, 0, 0, 0, 93, 254, 227,
62, 54, 60, 133, 191, 0, 0, 64, 64, 12, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5,
48, 117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0,
92, 4, 144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117,
48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
48, 117, 100, 0, 100, 0, 48, 117, 48, 117, 100, 0, 100, 0,
100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0,
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 44, 1, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 8, 7, 8, 7,
8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 112, 23, 112, 23,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255, 255, 255,
255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255, 255,
255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0,
0, 0, 83, 141, 0, 0
};
/*!
* @brief Capture the system time in microseconds
*
* @return system_current_time current system timestamp in microseconds
*/
static int64_t get_timestamp_us()
{
int64_t tick = xTaskGetTickCount();
return tick * 1000;
}
/*!
* @brief Handling of the ready outputs
*
* @param[in] timestamp time in nanoseconds
* @param[in] iaq IAQ signal
* @param[in] iaq_accuracy accuracy of IAQ signal
* @param[in] temperature temperature signal
* @param[in] humidity humidity signal
* @param[in] pressure pressure signal
* @param[in] raw_temperature raw temperature signal
* @param[in] raw_humidity raw humidity signal
* @param[in] gas raw gas sensor signal
* @param[in] bsec_status value returned by the bsec_do_steps() call
*
* @return none
*/
static void output_ready(
int64_t timestamp,
float iaq,
uint8_t iaq_accuracy,
float temperature,
float humidity,
float pressure,
float raw_temperature,
float raw_humidity,
float gas,
bsec_library_return_t bsec_status,
float static_iaq,
float co2_equivalent,
float breath_voc_equivalent
) {
last_bsec_data.temperature = temperature;
last_bsec_data.humidity = humidity;
last_bsec_data.pressure = pressure / 100.;
last_bsec_data.gas_resistance = gas;
last_bsec_data.timestamp = timestamp;
last_bsec_data.accuracy = iaq_accuracy;
last_bsec_data.indoor_air_quality = iaq;
last_bsec_data.static_indoor_air_quality = static_iaq;
last_bsec_data.co2_equivalent = co2_equivalent;
last_bsec_data.breath_voc_equivalent = breath_voc_equivalent;
__sync_synchronize();
last_bme680_timestamp = timestamp;
bleESS_update_from_bsec_data(&last_bsec_data);
if (debug) {
LOG_INFO(
"bsec",
"time[ms]: %u, IAQ: %u, IAQ ACC[0-3]: %u, T[.1C]: %u, Hum[.1%%]: %u, P[Pa]: %u, Raw T[.1C]: %u, Raw Hum[.1%%]: %u, Gas[Ohm]: %u, Static IAQ: %u, CO2[ppm]: %u, Breath VOC[ppb]: %u",
(unsigned int)(timestamp / 1e6),
(unsigned int)(iaq),
(unsigned int)(iaq_accuracy),
(unsigned int)(temperature * 10),
(unsigned int)(humidity * 10),
(unsigned int)(pressure),
(unsigned int)(raw_temperature * 10),
(unsigned int)(raw_humidity * 10),
(unsigned int)(gas),
(unsigned int)(static_iaq),
(unsigned int)(co2_equivalent),
(unsigned int)(breath_voc_equivalent * 1e3)
);
}
}
int epic_bsec_read_sensors(struct bsec_sensor_data *data)
{
if (data == NULL) {
return -EFAULT;
}
if (!bsec_task_active) {
return -ENODEV;
}
/* TODO: could also return -EINVAL */
while (last_bme680_timestamp == 0)
vTaskDelay(pdMS_TO_TICKS(10));
*data = last_bsec_data;
return 0;
}
static uint32_t bsec_load(char *path, uint8_t *buffer, uint32_t n_buffer)
{
uint32_t len = 0;
int fd, res;
LOG_DEBUG("bsec", "load %s %lu", path, n_buffer);
if ((fd = epic_file_open(path, "r")) < 0) {
LOG_DEBUG("bsec", "Open failed");
return 0;
}
uint32_t header;
if ((res = epic_file_read(fd, &header, sizeof(header))) !=
sizeof(header)) {
LOG_WARN("bsec", "Header failed");
goto done;
}
if (header > n_buffer) {
LOG_WARN("bsec", "Too large");
goto done;
}
if (epic_file_read(fd, buffer, header) != (int)header) {
LOG_WARN("bsec", "Read failed");
goto done;
}
len = header;
LOG_DEBUG("bsec", "Success");
done:
epic_file_close(fd);
return len;
}
/*!
* @brief Load previous library state from non-volatile memory
*
* @param[in,out] state_buffer buffer to hold the loaded state string
* @param[in] n_buffer size of the allocated state buffer
*
* @return number of bytes copied to state_buffer
*/
static uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
{
return bsec_load("bsec_iaq.state", state_buffer, n_buffer);
}
/*!
* @brief Save library state to non-volatile memory
*
* @param[in] state_buffer buffer holding the state to be stored
* @param[in] length length of the state string to be stored
*
* @return none
*/
static void state_save(const uint8_t *state_buffer, uint32_t length)
{
int fd, res;
LOG_DEBUG("bsec", "state_save %d", (int)length);
if ((fd = epic_file_open("bsec_iaq.state", "w")) < 0) {
LOG_WARN("bsec", "Open failed");
return;
}
uint32_t header = length;
if ((res = epic_file_write(fd, &header, sizeof(header))) !=
sizeof(header)) {
LOG_WARN("bsec", "Header failed");
goto done;
}
if (epic_file_write(fd, state_buffer, header) != (int)header) {
LOG_WARN("bsec", "Write failed");
goto done;
}
LOG_DEBUG("bsec", "stack high: %lu", uxTaskGetStackHighWaterMark(NULL));
done:
epic_file_close(fd);
}
/*!
* @brief Delete the library state from non-volatile memory
*
* @return none
*/
static void state_delete(void)
{
LOG_DEBUG("bsec", "state_delete");
epic_file_unlink("bsec_iaq.state");
}
static int8_t
i2c_write(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_write(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static int8_t i2c_read(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
{
int8_t ret;
hwlock_acquire(HWLOCK_I2C);
ret = card10_bosch_i2c_read(addr, reg, p_buf, size);
hwlock_release(HWLOCK_I2C);
return ret;
}
static void delay(uint32_t msec)
{
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
/* We need to fall back to hardware waits if not running
* in a task context */
card10_bosch_delay(msec);
} else {
vTaskDelay(pdMS_TO_TICKS(msec));
}
}
/*!
* @brief Load library config from non-volatile memory
*
* @param[in,out] config_buffer buffer to hold the loaded state string
* @param[in] n_buffer size of the allocated state buffer
*
* @return number of bytes copied to config_buffer
*/
static uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
{
uint32_t len = bsec_load("bsec_iaq.config", config_buffer, n_buffer);
if (len == 0) {
LOG_INFO("bsec", "Using default bsec_config_generic_18v_3s_4d");
len = sizeof(bsec_config_generic_18v_3s_4d);
memcpy(config_buffer, bsec_config_generic_18v_3s_4d, len);
}
return len;
}
#if ULP
void ulp_plus_trigger_iaq()
{
/* We call bsec_update_subscription() in order to instruct BSEC to perform an extra measurement at the next
* possible time slot
*/
bsec_sensor_configuration_t requested_virtual_sensors[1];
uint8_t n_requested_virtual_sensors = 1;
bsec_sensor_configuration_t
required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
bsec_library_return_t status = BSEC_OK;
/* To trigger a ULP plus, we request the IAQ virtual sensor with a specific sample rate code */
requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ;
requested_virtual_sensors[0].sample_rate =
BSEC_SAMPLE_RATE_ULP_MEASUREMENT_ON_DEMAND;
/* Call bsec_update_subscription() to enable/disable the requested virtual sensors */
status = bsec_update_subscription(
requested_virtual_sensors,
n_requested_virtual_sensors,
required_sensor_settings,
&n_required_sensor_settings
);
/* The status code would tell is if the request was accepted. It will be rejected if the sensor is not already in
* ULP mode, or if the time difference between requests is too short, for example. */
}
#endif
bool bsec_active(void)
{
return bsec_task_active;
}
int bsec_read_bme680(struct bme680_sensor_data *data)
{
if (!bsec_task_active) {
return BME680_E_COM_FAIL;
}
if (data == NULL) {
return -EFAULT;
}
while (last_bme680_timestamp == 0)
vTaskDelay(pdMS_TO_TICKS(10));
data->temperature = last_bsec_data.temperature;
data->humidity = last_bsec_data.humidity;
data->pressure = last_bsec_data.pressure;
data->gas_resistance = last_bsec_data.gas_resistance;
return BME680_OK;
}
/**
* Checks config and activates the BSEC libary if requested.
*
* Initializes the BSEC library before starting the task to
* reduce the stack size needed for the task by at least 250 bytes
*/
int bsec_activate(void)
{
return_values_init ret;
#if ULP
float sample_rate = BSEC_SAMPLE_RATE_ULP;
#else
float sample_rate = BSEC_SAMPLE_RATE_LP;
#endif
if (!config_get_boolean_with_default("bsec_enable", false)) {
return -1;
}
debug = config_get_boolean_with_default("bsec_debug", false);
float temperature_offset =
config_get_integer_with_default("bsec_offset", -22) / 10.;
if (temperature_offset != 0.0) {
LOG_INFO(
"bsec",
"BSEC Temp offset %d/10 K",
(int)(temperature_offset * 10)
);
}
/* Puts AT LEAST 2 * #BSEC_MAX_PROPERTY_BLOB_SIZE = 2 * 454 = 908 bytes onto the stack */
ret = bsec_iot_init(
sample_rate,
-temperature_offset,
i2c_write,
i2c_read,
delay,
state_load,
config_load
);
if (ret.bsec_status == BSEC_E_CONFIG_VERSIONMISMATCH) {
/* BSEC version changed and old state is not compatible anymore */
/* If the config is also not valid anymore, the user will have
* to fix that. */
state_delete();
ret = bsec_iot_init(
sample_rate,
-temperature_offset,
i2c_write,
i2c_read,
delay,
state_load,
config_load
);
}
if (ret.bme680_status) {
LOG_WARN("bsec", "bme680 init failed: %d", ret.bme680_status);
/* Could not initialize BME680 */
return -1;
} else if (ret.bsec_status) {
LOG_WARN("bsec", "bsec init failed: %d", ret.bsec_status);
/* Could not initialize BSEC library */
return -1;
}
return 0;
}
void vBSECTask(void *pvParameters)
{
bsec_task_active = true;
bsec_task_id = xTaskGetCurrentTaskHandle();
#if ULP
/* State is saved every 100 samples, which means every 100 * 300 secs = 500 minutes */
const int stat_save_interval = 100;
#else
/* State is saved every 10.000 samples, which means every 10.000 * 3 secs = 500 minutes */
const int stat_save_interval = 10000;
#endif
/* Call to endless loop function which reads and processes data based on sensor settings */
/* Puts AT LEAST 2 * BSEC_MAX_STATE_BLOB_SIZE + 8 * sizeof(bsec_input_t) =
* 2 * 139 + 8 * 20 = 438 bytes onto the stack */
bsec_iot_loop(
delay,
get_timestamp_us,
output_ready,
state_save,
stat_save_interval
);
}
#include "epicardium.h"
#include "modules/modules.h"
#include "os/core.h"
#include "portexpander.h"
#include "MAX77650-Arduino-Library.h"
#include <stdint.h>
static const uint8_t pin_mask[] = {
[BUTTON_LEFT_BOTTOM] = 1 << 5,
[BUTTON_RIGHT_BOTTOM] = 1 << 3,
[BUTTON_RIGHT_TOP] = 1 << 6,
};
uint8_t epic_buttons_read(uint8_t mask)
{
uint8_t ret = 0;
hwlock_acquire(HWLOCK_I2C);
if (portexpander_detected() && (mask & 0x7)) {
/*
* Not using PB_Get() here as that performs one I2C transaction
* per button.
*/
uint8_t pin_status = ~portexpander_in_get(0xFF);
for (uint8_t m = 1; m < 0x8; m <<= 1) {
if (mask & m && pin_status & pin_mask[m]) {
ret |= m;
}
}
}
if (mask & BUTTON_RESET && MAX77650_getDebounceStatusnEN0()) {
ret |= BUTTON_RESET;
}
hwlock_release(HWLOCK_I2C);
return ret;
}
#include "epicardium.h"
#include "drivers/display/lcd.h"
#include "drivers/display/epic_ctx.h"
#include "FreeRTOS.h"
#include "LCD_Driver.h"
#include "gpio.h"
#include "task.h"
#include "tmr.h"
#include "tmr_utils.h"
#include <machine/endian.h>
#include <string.h>
static TaskHandle_t lock = NULL;
static int check_lock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
if (task != lock) {
return -EBUSY;
} else {
return 0;
}
}
static uint16_t rgb888_to_rgb565(uint8_t *bytes)
{
return ((bytes[0] & 0b11111000) << 8) | ((bytes[1] & 0b11111100) << 3) |
(bytes[2] >> 3);
}
static inline void
rgb565_to_rgb888(uint16_t pixel, uint8_t *red, uint8_t *green, uint8_t *blue)
{
*blue = (pixel & 31) << 3;
*green = ((pixel >> 5) & 63) << 2;
*red = ((pixel >> 11) & 31) << 3;
}
int epic_disp_print(
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
return epic_disp_print_adv(DISP_FONT20, posx, posy, pString, fg, bg);
}
static const float font_map[] = {
[DISP_FONT8] = 8.0f, [DISP_FONT12] = 12.0f, [DISP_FONT16] = 16.0f,
[DISP_FONT20] = 20.0f, [DISP_FONT24] = 24.0f,
};
int epic_disp_print_adv(
uint8_t font,
int16_t posx,
int16_t posy,
const char *pString,
uint16_t fg,
uint16_t bg
) {
uint8_t r, g, b;
int cl = check_lock();
if (cl < 0) {
return cl;
}
if (font >= (sizeof(font_map) / sizeof(font_map[0]))) {
return -EINVAL;
}
float font_size = font_map[font];
ctx_font_size(epicardium_ctx, font_size);
if (fg != bg) {
/* non-transparent background */
rgb565_to_rgb888(bg, &r, &g, &b);
ctx_rgba8(epicardium_ctx, r, g, b, 255);
float width = ctx_text_width(epicardium_ctx, pString);
ctx_rectangle(epicardium_ctx, posx, posy, width, font_size);
ctx_fill(epicardium_ctx);
}
rgb565_to_rgb888(fg, &r, &g, &b);
ctx_rgba8(epicardium_ctx, r, g, b, 255);
ctx_move_to(epicardium_ctx, posx, (float)posy + font_size * 0.8f);
ctx_text(epicardium_ctx, pString);
return 0;
}
int epic_disp_clear(uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
/*
* We could use ctx for this but it's much easier to just clear the
* framebuffer directly.
*/
for (size_t i = 0; i < sizeof(epicardium_ctx_fb); i += 2) {
epicardium_ctx_fb[i] = color >> 8;
epicardium_ctx_fb[i + 1] = color & 0xff;
}
return 0;
}
int epic_disp_pixel(int16_t x, int16_t y, uint16_t color)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_set_pixel_u8(epicardium_ctx, x, y, r, g, b, 255);
return 0;
}
static uint16_t rgb565_pixel_from_buf(
uint8_t *img,
enum epic_rgb_format format,
int16_t width,
int16_t x,
int16_t y,
uint8_t *alpha
) {
uint16_t tmp16;
uint8_t rgba[4];
switch (format) {
case EPIC_RGB565:
*alpha = 255;
memcpy(&tmp16, &img[y * width * 2 + x * 2], 2);
return tmp16;
case EPIC_RGBA5551:
memcpy(&tmp16, &img[y * width * 2 + x * 2], 2);
*alpha = (tmp16 & 0x01) ? 255 : 0;
return (tmp16 & 0xFFC0) | ((tmp16 & 0x3E) >> 1);
case EPIC_RGB8:
*alpha = 255;
memcpy(rgba, &img[y * width * 3 + x * 3], 3);
return rgb888_to_rgb565(rgba);
case EPIC_RGBA8:
memcpy(rgba, &img[y * width * 4 + x * 4], 4);
*alpha = rgba[3];
return rgb888_to_rgb565(rgba);
default:
return 0xFFFF;
}
}
int epic_disp_blit(
int16_t pos_x,
int16_t pos_y,
int16_t width,
int16_t height,
void *img,
enum epic_rgb_format format
) {
int cl = check_lock();
if (cl < 0) {
return cl;
}
for (int16_t xsrc = 0; xsrc < width; xsrc += 1) {
for (int16_t ysrc = 0; ysrc < height; ysrc += 1) {
int16_t xscreen = pos_x + xsrc;
int16_t yscreen = pos_y + ysrc;
size_t offset = yscreen * 160 * 2 + xscreen * 2;
if (xscreen < 0 || xscreen >= 160 || yscreen < 0 ||
yscreen >= 80) {
continue;
}
uint8_t alpha = 255;
uint16_t pixel = rgb565_pixel_from_buf(
img, format, width, xsrc, ysrc, &alpha
);
if (alpha == 0) {
continue;
}
epicardium_ctx_fb[offset] = (pixel & 0xFF00) >> 8;
epicardium_ctx_fb[offset + 1] = pixel & 0xFF;
}
}
return 0;
}
int epic_disp_line(
int16_t xstart,
int16_t ystart,
int16_t xend,
int16_t yend,
uint16_t color,
enum disp_linestyle linestyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0) {
return cl;
}
float xstartf = xstart, ystartf = ystart, xendf = xend, yendf = yend;
/*
* For odd line widths, shift the line by half a pixel so it aligns
* perfectly with the pixel grid.
*/
if (pixelsize % 2 == 1) {
xstartf += 0.5f;
ystartf += 0.5f;
yendf += 0.5f;
xendf += 0.5f;
}
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
ctx_line_width(epicardium_ctx, pixelsize);
ctx_move_to(epicardium_ctx, xstartf, ystartf);
ctx_line_to(epicardium_ctx, xendf, yendf);
ctx_stroke(epicardium_ctx);
return 0;
}
int epic_disp_rect(
int16_t xstart,
int16_t ystart,
int16_t xend,
int16_t yend,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_rectangle(
epicardium_ctx,
xstart,
ystart,
xend - xstart + 1,
yend - ystart + 1
);
switch (fillstyle) {
case FILLSTYLE_EMPTY:
ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
ctx_line_width(epicardium_ctx, pixelsize);
ctx_stroke(epicardium_ctx);
break;
case FILLSTYLE_FILLED:
ctx_rgba8(epicardium_ctx, r, g, b, 255);
ctx_fill(epicardium_ctx);
break;
}
return 0;
}
int epic_disp_circ(
int16_t x,
int16_t y,
uint16_t rad,
uint16_t color,
enum disp_fillstyle fillstyle,
uint16_t pixelsize
) {
int cl = check_lock();
if (cl < 0)
return cl;
uint8_t r, g, b;
rgb565_to_rgb888(color, &r, &g, &b);
ctx_arc(epicardium_ctx, x, y, rad, 0.0f, CTX_PI * 1.95, 0);
switch (fillstyle) {
case FILLSTYLE_EMPTY:
ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
ctx_line_width(epicardium_ctx, pixelsize);
ctx_stroke(epicardium_ctx);
break;
case FILLSTYLE_FILLED:
ctx_rgba8(epicardium_ctx, r, g, b, 255);
ctx_fill(epicardium_ctx);
break;
}
return 0;
}
int epic_disp_update()
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
lcd_write_fb(epicardium_ctx_fb);
return 0;
}
int epic_disp_framebuffer(union disp_framebuffer *fb)
{
int cl = check_lock();
if (cl < 0) {
return cl;
}
/*
* Flip the screen because that's what this API call historically
* expects.
*/
lcd_set_screenflip(true);
lcd_write_fb(fb->raw);
lcd_set_screenflip(false);
return 0;
}
int epic_disp_backlight(uint16_t brightness)
{
/* TODO: lock? */
if (brightness == 0) {
lcd_set_sleep(true);
} else {
lcd_set_sleep(false);
}
LCD_SetBacklight(brightness);
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_update_backlight_clock(void)
{
LCD_UpdateBacklightClock();
}
void disp_forcelock()
{
TaskHandle_t task = xTaskGetCurrentTaskHandle();
lock = task;
}
#pragma once
#include "ctx.h"
extern Ctx *epicardium_ctx;
extern uint8_t epicardium_ctx_fb[160 * 80 * 2];
#include "drivers/display/lcd.h"
#include "drivers/display/epic_ctx.h"
#include "drivers/drivers.h"
#include "display.h"
#include "ctx.h"
#include <stdint.h>
#include <machine/endian.h>
#if BYTE_ORDER == LITTLE_ENDIAN
#define CARD10_CTX_FORMAT CTX_FORMAT_RGB565_BYTESWAPPED
#else
#define CARD10_CTX_FORMAT CTX_FORMAT_RGB565
#endif
uint8_t epicardium_ctx_fb[160 * 80 * 2] = { 0 };
Ctx *epicardium_ctx = NULL;
void disp_init(void)
{
/*
* The bootloader has already initialized the display, so we only need
* to do the bare minimum here.
*/
lcd_reconfigure();
/*
* Initialize the graphics context.
*/
disp_ctx_reinit();
}
void disp_ctx_reinit(void)
{
if (epicardium_ctx != NULL) {
ctx_free(epicardium_ctx);
}
epicardium_ctx = ctx_new_for_framebuffer(
epicardium_ctx_fb, 160, 80, 160 * 2, CARD10_CTX_FORMAT
);
/* set some defaults */
ctx_rgba(epicardium_ctx, 1.0f, 1.0f, 1.0f, 1.0f);
ctx_rgba_stroke(epicardium_ctx, 1.0f, 1.0f, 1.0f, 1.0f);
ctx_font(epicardium_ctx, "ctx-mono");
}
#include "os/core.h"
#include "MAX77650-Arduino-Library.h"
#include "gpio.h"
#include "mxc_delay.h"
#include "portexpander.h"
#include "spi.h"
#include <machine/endian.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* HAL Interfaces {{{ */
static const gpio_cfg_t GPIO_PIN_DC = {
PORT_1, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE
};
static void lcd_hw_init(void)
{
GPIO_Config(&GPIO_PIN_DC);
/* for the reset pin */
if (!portexpander_detected()) {
/* Open-drain */
MAX77650_setDRV(false);
/* Output */
MAX77650_setDIR(false);
}
}
static void lcd_set_dc(bool state)
{
if (state) {
GPIO_OutSet(&GPIO_PIN_DC);
} else {
GPIO_OutClr(&GPIO_PIN_DC);
}
}
static void lcd_set_rst(bool state)
{
if (!portexpander_detected()) {
MAX77650_setDO(state ? true : false);
} else {
portexpander_out_put(PIN_4, state ? 0xFF : 0);
}
}
/** Bit Rate. Display has 15 MHz limit */
#define SPI_SPEED (15 * 1000 * 1000)
static void lcd_spi_write(const uint8_t *data, size_t count)
{
const sys_cfg_spi_t spi_master_cfg = {
.map = MAP_A,
.ss0 = Enable,
.ss1 = Disable,
.ss2 = Disable,
.num_io = 2,
};
spi_req_t request = {
.ssel = 0,
.deass = 1,
.ssel_pol = SPI17Y_POL_LOW,
.tx_data = data,
.rx_data = NULL,
.width = SPI17Y_WIDTH_1,
.len = count,
.bits = 8,
.rx_num = 0,
.tx_num = 0,
.callback = NULL,
};
if (SPI_Init(SPI2, 0, SPI_SPEED, spi_master_cfg) != 0) {
panic("Error configuring display SPI");
}
SPI_MasterTrans(SPI2, &request);
}
static void lcd_delay(size_t millis)
{
// TODO: Is this what we want?
mxc_delay(millis * 1000);
}
/* HAL Interfaces }}} */
enum lcd_commands {
/** Sleep In */
LCD_SLPIN = 0x10,
/** Sleep Out */
LCD_SLPOUT = 0x11,
/** Display Inversion On */
LCD_INVON = 0x21,
/** Display On */
LCD_DISPON = 0x29,
/** Column Address Set */
LCD_CASET = 0x2A,
/** Row Address Set */
LCD_RASET = 0x2B,
/** Memory Write */
LCD_RAMWR = 0x2C,
/** Memory Data Access Control */
LCD_MADCTL = 0x36,
/** Interface Pixel Format */
LCD_COLMOD = 0x3A,
/** Frame Rate Control (In normal mode/ Full colors) */
LCD_FRMCTR1 = 0xB1,
/** Frame Rate Control (In Idle mode/ 8-colors) */
LCD_FRMCTR2 = 0xB2,
/** Frame Rate Control (In Partial mode/ full colors) */
LCD_FRMCTR3 = 0xB3,
/** Display Inversion Control */
LCD_INVCTR = 0xB4,
/** Power Control 1 */
LCD_PWCTR1 = 0xC0,
/** Power Control 2 */
LCD_PWCTR2 = 0xC1,
/** Power Control 3 (in Normal mode/ Full colors) */
LCD_PWCTR3 = 0xC2,
/** Power Control 4 (in Idle mode/ 8-colors) */
LCD_PWCTR4 = 0xC3,
/** Power Control 5 (in Partial mode/ full-colors) */
LCD_PWCTR5 = 0xC4,
/** VCOM Control 1 */
LCD_VMCTR1 = 0xC5,
/** Gamma (+ polarity) Correction Characteristics Setting */
LCD_GMCTRP1 = 0xE0,
/** Gamma (- polarity) Correction Characteristics Setting */
LCD_GMCTRN1 = 0xE1,
};
enum madctl_bits {
MADCTL_MY = 0x80,
MADCTL_MX = 0x40,
MADCTL_MV = 0x20,
MADCTL_ML = 0x10,
MADCTL_RGB = 0x08,
MADCTL_MH = 0x04,
};
static void
lcd_send_command(enum lcd_commands cmd, const uint8_t *args, size_t count)
{
lcd_set_dc(false);
lcd_spi_write((uint8_t *)&cmd, 1);
if (args != NULL && count != 0) {
lcd_set_dc(true);
lcd_spi_write(args, count);
}
}
static void lcd_hard_reset(void)
{
lcd_delay(20);
lcd_set_rst(false);
lcd_delay(20);
lcd_set_rst(true);
lcd_delay(20);
}
void lcd_set_sleep(bool sleep)
{
static int current_sleep = -1;
if (sleep == current_sleep) {
return;
}
current_sleep = sleep;
if (sleep) {
lcd_send_command(LCD_SLPIN, NULL, 0);
} else {
lcd_send_command(LCD_SLPOUT, NULL, 0);
}
}
/**
* Perform a minimal initialization under the assumption that the bootloader has
* already turned on the display. This is faster and prevents visible
* reinitialization artifacts.
*/
void lcd_reconfigure(void)
{
/* Invert Display (twice for unknown reasons ...). */
lcd_send_command(LCD_INVON, NULL, 0);
lcd_send_command(LCD_INVON, NULL, 0);
/* Set framerate control values for all modes to the same values. */
const uint8_t frmctr[] = { 0x05, 0x3A, 0x3A, 0x05, 0x3A, 0x3A };
lcd_send_command(LCD_FRMCTR1, frmctr, 3);
lcd_send_command(LCD_FRMCTR2, frmctr, 3);
lcd_send_command(LCD_FRMCTR3, frmctr, 6);
/* Set display inversion control (unsure what this does?). */
const uint8_t invctr[] = { 0x03 };
lcd_send_command(LCD_INVCTR, invctr, sizeof(invctr));
/* Configure GVDD voltage to 4.7V. */
const uint8_t pwctr1[] = { 0x62, 0x02, 0x04 };
lcd_send_command(LCD_PWCTR1, pwctr1, sizeof(pwctr1));
/* Configure only ignored bits? */
const uint8_t pwctr2[] = { 0xC0 };
lcd_send_command(LCD_PWCTR2, pwctr2, sizeof(pwctr2));
/*
* Configure "large" amount of current in operational amplifier and
* booster step-ups for all modes.
*/
const uint8_t pwctr3[] = { 0x0D, 0x00 }, pwctr4[] = { 0x8D, 0x6A },
pwctr5[] = { 0x8D, 0xEE };
lcd_send_command(LCD_PWCTR3, pwctr3, sizeof(pwctr3));
lcd_send_command(LCD_PWCTR4, pwctr4, sizeof(pwctr4));
lcd_send_command(LCD_PWCTR5, pwctr5, sizeof(pwctr5));
/* Configure VCOMH voltage to 2.850V. */
const uint8_t vmctr1[] = { 0x0E };
lcd_send_command(LCD_VMCTR1, vmctr1, sizeof(vmctr1));
/* Write positive and negative gamma correction values. */
const uint8_t gmctrp1[] = {
0x10, 0x0E, 0x02, 0x03, 0x0E, 0x07, 0x02, 0x07,
0x0A, 0x12, 0x27, 0x37, 0x00, 0x0D, 0x0E, 0x10,
};
const uint8_t gmctrn1[] = {
0x10, 0x0E, 0x03, 0x03, 0x0F, 0x06, 0x02, 0x08,
0x0A, 0x13, 0x26, 0x36, 0x00, 0x0D, 0x0E, 0x10,
};
lcd_send_command(LCD_GMCTRP1, gmctrp1, sizeof(gmctrp1));
lcd_send_command(LCD_GMCTRN1, gmctrn1, sizeof(gmctrn1));
/* Configure 16-bit pixel format. */
const uint8_t colmod[] = { 0x05 };
lcd_send_command(LCD_COLMOD, colmod, sizeof(colmod));
/*
* Configure "MADCTL", which defines the pixel and color access order.
*/
const uint8_t madctl[] = { MADCTL_MX | MADCTL_MV | MADCTL_RGB };
/*
* Waveshare Driver:
* const uint8_t madctl[] = { MADCTL_MY | MADCTL_MV | MADCTL_RGB };
*/
lcd_send_command(LCD_MADCTL, madctl, sizeof(madctl));
/* Turn the display on. */
lcd_send_command(LCD_DISPON, NULL, 0);
}
/**
* Perform a full initialization of the display. This will ensure the display
* is in a deterministic state.
*/
void lcd_initialize(void)
{
lcd_hw_init();
lcd_hard_reset();
lcd_send_command(LCD_SLPOUT, NULL, 0);
lcd_delay(120);
lcd_reconfigure();
}
/**
* Write a partial display update.
*
* The rectangle from column ``xstart`` to ``xend`` (inclusive) and row
* ``ystart`` to ``yend`` (inclusive) will be updated with the contents of
* ``fb``.
*
* ``fb`` **must** have a size of
* ``(xend - xstart + 1) * (yend - ystart + 1) * 2`` bytes.
*/
void lcd_write_fb_partial(
uint16_t xstart,
uint16_t ystart,
uint16_t xend,
uint16_t yend,
const uint8_t *fb
) {
uint16_t param_buffer[2];
/* Column start and end are offset by 1. */
param_buffer[0] = __htons(xstart + 1);
param_buffer[1] = __htons(xend + 1);
lcd_send_command(
LCD_CASET, (uint8_t *)param_buffer, sizeof(param_buffer)
);
/* Row start and end are offset by a magic 26. */
param_buffer[0] = __htons(ystart + 26);
param_buffer[1] = __htons(yend + 26);
lcd_send_command(
LCD_RASET, (uint8_t *)param_buffer, sizeof(param_buffer)
);
/* Now write out the actual framebuffer contents. */
size_t fb_size = (xend - xstart + 1) * (yend - ystart + 1) * 2;
lcd_send_command(LCD_RAMWR, fb, fb_size);
}
/**
* Write out a full framebuffer update.
*
* ``fb`` **must** be 160 * 80 * 2 = **25600** bytes in size. The pixels are
* ordered in rows starting at the top left of the screen. Each pixel must have
* its bytes laid out as big endian (while the CPU is little endian!).
*/
void lcd_write_fb(const uint8_t *fb)
{
lcd_write_fb_partial(0, 0, 159, 79, fb);
}
/**
* Flip the screen orientation upside down.
*
* Historically we had software perform a flip of the framebuffer before
* sending it out. This function provides a way to make the hardware accept
* such a flipped framebuffer. This exists mostly to support the
* :c:func:`epic_disp_framebuffer()` API call for legacy l0dables.
*/
void lcd_set_screenflip(bool flipped)
{
const uint8_t madctl_upright[] = { MADCTL_MX | MADCTL_MV | MADCTL_RGB };
const uint8_t madctl_flipped[] = { MADCTL_MY | MADCTL_MV | MADCTL_RGB };
if (flipped) {
lcd_send_command(LCD_MADCTL, madctl_flipped, 1);
} else {
lcd_send_command(LCD_MADCTL, madctl_upright, 1);
}
}
#pragma once
#include <stdbool.h>
#include <stdint.h>
void lcd_set_sleep(bool sleep);
void lcd_reconfigure(void);
void lcd_initialize(void);
void lcd_write_fb_partial(
uint16_t xstart,
uint16_t ystart,
uint16_t xend,
uint16_t yend,
const uint8_t *fb
);
void lcd_write_fb(const uint8_t *fb);
void lcd_set_screenflip(bool flipped);
#ifndef DRIVERS_H
#define DRIVERS_H
#include "FreeRTOS.h"
#include "gpio.h"
#include "os/mutex.h"
#include "epicardium.h"
#include <stdint.h>
#include <stdbool.h>
/* ---------- 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);
void serial_flush(void);
extern TaskHandle_t serial_task_id;
/* Turn off the print queue and do prints synchroneous from now on. */
void serial_return_to_synchronous();
// For the eSetBit xTaskNotify task semaphore trigger
enum serial_notify{
SERIAL_WRITE_NOTIFY = 0x01,
SERIAL_READ_NOTIFY = 0x02,
};
/* ---------- 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_DISABLED = 0x0,
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);
/* ---------- Display ------------------------------------------------------ */
/* Do display and graphics initialization/configuration. */
void disp_init(void);
/* Reinitialize the graphics context. */
void disp_ctx_reinit(void);
/* Forces an unlock of the display. Only to be used in Epicardium */
void disp_forcelock();
void disp_update_backlight_clock(void);
/* ---------- BHI160 ------------------------------------------------------- */
#define BHI160_FIFO_SIZE 128
#define BHI160_MUTEX_WAIT_MS 50
void vBhi160Task(void *pvParameters);
/* ---------- BME680 ------------------------------------------------------- */
void bme680_periodic(int period);
/* ---------- MAX86150 ----------------------------------------------------- */
#define MAX86150_MUTEX_WAIT_MS 50
void vMAX86150Task(void *pvParameters);
void max86150_mutex_init(void);
/* ---------- MAX30001 ----------------------------------------------------- */
void vMAX30001Task(void *pvParameters);
void max30001_mutex_init(void);
/* ---------- GPIO --------------------------------------------------------- */
extern gpio_cfg_t gpio_configs[];
/* ---------- BSEC / BME680 ------------------------------------------------ */
int bsec_activate(void);
void vBSECTask(void *pvParameters);
bool bsec_active(void);
struct bme680_sensor_data;
int bsec_read_bme680(struct bme680_sensor_data *data);
/* ---------- Sleep -------------------------------------------------------- */
void sleep_deepsleep(void);
/* ---------- RNG ---------------------------------------------------------- */
void rng_init(void);
#endif /* DRIVERS_H */
#include "epicardium.h"
#include "gpio.h"
#include "max32665.h"
#include "mxc_sys.h"
#include "adc.h"
#include "mxc_errors.h"
#include "os/core.h"
#include "modules/modules.h"
/*
* Despite what the schematic (currently, 2019-08-18) says these are the correct
* pins for wristband GPIO 1-4 (not 0-3 as the schematic states)
*/
gpio_cfg_t gpio_configs[] = {
[EPIC_GPIO_WRISTBAND_1] = { PORT_0,
PIN_21,
GPIO_FUNC_OUT,
GPIO_PAD_NONE },
[EPIC_GPIO_WRISTBAND_2] = { PORT_0,
PIN_22,
GPIO_FUNC_OUT,
GPIO_PAD_NONE },
[EPIC_GPIO_WRISTBAND_3] = { PORT_0,
PIN_29,
GPIO_FUNC_OUT,
GPIO_PAD_NONE },
[EPIC_GPIO_WRISTBAND_4] = { PORT_0,
PIN_20,
GPIO_FUNC_OUT,
GPIO_PAD_NONE },
};
static int s_adc_channels[] = {
[EPIC_GPIO_WRISTBAND_1] = ADC_CH_5,
[EPIC_GPIO_WRISTBAND_2] = ADC_CH_6,
/* on P0.29, there is no ADC available
* see GPIO matrix in MAX32665-MAX32668.pdf,
* pages 32,33
*/
[EPIC_GPIO_WRISTBAND_3] = -1,
[EPIC_GPIO_WRISTBAND_4] = ADC_CH_4,
};
int epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode)
{
if (pin < EPIC_GPIO_WRISTBAND_1 || pin > EPIC_GPIO_WRISTBAND_4)
return -EINVAL;
gpio_cfg_t *cfg = &gpio_configs[pin];
if (mode & EPIC_GPIO_MODE_ADC) {
if (s_adc_channels[pin] == -1) {
LOG_WARN("gpio", "ADC not available on pin %d", pin);
return -EINVAL;
}
cfg->func = GPIO_FUNC_ALT1;
if (mode & EPIC_GPIO_MODE_OUT) {
return -EINVAL;
}
} else if (mode & EPIC_GPIO_MODE_IN) {
cfg->func = GPIO_FUNC_IN;
if (mode & EPIC_GPIO_MODE_OUT) {
return -EINVAL;
}
} else if (mode & EPIC_GPIO_MODE_OUT) {
cfg->func = GPIO_FUNC_OUT;
if (mode & EPIC_GPIO_MODE_IN) {
return -EINVAL;
}
} else {
return -EINVAL;
}
if (!(mode & EPIC_GPIO_MODE_ADC)) {
if (mode & EPIC_GPIO_PULL_UP) {
cfg->pad = GPIO_PAD_PULL_UP;
} else if (mode & EPIC_GPIO_PULL_DOWN) {
cfg->pad = GPIO_PAD_PULL_DOWN;
} else {
cfg->pad = GPIO_PAD_NONE;
}
} else {
cfg->pad = GPIO_PAD_NONE;
}
if (GPIO_Config(cfg) != E_NO_ERROR)
return -EINVAL;
return 0;
}
int epic_gpio_get_pin_mode(uint8_t pin)
{
if (pin < EPIC_GPIO_WRISTBAND_1 || pin > EPIC_GPIO_WRISTBAND_4)
return -EINVAL;
gpio_cfg_t *cfg = &gpio_configs[pin];
int res = 0;
if (cfg->func == GPIO_FUNC_IN)
res |= EPIC_GPIO_MODE_IN;
else if (cfg->func == GPIO_FUNC_OUT)
res |= EPIC_GPIO_MODE_OUT;
else if (cfg->func == GPIO_FUNC_ALT1)
res |= EPIC_GPIO_MODE_ADC;
if (cfg->pad == GPIO_PAD_PULL_UP)
res |= EPIC_GPIO_PULL_UP;
else if (cfg->pad == GPIO_PAD_PULL_DOWN)
res |= EPIC_GPIO_PULL_DOWN;
return res;
}
int epic_gpio_write_pin(uint8_t pin, bool on)
{
if (pin < EPIC_GPIO_WRISTBAND_1 || pin > EPIC_GPIO_WRISTBAND_4)
return -EINVAL;
gpio_cfg_t *cfg = &gpio_configs[pin];
if (cfg->func == GPIO_FUNC_IN)
return -EINVAL;
if (on)
GPIO_OutSet(cfg);
else
GPIO_OutClr(cfg);
return 0;
}
int epic_gpio_read_pin(uint8_t pin)
{
if (pin < EPIC_GPIO_WRISTBAND_1 || pin > EPIC_GPIO_WRISTBAND_4)
return -EINVAL;
gpio_cfg_t *cfg = &gpio_configs[pin];
if (cfg->func == GPIO_FUNC_OUT) {
return GPIO_OutGet(cfg) != 0;
} else if (cfg->func == GPIO_FUNC_IN) {
return GPIO_InGet(cfg) != 0;
} else if (cfg->func == GPIO_FUNC_ALT1) {
hwlock_acquire(HWLOCK_ADC);
ADC_StartConvert(s_adc_channels[pin], 0, 0);
uint16_t value;
int rc = ADC_GetData(&value);
hwlock_release(HWLOCK_ADC);
if (rc < 0) {
return -EIO;
}
return (int)value;
} else {
return -EINVAL;
}
}
#include "leds.h"
#include "pmic.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "task.h"
#include "epicardium.h"
#include "modules/modules.h"
#include <stdbool.h>
/*
* TODO: create smth like vTaskDelay(pdMS_TO_TICKS(//put ms here)) for us,
* remove blocking delay from /lib/leds.c to avoid process blocking
*/
#define NUM_LEDS 15 /* Take from lib/card10/leds.c */
static void do_update(void)
{
hwlock_acquire(HWLOCK_LED);
hwlock_acquire(HWLOCK_I2C);
leds_update_power();
leds_update();
hwlock_release(HWLOCK_I2C);
hwlock_release(HWLOCK_LED);
}
void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep(led, r, g, b);
do_update();
}
void epic_leds_set_hsv(int led, float h, float s, float v)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep_hsv(led, h, s, v);
do_update();
}
void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep(led, r, g, b);
}
int epic_leds_get_rgb(int led, uint8_t *rgb)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return -EPERM;
if (led < 0 || led >= NUM_LEDS)
return -EINVAL;
leds_get_rgb(led, rgb);
return 0;
}
void epic_leds_prep_hsv(int led, float h, float s, float v)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep_hsv(led, h, s, v);
}
void epic_leds_set_all(uint8_t *pattern_ptr, uint8_t len)
{
uint8_t(*pattern)[3] = (uint8_t(*)[3])pattern_ptr;
for (int i = 0; i < len; i++) {
if (i == PERSONAL_STATE_LED && personal_state_enabled())
continue;
leds_prep(i, pattern[i][0], pattern[i][1], pattern[i][2]);
}
do_update();
}
void epic_leds_set_all_hsv(float *pattern_ptr, uint8_t len)
{
float(*pattern)[3] = (float(*)[3])pattern_ptr;
for (int i = 0; i < len; i++) {
if (i == PERSONAL_STATE_LED && personal_state_enabled())
continue;
leds_prep_hsv(i, pattern[i][0], pattern[i][1], pattern[i][2]);
}
do_update();
}
void epic_leds_dim_top(uint8_t value)
{
leds_set_dim_top(value);
if (personal_state_enabled() == 0) {
do_update();
}
}
void epic_leds_dim_bottom(uint8_t value)
{
leds_set_dim_bottom(value);
if (personal_state_enabled() == 0) {
do_update();
}
}
void epic_leds_set_rocket(int led, uint8_t value)
{
hwlock_acquire(HWLOCK_I2C);
pmic_set_led(led, value > 31 ? 31 : value);
hwlock_release(HWLOCK_I2C);
}
int epic_leds_get_rocket(int led)
{
int ret = 0;
hwlock_acquire(HWLOCK_I2C);
ret = pmic_get_led(led);
hwlock_release(HWLOCK_I2C);
return ret;
}
static StaticTimer_t flash_timer_data[3];
static TimerHandle_t flash_timer[] = { NULL, NULL, NULL };
static void rocket_timer_callback(TimerHandle_t flash_timer)
{
uint32_t id = (uint32_t)pvTimerGetTimerID(flash_timer);
epic_leds_set_rocket(id, 0);
}
void epic_leds_flash_rocket(int led, uint8_t value, int millis)
{
int ticks = millis * (configTICK_RATE_HZ / 1000);
int32_t id = led;
if (flash_timer[id] == NULL) {
flash_timer[id] = xTimerCreateStatic(
"flashtimer",
ticks,
pdFALSE,
(void *)id,
rocket_timer_callback,
&flash_timer_data[id]
);
epic_leds_set_rocket(led, value);
}
epic_leds_set_rocket(led, value);
xTimerChangePeriod(flash_timer[id], ticks, 0);
}
void epic_set_flashlight(bool power)
{
hwlock_acquire(HWLOCK_I2C);
leds_flashlight(power);
hwlock_release(HWLOCK_I2C);
}
void epic_leds_update(void)
{
do_update();
}
void epic_leds_set_powersave(bool eco)
{
hwlock_acquire(HWLOCK_I2C);
leds_powersave(eco);
hwlock_release(HWLOCK_I2C);
}
void epic_leds_set_gamma_table(uint8_t rgb_channel, uint8_t *gamma_table)
{
leds_set_gamma_table(rgb_channel, gamma_table);
}
void epic_leds_clear_all(uint8_t r, uint8_t g, uint8_t b)
{
for (int i = 0; i < NUM_LEDS; i++) {
if (i == PERSONAL_STATE_LED && personal_state_enabled())
continue;
leds_prep(i, r, g, b);
}
do_update();
}
#include "FreeRTOS.h"
#include "timers.h"
#include "led.h"
#include "epicardium.h"
#include "os/core.h"
#include "os/work_queue.h"
#include "modules/modules.h"
#include "mxc_config.h"
#include "led.h"
#include "adc.h"
#include "gpio.h"
#include <errno.h>
#include "FreeRTOS.h"
#include "timers.h"
#define READ_FREQ pdMS_TO_TICKS(100)
......@@ -12,7 +17,7 @@ static uint16_t last_value;
static TimerHandle_t poll_timer;
static StaticTimer_t poll_timer_buffer;
int epic_light_sensor_init()
static int light_sensor_init()
{
const sys_cfg_adc_t sys_adc_cfg =
NULL; /* No system specific configuration needed. */
......@@ -23,15 +28,34 @@ int epic_light_sensor_init()
return 0;
}
void readAdcCallback()
uint16_t epic_light_sensor_read()
{
hwlock_acquire(HWLOCK_ADC);
ADC_StartConvert(ADC_CH_7, 0, 0);
ADC_GetData(&last_value);
hwlock_release(HWLOCK_ADC);
return last_value;
}
static void workpoll(void *data)
{
epic_light_sensor_read();
}
static void poll(TimerHandle_t xTimer)
{
workqueue_schedule(workpoll, NULL);
}
int epic_light_sensor_run()
{
epic_light_sensor_init();
int ret = 0;
hwlock_acquire(HWLOCK_ADC);
light_sensor_init();
if (!poll_timer) {
poll_timer = xTimerCreateStatic(
......@@ -39,7 +63,7 @@ int epic_light_sensor_run()
READ_FREQ,
pdTRUE,
NULL,
readAdcCallback,
poll,
&poll_timer_buffer
);
// since &poll_timer_buffer is not NULL, xTimerCreateStatic should allways succeed, so
......@@ -47,10 +71,12 @@ int epic_light_sensor_run()
}
if (xTimerIsTimerActive(poll_timer) == pdFALSE) {
if (xTimerStart(poll_timer, 0) != pdPASS) {
return -EBUSY;
ret = -EBUSY;
}
}
return 0;
hwlock_release(HWLOCK_ADC);
return ret;
}
int epic_light_sensor_stop()
......@@ -63,6 +89,7 @@ int epic_light_sensor_stop()
if (xTimerStop(poll_timer, 0) != pdPASS) {
return -EBUSY;
}
return 0;
}
......