This commit is contained in:
Peter McGoron 2021-08-10 16:52:12 -04:00
commit 5bfce94bbc
10 changed files with 1050 additions and 0 deletions

124
COPYING.md Normal file
View File

@ -0,0 +1,124 @@
# Creative Commons Legal Code --- CC0 1.0 Universal #
**CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.**
## Statement of Purpose ##
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
### 1. Copyright and Related Rights. ###
A Work made available under CC0 may be protected by copyright and related
or neighboring rights ("Copyright and Related Rights"). Copyright and
Related Rights include, but are not limited to, the following:
1. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
2. moral rights retained by the original author(s) and/or performer(s);
3. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
4. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
5. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
6. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
7. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
### 2. Waiver. ###
To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably
and unconditionally waives, abandons, and surrenders all of Affirmer's
Copyright and Related Rights and associated claims and causes of action,
whether now known or unknown (including existing as well as future claims
and causes of action), in the Work (i) in all territories worldwide, (ii)
for the maximum duration provided by applicable law or treaty (including
future time extensions), (iii) in any current or future medium and for
any number of copies, and (iv) for any purpose whatsoever, including
without limitation commercial, advertising or promotional purposes (the
"Waiver"). Affirmer makes the Waiver for the benefit of each member
of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal
or equitable action to disrupt the quiet enjoyment of the Work by the
public as contemplated by Affirmer's express Statement of Purpose.
### 3. Public License Fallback. ###
Should any part of the Waiver for any reason be judged legally
invalid or ineffective under applicable law, then the Waiver shall
be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent
the Waiver is so judged Affirmer hereby grants to each affected person
a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright
and Related Rights in the Work (i) in all territories worldwide, (ii)
for the maximum duration provided by applicable law or treaty (including
future time extensions), (iii) in any current or future medium and for
any number of copies, and (iv) for any purpose whatsoever, including
without limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law,
such partial invalidity or ineffectiveness shall not invalidate the
remainder of the License, and in such case Affirmer hereby affirms that
he or she will not (i) exercise any of his or her remaining Copyright
and Related Rights in the Work or (ii) assert any associated claims and
causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
### 4. Limitations and Disclaimers. ###
1. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
2. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
3. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
4. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

51
Dagon.ino Normal file
View File

@ -0,0 +1,51 @@
#include "dagon.h"
static struct libscomp_input in;
static struct libscomp_cmd cmdarr[] = {
{"CON", dagon_con},
{"IDN", dagon_idn},
{"RMP", dagon_rmp},
{"RST", dagon_rst},
{"SET", dagon_set},
{"VLT", dagon_vlt}
};
static struct libscomp_cmd_store store = {
cmdarr, sizeof cmdarr / sizeof cmdarr[0]
};
void setup() {
Serial.begin(115200);
while (!Serial);
spi_init();
libscomp_reset(&in);
Serial.setTimeout(10);
}
void loop() {
static char buf[4096];
struct libscomp_line l;
char *p = buf;
enum libscomp_input_r r;
size_t vl = Serial.readBytes(buf, sizeof buf - 1);
buf[vl] = 0;
do {
r = libscomp_read(&in, &p, &l);
switch (r) {
case LIBSCOMP_OVERFLOW:
serial_printf("ERROR\toverflow\n");
break;
case LIBSCOMP_ARG_OVERFLOW:
serial_printf("ERROR\targument overflow\n");
break;
case LIBSCOMP_COMPLETE:
if (libscomp_exec(&store, &l, nullptr) == LIBSCOMP_NOT_FOUND)
print_start(&l, "ERROR\tno known command\n");
break;
// LIBSCOMP_MORE left out
}
} while (r != LIBSCOMP_MORE);
}

318
DueTimer.cpp Normal file
View File

