Merge pull request #2 from microsoft/services
Move services to jacdac-c
This commit is contained in:
Коммит
e6e1ca5fd7
14
Makefile
14
Makefile
|
@ -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 $<
|
||||
|
||||
|
|
2
jacdac-c
2
jacdac-c
|
@ -1 +1 @@
|
|||
Subproject commit 0acb5a5c5cdd65850f1db6ac65a5eee12b34891a
|
||||
Subproject commit 2634225a54ff4577dd63d887409a7d8309d44241
|
18
lib/blhw.h
18
lib/blhw.h
|
@ -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);
|
||||
|
|
35
lib/lib.h
35
lib/lib.h
|
@ -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);
|
||||
|
|
17
lib/tinyhw.h
17
lib/tinyhw.h
|
@ -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;
|
||||
|
|
254
services/acc.c
254
services/acc.c
|
@ -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
|
611
services/light.c
611
services/light.c
|
@ -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) {
|
||||
|
|
126
services/motor.c
126
services/motor.c
|
@ -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);
|
||||
}
|
201
services/power.c
201
services/power.c
|
@ -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);
|
||||
}
|
16
stm32/pins.c
16
stm32/pins.c
|
@ -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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче