Merge pull request #2 from microsoft/services

Move services to jacdac-c
This commit is contained in:
James Devine 2020-06-24 22:21:00 +01:00 коммит произвёл GitHub
Родитель 8a5834a99a 19faa65254
Коммит e6e1ca5fd7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
26 изменённых файлов: 29 добавлений и 2015 удалений

Просмотреть файл

@ -26,6 +26,7 @@ CFLAGS = $(DEFINES) \
CONFIG_DEPS = \
$(wildcard jacdac-c/inc/*.h) \
$(wildcard jacdac-c/inc/interfaces/*.h) \
$(wildcard jacdac-c/services/interfaces/*.h) \
$(wildcard lib/*.h) \
$(wildcard bl/*.h) \
$(wildcard $(PLATFORM)/*.h) \
@ -49,11 +50,12 @@ PROFILES = $(patsubst targets/$(TARGET)/profile/%.c,%,$(wildcard targets/$(TARGE
ifeq ($(BL),)
DEFINES += -DDEVICE_DMESG_BUFFER_SIZE=1024
C_SRC += $(wildcard jacdac-c/source/*.c)
C_SRC += $(wildcard services/*.c)
C_SRC += $(wildcard jacdac-c/implementation/simple_alloc.c)
C_SRC += $(wildcard jacdac-c/implementation/sensor.c)
C_SRC += $(wildcard jacdac-c/implementation/simple_rx.c)
C_SRC += $(wildcard jacdac-c/implementation/tx_queue.c)
C_SRC += $(wildcard jacdac-c/services/*.c)
C_SRC += $(wildcard services/main.c)
C_SRC += $(wildcard jacdac-c/source/interfaces/simple_alloc.c)
C_SRC += $(wildcard jacdac-c/source/interfaces/sensor.c)
C_SRC += $(wildcard jacdac-c/source/interfaces/simple_rx.c)
C_SRC += $(wildcard jacdac-c/source/interfaces/tx_queue.c)
C_SRC += $(wildcard lib/*.c)
C_SRC += $(wildcard $(PLATFORM)/*.c)
C_SRC += $(HALSRC)
@ -201,7 +203,7 @@ FW_VERSION = $(shell git describe --always --dirty --tags | sed -e 's/-[0-9]\{1,
refresh-version:
@mkdir -p $(BUILT_BIN)
echo 'const char app_fw_version[] = "$(FW_VERSION)";' > $(BUILT_BIN)/version.c
$(BUILT_BIN)/version.o: $(BUILT_BIN)/version.c
$(V)$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<

@ -1 +1 @@
Subproject commit 0acb5a5c5cdd65850f1db6ac65a5eee12b34891a
Subproject commit 2634225a54ff4577dd63d887409a7d8309d44241

Просмотреть файл

@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "hwconfig.h"
#include "services/interfaces/jd_pins.h"
#define CONCAT_1(a, b) a##b
#define CONCAT_0(a, b) CONCAT_1(a, b)
@ -12,23 +13,6 @@
#define RAM_FUNC __attribute__((noinline, long_call, section(".data")))
// pins.c
void _pin_set(int pin, int v);
static inline void pin_set(int pin, int v) {
if ((uint8_t)pin == 0xff)
return;
_pin_set(pin, v);
}
void pin_setup_output(int pin);
void pin_set_opendrain(int pin);
void pin_toggle(int pin);
int pin_get(int pin);
// pull: -1, 0, 1
void pin_setup_input(int pin, int pull);
void pin_setup_output_af(int pin, int af);
void pin_setup_analog_input(int pin);
void pin_pulse(int pin, int times);
// init.c
bool clk_is_pll(void);
void clk_set_pll(int on);

Просмотреть файл

@ -8,6 +8,8 @@
#include "board.h"
#include "jd_protocol.h"
#include "services/interfaces/jd_oled.h"
#include "services/interfaces/jd_hw_pwr.h"
void tim_init(void);
uint64_t tim_get_micros(void);
@ -24,40 +26,7 @@ int itoa(int n, char *s);
int string_reverse(char *s);
uint32_t random_int(uint32_t max);
// pwr.c
// enter/leave high-speed no-deep-sleep mode
void pwr_enter_pll(void);
void pwr_leave_pll(void);
bool pwr_in_pll(void);
// enter/leave no-deep-sleep mode
void pwr_enter_tim(void);
void pwr_leave_tim(void);
// go to sleep, deep or shallow
void pwr_sleep(void);
// do WFI until PLL/TIM mode is left
void pwr_wait_tim(void);
#define CHECK(cond) \
if (!(cond)) \
jd_panic()
uint32_t hw_temp(void);
uint32_t hw_humidity(void);
#define OLED_WIDTH 64
#define OLED_HEIGHT 48
typedef struct oled_state {
uint8_t databuf[OLED_WIDTH * OLED_HEIGHT / 8];
} oled_state_t;
void oled_set_pixel(oled_state_t *ctx, int x, int y);
void oled_setup(oled_state_t *ctx);
void oled_flush(oled_state_t *ctx);
// qma7981.c
void acc_hw_init(void);
void acc_hw_sleep(void);
void acc_hw_get(int16_t sample[3]);

Просмотреть файл

@ -81,7 +81,7 @@ void oled_flush(oled_state_t *ctx) {
commands(setaddr, sizeof(setaddr));
#endif
for (unsigned i = 0; i < sizeof(ctx->databuf); i += OLED_WIDTH) {
command(0xb0 | (i / OLED_WIDTH)); // pageaddr
command(0xb0 | (i / OLED_WIDTH)); // pageaddr
command(0x12);
command(0x00);
data(ctx->databuf + i, OLED_WIDTH);

Просмотреть файл

@ -1,6 +1,7 @@
#pragma once
#include "blhw.h"
#include "services/interfaces/jd_adc.h"
#ifndef RTC_SECOND_IN_US
// use a little more than 10ms, so we don't have issues with wrap around
@ -21,10 +22,6 @@ void dspi_tx(const void *data, uint32_t numbytes, cb_t doneHandler);
#define LIGHT_TYPE_APA102 0x10
#define LIGHT_TYPE_SK9822 0x11
void px_init(int light_type);
void px_tx(const void *data, uint32_t numbytes, uint8_t intensity, cb_t doneHandler);
#define PX_WORDS(NUM_PIXELS) (((NUM_PIXELS)*3 + 3) / 4)
// i2c.c
void i2c_init(void);
// addr are 7bit
@ -34,15 +31,6 @@ int i2c_read_buf(uint8_t addr, uint8_t reg, void *dst, unsigned len);
int i2c_write_reg(uint8_t addr, uint8_t reg, uint8_t val);
int i2c_read_reg(uint8_t addr, uint8_t reg);
// adc.c
void adc_init_random(void);
uint16_t adc_read_temp(void);
bool adc_can_read_pin(uint8_t pin);
void adc_prep_read_pin(uint8_t pin);
uint16_t adc_convert(void);
void adc_disable(void);
uint16_t adc_read_pin(uint8_t pin); // equivalent to the three steps above
// rtc.c
void rtc_init(void);
void rtc_sleep(bool forceShallow);
@ -74,6 +62,3 @@ void target_reset(void);
RAM_FUNC
void target_wait_cycles(int n);
int target_in_irq(void);
extern uint8_t cpu_mhz;
extern uint16_t tim_max_sleep;

Просмотреть файл

@ -1,254 +0,0 @@
#include "lib.h"
// shake/gesture detection based on
// https://github.com/lancaster-university/codal-core/blob/master/source/driver-models/Accelerometer.cpp
/*
MakeCode accelerator position:
Laying flat: 0,0,-1000
Standing on left edge: -1000,0,0
Standing on bottom edge: 0,1000,0
*/
// values for QMA7981
#define SAMPLING_PERIOD (7695 * 2) // 64.98Hz
#define MAX_RANGE 32
#define DEFAULT_RANGE 8
#ifdef PIN_ACC_INT
static uint8_t got_acc_int;
static void acc_int(void) {
got_acc_int = 1;
}
#endif
#define ACCELEROMETER_EVT_NONE 0
#define ACCELEROMETER_EVT_TILT_UP 1
#define ACCELEROMETER_EVT_TILT_DOWN 2
#define ACCELEROMETER_EVT_TILT_LEFT 3
#define ACCELEROMETER_EVT_TILT_RIGHT 4
#define ACCELEROMETER_EVT_FACE_UP 5
#define ACCELEROMETER_EVT_FACE_DOWN 6
#define ACCELEROMETER_EVT_FREEFALL 7
#define ACCELEROMETER_EVT_3G 8
#define ACCELEROMETER_EVT_6G 9
#define ACCELEROMETER_EVT_8G 10
#define ACCELEROMETER_EVT_SHAKE 11
#define ACCELEROMETER_EVT_2G 12
#define ACCELEROMETER_REST_TOLERANCE 200
#define ACCELEROMETER_TILT_TOLERANCE 200
#define ACCELEROMETER_FREEFALL_TOLERANCE 400
#define ACCELEROMETER_SHAKE_TOLERANCE 400
#define ACCELEROMETER_SHAKE_COUNT_THRESHOLD 4
#define ACCELEROMETER_GESTURE_DAMPING 5
#define ACCELEROMETER_SHAKE_DAMPING 10
#define ACCELEROMETER_SHAKE_RTX 30
#define ACCELEROMETER_REST_THRESHOLD (ACCELEROMETER_REST_TOLERANCE * ACCELEROMETER_REST_TOLERANCE)
#define ACCELEROMETER_FREEFALL_THRESHOLD \
((uint32_t)ACCELEROMETER_FREEFALL_TOLERANCE * (uint32_t)ACCELEROMETER_FREEFALL_TOLERANCE)
struct Point {
int16_t x, y, z;
};
struct ShakeHistory {
uint8_t shaken : 1, x : 1, y : 1, z : 1;
uint8_t count;
uint8_t timer;
};
struct srv_state {
SENSOR_COMMON;
uint8_t sigma;
uint8_t impulseSigma;
uint16_t g_events;
uint16_t currentGesture, lastGesture;
uint32_t nextSample;
struct Point sample;
struct ShakeHistory shake;
};
#define sample state->sample
#define shake state->shake
static void emit_g_event(srv_t *state, int ev) {
if (state->g_events & (1 << ev))
return;
state->g_events |= 1 << ev;
jd_send_event(state, ev);
}
static uint16_t instantaneousPosture(srv_t *state, uint32_t force) {
bool shakeDetected = false;
// Test for shake events.
// We detect a shake by measuring zero crossings in each axis. In other words, if we see a
// strong acceleration to the left followed by a strong acceleration to the right, then we can
// infer a shake. Similarly, we can do this for each axis (left/right, up/down, in/out).
//
// If we see enough zero crossings in succession (ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we
// decide that the device has been shaken.
if ((sample.x < -ACCELEROMETER_SHAKE_TOLERANCE && shake.x) ||
(sample.x > ACCELEROMETER_SHAKE_TOLERANCE && !shake.x)) {
shakeDetected = true;
shake.x = !shake.x;
}
if ((sample.y < -ACCELEROMETER_SHAKE_TOLERANCE && shake.y) ||
(sample.y > ACCELEROMETER_SHAKE_TOLERANCE && !shake.y)) {
shakeDetected = true;
shake.y = !shake.y;
}
if ((sample.z < -ACCELEROMETER_SHAKE_TOLERANCE && shake.z) ||
(sample.z > ACCELEROMETER_SHAKE_TOLERANCE && !shake.z)) {
shakeDetected = true;
shake.z = !shake.z;
}
// If we detected a zero crossing in this sample period, count this.
if (shakeDetected && shake.count < ACCELEROMETER_SHAKE_COUNT_THRESHOLD) {
shake.count++;
if (shake.count == 1)
shake.timer = 0;
if (shake.count == ACCELEROMETER_SHAKE_COUNT_THRESHOLD) {
shake.shaken = 1;
shake.timer = 0;
return ACCELEROMETER_EVT_SHAKE;
}
}
// measure how long we have been detecting a SHAKE event.
if (shake.count > 0) {
shake.timer++;
// If we've issued a SHAKE event already, and sufficient time has assed, allow another SHAKE
// event to be issued.
if (shake.shaken && shake.timer >= ACCELEROMETER_SHAKE_RTX) {
shake.shaken = 0;
shake.timer = 0;
shake.count = 0;
}
// Decay our count of zero crossings over time. We don't want them to accumulate if the user
// performs slow moving motions.
else if (!shake.shaken && shake.timer >= ACCELEROMETER_SHAKE_DAMPING) {
shake.timer = 0;
if (shake.count > 0)
shake.count--;
}
}
if (force < ACCELEROMETER_FREEFALL_THRESHOLD)
return ACCELEROMETER_EVT_FREEFALL;
// Determine our posture.
if (sample.x < (-1000 + ACCELEROMETER_TILT_TOLERANCE))
return ACCELEROMETER_EVT_TILT_LEFT;
if (sample.x > (1000 - ACCELEROMETER_TILT_TOLERANCE))
return ACCELEROMETER_EVT_TILT_RIGHT;
if (sample.y < (-1000 + ACCELEROMETER_TILT_TOLERANCE))
return ACCELEROMETER_EVT_TILT_DOWN;
if (sample.y > (1000 - ACCELEROMETER_TILT_TOLERANCE))
return ACCELEROMETER_EVT_TILT_UP;
if (sample.z < (-1000 + ACCELEROMETER_TILT_TOLERANCE))
return ACCELEROMETER_EVT_FACE_UP;
if (sample.z > (1000 - ACCELEROMETER_TILT_TOLERANCE))
return ACCELEROMETER_EVT_FACE_DOWN;
return ACCELEROMETER_EVT_NONE;
}
#define G(g) ((g * 1024) * (g * 1024))
static void process_events(srv_t *state) {
// works up to 16g
uint32_t force = sample.x * sample.x + sample.y * sample.y + sample.z * sample.z;
if (force > G(2)) {
state->impulseSigma = 0;
if (force > G(2))
emit_g_event(state, ACCELEROMETER_EVT_2G);
if (force > G(3))
emit_g_event(state, ACCELEROMETER_EVT_3G);
if (force > G(6))
emit_g_event(state, ACCELEROMETER_EVT_6G);
if (force > G(8))
emit_g_event(state, ACCELEROMETER_EVT_8G);
}
if (state->impulseSigma < 5)
state->impulseSigma++;
else
state->g_events = 0;
// Determine what it looks like we're doing based on the latest sample...
uint16_t g = instantaneousPosture(state, force);
if (g == ACCELEROMETER_EVT_SHAKE) {
jd_send_event(state, ACCELEROMETER_EVT_SHAKE);
} else {
// Perform some low pass filtering to reduce jitter from any detected effects
if (g == state->currentGesture) {
if (state->sigma < ACCELEROMETER_GESTURE_DAMPING)
state->sigma++;
} else {
state->currentGesture = g;
state->sigma = 0;
}
// If we've reached threshold, update our record and raise the relevant event...
if (state->currentGesture != state->lastGesture &&
state->sigma >= ACCELEROMETER_GESTURE_DAMPING) {
state->lastGesture = state->currentGesture;
jd_send_event(state, state->lastGesture);
}
}
}
void acc_process(srv_t *state) {
#ifdef PIN_ACC_INT
if (!got_acc_int)
return;
got_acc_int = 0;
#else
if (!jd_should_sample(&state->nextSample, SAMPLING_PERIOD))
return;
#endif
acc_hw_get(&sample.x);
static int cnt;
if(cnt++>50){
cnt=0;
DMESG("%d %d %d", sample.x, sample.y, sample.z);
}
process_events(state);
sensor_process_simple(state, &sample, sizeof(sample));
}
void acc_handle_packet(srv_t *state, jd_packet_t *pkt) {
sensor_handle_packet_simple(state, pkt, &sample, sizeof(sample));
}
SRV_DEF(acc, JD_SERVICE_CLASS_ACCELEROMETER);
void acc_init(void) {
SRV_ALLOC(acc);
acc_hw_init();
#ifdef PIN_ACC_INT
pin_setup_input(PIN_ACC_INT, -1);
exti_set_callback(PIN_ACC_INT, acc_int, EXTI_RISING);
#endif
}

Просмотреть файл

@ -1,70 +0,0 @@
#include "lib.h"
#define EVT_DOWN 1
#define EVT_UP 2
#define EVT_CLICK 3
#define EVT_LONG_CLICK 4
struct srv_state {
SENSOR_COMMON;
uint8_t pin;
uint8_t blpin;
uint8_t pressed;
uint8_t prev_pressed;
uint8_t num_zero;
uint8_t active;
uint32_t press_time;
uint32_t nextSample;
};
static void update(srv_t *state) {
state->pressed = pin_get(state->pin) == state->active;
if (state->pressed != state->prev_pressed) {
state->prev_pressed = state->pressed;
pin_set(state->blpin, state->pressed);
if (state->pressed) {
jd_send_event(state, EVT_DOWN);
state->press_time = now;
} else {
jd_send_event(state, EVT_UP);
uint32_t presslen = now - state->press_time;
if (presslen > 500000)
jd_send_event(state, EVT_LONG_CLICK);
else
jd_send_event(state, EVT_CLICK);
state->num_zero = 0;
}
}
}
void btn_process(srv_t *state) {
if (jd_should_sample(&state->nextSample, 9000)) {
update(state);
if (sensor_should_stream(state) && (state->pressed || state->num_zero < 20)) {
state->num_zero++;
jd_send(state->service_number, JD_CMD_GET_REG | JD_REG_READING, &state->pressed,
sizeof(state->pressed));
}
}
}
void btn_handle_packet(srv_t *state, jd_packet_t *pkt) {
sensor_handle_packet_simple(state, pkt, &state->pressed, sizeof(state->pressed));
}
SRV_DEF(btn, JD_SERVICE_CLASS_BUTTON);
void btn_init(uint8_t pin, uint8_t blpin) {
SRV_ALLOC(btn);
state->pin = pin;
state->blpin = blpin;
if (blpin == 0xff) {
state->active = 0;
} else {
state->active = 1;
pin_setup_output(blpin);
}
pin_setup_input(state->pin, state->active == 0 ? 1 : -1);
update(state);
}

Просмотреть файл

@ -1,57 +0,0 @@
#include "lib.h"
struct srv_state {
SENSOR_COMMON;
uint8_t state, inited;
uint8_t pin0, pin1;
int32_t sample, position;
uint32_t nextSample;
};
static const int8_t posMap[] = {0, +1, -1, +2, -1, 0, -2, +1, +1, -2, 0, -1, +2, -1, +1, 0};
static void update(srv_t *state) {
// based on comments in https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h
uint16_t s = state->state & 3;
if (pin_get(state->pin0))
s |= 4;
if (pin_get(state->pin1))
s |= 8;
state->state = (s >> 2);
if (posMap[s]) {
state->position += posMap[s];
state->sample = state->position >> 2;
}
}
static void maybe_init(srv_t *state) {
if (state->got_query && !state->inited) {
state->inited = true;
tim_max_sleep = 1000;
pin_setup_input(state->pin0, 1);
pin_setup_input(state->pin1, 1);
update(state);
}
}
void crank_process(srv_t *state) {
maybe_init(state);
if (jd_should_sample(&state->nextSample, 900) && state->inited)
update(state);
sensor_process_simple(state, &state->sample, sizeof(state->sample));
}
void crank_handle_packet(srv_t *state, jd_packet_t *pkt) {
sensor_handle_packet_simple(state, pkt, &state->sample, sizeof(state->sample));
}
SRV_DEF(crank, JD_SERVICE_CLASS_ROTARY_ENCODER);
// specify pin0/1 so that clockwise rotations gives higher readings
void crank_init(uint8_t pin0, uint8_t pin1) {
SRV_ALLOC(crank);
state->pin0 = pin0;
state->pin1 = pin1;
}

Просмотреть файл

@ -1,154 +0,0 @@
#include "lib.h"
#define JD_ARCADE_CONTROLS_BUTTON_LEFT 0x0001
#define JD_ARCADE_CONTROLS_BUTTON_UP 0x0002
#define JD_ARCADE_CONTROLS_BUTTON_RIGHT 0x0003
#define JD_ARCADE_CONTROLS_BUTTON_DOWN 0x0004
#define JD_ARCADE_CONTROLS_BUTTON_A 0x0005
#define JD_ARCADE_CONTROLS_BUTTON_B 0x0006
#define JD_ARCADE_CONTROLS_BUTTON_MENU 0x0007
#define JD_ARCADE_CONTROLS_BUTTON_MENU2 0x0008
#define JD_ARCADE_CONTROLS_BUTTON_RESET 0x0009
#define JD_ARCADE_CONTROLS_BUTTON_EXIT 0x000a
typedef struct {
uint8_t flags;
uint8_t numplayers;
uint16_t buttons[0];
} jd_arcade_controls_advertisement_data_t;
typedef struct {
uint16_t button;
uint8_t player_index;
uint8_t pressure; // for analog joysticks mostly, for digital inputs should be 0xff
} jd_arcade_controls_report_entry_t;
typedef struct {
jd_arcade_controls_report_entry_t pressedButtons[0];
} jd_arcade_controls_report_t;
#define EVT_DOWN 1
#define EVT_UP 2
// these two below not implemented yet
#define EVT_CLICK 3
#define EVT_LONG_CLICK 4
#define BUTTON_FLAGS 0
#define NUM_PLAYERS 1
struct srv_state {
SENSOR_COMMON;
uint8_t inited;
uint32_t btn_state;
uint8_t pin;
uint8_t pressed;
uint8_t prev_pressed;
uint8_t num_zero;
uint8_t num_pins;
uint8_t active;
const uint8_t *button_pins;
const uint8_t *led_pins;
uint32_t press_time;
uint32_t nextSample;
};
static uint32_t buttons_state(srv_t *state) {
if (!state->inited) {
state->inited = true;
for (int i = 0; i < state->num_pins; ++i) {
pin_setup_input(state->button_pins[i], state->active ? -1 : 1);
if (state->led_pins)
pin_setup_output(state->led_pins[i]);
}
}
uint32_t r = 0;
for (int i = 0; i < state->num_pins; ++i) {
if (state->button_pins[i] == 0xff)
continue;
if (pin_get(state->button_pins[i]) == state->active) {
r |= (1 << i);
}
}
return r;
}
static void update(srv_t *state) {
uint32_t newstate = buttons_state(state);
if (newstate != state->btn_state) {
for (int i = 0; i < state->num_pins; ++i) {
uint32_t isPressed = (newstate & (1 << i));
uint32_t wasPressed = (state->btn_state & (1 << i));
if (isPressed != wasPressed) {
if (state->led_pins && state->led_pins[i] != 0xff)
pin_set(state->led_pins[i], isPressed);
jd_send_event_ext(state, isPressed ? EVT_DOWN : EVT_UP, i + 1);
}
}
state->btn_state = newstate;
}
}
static void send_report(srv_t *state) {
jd_arcade_controls_report_entry_t reports[state->num_pins], *report;
report = reports;
for (int i = 0; i < state->num_pins; ++i) {
if (state->btn_state & (1 << i)) {
report->button = i + 1;
report->player_index = 0;
report->pressure = 0xff;
report++;
}
}
jd_send(state->service_number, JD_CMD_GET_REG | JD_REG_READING, reports,
(uint8_t *)report - (uint8_t *)reports);
}
static void ad_data(srv_t *state) {
uint16_t addata[state->num_pins + 1];
uint16_t *dst = addata;
*dst++ = BUTTON_FLAGS | (NUM_PLAYERS << 8);
for (int i = 0; i < state->num_pins; ++i) {
if (state->button_pins[i] != 0xff) {
*dst++ = i + 1;
}
}
jd_send(state->service_number, JD_CMD_ADVERTISEMENT_DATA, addata,
(uint8_t *)dst - (uint8_t *)addata);
}
void gamepad_process(srv_t *state) {
if (jd_should_sample(&state->nextSample, 9000)) {
update(state);
if (sensor_should_stream(state) && (state->btn_state || state->num_zero < 20)) {
state->num_zero++;
send_report(state);
}
}
}
void gamepad_handle_packet(srv_t *state, jd_packet_t *pkt) {
sensor_handle_packet(state, pkt);
if (pkt->service_command == (JD_CMD_GET_REG | JD_REG_READING))
send_report(state);
else if (pkt->service_command == JD_CMD_ADVERTISEMENT_DATA)
ad_data(state);
}
SRV_DEF(gamepad, JD_SERVICE_CLASS_ARCADE_CONTROLS);
void gamepad_init(uint8_t num_pins, const uint8_t *pins, const uint8_t *ledPins) {
SRV_ALLOC(gamepad);
state->num_pins = num_pins;
state->button_pins = pins;
state->led_pins = ledPins;
state->active = ledPins ? 1 : 0;
update(state);
}

Просмотреть файл

@ -1,22 +0,0 @@
#include "lib.h"
struct srv_state {
SENSOR_COMMON;
};
void humidity_process(srv_t *state) {
uint32_t humidity = hw_humidity();
sensor_process_simple(state, &humidity, sizeof(humidity));
}
void humidity_handle_packet(srv_t *state, jd_packet_t *pkt) {
uint32_t humidity = hw_humidity();
sensor_handle_packet_simple(state, pkt, &humidity, sizeof(humidity));
}
SRV_DEF(humidity, JD_SERVICE_CLASS_HUMIDITY);
void humidity_init(void) {
SRV_ALLOC(humidity);
state->streaming_interval = 1000;
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "dmesg.h"
#include "pinnames.h"
#include "jd_services.h"
#include "interfaces/jd_app.h"
#include "blhw.h"
#ifdef BL
@ -26,31 +27,4 @@
};
#else
#define DEVICE_CLASS(dev_class, dev_class_name) const char app_dev_class_name[] = dev_class_name;
#endif
void app_init_services(void);
void ctrl_init(void);
void jdcon_init(void);
void jdcon_logv(int level, const char *format, va_list ap);
void jdcon_log(const char *format, ...);
void jdcon_warn(const char *format, ...);
void acc_init(void);
void crank_init(uint8_t pin0, uint8_t pin1);
void light_init(void);
void snd_init(uint8_t pin);
void pwm_light_init(uint8_t pin);
void servo_init(uint8_t pin);
void btn_init(uint8_t pin, uint8_t blpin);
void touch_init(uint8_t pin);
void oled_init(void);
void temp_init(void);
void humidity_init(void);
void gamepad_init(uint8_t num_pins, const uint8_t *pins, const uint8_t *ledPins);
void power_init(void);
void slider_init(uint8_t pinL, uint8_t pinM, uint8_t pinH);
void motor_init(uint8_t pin1, uint8_t pin2, uint8_t pin_nsleep);
extern const char app_dev_class_name[];
#endif

Просмотреть файл

@ -1,611 +0,0 @@
#include "lib.h"
#define DEFAULT_INTENSITY 15
#define DEFAULT_NUMPIXELS 15
#define DEFAULT_MAXPOWER 200
#define LIGHT_REG_LIGHTTYPE 0x80
#define LIGHT_REG_NUMPIXELS 0x81
//#define LIGHT_REG_DURATION 0x82
//#define LIGHT_REG_COLOR 0x83
#define LIGHT_REG_ACTUAL_INTENSITY 0x180
#define LIGHT_CMD_RUN 0x81
// #define LOG DMESG
// #define LOG NOLOG
/*
* `0xD0: set_all(C+)` - set all pixels in current range to given color pattern
* `0xD1: fade(C+)` - set `N` pixels to color between colors in sequence
* `0xD2: fade_hsv(C+)` - similar to `fade()`, but colors are specified and faded in HSV
* `0xD3: rotate_fwd(K)` - rotate (shift) pixels by `K` positions away from the connector
* `0xD4: rotate_back(K)` - same, but towards the connector
* `0xD5: show(M=50)` - send buffer to strip and wait `M` milliseconds
* `0xD6: range(P=0, N=length)` - range from pixel `P`, `N` pixels long
* `0xD7: mode(K=0)` - set update mode
* `0xD8: mode1(K=0)` - set update mode for next command only
*/
#define LIGHT_PROG_SET_ALL 0xD0
#define LIGHT_PROG_FADE 0xD1
#define LIGHT_PROG_FADE_HSV 0xD2
#define LIGHT_PROG_ROTATE_FWD 0xD3
#define LIGHT_PROG_ROTATE_BACK 0xD4
#define LIGHT_PROG_SHOW 0xD5
#define LIGHT_PROG_RANGE 0xD6
#define LIGHT_PROG_MODE 0xD7
#define LIGHT_PROG_MODE1 0xD8
#define LIGHT_MODE_REPLACE 0x00
#define LIGHT_MODE_ADD_RGB 0x01
#define LIGHT_MODE_SUBTRACT_RGB 0x02
#define LIGHT_MODE_MULTIPLY_RGB 0x03
#define LIGHT_MODE_LAST 0x03
#define LIGHT_PROG_COLN 0xC0
#define LIGHT_PROG_COL1 0xC1
#define LIGHT_PROG_COL2 0xC2
#define LIGHT_PROG_COL3 0xC3
#define LIGHT_PROG_COL1_SET 0xCF
#define PROG_EOF 0
#define PROG_CMD 1
#define PROG_NUMBER 3
#define PROG_COLOR_BLOCK 4
typedef union {
struct {
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t _a; // not really used
};
uint32_t val;
} RGB;
REG_DEFINITION( //
light_regs, //
REG_SRV_BASE, //
REG_U8(JD_REG_INTENSITY), //
REG_U8(LIGHT_REG_ACTUAL_INTENSITY), //
REG_U8(LIGHT_REG_LIGHTTYPE), //
REG_U16(LIGHT_REG_NUMPIXELS), //
REG_U16(JD_REG_MAX_POWER), //
)
struct srv_state {
SRV_COMMON;
uint8_t requested_intensity;
uint8_t intensity;
uint8_t lighttype;
uint16_t numpixels;
uint16_t maxpower;
uint16_t pxbuffer_allocated;
uint8_t *pxbuffer;
volatile uint8_t in_tx;
volatile uint8_t dirty;
uint8_t inited;
uint8_t prog_mode;
uint8_t prog_tmpmode;
uint16_t range_start;
uint16_t range_end;
uint16_t range_len;
uint16_t range_ptr;
uint8_t prog_ptr;
uint8_t prog_size;
uint32_t prog_next_step;
uint8_t prog_data[JD_SERIAL_PAYLOAD_SIZE + 1];
uint8_t anim_flag;
uint32_t anim_step, anim_value;
srv_cb_t anim_fn;
uint32_t anim_end;
};
static srv_t *state_;
static inline RGB rgb(uint8_t r, uint8_t g, uint8_t b) {
RGB x = {.r = r, .g = g, .b = b};
return x;
}
static RGB hsv(uint8_t hue, uint8_t sat, uint8_t val) {
// scale down to 0..192
hue = (hue * 192) >> 8;
// reference: based on FastLED's hsv2rgb rainbow algorithm
// [https://github.com/FastLED/FastLED](MIT)
int invsat = 255 - sat;
int brightness_floor = (val * invsat) >> 8;
int color_amplitude = val - brightness_floor;
int section = (hue / 0x40) >> 0; // [0..2]
int offset = (hue % 0x40) >> 0; // [0..63]
int rampup = offset;
int rampdown = (0x40 - 1) - offset;
int rampup_amp_adj = ((rampup * color_amplitude) / (256 / 4));
int rampdown_amp_adj = ((rampdown * color_amplitude) / (256 / 4));
int rampup_adj_with_floor = (rampup_amp_adj + brightness_floor);
int rampdown_adj_with_floor = (rampdown_amp_adj + brightness_floor);
int r, g, b;
if (section) {
if (section == 1) {
// section 1: 0x40..0x7F
r = brightness_floor;
g = rampdown_adj_with_floor;
b = rampup_adj_with_floor;
} else {
// section 2; 0x80..0xBF
r = rampup_adj_with_floor;
g = brightness_floor;
b = rampdown_adj_with_floor;
}
} else {
// section 0: 0x00..0x3F
r = rampdown_adj_with_floor;
g = rampup_adj_with_floor;
b = brightness_floor;
}
return rgb(r, g, b);
}
static bool is_empty(const uint32_t *data, uint32_t size) {
for (unsigned i = 0; i < size; ++i) {
if (data[i])
return false;
}
return true;
}
static bool is_enabled(srv_t *state) {
// TODO check if all pixels are zero
return state->numpixels > 0 && state->intensity > 0;
}
static void reset_range(srv_t *state) {
state->range_ptr = state->range_start;
}
static int mulcol(int c, int m) {
int c2 = (c * m) >> 7;
if (m < 128 && c == c2)
c2--;
else if (m > 128 && c == c2)
c2++;
return c2;
}
static int clamp(int c) {
if (c < 0)
return 0;
if (c > 255)
return 255;
return c;
}
static bool set_next(srv_t *state, RGB c) {
if (state->range_ptr >= state->range_end)
return false;
uint8_t *p = &state->pxbuffer[state->range_ptr++ * 3];
// fast path
if (state->prog_tmpmode == LIGHT_MODE_REPLACE) {
p[0] = c.r;
p[1] = c.g;
p[2] = c.b;
return true;
}
int r = p[0], g = p[1], b = p[2];
switch (state->prog_tmpmode) {
case LIGHT_MODE_ADD_RGB:
r += c.r;
g += c.g;
b += c.b;
break;
case LIGHT_MODE_SUBTRACT_RGB:
r -= c.r;
g -= c.g;
b -= c.b;
break;
case LIGHT_MODE_MULTIPLY_RGB:
r = mulcol(r, c.r);
g = mulcol(g, c.g);
b = mulcol(b, c.b);
break;
}
p[0] = clamp(r);
p[1] = clamp(g);
p[2] = clamp(b);
return true;
}
static void show(srv_t *state) {
state->dirty = 1;
}
#define SCALE0(c, i) ((((c)&0xff) * (1 + (i & 0xff))) >> 8)
#define SCALE(c, i) \
(SCALE0((c) >> 0, i) << 0) | (SCALE0((c) >> 8, i) << 8) | (SCALE0((c) >> 16, i) << 16)
static void tx_done(void) {
pwr_leave_pll();
state_->in_tx = 0;
}
static void limit_intensity(srv_t *state) {
uint8_t *d = (uint8_t *)state->pxbuffer;
unsigned n = state->numpixels * 3;
int prev_intensity = state->intensity;
if (state->requested_intensity > state->intensity)
state->intensity += 1 + (state->intensity >> 5);
if (state->intensity > state->requested_intensity)
state->intensity = state->requested_intensity;
int current_full = 0;
int current = 0;
while (n--) {
uint8_t v = *d++;
current += SCALE0(v, state->intensity);
current_full += v;
}
// 46uA per step of LED
current *= 46;
current_full *= 46;
// 14mA is the chip at 48MHz, 930uA per LED is static
int base_current = 14000 + 930 * state->numpixels;
int current_limit = state->maxpower * 1000 - base_current;
if (current <= current_limit) {
// LOG("curr: %dmA; not limiting %d", (base_current + current) / 1000, state->intensity);
return;
}
int inten = current_limit / (current_full >> 8) - 1;
if (inten < 0)
inten = 0;
// only display message if the limit wasn't just a result of relaxing above
if (inten < prev_intensity)
LOG("limiting %d -> %d; %dmA", state->intensity, inten,
(base_current + (current_full * inten >> 8)) / 1000);
state->intensity = inten;
}
static RGB prog_fetch_color(srv_t *state) {
uint32_t ptr = state->prog_ptr;
if (ptr + 3 > state->prog_size)
return rgb(0, 0, 0);
uint8_t *d = state->prog_data + ptr;
state->prog_ptr = ptr + 3;
return rgb(d[0], d[1], d[2]);
}
static int prog_fetch(srv_t *state, uint32_t *dst) {
if (state->prog_ptr >= state->prog_size)
return PROG_EOF;
uint8_t *d = state->prog_data;
uint8_t c = d[state->prog_ptr++];
if (!(c & 0x80)) {
*dst = c;
return PROG_NUMBER;
} else if ((c & 0xc0) == 0x80) {
*dst = ((c & 0x3f) << 8) | d[state->prog_ptr++];
return PROG_NUMBER;
} else
switch (c) {
case LIGHT_PROG_COL1:
*dst = 1;
return PROG_COLOR_BLOCK;
case LIGHT_PROG_COL2:
*dst = 2;
return PROG_COLOR_BLOCK;
case LIGHT_PROG_COL3:
*dst = 3;
return PROG_COLOR_BLOCK;
case LIGHT_PROG_COLN:
*dst = d[state->prog_ptr++];
return PROG_COLOR_BLOCK;
default:
*dst = c;
return PROG_CMD;
}
}
static int prog_fetch_num(srv_t *state, int defl) {
uint8_t prev = state->prog_ptr;
uint32_t res;
int r = prog_fetch(state, &res);
if (r == PROG_NUMBER)
return res;
else {
state->prog_ptr = prev; // rollback
return defl;
}
}
static int prog_fetch_cmd(srv_t *state) {
uint32_t cmd;
// skip until there's a command
for (;;) {
switch (prog_fetch(state, &cmd)) {
case PROG_CMD:
return cmd;
case PROG_COLOR_BLOCK:
while (cmd--)
prog_fetch_color(state);
break;
case PROG_EOF:
return 0;
}
}
}
static void prog_set(srv_t *state, uint32_t len) {
reset_range(state);
uint8_t start = state->prog_ptr;
for (;;) {
state->prog_ptr = start;
bool ok = false;
for (uint32_t i = 0; i < len; ++i) {
// don't break the loop immedietely if !ok - make sure the prog counter advances
ok = set_next(state, prog_fetch_color(state));
}
if (!ok)
break;
}
}
static void prog_fade(srv_t *state, uint32_t len, bool usehsv) {
if (len < 2) {
prog_set(state, len);
return;
}
unsigned colidx = 0;
uint8_t endp = state->prog_ptr + 3 * len;
RGB col0 = prog_fetch_color(state);
RGB col1 = prog_fetch_color(state);
uint32_t colstep = ((len - 1) << 16) / state->range_len;
uint32_t colpos = 0;
reset_range(state);
for (;;) {
while (colidx < (colpos >> 16)) {
colidx++;
col0 = col1;
col1 = prog_fetch_color(state);
}
uint32_t fade1 = colpos & 0xffff;
uint32_t fade0 = 0xffff - fade1;
#define MIX(f) (col0.f * fade0 + col1.f * fade1 + 0x8000) >> 16
RGB col = rgb(MIX(r), MIX(g), MIX(b));
if (!set_next(state, usehsv ? hsv(col.r, col.g, col.b) : col))
break;
colpos += colstep;
}
state->prog_ptr = endp;
}
#define AT(idx) ((uint8_t *)state->pxbuffer)[(idx)*3]
static void prog_rot(srv_t *state, uint32_t shift) {
if (shift == 0 || shift >= state->range_len)
return;
uint8_t *first = &AT(state->range_start);
uint8_t *middle = &AT(state->range_start + shift);
uint8_t *last = &AT(state->range_end);
uint8_t *next = middle;
while (first != next) {
uint8_t tmp = *first;
*first++ = *next;
*next++ = tmp;
if (next == last)
next = middle;
else if (first == middle)
middle = next;
}
}
static int fetch_mode(srv_t *state) {
int m = prog_fetch_num(state, 0);
if (m > LIGHT_MODE_LAST)
return 0;
return m;
}
static void prog_process(srv_t *state) {
if (state->prog_ptr >= state->prog_size)
return;
// don't run programs while sending data
if (state->in_tx || in_future(state->prog_next_step))
return;
// full speed ahead! the code below can be a bit heavy and we want it done quickly
pwr_enter_pll();
for (;;) {
int cmd = prog_fetch_cmd(state);
LOG("cmd:%x", cmd);
if (!cmd)
break;
if (cmd == LIGHT_PROG_SHOW) {
uint32_t k = prog_fetch_num(state, 50);
// base the next step of previous expect step time, now current time
// to keep the clock synchronized
state->prog_next_step += k * 1000;
show(state);
break;
}
switch (cmd) {
case LIGHT_PROG_COL1_SET:
state->range_ptr = state->range_start + prog_fetch_num(state, 0);
set_next(state, prog_fetch_color(state));
break;
case LIGHT_PROG_FADE:
case LIGHT_PROG_FADE_HSV:
case LIGHT_PROG_SET_ALL: {
uint32_t len;
if (prog_fetch(state, &len) != PROG_COLOR_BLOCK || len == 0)
continue; // bailout
LOG("%x l=%d", cmd, len);
if (cmd == LIGHT_PROG_SET_ALL)
prog_set(state, len);
else
prog_fade(state, len, cmd == LIGHT_PROG_FADE_HSV);
break;
}
case LIGHT_PROG_ROTATE_BACK:
case LIGHT_PROG_ROTATE_FWD: {
int k = prog_fetch_num(state, 1);
int len = state->range_len;
if (len == 0)
continue;
while (k >= len)
k -= len;
if (cmd == LIGHT_PROG_ROTATE_FWD && k != 0)
k = len - k;
LOG("rot %x %d l=%d", cmd, k, len);
prog_rot(state, k);
break;
}
case LIGHT_PROG_MODE1:
state->prog_tmpmode = fetch_mode(state);
break;
case LIGHT_PROG_MODE:
state->prog_mode = fetch_mode(state);
break;
case LIGHT_PROG_RANGE: {
int start = prog_fetch_num(state, 0);
int len = prog_fetch_num(state, state->numpixels);
if (start > state->numpixels)
start = state->numpixels;
int end = start + len;
if (end > state->numpixels)
end = state->numpixels;
state->range_start = start;
state->range_end = end;
state->range_len = end - start;
break;
}
}
if (cmd != LIGHT_PROG_MODE1)
state->prog_tmpmode = state->prog_mode;
}
pwr_leave_pll();
}
void light_process(srv_t *state) {
prog_process(state);
if (!is_enabled(state))
return;
if (state->dirty && !state->in_tx) {
state->dirty = 0;
if (is_empty((uint32_t *)state->pxbuffer, PX_WORDS(state->numpixels))) {
jd_power_enable(0);
return;
} else {
jd_power_enable(1);
}
state->in_tx = 1;
pwr_enter_pll();
limit_intensity(state);
px_tx(state->pxbuffer, state->numpixels * 3, state->intensity, tx_done);
}
}
static void sync_config(srv_t *state) {
if (!is_enabled(state)) {
jd_power_enable(0);
return;
}
if (!state->inited) {
state->inited = true;
px_init(state->lighttype);
}
int needed = PX_WORDS(state->numpixels);
if (needed > state->pxbuffer_allocated) {
state->pxbuffer_allocated = needed;
state->pxbuffer = jd_alloc(needed * 4);
}
jd_power_enable(1);
}
static void handle_run_cmd(srv_t *state, jd_packet_t *pkt) {
state->prog_size = pkt->service_size;
state->prog_ptr = 0;
memcpy(state->prog_data, pkt->data, state->prog_size);
state->range_start = 0;
state->range_end = state->range_len = state->numpixels;
state->prog_tmpmode = state->prog_mode = 0;
state->prog_next_step = now;
sync_config(state);
}
void light_handle_packet(srv_t *state, jd_packet_t *pkt) {
switch (pkt->service_command) {
case LIGHT_CMD_RUN:
handle_run_cmd(state, pkt);
break;
default:
service_handle_register(state, pkt, light_regs);
break;
}
}
SRV_DEF(light, JD_SERVICE_CLASS_LIGHT);
void light_init(void) {
SRV_ALLOC(light);
state_ = state; // there is global singleton state
state->intensity = DEFAULT_INTENSITY;
state->numpixels = DEFAULT_NUMPIXELS;
state->maxpower = DEFAULT_MAXPOWER;
#if 0
state->maxpower = 20;
state->numpixels = 14;
state->intensity = 0xff;
state->color = 0x00ff00;
state->duration = 100;
sync_config(state);
start_animation(state, 2);
#endif
}

Просмотреть файл

@ -1,4 +1,5 @@
#include "lib.h"
#include "services/interfaces/jd_hw_pwr.h"
uint32_t now;
@ -159,7 +160,7 @@ static void led_panic_blink(void) {
target_wait_us(70000);
}
void jd_panic(void) {
void hw_panic(void) {
DMESG("PANIC!");
target_disable_irq();
while (1) {

Просмотреть файл

@ -1,126 +0,0 @@
#include "lib.h"
#define PWM_BITS 9
#define SERVO_PERIOD (1 << PWM_BITS)
typedef struct channel {
uint8_t pin;
uint8_t pwm_pin;
uint16_t target_duty;
uint16_t current_duty;
} channel_t;
struct srv_state {
SRV_COMMON;
int16_t value;
uint8_t intensity;
uint8_t pin_en;
uint8_t is_on;
channel_t ch1;
channel_t ch2;
uint32_t duty_step_sample;
};
REG_DEFINITION( //
motor_regs, //
REG_SRV_BASE, //
REG_S16(JD_REG_VALUE), //
REG_U8(JD_REG_INTENSITY), //
)
static void disable_ch(channel_t *ch) {
pin_set(ch->pin, 0);
if (ch->pwm_pin) {
pwm_set_duty(ch->pwm_pin, 0);
pwm_enable(ch->pwm_pin, 0);
}
ch->current_duty = 0;
ch->target_duty = 0;
}
static void enable_ch(channel_t *ch) {
if (!ch->pwm_pin)
ch->pwm_pin = pwm_init(ch->pin, SERVO_PERIOD, 0, cpu_mhz / 16);
pwm_enable(ch->pwm_pin, 1);
}
static void set_pwr(srv_t *state, int on) {
if (state->is_on == on)
return;
LOG("PWR %d", on);
disable_ch(&state->ch1);
disable_ch(&state->ch2);
if (on) {
pwr_enter_tim();
enable_ch(&state->ch1);
enable_ch(&state->ch2);
pin_set(state->pin_en, 1);
} else {
pin_set(state->pin_en, 0);
pwr_leave_tim();
}
state->is_on = on;
LOG("PWR OK");
}
static void duty_step(channel_t *ch) {
if (ch->target_duty == ch->current_duty)
return;
// if we're below, step up
if (ch->target_duty > ch->current_duty)
ch->current_duty += SERVO_PERIOD / 32;
// never go above, and also if need to step down, so it immedietely
if (ch->target_duty < ch->current_duty)
ch->current_duty = ch->target_duty;
pwm_set_duty(ch->pwm_pin, ch->current_duty);
}
void motor_process(srv_t *state) {
if (!jd_should_sample(&state->duty_step_sample, 9000))
return;
duty_step(&state->ch1);
duty_step(&state->ch2);
}
static int pwm_value(int v) {
v >>= 15 - PWM_BITS;
if (v >= (1 << PWM_BITS))
v = (1 << PWM_BITS) - 1;
return v;
}
void motor_handle_packet(srv_t *state, jd_packet_t *pkt) {
if (service_handle_register(state, pkt, motor_regs)) {
set_pwr(state, !!state->intensity);
if (state->is_on) {
LOG("PWM set %d", state->value);
if (state->value < 0) {
state->ch1.target_duty = 0;
state->ch2.target_duty = pwm_value(-state->value);
} else {
state->ch1.target_duty = pwm_value(state->value);
state->ch2.target_duty = 0;
}
LOG("PWM set %d %d", state->ch1.target_duty, state->ch2.target_duty);
}
}
}
SRV_DEF(motor, JD_SERVICE_CLASS_MOTOR);
void motor_init(uint8_t pin1, uint8_t pin2, uint8_t pin_nsleep) {
SRV_ALLOC(motor);
state->ch1.pin = pin1;
state->ch2.pin = pin2;
state->pin_en = pin_nsleep;
pin_setup_output(state->pin_en);
pin_setup_output(state->ch1.pin);
pin_setup_output(state->ch2.pin);
}

Просмотреть файл

@ -1,26 +0,0 @@
#include "lib.h"
struct srv_state {
SRV_COMMON;
uint8_t intensity;
oled_state_t oled;
};
REG_DEFINITION( //
oled_regs, //
REG_SRV_BASE, //
REG_U8(JD_REG_INTENSITY), //
)
void oled_process(srv_t *state) {}
void oled_handle_packet(srv_t *state, jd_packet_t *pkt) {
if (service_handle_register(state, pkt, oled_regs)) {
}
}
SRV_DEF(oled, JD_SERVICE_CLASS_MONO_DISPLAY);
void oled_init(void) {
SRV_ALLOC(oled);
oled_setup(&state->oled);
}

Просмотреть файл

@ -1,201 +0,0 @@
#include "lib.h"
#define CHECK_PERIOD 1000 // how often to probe the ADC, in us
#define OVERLOAD_MS 1000 // how long to shut down the power for after overload, in ms
// calibrate readings
#define MA_SCALE 1890
#define MV_SCALE 419
#define PIN_PRE_SENSE PA_4
#define PIN_GND_SENSE PA_5
#define PIN_OVERLOAD PA_6
#define PIN_PULSE PA_3
// both in ms
#define PWR_REG_KEEP_ON_PULSE_DURATION 0x80
#define PWR_REG_KEEP_ON_PULSE_PERIOD 0x81
#define PWR_REG_BAT_VOLTAGE 0x180
#define PWR_REG_OVERLOAD 0x181
#define READING_WINDOW 3
struct srv_state {
SENSOR_COMMON;
uint8_t intensity;
uint8_t overload;
uint16_t max_power;
uint16_t curr_power;
uint16_t battery_voltage;
uint16_t pulse_duration;
uint32_t pulse_period;
uint32_t last_pulse;
uint32_t nextSample;
uint32_t overloadExpire;
uint16_t nsum;
uint32_t sum_gnd;
uint32_t sum_pre;
uint16_t readings[READING_WINDOW - 1];
uint8_t pwr_on;
};
REG_DEFINITION( //
power_regs, //
REG_SENSOR_BASE, //
REG_U8(JD_REG_INTENSITY), //
REG_U8(PWR_REG_OVERLOAD), //
REG_U16(JD_REG_MAX_POWER), //
REG_U16(JD_REG_READING), //
REG_U16(PWR_REG_BAT_VOLTAGE), //
REG_U16(PWR_REG_KEEP_ON_PULSE_DURATION), //
REG_U16(PWR_REG_KEEP_ON_PULSE_PERIOD), //
)
static void sort_ints(uint16_t arr[], int n) {
for (int i = 1; i < n; i++)
for (int j = i; j > 0 && arr[j - 1] > arr[j]; j--) {
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
}
}
static void overload(srv_t *state) {
jd_power_enable(0);
state->pwr_on = 0;
state->overloadExpire = now + OVERLOAD_MS * 1000;
state->overload = 1;
}
static int overload_detect(srv_t *state, uint16_t readings[READING_WINDOW]) {
sort_ints(readings, READING_WINDOW);
int med_gnd = readings[READING_WINDOW / 2];
int ma = (med_gnd * MA_SCALE) >> 7;
if (ma > state->max_power) {
DMESG("current %dmA", ma);
overload(state);
return 1;
}
return 0;
}
static void turn_on_power(srv_t *state) {
DMESG("power on");
uint16_t readings[READING_WINDOW] = {0};
unsigned rp = 0;
adc_prep_read_pin(PIN_GND_SENSE);
uint32_t t0 = tim_get_micros();
jd_power_enable(1);
for (int i = 0; i < 1000; ++i) {
int gnd = adc_convert();
readings[rp++] = gnd;
if (rp == READING_WINDOW)
rp = 0;
if (overload_detect(state, readings)) {
DMESG("overload after %d readings %d us", i + 1, (uint32_t)tim_get_micros() - t0);
(void)t0;
adc_disable();
return;
}
}
adc_disable();
state->pwr_on = 1;
}
void power_process(srv_t *state) {
sensor_process_simple(state, &state->curr_power, sizeof(state->curr_power));
if (!jd_should_sample(&state->nextSample, CHECK_PERIOD * 9 / 10))
return;
if (state->pulse_period && state->pulse_duration) {
uint32_t pulse_delta = now - state->last_pulse;
if (pulse_delta > state->pulse_period * 1000) {
pulse_delta = 0;
state->last_pulse = now;
}
pin_set(PIN_PULSE, pulse_delta < state->pulse_duration * 1000);
}
if (state->overload && in_past(state->overloadExpire))
state->overload = 0;
pin_set(PIN_OVERLOAD, state->overload);
int should_be_on = state->intensity && !state->overload;
if (should_be_on != state->pwr_on) {
if (should_be_on) {
turn_on_power(state);
} else {
DMESG("power off");
state->pwr_on = 0;
jd_power_enable(0);
}
}
int gnd = adc_read_pin(PIN_GND_SENSE);
int pre = adc_read_pin(PIN_PRE_SENSE);
uint16_t sortedReadings[READING_WINDOW];
memcpy(sortedReadings + 1, state->readings, sizeof(state->readings));
sortedReadings[0] = gnd;
memcpy(state->readings, sortedReadings, sizeof(state->readings));
if (overload_detect(state, sortedReadings)) {
DMESG("overload detected");
}
state->nsum++;
state->sum_gnd += gnd;
state->sum_pre += pre;
if (state->nsum >= 1000) {
state->curr_power = (state->sum_gnd * MA_SCALE) / (128 * state->nsum);
state->battery_voltage = (state->sum_pre * MV_SCALE) / (256 * state->nsum);
DMESG("%dmV %dmA %s", state->battery_voltage, state->curr_power,
state->pwr_on ? "ON" : "OFF");
state->nsum = 0;
state->sum_gnd = 0;
state->sum_pre = 0;
}
}
void power_handle_packet(srv_t *state, jd_packet_t *pkt) {
if (sensor_handle_packet_simple(state, pkt, &state->curr_power, sizeof(state->curr_power)))
return;
switch (service_handle_register(state, pkt, power_regs)) {
case PWR_REG_KEEP_ON_PULSE_PERIOD:
case PWR_REG_KEEP_ON_PULSE_DURATION:
if (state->pulse_period && state->pulse_duration) {
// assuming 22R in 0805, we get around 1W or power dissipation, but can withstand only
// 1/8W continous so we limit duty cycle to 10%, and make sure it doesn't stay on for
// more than 1s
if (state->pulse_duration > 1000)
state->pulse_duration = 1000;
if (state->pulse_period < state->pulse_duration * 10)
state->pulse_period = state->pulse_duration * 10;
}
break;
}
}
SRV_DEF(power, JD_SERVICE_CLASS_POWER);
void power_init(void) {
SRV_ALLOC(power);
state->intensity = 1;
state->max_power = 500;
// These should be reasonable defaults.
// Only 1 out of 14 batteries tested wouldn't work with these settings.
// 0.6/20s is 7mA (at 22R), so ~6 weeks on 10000mAh battery (mAh are quoted for 3.7V not 5V).
// These can be tuned by the user for better battery life.
// Note that the power_process() above at 1kHz takes about 1mA on its own.
state->pulse_duration = 600;
state->pulse_period = 20000;
state->last_pulse = now - state->pulse_duration * 1000;
tim_max_sleep = CHECK_PERIOD;
pin_setup_output(PIN_OVERLOAD);
pin_setup_output(PIN_PULSE);
}

Просмотреть файл

@ -1,136 +0,0 @@
#include "lib.h"
#define DEFAULT_MAXPOWER 100
#define PWM_REG_CURR_ITERATION 0x80
#define PWM_REG_MAX_ITERATIONS 0x81
#define PWM_REG_STEPS 0x82
#define PWM_REG_MAX_STEPS 0x180
#define MAX_STEPS 10
#define UPDATE_US 10000
#define PWM_PERIOD_BITS 13 // at 12 bit you can still see the steps at the low end
#define PWM_PERIOD (1 << PWM_PERIOD_BITS)
typedef struct {
uint16_t start_intensity;
uint16_t duration; // in ms
} __attribute__((aligned(4))) step_t;
struct srv_state {
SRV_COMMON;
uint16_t intensity;
uint16_t maxpower;
uint16_t curr_iteration;
uint16_t max_iterations;
uint8_t max_steps;
step_t steps[MAX_STEPS];
// internal state
uint32_t step_start_time;
uint32_t nextFrame;
uint8_t is_on;
uint8_t pwm_pin;
uint8_t pin;
};
REG_DEFINITION( //
pwm_light_regs, //
REG_SRV_BASE, //
REG_U16(JD_REG_INTENSITY), //
REG_U16(JD_REG_MAX_POWER), //
REG_U16(PWM_REG_CURR_ITERATION), //
REG_U16(PWM_REG_MAX_ITERATIONS), //
REG_U8(PWM_REG_MAX_STEPS), //
REG_BYTES(PWM_REG_STEPS, MAX_STEPS * sizeof(step_t)), //
)
static const uint16_t stable[] = {0xffff, 10000, 0xffff, 0};
static void set_pwr(srv_t *state, int on) {
if (state->is_on == on)
return;
if (on) {
pwr_enter_tim();
// set prescaler to 1 - as fast as possible
if (!state->pwm_pin)
state->pwm_pin = pwm_init(state->pin, PWM_PERIOD, PWM_PERIOD - 1, 1);
pwm_enable(state->pwm_pin, 1);
} else {
pin_set(state->pin, 1);
pwm_enable(state->pwm_pin, 0);
pwr_leave_tim();
}
state->is_on = on;
}
void pwm_light_process(srv_t *state) {
if (!jd_should_sample(&state->nextFrame, UPDATE_US))
return;
int step_intensity = -1;
while (state->curr_iteration <= state->max_iterations) {
uint32_t tm = tim_get_micros() >> 10;
uint32_t delta = tm - state->step_start_time;
uint32_t pos = 0;
step_t *st = &state->steps[0];
for (int i = 0; i < MAX_STEPS; ++i) {
unsigned d = st[i].duration;
if (d == 0)
break;
pos += d;
int into = pos - delta;
if (into >= 0) {
int i0 = st[i].start_intensity;
int i1 = st[i + 1].start_intensity;
if (d == 0 || i0 == i1)
step_intensity = i0;
else
step_intensity = (unsigned)(i0 * into + i1 * (d - into)) / d;
break;
}
}
if (step_intensity >= 0 || pos == 0)
break;
state->curr_iteration++;
state->step_start_time = tm;
}
if (step_intensity < 0)
step_intensity = 0;
step_intensity = ((uint32_t)step_intensity * state->intensity) >> 16;
int v = step_intensity >> (16 - PWM_PERIOD_BITS);
if (v == 0) {
set_pwr(state, 0);
} else {
set_pwr(state, 1);
pwm_set_duty(state->pwm_pin, PWM_PERIOD - v);
}
}
void pwm_light_handle_packet(srv_t *state, jd_packet_t *pkt) {
switch (service_handle_register(state, pkt, pwm_light_regs)) {
case PWM_REG_STEPS:
state->curr_iteration = 0;
state->step_start_time = tim_get_micros() >> 10;
break;
}
}
SRV_DEF(pwm_light, JD_SERVICE_CLASS_PWM_LIGHT);
void pwm_light_init(uint8_t pin) {
SRV_ALLOC(pwm_light);
state->pin = pin;
state->max_steps = MAX_STEPS;
memcpy(state->steps, stable, sizeof(stable));
state->max_iterations = 0xffff;
state->intensity = 0;
}

Просмотреть файл

@ -1,53 +0,0 @@
#include "lib.h"
#define SERVO_PERIOD 20000
struct srv_state {
SRV_COMMON;
uint32_t pulse;
uint8_t intensity;
uint8_t pwm_pin;
uint8_t is_on;
uint8_t pin;
};
REG_DEFINITION( //
servo_regs, //
REG_SRV_BASE, //
REG_U32(JD_REG_VALUE), //
REG_U8(JD_REG_INTENSITY), //
)
static void set_pwr(srv_t *state, int on) {
if (state->is_on == on)
return;
if (on) {
pwr_enter_pll();
// configure at 1MHz
if (!state->pwm_pin)
state->pwm_pin = pwm_init(state->pin, SERVO_PERIOD, 0, cpu_mhz);
pwm_enable(state->pwm_pin, 1);
} else {
pin_set(state->pin, 0);
pwm_enable(state->pwm_pin, 0);
pwr_leave_pll();
}
jd_power_enable(on);
state->is_on = on;
}
void servo_process(srv_t *state) {}
void servo_handle_packet(srv_t *state, jd_packet_t *pkt) {
if (service_handle_register(state, pkt, servo_regs)) {
set_pwr(state, !!state->intensity);
if (state->is_on)
pwm_set_duty(state->pwm_pin, state->pulse);
}
}
SRV_DEF(servo, JD_SERVICE_CLASS_SERVO);
void servo_init(uint8_t pin) {
SRV_ALLOC(servo);
state->pin = pin;
}

Просмотреть файл

@ -1,51 +0,0 @@
#include "lib.h"
struct srv_state {
SENSOR_COMMON;
uint8_t inited;
uint8_t pinH, pinL, pinM;
uint32_t sample;
uint32_t nextSample;
};
static void update(srv_t *state) {
pin_setup_output(state->pinH);
pin_set(state->pinH, 1);
pin_setup_output(state->pinL);
pin_set(state->pinL, 0);
state->sample = adc_read_pin(state->pinM) << 4;
// save power
pin_setup_analog_input(state->pinH);
pin_setup_analog_input(state->pinL);
}
static void maybe_init(srv_t *state) {
if (state->got_query && !state->inited) {
state->inited = true;
update(state);
}
}
void slider_process(srv_t *state) {
maybe_init(state);
if (jd_should_sample(&state->nextSample, 9000) && state->inited)
update(state);
sensor_process_simple(state, &state->sample, sizeof(state->sample));
}
void slider_handle_packet(srv_t *state, jd_packet_t *pkt) {
sensor_handle_packet_simple(state, pkt, &state->sample, sizeof(state->sample));
}
SRV_DEF(slider, JD_SERVICE_CLASS_SLIDER);
void slider_init(uint8_t pinL, uint8_t pinM, uint8_t pinH) {
SRV_ALLOC(slider);
state->pinL = pinL;
state->pinM = pinM;
state->pinH = pinH;
}

Просмотреть файл

@ -1,78 +0,0 @@
#include "lib.h"
#define CMD_PLAY_TONE 0x80
#ifndef SND_OFF
#define SND_OFF 1
#endif
struct srv_state {
SRV_COMMON;
uint8_t volume;
uint8_t pwm_pin;
uint8_t is_on;
uint8_t pin;
uint32_t end_tone_time;
uint16_t period;
};
REG_DEFINITION( //
snd_regs, //
REG_SRV_BASE, //
REG_U8(JD_REG_INTENSITY), //
)
static void set_pwr(srv_t *state, int on) {
if (state->is_on == on)
return;
if (on) {
pwr_enter_tim();
} else {
pin_set(state->pin, SND_OFF);
pwm_enable(state->pwm_pin, 0);
pwr_leave_tim();
}
state->is_on = on;
}
static void play_tone(srv_t *state, uint32_t period, uint32_t duty) {
duty = (duty * state->volume) >> 8;
#if SND_OFF == 1
duty = period - duty;
#endif
set_pwr(state, 1);
state->pwm_pin = pwm_init(state->pin, period, duty, cpu_mhz);
}
void snd_process(srv_t *state) {
if (state->period && in_past(state->end_tone_time))
state->period = 0;
if (state->period == 0) {
set_pwr(state, 0);
return;
}
}
void snd_handle_packet(srv_t *state, jd_packet_t *pkt) {
service_handle_register(state, pkt, snd_regs);
switch (pkt->service_command) {
case CMD_PLAY_TONE:
if (pkt->service_size >= 6) {
state->end_tone_time = now + ((uint16_t *)pkt->data)[2] * 1000;
state->period = ((uint16_t *)pkt->data)[0];
play_tone(state, state->period, ((uint16_t *)pkt->data)[1]);
}
snd_process(state);
break;
}
}
SRV_DEF(snd, JD_SERVICE_CLASS_MUSIC);
void snd_init(uint8_t pin) {
SRV_ALLOC(snd);
state->pin = pin;
state->volume = 255;
pin_set(state->pin, SND_OFF);
pin_setup_output(state->pin);
}

Просмотреть файл

@ -1,22 +0,0 @@
#include "lib.h"
struct srv_state {
SENSOR_COMMON;
};
void temp_process(srv_t *state) {
uint32_t temp = hw_temp();
sensor_process_simple(state, &temp, sizeof(temp));
}
void temp_handle_packet(srv_t *state, jd_packet_t *pkt) {
uint32_t temp = hw_temp();
sensor_handle_packet_simple(state, pkt, &temp, sizeof(temp));
}
SRV_DEF(temp, JD_SERVICE_CLASS_THERMOMETER);
void temp_init(void) {
SRV_ALLOC(temp);
state->streaming_interval = 1000;
}

Просмотреть файл

@ -1,42 +0,0 @@
#include "lib.h"
#define EVT_DOWN 1
#define EVT_UP 2
#define EVT_CLICK 3
#define EVT_LONG_CLICK 4
struct srv_state {
SENSOR_COMMON;
uint8_t pin;
uint16_t reading;
uint32_t nextSample;
};
static void update(srv_t *state) {
uint8_t pin = state->pin;
pin_set(pin, 1);
pin_setup_output(pin);
pin_setup_analog_input(pin);
target_wait_us(50);
state->reading = adc_read_pin(pin);
}
void touch_process(srv_t *state) {
if (jd_should_sample(&state->nextSample, 50000)) {
update(state);
sensor_process_simple(state, &state->reading, sizeof(state->reading));
}
}
void touch_handle_packet(srv_t *state, jd_packet_t *pkt) {
sensor_handle_packet_simple(state, pkt, &state->reading, sizeof(state->reading));
}
SRV_DEF(touch, JD_SERVICE_CLASS_BUTTON);
void touch_init(uint8_t pin) {
SRV_ALLOC(touch);
state->pin = pin;
pin_setup_input(state->pin, 0);
update(state);
}

Просмотреть файл

@ -1,5 +1,14 @@
#include "jdstm.h"
void pin_set(int pin, int v) {
if ((uint8_t)pin == 0xff)
return;
if (v)
LL_GPIO_SetOutputPin(PIN_PORT(pin), PIN_MASK(pin));
else
LL_GPIO_ResetOutputPin(PIN_PORT(pin), PIN_MASK(pin));
}
void pin_setup_output(int pin) {
if ((uint8_t)pin == 0xff)
return;
@ -64,13 +73,6 @@ void pin_pulse(int pin, int times) {
}
}
void _pin_set(int pin, int v) {
if (v)
LL_GPIO_SetOutputPin(PIN_PORT(pin), PIN_MASK(pin));
else
LL_GPIO_ResetOutputPin(PIN_PORT(pin), PIN_MASK(pin));
}
void pin_toggle(int pin) {
LL_GPIO_TogglePin(PIN_PORT(pin), PIN_MASK(pin));
}

Просмотреть файл

@ -3,5 +3,5 @@
DEVICE_CLASS(0x30a16d3c, "JM Power v1.0");
void app_init_services() {
power_init();
power_init(PA_4, PA_5, PA_6, PA_3);
}

Просмотреть файл

@ -3,5 +3,5 @@
DEVICE_CLASS(0x30a16d3c, "JM Power v1.0");
void app_init_services() {
power_init();
power_init(PA_4, PA_5, PA_6, PA_3);
}