Skip to content

Commit

Permalink
Infrastructure, mostly.
Browse files Browse the repository at this point in the history
  • Loading branch information
Onwrikbaar committed Jan 28, 2025
1 parent 2508011 commit 864cacb
Show file tree
Hide file tree
Showing 20 changed files with 1,807 additions and 1,705 deletions.
2 changes: 1 addition & 1 deletion Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Conceptually, the firmware consists of two layers:
2. The hardware-independent application logic, implementing the _policies_.

### About the code
- All C modules, with the exception of the BSP, are less than 400 lines long.
- All C modules, with the exception of the BSP, are less than 500 lines long.
- Functions are short, or have low cyclomatic complexity.
- Functions are _pure_ whenever possible.
- There are no global variables, preventing unwanted coupling between modules as well as violation of invariants.
Expand Down
3,270 changes: 1,650 additions & 1,620 deletions firmware/build/neodk_g071.hex

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions firmware/inc/attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ typedef enum {
AI_BOX_NAME, AI_PT_DESCRIPTOR_QUEUE
} AttributeId;

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

#ifdef __cplusplus
extern "C" {
#endif

SubscriptionId Attribute_awaitRead(AttributeId, AttrNotifier, void *target);
SubscriptionId Attribute_subscribe(AttributeId, AttrNotifier, void *target);
void Attribute_changed(AttributeId, ElementEncoding, uint8_t const *data, uint16_t size);
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);

#ifdef __cplusplus
}
Expand Down
3 changes: 2 additions & 1 deletion firmware/inc/bsp_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ int BSP_closeSerialPort(int fd);
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 BSP_stopSequencerClock(void);
uint32_t BSP_getSequencerClock(void);
bool BSP_scheduleBurst(Burst const *);
bool BSP_startBurst(Burst const *);

Expand Down
11 changes: 10 additions & 1 deletion firmware/inc/burst.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define MAX_PULSE_WIDTH_¼_µs 800

#define MIN_PULSE_PACE_µs 5000
#define MAX_PULSE_PACE_µs 63000
#define MAX_PULSE_PACE_µs 62500 // 16 Hz.

