Skip to content

Commit

Permalink
More sequencer logic.
Browse files Browse the repository at this point in the history
  • Loading branch information
Onwrikbaar committed Feb 4, 2025
1 parent 864cacb commit 7c5d075
Show file tree
Hide file tree
Showing 17 changed files with 1,786 additions and 1,713 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ This project is intended for experienced electronics hobbyists and professionals
- a pulse-based device that can store and play user-created routines (planned for 2025).

### Caveat
NeoDK is _NOT_ a ready-for-play e-stim box. It is a minimal viable design (MVD) of a powerful and highly efficient electrostimulation device that is not limited by 2-pole 'channels' or strictly TENS-like waveforms. Schematic and board CAD files are included, but to get a working device involves some soldering. To get more than the basic functionality requires programming in C.
NeoDK is _NOT_ a ready-for-play e-stim box. It is a minimal viable design (MVD) of a powerful and highly efficient electrostimulation device that is not limited by 2-pole 'channels' or strictly TENS-like waveforms. Schematic and board CAD files are included, but to get a working device involves some (easy) soldering. To get more than the basic functionality requires programming in C, or being patient.

### How to get a working NeoDK
By far the easiest way to build NeoDK, is to order the SMD-populated PCB from JLCPCB (using the files in directory JLCPCB_prod) and buy the six through hole components separately. Soldering the through hole components onto the board is easy.
Expand Down
3,252 changes: 1,636 additions & 1,616 deletions firmware/build/neodk_g071.hex

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions firmware/inc/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,23 @@

#include "matter.h"


typedef enum {
AI_FIRMWARE_VERSION = 2, AI_VOLTAGES, AI_CLOCK_MICROS,
AI_ALL_PATTERN_NAMES, AI_CURRENT_PATTERN_NAME, AI_INTENSITY_PERCENT, AI_PLAY_PAUSE_STOP,
AI_BOX_NAME, AI_PT_DESCRIPTOR_QUEUE
} AttributeId;

typedef void (*AttrNotifier)(void *target, AttributeId, uint16_t trans_id, ElementEncoding, uint8_t const *data, uint16_t size);
typedef void (*AttrNotifier)(void *target, AttributeId, TransactionId, ElementEncoding, uint8_t const *data, uint16_t size);

#define NO_TRANS_ID (TransactionId)0U