@ -0,0 +1,318 @@
/*
DueTimer.cpp - Implementation of Timers defined on DueTimer.h
For instructions, go to https://github.com/ivanseidel/DueTimer
Created by Ivan Seidel Gomes, March, 2013.
Modified by Philipp Klaus, June 2013.
Thanks to stimmer (from Arduino forum), for coding the "timer soul" (Register stuff)
Released into the public domain.
*/
#include "DueTimer.h"
const DueTimer::Timer DueTimer::Timers[NUM_TIMERS] = {
{TC0,0,TC0_IRQn},
{TC0,1,TC1_IRQn},
{TC0,2,TC2_IRQn},
{TC1,0,TC3_IRQn},
{TC1,1,TC4_IRQn},
{TC1,2,TC5_IRQn},
#if NUM_TIMERS > 6
{TC2,0,TC6_IRQn},
{TC2,1,TC7_IRQn},
{TC2,2,TC8_IRQn},
#endif
};
// Fix for compatibility with Servo library
#ifdef USING_SERVO_LIB
// Set callbacks as used, allowing DueTimer::getAvailable() to work
void (*DueTimer::callbacks[NUM_TIMERS])() = {
(void (*)()) 1, // Timer 0 - Occupied
(void (*)()) 0, // Timer 1
(void (*)()) 1, // Timer 2 - Occupied
(void (*)()) 1, // Timer 3 - Occupied
(void (*)()) 1, // Timer 4 - Occupied
(void (*)()) 1, // Timer 5 - Occupied
#if NUM_TIMERS > 6
(void (*)()) 0, // Timer 6
(void (*)()) 0, // Timer 7
(void (*)()) 0 // Timer 8
#endif
};
#else
void (*DueTimer::callbacks[NUM_TIMERS])() = {};
#endif
#if NUM_TIMERS > 6
double DueTimer::_frequency[NUM_TIMERS] = {-1,-1,-1,-1,-1,-1,-1,-1,-1};
#else
double DueTimer::_frequency[NUM_TIMERS] = {-1,-1,-1,-1,-1,-1};
#endif
/*
Initializing all timers, so you can use them like this: Timer0.start();
*/
DueTimer Timer(0);
DueTimer Timer1(1);
// Fix for compatibility with Servo library
#ifndef USING_SERVO_LIB
DueTimer Timer0(0);
DueTimer Timer2(2);
DueTimer Timer3(3);
DueTimer Timer4(4);
DueTimer Timer5(5);
#endif
#if NUM_TIMERS > 6
DueTimer Timer6(6);
DueTimer Timer7(7);
DueTimer Timer8(8);
#endif
DueTimer::DueTimer(unsigned short _timer) : timer(_timer){
/*
The constructor of the class DueTimer
*/
}
DueTimer DueTimer::getAvailable(void){
/*
Return the first timer with no callback set
*/
for(int i = 0; i < NUM_TIMERS; i++){
if(!callbacks[i])
return DueTimer(i);
}
// Default, return Timer0;
return DueTimer(0);
}
DueTimer& DueTimer::attachInterrupt(void (*isr)()){
/*
Links the function passed as argument to the timer of the object
*/
callbacks[timer] = isr;
return *this;
}
DueTimer& DueTimer::detachInterrupt(void){
/*
Links the function passed as argument to the timer of the object
*/
stop(); // Stop the currently running timer
callbacks[timer] = NULL;
return *this;
}
DueTimer& DueTimer::start(double microseconds){
/*
Start the timer
If a period is set, then sets the period and start the timer
*/
if(microseconds > 0)
setPeriod(microseconds);
if(_frequency[timer] <= 0)
setFrequency(1);
NVIC_ClearPendingIRQ(Timers[timer].irq);
NVIC_EnableIRQ(Timers[timer].irq);
TC_Start(Timers[timer].tc, Timers[timer].channel);
return *this;
}
DueTimer& DueTimer::stop(void){
/*
Stop the timer
*/
NVIC_DisableIRQ(Timers[timer].irq);
TC_Stop(Timers[timer].tc, Timers[timer].channel);
return *this;
}
uint8_t DueTimer::bestClock(double frequency, uint32_t& retRC){
/*
Pick the best Clock, thanks to Ogle Basil Hall!
Timer Definition
TIMER_CLOCK1 MCK / 2
TIMER_CLOCK2 MCK / 8
TIMER_CLOCK3 MCK / 32
TIMER_CLOCK4 MCK /128
*/
const struct {
uint8_t flag;
uint8_t divisor;
} clockConfig[] = {
{ TC_CMR_TCCLKS_TIMER_CLOCK1, 2 },
{ TC_CMR_TCCLKS_TIMER_CLOCK2, 8 },
{ TC_CMR_TCCLKS_TIMER_CLOCK3, 32 },
{ TC_CMR_TCCLKS_TIMER_CLOCK4, 128 }
};
float ticks;
float error;
int clkId = 3;
int bestClock = 3;
float bestError = 9.999e99;
do
{
ticks = (float) SystemCoreClock / frequency / (float) clockConfig[clkId].divisor;
// error = abs(ticks - round(ticks));
error = clockConfig[clkId].divisor * abs(ticks - round(ticks)); // Error comparison needs scaling
if (error < bestError)
{
bestClock = clkId;
bestError = error;
}
} while (clkId-- > 0);
ticks = (float) SystemCoreClock / frequency / (float) clockConfig[bestClock].divisor;
retRC = (uint32_t) round(ticks);
return clockConfig[bestClock].flag;
}
DueTimer& DueTimer::setFrequency(double frequency){
/*
Set the timer frequency (in Hz)
*/
// Prevent negative frequencies
if(frequency <= 0) { frequency = 1; }
// Remember the frequency — see below how the exact frequency is reported instead
//_frequency[timer] = frequency;
// Get current timer configuration
Timer t = Timers[timer];
uint32_t rc = 0;
uint8_t clock;
// Tell the Power Management Controller to disable
// the write protection of the (Timer/Counter) registers:
pmc_set_writeprotect(false);
// Enable clock for the timer
pmc_enable_periph_clk((uint32_t)t.irq);
// Find the best clock for the wanted frequency
clock = bestClock(frequency, rc);
switch (clock) {
case TC_CMR_TCCLKS_TIMER_CLOCK1:
_frequency[timer] = (double)SystemCoreClock / 2.0 / (double)rc;
break;
case TC_CMR_TCCLKS_TIMER_CLOCK2:
_frequency[timer] = (double)SystemCoreClock / 8.0 / (double)rc;
break;
case TC_CMR_TCCLKS_TIMER_CLOCK3:
_frequency[timer] = (double)SystemCoreClock / 32.0 / (double)rc;
break;
default: // TC_CMR_TCCLKS_TIMER_CLOCK4
_frequency[timer] = (double)SystemCoreClock / 128.0 / (double)rc;
break;
}
// Set up the Timer in waveform mode which creates a PWM
// in UP mode with automatic trigger on RC Compare
// and sets it up with the determined internal clock as clock input.
TC_Configure(t.tc, t.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock);
// Reset counter and fire interrupt when RC value is matched:
TC_SetRC(t.tc, t.channel, rc);
// Enable the RC Compare Interrupt...
t.tc->TC_CHANNEL[t.channel].TC_IER=TC_IER_CPCS;
// ... and disable all others.
t.tc->TC_CHANNEL[t.channel].TC_IDR=~TC_IER_CPCS;
return *this;
}
DueTimer& DueTimer::setPeriod(double microseconds){
/*
Set the period of the timer (in microseconds)
*/
// Convert period in microseconds to frequency in Hz
double frequency = 1000000.0 / microseconds;
setFrequency(frequency);
return *this;
}
double DueTimer::getFrequency(void) const {
/*
Get current time frequency
*/
return _frequency[timer];
}
double DueTimer::getPeriod(void) const {
/*
Get current time period
*/
return 1.0/getFrequency()*1000000;
}
/*
Implementation of the timer callbacks defined in
arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h
*/
// Fix for compatibility with Servo library
#ifndef USING_SERVO_LIB
void TC0_Handler(void){
TC_GetStatus(TC0, 0);
DueTimer::callbacks[0]();
}
#endif
void TC1_Handler(void){
TC_GetStatus(TC0, 1);
DueTimer::callbacks[1]();
}
// Fix for compatibility with Servo library
#ifndef USING_SERVO_LIB
void TC2_Handler(void){
TC_GetStatus(TC0, 2);
DueTimer::callbacks[2]();
}
void TC3_Handler(void){
TC_GetStatus(TC1, 0);
DueTimer::callbacks[3]();
}
void TC4_Handler(void){
TC_GetStatus(TC1, 1);
DueTimer::callbacks[4]();
}
void TC5_Handler(void){
TC_GetStatus(TC1, 2);
DueTimer::callbacks[5]();
}
#endif
#if NUM_TIMERS > 6
void TC6_Handler(void){
TC_GetStatus(TC2, 0);
DueTimer::callbacks[6]();
}
void TC7_Handler(void){
TC_GetStatus(TC2, 1);
DueTimer::callbacks[7]();
}
void TC8_Handler(void){
TC_GetStatus(TC2, 2);
DueTimer::callbacks[8]();
}
#endif

