dagon/comm.cpp

165 lines
4.1 KiB
C++

#include <limits.h>
#include <assert.h>
#include <stdio.h>
#include "DueTimer.h"
#include "dagon.h"
bool serial_vprintf(const char *fmt, va_list va) {
char buf[4096];
if (vsnprintf(buf, sizeof buf, fmt, va) >= sizeof buf)
return false;
Serial.print(buf);
return true;
}
bool serial_printf(const char *fmt, ...) {
va_list va;
va_start(va, fmt);
bool r = serial_vprintf(fmt, va);
va_end(va);
return r;
}
bool print_start(struct libscomp_line *l, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
if (l->name) {
Serial.print(":");
Serial.print(l->buf[0]);
Serial.print("\t");
}
bool b = serial_vprintf(fmt, va);
va_end(va);
return b;
}
static long pstr(const char *s, long max) {
long v;
char *p;
v = strtol(s, &p, 0);
if (*p || v < 0 || v >= max)
return -1;
return v;
}
#define inl __attribute__((always_inline))
\
#define mkhandler(nam, cond) static int nam(struct libscomp_line *) inl; \
int dagon_##nam(struct libscomp_line *l, void *p unused) { \
if (!([](size_t len) { return (cond); })(l->len - l->name - 1)) \
return 1; \
return nam(l); \
} static int nam(struct libscomp_line *l)
volatile struct dagon dg;
mkhandler(idn, len == 0) {
print_start(l, "IDN\tDagon\t0\n");
return LIBSCOMP_CMD_OK;
}
#define V "%" PRIu32
mkhandler(vlt, len == 0) {
print_start(l, "VLT\t" V "\t" V "\t" V "\t" V "\t",
dg.ch[0].volt, dg.ch[1].volt, dg.ch[2].volt,
dg.ch[3].volt);
return LIBSCOMP_CMD_OK;
}
#undef V
// GNU extension (statement expression)
#define PARSE_INT(v, m, typ) ({ \
long parse__v = pstr(v, m); \
if (parse__v < 0) { \
print_start(l, "ERROR\t%s invalid %s\n", v, typ); \
return 1; \
} \
parse__v; \
})
/* [:??] SET CH V */
mkhandler(set, len == 2) {
long ch, v;
char *s;
const size_t arg1 = l->name + 1;
ch = PARSE_INT(l->buf[arg1], DACS, "Channel");
v = PARSE_INT(l->buf[arg1 + 1], V_MAX + 1, "Voltage");
dg.ch[ch].volt = v;
print_start(l, "SET\n");
spi_set(v, ch);
return LIBSCOMP_CMD_OK;
}
static DueTimer timers[DACS] = {Timer0,Timer1,Timer2,Timer3};
template<size_t CH>
static void irq() {
uint32_t n = dg.ch[CH].volt + dg.ch[CH].step;
if (n <= dg.ch[CH].volt || n >= dg.ch[CH].end) {
n = dg.ch[CH].end;
timers[CH].stop().detachInterrupt();
}
dg.ch[CH].volt = n;
spi_set(n, CH);
}
static void (*const irqs[DACS])() = {
irq<0>, irq<1>, irq<2>, irq<3>
};
/* [:??] RMP CH END START INTERVAL STEP [...] */
mkhandler(rmp, len % 5 == 0 && len >= 5) {
long ch, start, end, inter, step;
size_t arg = l->name + 1;
while (arg < l->len) {
ch = PARSE_INT(l->buf[arg], DACS, "Channel");
if (dg.ch[ch].used) {
print_start(l, "ERROR\tChannel %ld used\n", ch);
return 1;
}
end = PARSE_INT(l->buf[arg + 1], V_MAX + 1, "End voltage");
start = PARSE_INT(l->buf[arg + 2], end, "Start voltage");
inter = PARSE_INT(l->buf[arg + 3], LONG_MAX, "Time");
if (inter == 0) {
print_start(l, "ERROR\tZero interval\n");
return 1;
}
step = PARSE_INT(l->buf[arg + 4], V_MAX + 1, "Step voltage");
if (step == 0) {
print_start(l, "ERROR\tZero step\n");
return 1;
}
arg += 5;
dg.ch[ch].used = true;
dg.ch[ch].end = end;
dg.ch[ch].step = step;
spi_write(start, ch);
timers[ch].attachInterrupt(irqs[ch]).start(inter);
}
return LIBSCOMP_CMD_OK;
}
#define b2s(b) ((b) ? "true" : "false")
mkhandler(con, len == 0) {
print_start(l, "CON\t%s\t%s\t%s\t%s\n",
b2s(dg.conn[0]), b2s(dg.conn[1]),
b2s(dg.conn[2]), b2s(dg.conn[3])
);
return LIBSCOMP_CMD_OK;
}
mkhandler(rst, len == 0) {
spi_reset();
}