#ifdef __cplusplus
extern "C" {
#endif

SubscriptionId Attribute_awaitRead(AttributeId, uint16_t trans_id, AttrNotifier, void *target);
SubscriptionId Attribute_subscribe(AttributeId, uint16_t trans_id, AttrNotifier, void *target);
void Attribute_changed(AttributeId, uint16_t trans_id, ElementEncoding, uint8_t const *data, uint16_t size);
SubscriptionId Attribute_awaitRead(AttributeId, TransactionId, AttrNotifier, void *target);
SubscriptionId Attribute_subscribe(AttributeId, TransactionId, AttrNotifier, void *target);
void Attribute_changed(AttributeId, TransactionId, ElementEncoding, uint8_t const *data, uint16_t size);

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions firmware/inc/bsp_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ uint16_t BSP_setPrimaryVoltage_mV(uint16_t V_prim_mV);
void BSP_primaryVoltageEnable(bool must_be_on);
void BSP_startSequencerClock(uint32_t time_µs);
void BSP_stopSequencerClock(void);
void BSP_resumeSequencerClock(void);
uint32_t BSP_getSequencerClock(void);
bool BSP_scheduleBurst(Burst const *);
bool BSP_startBurst(Burst const *);
Expand Down
8 changes: 4 additions & 4 deletions firmware/inc/burst.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
#include <stdbool.h>
#include <stdint.h>

#define MIN_PULSE_WIDTH_¼_µs 6
#define MAX_PULSE_WIDTH_¼_µs 800
#define MIN_PULSE_WIDTH_¼µs 6
#define MAX_PULSE_WIDTH_¼µs 800

#define MIN_PULSE_PACE_µs 5000
#define MAX_PULSE_PACE_µs 62500 // 16 Hz.
Expand All @@ -27,14 +27,14 @@ typedef struct {
uint8_t elcon[2];
uint16_t pace_µs;
uint16_t nr_of_pulses;
uint16_t pulse_width_¼_µs;
uint16_t pulse_width_¼µs;
uint8_t phase;
uint8_t amplitude;
uint8_t flags;
} Burst;

typedef struct {
int8_t delta_width_¼_µs; // [0.25 µs]. Changes the duration of a pulse.
int8_t delta_width_¼µs; // [0.25 µs]. Changes the duration of a pulse.
int8_t delta_pace_µs; // [µs]. Modifies the time between pulses.
} Deltas;

Expand Down
6 changes: 4 additions & 2 deletions firmware/inc/pattern_iter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ typedef struct {
PatternDescr const *pattern_descr;
uint16_t elcon_nr;
uint8_t pulse_width_micros;
uint8_t amplitude;
uint8_t step_nr;
uint16_t nr_of_reps;
uint8_t segment_nr;
} PatternIterator;


void PatternIterator_init(PatternIterator *, PatternDescr const *);
bool PatternIterator_init(PatternIterator *, PatternDescr const *);
void PatternIterator_setPulseWidth(PatternIterator *, uint8_t width_µs);
void PatternIterator_setIntensity(PatternIterator *, uint8_t intensity);
bool PatternIterator_scheduleNextBurst(PatternIterator *);
char const *PatternIterator_name(PatternIterator *);
char const *PatternIterator_name(PatternIterator const *);
bool PatternIterator_done(PatternIterator *);

#endif
2 changes: 1 addition & 1 deletion firmware/inc/sequencer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

typedef struct _Sequencer Sequencer; // Opaque type.

typedef enum { PS_UNKNOWN, PS_IDLE, PS_PAUSED, PS_PLAYING,PS_STREAMING } PlayState;
typedef enum { PS_UNKNOWN, PS_IDLE, PS_PAUSED, PS_PLAYING } PlayState;

// Class method.
Sequencer *Sequencer_new(void);
Expand Down
1 change: 1 addition & 0 deletions firmware/maolib/inc/matter.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef enum {
EE_NULL, EE_STRUCT, EE_ARRAY, EE_LIST, EE_END_OF_CONTAINER
} ElementEncoding;

typedef uint16_t TransactionId;
typedef uint16_t SubscriptionId;

#ifdef __cplusplus
Expand Down
7 changes: 5 additions & 2 deletions firmware/maolib/inc/ptd_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

#include "pulse_train.h"

#define QF_QUEUE_CHANGED 0x1

typedef enum { PE_NONE, PE_BAD_PHASE, PE_BUFFER_FULL, PE_BAD_TIMESTAMP, PE_WRITE_FAILED } PtdErrType;
typedef struct _PtdQueue PtdQueue; // Opaque type.

#ifdef __cplusplus
Expand All @@ -30,8 +33,8 @@ PtdQueue *PtdQueue_new(uint16_t nr_of_descriptors);
// Instance methods.
void PtdQueue_clear(PtdQueue *);
bool PtdQueue_isEmpty(PtdQueue const *);
uint16_t PtdQueue_nrOfBytesFree(PtdQueue const *, uint16_t[2]);
bool PtdQueue_addDescriptor(PtdQueue *, PulseTrain const *, uint16_t sz, uint8_t *err);
void PtdQueue_nrOfBytesFree(PtdQueue const *, uint16_t[2]);
bool PtdQueue_addDescriptor(PtdQueue *, PulseTrain const *, uint16_t sz, PtdErrType *);
bool PtdQueue_getNextBurst(PtdQueue *, Burst *);
void PtdQueue_delete(PtdQueue *);

Expand Down
Binary file modified firmware/maolib/libmao.a
Binary file not shown.
6 changes: 3 additions & 3 deletions firmware/src/attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,21 @@ static SubscriptionId setSubForId(AttributeId ai, AttrNotifier notify, void *tar
* Below are the functions implementing this module's interface.
*/

SubscriptionId Attribute_awaitRead(AttributeId ai, uint16_t trans_id, AttrNotifier notify, void *target)
SubscriptionId Attribute_awaitRead(AttributeId ai, TransactionId trans_id, AttrNotifier notify, void *target)
{
// BSP_logf("%s for id=%hu\n", __func__, ai);
return setSubForId(ai, notify, target, 1);
}


SubscriptionId Attribute_subscribe(AttributeId ai, uint16_t trans_id, AttrNotifier notify, void *target)
SubscriptionId Attribute_subscribe(AttributeId ai, TransactionId trans_id, AttrNotifier notify, void *target)
{
// BSP_logf("%s for id=%hu\n", __func__, ai);
return setSubForId(ai, notify, target, 0);
}


void Attribute_changed(AttributeId ai, uint16_t trans_id, ElementEncoding enc, uint8_t const *data, uint16_t size)
void Attribute_changed(AttributeId ai, TransactionId trans_id, ElementEncoding enc, uint8_t const *data, uint16_t size)
{
Subscription *sub = findSubForId(ai);
if (sub == NULL) return;
Expand Down
30 changes: 22 additions & 8 deletions firmware/src/bsp_stm32g071.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,15 @@ static uint16_t Vcap_mV_ToDacVal(uint16_t Vcap_mV)
}


static void setPrimaryVoltage(Burst const *burst)
{
if (bsp.burst.amplitude != 0) { // 0 means do not change.
// Scale amplitude 0..255 to 0..8160 mV (for now).
BSP_setPrimaryVoltage_mV(bsp.burst.amplitude * 32);
}
}


static void doUartAction(USART_TypeDef *uart, ChannelAction action)
{
switch (action)
Expand Down Expand Up @@ -555,11 +564,9 @@ void TIM2_IRQHandler(void)
{
if (seq_clock->SR & TIM_SR_CC1IF) {
seq_clock->SR &= ~TIM_SR_CC1IF; // Clear the interrupt.
// BSP_logf("T2 at %u µs\n",seq_clock->CNT);
if (bsp.burst.amplitude != 0) {
// Scale amplitude 0..255 to 0..8160 mV (for now).
BSP_setPrimaryVoltage_mV(bsp.burst.amplitude * 32);
}
// BSP_logf("T2 at %u µs\n", seq_clock->CNT);
setPrimaryVoltage(&bsp.burst);
bsp.pulse_seqnr = 0;
BSP_startBurst(&bsp.burst);
} else {
spuriousIRQ(&bsp);
Expand Down Expand Up @@ -698,7 +705,7 @@ void BSP_init()

char const *BSP_firmwareVersion()
{
return "v0.52-beta";
return "v0.53-beta";
}


Expand Down Expand Up @@ -878,7 +885,14 @@ void BSP_stopSequencerClock()
}


uint32_t BSP_getSequencerClock(void)
void BSP_resumeSequencerClock()
{
BSP_logf("%s at %u µs\n", __func__, seq_clock->CNT);
seq_clock->CR1 |= TIM_CR1_CEN; // Enable the counter.
}


uint32_t BSP_getSequencerClock()
{
return seq_clock->CNT;
}
Expand All @@ -888,6 +902,7 @@ bool BSP_scheduleBurst(Burst const *burst)
{
bsp.burst = *burst;
seq_clock->CCR1 = burst->start_time_µs;
// setPrimaryVoltage(burst);
// BSP_logf("SB(%hhu) for t=%u @ %d µs\n", burst->phase, burst->start_time_µs, seq_clock->CNT);
// BSP_logf("SB(%hhu) d=%d µs\n", burst->phase, burst->start_time_µs - seq_clock->CNT);
return true;
Expand All @@ -896,7 +911,6 @@ bool BSP_scheduleBurst(Burst const *burst)

bool BSP_startBurst(Burst const *burst)
{
bsp.pulse_seqnr = 0;
// BSP_logf("%s at %u µs\n", __func__, seq_clock->CNT);
pulse_timer->ARR = burst->pace_µs - 1;
pulse_timer->RCR = burst->nr_of_pulses - 1;
Expand Down
12 changes: 6 additions & 6 deletions firmware/src/burst.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ bool Burst_isValid(Burst const *me)
// Pulse repetition rate must be in [16..200] Hz.
if (me->pace_µs < (MIN_PULSE_PACE_µs)) return false;
if (me->pace_µs > (MAX_PULSE_PACE_µs)) return false;
if (me->pulse_width_¼_µs < MIN_PULSE_WIDTH_¼_µs) return false;
if (me->pulse_width_¼µs < MIN_PULSE_WIDTH_¼µs) return false;
if (me->nr_of_pulses == 0) return false;
return true;
}
Expand All @@ -36,16 +36,16 @@ uint32_t Burst_duration_µs(Burst const *me)

uint8_t Burst_pulseWidth_µs(Burst const *me)
{
return (me->pulse_width_¼_µs + 2) / 4;
return (me->pulse_width_¼µs + 2) / 4;
}


void Burst_applyDeltas(Burst *me, Deltas const *deltas)
{
int32_t new_pw = me->pulse_width_¼_µs + deltas->delta_width_¼_µs;
if (new_pw < MIN_PULSE_WIDTH_¼_µs) new_pw = MIN_PULSE_WIDTH_¼_µs;
if (new_pw > MAX_PULSE_WIDTH_¼_µs) new_pw = MAX_PULSE_WIDTH_¼_µs;
me->pulse_width_¼_µs = new_pw;
int32_t new_pw = me->pulse_width_¼µs + deltas->delta_width_¼µs;
if (new_pw < MIN_PULSE_WIDTH_¼µs) new_pw = MIN_PULSE_WIDTH_¼µs;
if (new_pw > MAX_PULSE_WIDTH_¼µs) new_pw = MAX_PULSE_WIDTH_¼µs;
me->pulse_width_¼µs = new_pw;

int32_t new_pace = me->pace_µs + deltas->delta_pace_µs;
if (new_pace < MIN_PULSE_PACE_µs) new_pace = MIN_PULSE_PACE_µs;
Expand Down
16 changes: 9 additions & 7 deletions firmware/src/pattern_iter.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ static bool getNextBurst(PatternIterator *me, Burst *burst)
uint8_t const *elcon = getNextPattern(me, &burst->nr_of_pulses);
burst->elcon[0] = elcon[0];
burst->elcon[1] = elcon[1];
burst->pulse_width_¼_µs = me->pulse_width_micros * 4;
burst->pulse_width_¼µs = me->pulse_width_micros * 4;
burst->pace_µs = me->pattern_descr->pace_µs;
return true;
}
Expand All @@ -80,14 +80,15 @@ static bool startBurst(Burst const *burst, Deltas const *deltas)
* Below are the functions implementing this module's interface.
*/

void PatternIterator_init(PatternIterator *me, PatternDescr const *pd)
bool PatternIterator_init(PatternIterator *me, PatternDescr const *pd)
{
me->pattern_descr = pd;
me->amplitude = 0;
me->nr_of_reps = pd->nr_of_reps;
me->elcon_nr = 0;
M_ASSERT(pd->nr_of_steps != 0);
me->step_nr = 0;
me->segment_nr = 0; // 0 or 1.
return pd->nr_of_steps != 0;
}


Expand All @@ -102,11 +103,12 @@ bool PatternIterator_scheduleNextBurst(PatternIterator *me)
{
Burst burst;
if (getNextBurst(me, &burst)) {
if (burst.pulse_width_¼_µs > MAX_PULSE_WIDTH_¼_µs) {
burst.pulse_width_¼_µs = MAX_PULSE_WIDTH_¼_µs;
if (burst.pulse_width_¼µs > MAX_PULSE_WIDTH_¼µs) {
burst.pulse_width_¼µs = MAX_PULSE_WIDTH_¼µs;
}
// BSP_logf("Pulse width is %hu µs\n", burst.pulse_width_¼_µs / 4);
// BSP_logf("Pulse width is %hu µs\n", Burst_pulseWidth_µs(&burst));
burst.phase = getPhase(burst.elcon);
// TODO Set burst.amplitude?
Deltas deltas = {0};
return startBurst(&burst, &deltas);
}
Expand All @@ -115,7 +117,7 @@ bool PatternIterator_scheduleNextBurst(PatternIterator *me)
}


char const *PatternIterator_name(PatternIterator *me)
char const *PatternIterator_name(PatternIterator const *me)
{
return Patterns_name(me->pattern_descr);
}
Expand Down
8 changes: 6 additions & 2 deletions firmware/src/patterns.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ uint16_t Patterns_getCount()

char const *Patterns_name(PatternDescr const *pd)
{
M_ASSERT(pd != NULL);
return pd->name;
}

Expand Down Expand Up @@ -199,7 +200,10 @@ PatternDescr const *Patterns_findByName(char const *name, uint16_t len)

PatternDescr const *Patterns_getNext(PatternDescr const *pd)
{
uint16_t index = (pd == NULL ? 0 : pd - pattern_descriptors);
if (++index == M_DIM(pattern_descriptors)) index = 0;
uint16_t index = 0;
if (pd != NULL) {
index = pd - pattern_descriptors;
if (++index == M_DIM(pattern_descriptors)) index = 0;
}
return &pattern_descriptors[index];
}
12 changes: 6 additions & 6 deletions firmware/src/pulse_train.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Burst const *PulseTrain_getBurst(PulseTrain const *me, Burst *burst)
burst->elcon[1] = me->electrode_set[1];
burst->phase = PulseTrain_phase(me);
burst->pace_µs = me->pace_¼ms * 250;
burst->pulse_width_¼_µs = me->pulse_width_µs * 4;
burst->pulse_width_¼µs = me->pulse_width_µs * 4;
burst->nr_of_pulses = me->nr_of_pulses;
burst->amplitude = me->amplitude;
burst->flags = 0x0;
Expand All @@ -127,24 +127,24 @@ void PulseTrain_clearDeltas(PulseTrain *me)
}


void PulseTrain_setDeltas(PulseTrain *me, int8_t delta_width_¼_µs, int8_t delta_pace_µs)
void PulseTrain_setDeltas(PulseTrain *me, int8_t delta_width_¼µs, int8_t delta_pace_µs)
{
me->delta_pulse_width_¼µs = delta_width_¼_µs;
me->delta_pulse_width_¼µs = delta_width_¼µs;
me->delta_pace_µs = delta_pace_µs;
}


Deltas const *PulseTrain_getDeltas(PulseTrain const *me, uint16_t sz, Deltas *deltas)
{
deltas->delta_width_¼_µs = sz > offsetof(PulseTrain, delta_pulse_width_¼µs) ? me->delta_pulse_width_¼µs : 0;
deltas->delta_pace_µs = sz > offsetof(PulseTrain, delta_pace_µs) ? me->delta_pace_µs : 0;
deltas->delta_width_¼µs = sz > offsetof(PulseTrain, delta_pulse_width_¼µs) ? me->delta_pulse_width_¼µs : 0;
deltas->delta_pace_µs = sz > offsetof(PulseTrain, delta_pace_µs) ? me->delta_pace_µs : 0;
return deltas;
}


void PulseTrain_print(PulseTrain const *me, uint16_t sz)
{
BSP_logf("Pt %hhu: t=%u µs, ec=0x%x<>0x%x, phase=%hhu, np=%hu, pace=%hhu ¼ms, amp=%hhu, pw=%hhu µs, Δ=%hhd ¼µs\n",
BSP_logf("Pt %3hhu: t=%u µs, ec=0x%x<>0x%x, phase=%hhu, np=%hu, pace=%hhu ¼ms, amp=%hhu, pw=%hhu µs, Δ=%hhd ¼µs\n",
me->sequence_number, me->start_time_µs, me->electrode_set[0], me->electrode_set[1],
me->phase, me->nr_of_pulses, me->pace_¼ms, me->amplitude, me->pulse_width_µs,
sz > offsetof(PulseTrain, delta_pulse_width_¼µs) ? me->delta_pulse_width_¼µs : 0);
Expand Down
Loading

0 comments on commit 7c5d075

Please sign in to comment.