122
DueTimer.h Normal file
View File

@ -0,0 +1,122 @@
/*
DueTimer.h - DueTimer header file, definition of methods and attributes...
For instructions, go to https://github.com/ivanseidel/DueTimer
Created by Ivan Seidel Gomes, March, 2013.
Modified by Philipp Klaus, June 2013.
Released into the public domain.
*/
#include "Arduino.h"
#if defined(_SAM3XA_)
#ifndef DueTimer_h
#define DueTimer_h
#include <inttypes.h>
/*
This fixes compatibility for Arduono Servo Library.
Uncomment to make it compatible.
Note that:
+ Timers: 0,2,3,4,5 WILL NOT WORK, and will
neither be accessible by Timer0,...
*/
// #define USING_SERVO_LIB true
#ifdef USING_SERVO_LIB
#warning "HEY! You have set flag USING_SERVO_LIB. Timer0, 2,3,4 and 5 are not available"
#endif
#if defined TC2
#define NUM_TIMERS 9
#else
#define NUM_TIMERS 6
#endif
class DueTimer
{
protected:
// Represents the timer id (index for the array of Timer structs)
const unsigned short timer;
// Stores the object timer frequency
// (allows to access current timer period and frequency):
static double _frequency[NUM_TIMERS];
// Picks the best clock to lower the error
static uint8_t bestClock(double frequency, uint32_t& retRC);
// Make Interrupt handlers friends, so they can use callbacks
friend void TC0_Handler(void);
friend void TC1_Handler(void);
friend void TC2_Handler(void);
friend void TC3_Handler(void);
friend void TC4_Handler(void);
friend void TC5_Handler(void);
#if NUM_TIMERS > 6
friend void TC6_Handler(void);
friend void TC7_Handler(void);
friend void TC8_Handler(void);
#endif
static void (*callbacks[NUM_TIMERS])();
struct Timer
{
Tc *tc;
uint32_t channel;
IRQn_Type irq;
};
// Store timer configuration (static, as it's fixed for every object)
static const Timer Timers[NUM_TIMERS];
public:
static DueTimer getAvailable(void);
DueTimer(unsigned short _timer);
DueTimer& attachInterrupt(void (*isr)());
DueTimer& detachInterrupt(void);
DueTimer& start(double microseconds = -1);
DueTimer& stop(void);
DueTimer& setFrequency(double frequency);
DueTimer& setPeriod(double microseconds);
double getFrequency(void) const;
double getPeriod(void) const;
inline __attribute__((always_inline)) bool operator== (const DueTimer& rhs) const
{return timer == rhs.timer; };
inline __attribute__((always_inline)) bool operator!= (const DueTimer& rhs) const
{return timer != rhs.timer; };
};
// Just to call Timer.getAvailable instead of Timer::getAvailable() :
extern DueTimer Timer;
extern DueTimer Timer1;
// Fix for compatibility with Servo library
#ifndef USING_SERVO_LIB
extern DueTimer Timer0;
extern DueTimer Timer2;
extern DueTimer Timer3;
extern DueTimer Timer4;
extern DueTimer Timer5;
#endif
#if NUM_TIMERS > 6
extern DueTimer Timer6;
extern DueTimer Timer7;
extern DueTimer Timer8;
#endif
#endif
#else
#error Oops! Trying to include DueTimer on another device?
#endif