typedef struct {
uint32_t start_time_µs;
Expand All @@ -30,6 +30,7 @@ typedef struct {
uint16_t pulse_width_¼_µs;
uint8_t phase;
uint8_t amplitude;
uint8_t flags;
} Burst;

typedef struct {
Expand All @@ -38,10 +39,18 @@ typedef struct {
} Deltas;


#ifdef __cplusplus
extern "C" {
#endif

bool Burst_isValid(Burst const *);
uint32_t Burst_duration_µs(Burst const *);
uint8_t Burst_pulseWidth_µs(Burst const *);
void Burst_applyDeltas(Burst *, Deltas const *);
void Burst_print(Burst const *);

#ifdef __cplusplus
}
#endif

#endif
10 changes: 9 additions & 1 deletion firmware/inc/pulse_train.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@

typedef struct _PulseTrain PulseTrain; // Opaque type.

#ifdef __cplusplus
extern "C" {
#endif

// Class method.
uint16_t PulseTrain_size();
uint16_t PulseTrain_size(void);

// Instance methods.
PulseTrain *PulseTrain_init(PulseTrain *, uint8_t seq_nr, uint32_t timestamp, Burst const *burst);
Expand All @@ -33,4 +37,8 @@ Burst const *PulseTrain_getBurst(PulseTrain const *, Burst *);
Deltas const *PulseTrain_getDeltas(PulseTrain const *, uint16_t sz, Deltas *);
void PulseTrain_print(PulseTrain const *, uint16_t sz);

#ifdef __cplusplus
}
#endif

#endif
6 changes: 3 additions & 3 deletions firmware/inc/sequencer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* Created on: 27 Feb 2024
* Author: mark
* Copyright 2024 Neostim™
* Copyright 2024, 2025 Neostim™
*/

#ifndef INC_SEQUENCER_H_
Expand All @@ -17,7 +17,7 @@

typedef struct _Sequencer Sequencer; // Opaque type.

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

// Class method.
Sequencer *Sequencer_new(void);
Expand All @@ -34,7 +34,7 @@ uint8_t Sequencer_getIntensityPercentage(Sequencer const *);
void Sequencer_notifyIntensity(Sequencer const *);
void Sequencer_notifyPattern(Sequencer const *);
void Sequencer_notifyPlayState(Sequencer const *);
void Sequencer_notifyPtQueue(Sequencer const *);
void Sequencer_notifyPtQueue(Sequencer const *, uint16_t trans_id);

void Sequencer_stop(Sequencer *);
void Sequencer_delete(Sequencer *);
Expand Down
2 changes: 1 addition & 1 deletion firmware/maolib/inc/ao_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ typedef enum {
ET_COMMS_CAN_ACCEPT_DATA, ET_COMMS_HAS_DATA_AVAILABLE, ET_COMMS_ERROR,
ET_BUTTON_PUSHED, ET_BUTTON_HELD, ET_BUTTON_RELEASED,
ET_CHARGING_STARTED, ET_CHARGING_STOPPED,
ET_APP_TIMER_EXPIRED,
ET_LOG_FROM_IRQ, ET_APP_TIMER_EXPIRED,
// Application events start here.
ET_AO_FIRST_APP_EVENT
} EventType;
Expand Down
6 changes: 3 additions & 3 deletions firmware/maolib/inc/bsp_dbg.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Created on: 27 Mar 2021
* Author: mark
* Copyright 2021..2024 Neostim™
* Copyright 2021..2025 Neostim™
*/

#ifndef INC_BSP_DBG_H_
Expand All @@ -20,13 +20,13 @@
extern "C" {
#endif

void BSP_initDebug();
void BSP_initDebug(void);
int BSP_logf(char const *fmt, ...); // Print logging and debugging information.
int BSP_vlogf(char const *fmt, va_list args);
void BSP_assertionFailed(char const *filename, unsigned int line_number, char const *predicate);
bool BSP_getKey(char *pch);
int BSP_readConsole(char *cbuf, int nr_of_chars);
void BSP_closeDebug();
void BSP_closeDebug(void);

#ifdef __cplusplus
}
Expand Down
8 changes: 8 additions & 0 deletions firmware/maolib/inc/convenience.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@
#include <time.h>
#include <stdint.h>

#include "eventqueue.h"

#define M_DIM(arr) (sizeof arr / sizeof arr[0])

typedef int8_t DeviceId;
#define UNDEFINED_DEVICE_ID ((DeviceId)(-1))

typedef int (*CompareFunc)(const void *, const void *);
typedef void (*Action)(void *target, uint32_t);
typedef struct {
char const *fmt;
struct { uint32_t argv[4]; } as;
} LogArgs;

typedef struct {
Action action;
void *target;
Expand All @@ -38,6 +45,7 @@ void invokeSelector(Selector *, uint32_t);
int dumpBuffer(const char *prefix, const uint8_t *bbuf, uint8_t nb);
struct timespec *tsIncrementNanos(struct timespec *, int64_t nanoseconds);
char const *bytesToHexString(uint8_t const *pb, uint16_t nb);
void DBG_irqLogf(EventQueue *, char const *fmt, ...);

#ifdef __cplusplus
}
Expand Down
2 changes: 1 addition & 1 deletion firmware/maolib/inc/ptd_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ PtdQueue *PtdQueue_new(uint16_t nr_of_descriptors);
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);
bool PtdQueue_addDescriptor(PtdQueue *, PulseTrain const *, uint16_t sz, uint8_t *err);
bool PtdQueue_getNextBurst(PtdQueue *, Burst *);
void PtdQueue_delete(PtdQueue *);

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

SubscriptionId Attribute_awaitRead(AttributeId ai, AttrNotifier notify, void *target)
SubscriptionId Attribute_awaitRead(AttributeId ai, uint16_t 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, AttrNotifier notify, void *target)
SubscriptionId Attribute_subscribe(AttributeId ai, uint16_t 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, ElementEncoding enc, uint8_t const *data, uint16_t size)
void Attribute_changed(AttributeId ai, uint16_t trans_id, ElementEncoding enc, uint8_t const *data, uint16_t size)
{
Subscription *sub = findSubForId(ai);
if (sub == NULL) return;

sub->notify(sub->target, ai, enc, data, size);
sub->notify(sub->target, ai, trans_id, enc, data, size);
if (sub->times == 1) { // Subscription expired?
// BSP_logf("Cancelling subscription for id=%hu\n", sub->ai);
*sub = subscriptions[--nr_of_subs]; // Cancel it.
Expand Down
23 changes: 15 additions & 8 deletions firmware/src/bsp_stm32g071.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ static void initAppTimer()
static void initSequencerClock()
{
seq_clock->PSC = SystemCoreClock / SEQUENCER_CLOCK_FREQ_Hz - 1;
BSP_logf("%s: PSC=%u\n", __func__, seq_clock->PSC);
// BSP_logf("%s: PSC=%u\n", __func__, seq_clock->PSC);
seq_clock->CCMR1 |= TIM_CCMR1_OC1M_0;
seq_clock->DIER |= TIM_DIER_CC1IE; // Interrupt on match with compare register.
seq_clock->EGR |= TIM_EGR_UG; // Force update of the shadow registers.
Expand All @@ -268,7 +268,7 @@ static void initSequencerClock()
static void initPulseTimer()
{
pulse_timer->PSC = SystemCoreClock / PULSE_TIMER_FREQ_Hz - 1;
BSP_logf("%s: PSC=%u\n", __func__, pulse_timer->PSC);
// BSP_logf("%s: PSC=%u\n", __func__, pulse_timer->PSC);
// PWM mode 1 for timer channels 1 and 2, enable preload.
pulse_timer->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE
| TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
Expand Down Expand Up @@ -542,7 +542,6 @@ void TIM1_CC_IRQHandler(void)
// Burst_applyDeltas(&bsp.burst, &bsp.deltas);
}
if (bsp.pulse_seqnr == pulse_timer->RCR + 1) {
// BSP_logf("BC %hu\n", bsp.pulse_seqnr);
EventQueue_postEvent(bsp.pulse_delegate, ET_BURST_COMPLETED, NULL, 0);
}
if (pulse_timer->SR & 0xcffe0) {
Expand All @@ -557,8 +556,10 @@ 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);
// Scale amplitude 0..255 to 0..8160 mV (for now).
BSP_setPrimaryVoltage_mV(bsp.burst.amplitude * 32);
if (bsp.burst.amplitude != 0) {
// Scale amplitude 0..255 to 0..8160 mV (for now).
BSP_setPrimaryVoltage_mV(bsp.burst.amplitude * 32);
}
BSP_startBurst(&bsp.burst);
} else {
spuriousIRQ(&bsp);
Expand Down Expand Up @@ -697,7 +698,7 @@ void BSP_init()

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


Expand Down Expand Up @@ -877,12 +878,18 @@ void BSP_stopSequencerClock()
}


uint32_t BSP_getSequencerClock(void)
{
return seq_clock->CNT;
}


bool BSP_scheduleBurst(Burst const *burst)
{
bsp.burst = *burst;
// Burst_print(burst);
seq_clock->CCR1 = burst->start_time_µs;
BSP_logf("SB(%hhu) for t=%u @ %d µs\n", burst->phase, burst->start_time_µs, seq_clock->CNT);
// 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 Down
1 change: 1 addition & 0 deletions firmware/src/burst.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bool Burst_isValid(Burst const *me)

uint32_t Burst_duration_µs(Burst const *me)
{
// TODO Also account for delta_pace.
return me->nr_of_pulses * me->pace_µs;
}

Expand Down
20 changes: 10 additions & 10 deletions firmware/src/controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ static void readPatternNames(Controller *me, AttributeAction const *aa)
}


static void attributeChanged(Controller *me, AttributeId ai, ElementEncoding enc, uint8_t const *data, uint16_t data_size)
static void attributeChanged(Controller *me, AttributeId ai, uint16_t trans_id, ElementEncoding enc, uint8_t const *data, uint16_t data_size)
{
// BSP_logf("Controller_%s(%hu) size=%hu\n", __func__, ai, data_size);
uint16_t nbtw = sizeof(PacketHeader) + sizeof(AttributeAction);
uint8_t packet[nbtw + Matter_encodedDataLength(enc, data_size)];
initResponsePacket((PacketHeader *)packet);
initAttributeAction((AttributeAction *)(packet + sizeof(PacketHeader)), 0, OC_REPORT_DATA, ai);
initAttributeAction((AttributeAction *)(packet + sizeof(PacketHeader)), trans_id, OC_REPORT_DATA, ai);
nbtw += Matter_encode(packet + nbtw, enc, data, data_size);
DataLink_sendDatagram(me->datalink, packet, nbtw);
}
Expand Down Expand Up @@ -124,16 +124,16 @@ static void handleReadRequest(Controller *me, AttributeAction const *aa)
{
case AI_FIRMWARE_VERSION: {
char const *fw_version = BSP_firmwareVersion();
attributeChanged(me, aa->attribute_id, EE_UTF8_1LEN, (uint8_t const *)fw_version, strlen(fw_version));
attributeChanged(me, aa->attribute_id, aa->transaction_id, EE_UTF8_1LEN, (uint8_t const *)fw_version, strlen(fw_version));
break;
}
case AI_VOLTAGES:
Attribute_awaitRead(aa->attribute_id, (AttrNotifier)&attributeChanged, me);
Attribute_awaitRead(aa->attribute_id, aa->transaction_id, (AttrNotifier)&attributeChanged, me);
BSP_triggerADC();
break;
case AI_CLOCK_MICROS: {
uint64_t clock_micros = BSP_microsecondsSinceBoot();
attributeChanged(me, aa->attribute_id, EE_UNSIGNED_INT, (uint8_t const *)&clock_micros, sizeof clock_micros);
attributeChanged(me, aa->attribute_id, aa->transaction_id, EE_UNSIGNED_INT, (uint8_t const *)&clock_micros, sizeof clock_micros);
break;
}
case AI_ALL_PATTERN_NAMES:
Expand All @@ -149,10 +149,10 @@ static void handleReadRequest(Controller *me, AttributeAction const *aa)
Sequencer_notifyPlayState(me->sequencer);
break;
case AI_BOX_NAME:
attributeChanged(me, aa->attribute_id, EE_UTF8_1LEN, (uint8_t const *)me->box_name, strlen(me->box_name));
attributeChanged(me, aa->attribute_id, aa->transaction_id, EE_UTF8_1LEN, (uint8_t const *)me->box_name, strlen(me->box_name));
break;
case AI_PT_DESCRIPTOR_QUEUE:
Sequencer_notifyPtQueue(me->sequencer);
Sequencer_notifyPtQueue(me->sequencer, aa->transaction_id);
break;
default:
BSP_logf("%s: unknown attribute id=%hu\n", __func__, aa->attribute_id);
Expand Down Expand Up @@ -253,12 +253,12 @@ static void handleRequest(Controller *me, AttributeAction const *aa)
handleReadRequest(me, aa);
break;
case OC_WRITE_REQUEST:
logTransaction(aa, "write");
// logTransaction(aa, "write");
handleWriteRequest(me, aa);
break;
case OC_SUBSCRIBE_REQUEST:
logTransaction(aa, "subscribe to");
Attribute_subscribe(aa->attribute_id, (AttrNotifier)&attributeChanged, me);
Attribute_subscribe(aa->attribute_id, aa->transaction_id, (AttrNotifier)&attributeChanged, me);
handleReadRequest(me, aa);
break;
case OC_INVOKE_REQUEST:
Expand Down Expand Up @@ -332,7 +332,7 @@ Controller *Controller_new()

void Controller_init(Controller *me, Sequencer *sequencer, DataLink *datalink)
{
strncpy(me->box_name, "Mean Machine", sizeof me->box_name - 1);
strncpy(me->box_name, "Neostim Mean Machine", sizeof me->box_name - 1);
me->sequencer = sequencer;
me->datalink = datalink;
}
Expand Down
2 changes: 1 addition & 1 deletion firmware/src/datalink.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ static void rxErrorCallback(DataLink *me, uint32_t rx_error)

static void txCallback(DataLink *me, uint8_t *dst)
{
BSP_criticalSectionEnter();
uint32_t nbr = CircBuffer_read(&me->output_buffer, dst, 1);
M_ASSERT(nbr == 1);
// Once the buffer is empty, disable this callback (atomic).
BSP_criticalSectionEnter();
if (CircBuffer_availableData(&me->output_buffer) == 0) {
BSP_doChannelAction(me->channel_fd, CA_TX_CB_DISABLE);
}
Expand Down
Loading

0 comments on commit 864cacb

Please sign in to comment.