astra/iphone-encoder-counter
A CR2450-powered MSP430 FRAM counter combines an always-on dual-channel TMR rotation sensor, pushbutton input, and ST25DV NFC tag with an integrated tuned printed antenna and I²C/GPIO interface.
- Version
- 1.0.3
- License
- unset
- Stars
- 0
firmware/counter.h
#ifndef FIDGET_COUNTER_H
#define FIDGET_COUNTER_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/* phase = (SIN << 1) | COS. +1 is CCW viewed from above the sensor.
* A twist is four net quadrant transitions. Sub-turn reversals cancel.
* Counts are integer and saturate; they never wrap to zero. */
typedef struct {
uint64_t cw, ccw, presses;
uint32_t invalid_transitions;
int8_t pending;
uint8_t phase;
} counter_t;
void counter_init(counter_t *c, uint8_t phase);
uint64_t counter_total_twists(const counter_t *c);
int counter_step(counter_t *c, uint8_t phase);
/* Rebase after a reset / battery replacement. Unknown motion while unpowered
* cannot be recovered. Keep whole-turn totals, discard partial turn. */
void counter_rebase(counter_t *c, uint8_t phase);
typedef struct { bool stable, candidate, armed; uint32_t since; } button_t;
void button_init(button_t *b, bool down, uint32_t now_ms);
bool button_update(button_t *b, counter_t *c, bool down, uint32_t now_ms);
#define JOURNAL_BYTES 48
void journal_encode(uint8_t out[JOURNAL_BYTES],const counter_t *c,uint32_t sequence);
bool journal_decode(const uint8_t in[JOURNAL_BYTES],counter_t *c,uint32_t *sequence);
/* Returns selected slot 0/1, or -1 when neither is valid. */
int journal_restore(const uint8_t a[JOURNAL_BYTES],const uint8_t b[JOURNAL_BYTES],counter_t *c,uint32_t *sequence);
/* Hardware commit to inactive, aligned FRAM slot:
* 1. Write 16-bit zero at offset 46 (invalidate old commit).
* 2. Write bytes 0..45 as aligned volatile 16-bit stores.
* 3. Compiler barrier, then write 16-bit 0xA55A at offset 46 LAST.
* Keep old slot untouched. Serialize all writers. Do not use a raw memcpy
* of all 48 bytes as the hardware commit routine. */
#endif