1
README.md Normal file
View File

@ -0,0 +1 @@
Dagon is Arduino Due firmware for controlling a four-channel DAC.

164
comm.cpp Normal file
View File

@ -0,0 +1,164 @@
#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();
}

43
dagon.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <stdarg.h>
#include "libscomp_amalg.h"
#define DACS 4
#define V_MAX 0x3FFFF
#define V0 0x1FFFF
struct dagon_ch {
bool used;
uint32_t volt;
uint32_t end, step;
};
struct dagon {
bool conn[DACS];
struct dagon_ch ch[DACS];
};
extern volatile struct dagon dg;
bool serial_vprintf(const char *fmt, va_list va);
bool serial_printf(const char *fmt, ...);
bool print_start(struct libscomp_line *l, const char *fmt, ...);
#define unused __attribute__((unused))
#define declhandler(nm) \
int dagon_##nm(struct libscomp_line *, void *p unused)
declhandler(con);
declhandler(idn);
declhandler(rmp);
declhandler(rst);
declhandler(set);
declhandler(vlt);
uint32_t spi_write(uint32_t v, size_t dac);
uint32_t spi_transfer(uint32_t v, size_t dac);
void spi_init();
void spi_reset();
#define spi_register(v) ((v) << 20)
#define spi_set(v,d) spi_write(((v) & 0x3FFFF) << 2 | spi_register(1), d)
#define spi_get(d) spi_transfer(spi_register(0b1001) << 20, d)

110
libscomp_amalg.c Normal file
View File

@ -0,0 +1,110 @@
#include "libscomp_amalg.h"
#ifdef LIBSCOMP_FREESTANDING
int strcmp(const char *s1, const char *s2) {
while (*s1 == *s2) {
if (!*s1)
break;
s1++;
s2++;
}
return *s1 - *s2;
}
#else
# include <string.h>
#endif
struct libscomp_cmd *hg_bsearch(const char *name,
const struct libscomp_cmd_store *cmds) {
size_t lower = 0, upper = cmds->len-1;
size_t i;
int rel;
while (upper >= lower) {
i = (upper - lower)/2 + lower;
rel = strcmp(name, cmds->arr[i].name);
/* x > cmds[i] */
if (rel > 0)
lower = i + 1;
/* x < cmds[i] */
else if (rel < 0)
upper = i - 1;
else
return &cmds->arr[i];
}
return NULL;
}
int libscomp_exec(const struct libscomp_cmd_store *cmds,
struct libscomp_line *ln, void *ptr) {
struct libscomp_cmd *cmd;
cmd = hg_bsearch(ln->buf[ln->name], cmds);
if (!cmd)
return LIBSCOMP_NOT_FOUND;
return cmd->exec(ln,ptr);
}
enum { LIBSCOMP_READ, LIBSCOMP_DISCARD };
void libscomp_reset(struct libscomp_input *in) {
in->state = LIBSCOMP_READ;
in->len = 0;
}
static enum libscomp_input_r libscomp_parse(
struct libscomp_input *in, struct libscomp_line *line) {
char *s = in->intbuf;
enum libscomp_input_r r = LIBSCOMP_ARG_OVERFLOW;
line->name = (*s == ':');
line->len = 0;
while (line->len < LIBSCOMP_MAXARG) {
line->buf[line->len++] = s;
for (; *s && *s != '\t'; s++);
if (!*s) {
r = LIBSCOMP_COMPLETE;
break;
} else {
*s = 0;
s++;
}
}
libscomp_reset(in);
return r;
}
enum libscomp_input_r libscomp_read(struct libscomp_input *in,
char **s,
struct libscomp_line *line) {
char c;
while ((c = **s)) {
(*s)++;
if (in->state == LIBSCOMP_DISCARD) {
if (c == '\n') {
libscomp_reset(in);
return LIBSCOMP_OVERFLOW;
}
} else {
switch (c) {
case '\n':
in->intbuf[in->len] = 0;
return libscomp_parse(in, line);
default:
in->intbuf[in->len++] = c;
if (in->len == LIBSCOMP_MAXBUF)
in->state = LIBSCOMP_DISCARD;
}
}
}
return LIBSCOMP_MORE;
}

65
libscomp_amalg.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define LIBSCOMP_VERSION "0.1.0"
#include <stddef.h>
#define LIBSCOMP_MAXARG 32
#define LIBSCOMP_MAXBUF 1024
struct libscomp_line {
size_t name;
char *buf[LIBSCOMP_MAXARG];
size_t len;
};
struct libscomp_input {
int state;
char intbuf[LIBSCOMP_MAXBUF];
size_t len;
};
enum libscomp_input_r {
LIBSCOMP_MORE,
LIBSCOMP_OVERFLOW,
LIBSCOMP_ARG_OVERFLOW,
LIBSCOMP_COMPLETE
};
void libscomp_reset(struct libscomp_input *);
enum libscomp_input_r libscomp_read(struct libscomp_input *,
char **,
struct libscomp_line *);
enum libscomp_cmd_r {
LIBSCOMP_NOT_FOUND = -1,
LIBSCOMP_CMD_OK = 0
};
struct libscomp_cmd {
const char *name;
int (*exec)(struct libscomp_line *, void *);
};
struct libscomp_cmd_store {
struct libscomp_cmd *arr;
size_t len;
};
#if defined(__STDC_VERSION__)
#define libscomp_mkcmds(...) {(struct libscomp_cmd []){__VA_ARGS__}, \
sizeof((struct libscomp_cmd []){__VA_ARGS__}) / \
sizeof((struct libscomp_cmd []){__VA_ARGS__}[0])}
#endif
int libscomp_exec(const struct libscomp_cmd_store *cmds,
struct libscomp_line *ln,
void *ptr
);
#ifdef __cplusplus
} // extern "C"
#endif

52
spi.cpp Normal file
View File

@ -0,0 +1,52 @@
#include <SPI.h>
#include "dagon.h"
static const uint32_t ctrlmsg = 0b10010 | (0b0010 << 20);
static const uint32_t readbit = 1 << 23;
static const int DACSS[DACS] = {4, 10, 12, 52};
uint32_t spi_write(uint32_t v, size_t dac) {
uint32_t r;
digitalWrite(DACSS[dac], LOW);
r = SPI.transfer(v >> 16 & 0xFF) << 16;
r |= SPI.transfer(v >> 8 & 0xFF) << 8;
r |= SPI.transfer(v & 0xFF);
digitalWrite(DACSS[dac], HIGH);
return v;
}
uint32_t spi_transfer(uint32_t v, size_t dac) {
spi_write(v, dac);
delayMicroseconds(1);
return spi_write(0, dac);
}
void spi_init() {
SPI.begin();
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
for (size_t i = 0; i < DACS; i++) {
dg.conn[i] = false;
dg.ch[i].used = false;
dg.ch[i].volt = V0;
for (size_t attempts = 0; attempts < 5; attempts++) {
uint32_t c;
pinMode(DACSS[i], OUTPUT);
digitalWrite(DACSS[i], HIGH);
spi_write(ctrlmsg, i);
c = spi_transfer(ctrlmsg | readbit, i);
if (ctrlmsg & 0xFF == c & 0xFF) {
dg.conn[i] = true;
continue;
}
}
}
}
void spi_reset() {
for (size_t i = 0; i < DACS; i++)
spi_write(i, 0b0100 | spi_register(0b0100));
spi_init();
}