Compare commits
3 commits
71a82cfe2c
...
c1f87a11da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1f87a11da | ||
|
|
d76f6517db | ||
|
|
6d389a572b |
5 changed files with 736 additions and 37 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
*.d
|
||||
*.s
|
||||
*.eeprom
|
||||
*.hex
|
||||
*.map
|
||||
|
|
|
|||
18
Makefile
18
Makefile
|
|
@ -4,6 +4,7 @@ CPU_ECRIS_REMOTE=atxmega128a1
|
|||
CPU_lcd=atmega16
|
||||
CPU_sologse=atmega644
|
||||
CPU_chaos=atmega32m1
|
||||
CPU_leia=atmega32m1
|
||||
|
||||
pMCU-atmega16m1 = m16
|
||||
pMCU-atmega32m1 = m32m1
|
||||
|
|
@ -12,20 +13,19 @@ pMCU-atmega644 = m644
|
|||
|
||||
MCU = $(CPU_$*)
|
||||
|
||||
CC=avr-gcc -Wall -MMD -std=c99 -O3 -mmcu=$(MCU) \
|
||||
CC=avr-gcc -Wall -Wno-parentheses -MMD -std=c99 -O2 -mmcu=$(MCU) \
|
||||
-funsigned-char \
|
||||
-funsigned-bitfields \
|
||||
-fpack-struct \
|
||||
-fshort-enums \
|
||||
-fverbose-asm
|
||||
-fshort-enums
|
||||
|
||||
CFLAGS = $($*_CFLAGS) $(DEBUG)
|
||||
|
||||
%.s: %.c revision.h
|
||||
$(CC) -S $(CFLAGS) $<
|
||||
$(CC) -S $(CFLAGS) -fverbose-asm $<
|
||||
|
||||
%.o: %.c revision.h
|
||||
$(CC) -gstabs -c $(CFLAGS) $<
|
||||
$(CC) -g -c $(CFLAGS) $<
|
||||
|
||||
revision.h: .git
|
||||
git log --pretty='#define Revision 0x%h' --abbrev=8 HEAD^! > revision.h+
|
||||
|
|
@ -72,6 +72,12 @@ chaos.o: ads8688.h ltc1655.h spi_slave.h tick.h hvosc.h hvled.h
|
|||
chaos.s: ads8688.h ltc1655.h spi_slave.h tick.h hvosc.h hvled.h
|
||||
chaos.hex: chaos.eeprom
|
||||
|
||||
lfuse_leia=0xef
|
||||
leia_CFLAGS = -I.
|
||||
leia.o: spi_slave.h
|
||||
leia.s: spi_slave.h
|
||||
leia.hex: leia.eeprom
|
||||
|
||||
%.lfuse:
|
||||
$(AD) -B 5 -U lfuse:w:$(lfuse_$*):m
|
||||
|
||||
|
|
@ -81,7 +87,7 @@ chaos.hex: chaos.eeprom
|
|||
clean:
|
||||
rm -f *.hex *.o *.s *.map *.elf *.d
|
||||
|
||||
VPATH = .:LCD/lcd_slave_sync:sologse:chaos
|
||||
VPATH = .:LCD/lcd_slave_sync:sologse:chaos:leia
|
||||
lcd_CFLAGS = -I. -DDEBUG
|
||||
lcd.o: lcd.c spi_slave.h lcd_routines.h lcd_routines.c uart_atmega16.c
|
||||
|
||||
|
|
|
|||
|
|
@ -303,33 +303,6 @@ void hv_led_on()
|
|||
|
||||
char spi_was_busy;
|
||||
|
||||
static inline
|
||||
void spi_busy_init()
|
||||
{
|
||||
PCMSK2 |= 8; // PIND3 PCINT19
|
||||
while (!(PIND & 8));
|
||||
PCIFR = 4;
|
||||
}
|
||||
|
||||
static inline
|
||||
unsigned char spi_busy()
|
||||
{
|
||||
// Return true if the SSEL pin became low
|
||||
unsigned char f = PCIFR & 4;
|
||||
PCIFR = 4;
|
||||
spi_was_busy = f && !(PIND & 8);
|
||||
return spi_was_busy;
|
||||
}
|
||||
|
||||
static inline
|
||||
void clear_spi_busy()
|
||||
{
|
||||
const unsigned char *busy_msg = (const unsigned char *) "\xff\xff\xff" "EEY";
|
||||
if (spi_was_busy && !(PIND & 8))
|
||||
spi_slave_Tx(busy_msg, 6);
|
||||
spi_was_busy = 0;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void hv_safe()
|
||||
{
|
||||
|
|
@ -389,7 +362,7 @@ int main()
|
|||
}
|
||||
while (1) {
|
||||
sei();
|
||||
clear_spi_busy();
|
||||
clear_spi_busy("\xff\xff\xff" "EEY");
|
||||
unsigned char cmd[3];
|
||||
unsigned char resp[3];
|
||||
unsigned char c;
|
||||
|
|
|
|||
602
leia/leia.c
Normal file
602
leia/leia.c
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
// irena spi slave to drive two A5985 stepper motor controllers
|
||||
|
||||
// ATmega32M1
|
||||
//
|
||||
// SPI: slave, three bytes cmd, three bytes resp. main thread.
|
||||
// PCINT2: disable interrupts when SSEL toggles. Allows the SPI to work undisturbed.
|
||||
// TIMER1: Motor step period, 23µs resolution, 1.5s range
|
||||
// OCR1A: interrupt: assert STEP
|
||||
// OCR2B: interrupt: deassert STEP, RESET
|
||||
// ADC: Three NTCs ADC8,9,10, Iprim ADC3, internals.
|
||||
// Interrupt stores conversionresults and advances channels.
|
||||
// DAC: Motor current reference.
|
||||
// TIMER0: ADC conversion cadence, 92µs resolution, range 23ms range.
|
||||
// Interrupt: DAC ramp ticks
|
||||
// PB[3:6] ENABLE outputs.
|
||||
// PC[0:3] DIR, MSn outputs, step direction and microstepping
|
||||
// PD[2,4] STEP outputs
|
||||
// PD[0,1] inputs: end switch status
|
||||
// PD[5,7] inputs: motor controller fault
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "revision.h"
|
||||
const char revision[] = Id;
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#define SPI_Rx_SEI
|
||||
#include "spi_slave.h"
|
||||
|
||||
#define upcase(c) (!((c)&0x20))
|
||||
|
||||
#define LIMIT_PORT PIND
|
||||
#define STEP_PORT PORTD
|
||||
#define DIR_PORT PORTC
|
||||
#define RESET_PORT PORTB
|
||||
|
||||
// PORT D
|
||||
#define FAULT1 (1<<7)
|
||||
#define FAULT2 (1<<5)
|
||||
#define LIMIT1 (1<<0)
|
||||
#define LIMIT2 (1<<1)
|
||||
#define STEP1 (1<<4)
|
||||
#define STEP2 (1<<2)
|
||||
// PORT B
|
||||
#define MISO (1<<0)
|
||||
#define RESET (1<<3)
|
||||
#define ENABLE (1<<4)
|
||||
#define LEDON (1<<5)
|
||||
#define SLEEP (1<<6)
|
||||
// PORTC
|
||||
#define DIR (1<<0)
|
||||
#define MS1 (1<<3)
|
||||
#define MS2 (1<<2)
|
||||
#define MS3 (1<<1)
|
||||
|
||||
#define MAGIC 0x1e1a
|
||||
#define VERSION 1
|
||||
|
||||
struct conf {
|
||||
unsigned int magic; // 0
|
||||
unsigned char version; // 2
|
||||
uint8_t flags; // 3
|
||||
uint16_t period; // 4
|
||||
uint16_t slen; // 6
|
||||
uint8_t lmask; // 8
|
||||
uint8_t lval; // 9
|
||||
uint8_t step; // 10
|
||||
uint8_t enable; // 11
|
||||
uint8_t reset; // 12
|
||||
uint8_t dir; // 13
|
||||
uint16_t n_steps; // 14
|
||||
uint16_t dac; // 16
|
||||
uint16_t dac_ramp; // 18
|
||||
uint16_t dac_step; // 20
|
||||
uint8_t adc_idx; // 22
|
||||
uint8_t adc_incr; // 23
|
||||
uint8_t adc_period; // 24
|
||||
uint8_t pad[7]; // 25
|
||||
uint8_t adc_ch[16]; // 32
|
||||
uint8_t tail[16]; // 48
|
||||
} conf; // 64
|
||||
|
||||
enum {
|
||||
FLAG_WDT = 1,
|
||||
};
|
||||
|
||||
#define STEP_RESOLUTION 23148L // ns
|
||||
#define STEP_NS(ns) (((ns)+STEP_RESOLUTION/2)/STEP_RESOLUTION)
|
||||
#define TICK_RESOLUTION 92593L // ns
|
||||
#define TICK_NS(ns) (((ns)+TICK_RESOLUTION/2)/TICK_RESOLUTION)
|
||||
|
||||
__attribute__((section(".eeprom")))
|
||||
const struct conf runcon = {
|
||||
.magic = MAGIC,
|
||||
.version = VERSION,
|
||||
.period = STEP_NS(100000000),
|
||||
.slen = STEP_NS(25000),
|
||||
.lmask = LIMIT1 | LIMIT2 | FAULT1 | FAULT2,
|
||||
.lval = LIMIT1 | LIMIT2 | FAULT1 | FAULT2,
|
||||
.enable = SLEEP | RESET,
|
||||
.reset = RESET,
|
||||
.adc_ch = {3, 8, 9, 10, 11+0x80, 12+0x80, 17, 18,},
|
||||
.adc_incr = 8, // two conversions per channel
|
||||
.adc_period = TICK_NS(1000),
|
||||
.dac_step = 0x20,
|
||||
};
|
||||
|
||||
|
||||
void stepper_init()
|
||||
{
|
||||
TIMSK1 = 0;
|
||||
// 16-bit Timer 1 PWM by OCR1A, prescaled by 256
|
||||
TCCR1A = 1<<WGM10 | 1<<WGM11 ;
|
||||
TCCR1B = 1<<WGM12 | 1<<WGM13 | 4<<CS10;
|
||||
OCR1AH = conf.period >> 8;
|
||||
OCR1AL = conf.period;
|
||||
OCR1BH = conf.slen >> 8;
|
||||
OCR1BL = conf.slen;
|
||||
RESET_PORT = conf.enable & ~conf.reset;
|
||||
DIR_PORT = conf.dir;
|
||||
STEP_PORT = 0;
|
||||
DDRB = MISO | RESET | ENABLE | SLEEP | LEDON;
|
||||
DDRD = STEP1 | STEP2;
|
||||
DDRC = DIR | MS1 | MS2 | MS3;
|
||||
}
|
||||
|
||||
static inline
|
||||
void stepper_start(uint8_t reset)
|
||||
{
|
||||
RESET_PORT = conf.enable & ~reset;
|
||||
DIR_PORT = conf.dir;
|
||||
TCNT1H = 0;
|
||||
TCNT1L = 0;
|
||||
TIMSK1 = (1<<OCIE1B);
|
||||
}
|
||||
|
||||
static inline
|
||||
void stepper_stop()
|
||||
{
|
||||
TIMSK1 = 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
char stepper_status()
|
||||
{
|
||||
return TIMSK1;
|
||||
}
|
||||
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
if (!conf.n_steps || (LIMIT_PORT & conf.lmask) != conf.lval) {
|
||||
STEP_PORT = 0;
|
||||
TIMSK1 = 0;
|
||||
return;
|
||||
}
|
||||
conf.n_steps--;
|
||||
STEP_PORT = conf.step;
|
||||
}
|
||||
|
||||
ISR(TIMER1_COMPB_vect)
|
||||
{
|
||||
STEP_PORT = 0;
|
||||
RESET_PORT = conf.enable;
|
||||
TIMSK1 = TIFR1 = (1<<OCIE1B) | (1<<OCIE1A);
|
||||
}
|
||||
|
||||
ISR(TIMER0_COMPB_vect)
|
||||
{
|
||||
uint16_t d = conf.dac;
|
||||
if (d == conf.dac_ramp) {
|
||||
TIMSK0 = 0;
|
||||
return;
|
||||
}
|
||||
uint16_t s = conf.dac_step;
|
||||
if (d > conf.dac_ramp)
|
||||
s = -s;
|
||||
d += s;
|
||||
DACL = d;
|
||||
DACH = d>>8;
|
||||
conf.dac = d;
|
||||
}
|
||||
|
||||
static
|
||||
void dac_set(uint16_t d)
|
||||
{
|
||||
TIMSK0 = 0;
|
||||
DACL = d;
|
||||
DACH = d>>8;
|
||||
DACON = 1<<DALA | 1<<DAEN | 1<<DAOE;
|
||||
conf.dac = conf.dac_ramp = d;
|
||||
}
|
||||
|
||||
static
|
||||
void dac_ramp(uint16_t d)
|
||||
{
|
||||
uint16_t s = conf.dac_step;
|
||||
if (!s) {
|
||||
dac_set(d);
|
||||
return;
|
||||
}
|
||||
s = ~(s & (s-1));
|
||||
d &= s;
|
||||
TIMSK0 = 0;
|
||||
DACON = 1<<DALA | 1<<DAEN | 1<<DAOE;
|
||||
conf.dac = DAC & s;
|
||||
conf.dac_ramp = d;
|
||||
if (d != conf.dac)
|
||||
TIMSK0 = 1<<OCIE0B;
|
||||
}
|
||||
|
||||
static
|
||||
void adc_init()
|
||||
{
|
||||
TCCR0A = 2 << WGM00;
|
||||
TCCR0B = 5 << CS00;
|
||||
DIDR0 = 1<<ADC3D;
|
||||
DIDR1 = 1<<ADC8D | 1<<ADC9D | 1<<ADC10D;
|
||||
ADCSRB = 1<<AREFEN | 2<<ADTS0;
|
||||
ADCSRA = 1<<ADEN | 1<<ADIF | 6<<ADPS0;
|
||||
}
|
||||
|
||||
static
|
||||
void adc_start(uint8_t i)
|
||||
{
|
||||
i &= 15;
|
||||
conf.adc_idx = i<<4;
|
||||
OCR0A = OCR0B = conf.adc_period;
|
||||
if (!conf.adc_ch[i])
|
||||
return;
|
||||
ADMUX = 1<<REFS0 | 1<<ADLAR | conf.adc_ch[i];
|
||||
ADCSRA = 1<<ADSC | 1<<ADATE | 1<<ADIF | 1<<ADIE | 6<<ADPS0;
|
||||
}
|
||||
|
||||
static inline
|
||||
void adc_stop()
|
||||
{
|
||||
ADCSRA = 1<<ADIF | 6<<ADPS0;
|
||||
}
|
||||
|
||||
uint16_t adc[16];
|
||||
|
||||
ISR(ADC_vect)
|
||||
{
|
||||
uint8_t idx = conf.adc_idx;
|
||||
uint8_t i = idx >> 4;
|
||||
uint16_t a = ADCL << 8;
|
||||
a |= ADCH & 0xc0 | ADMUX & 0x1f | (ADMUX>>(REFS1-5)) & 0x20;
|
||||
adc[i] = a;
|
||||
idx += conf.adc_incr;
|
||||
i = idx >> 4;
|
||||
if (!conf.adc_ch[i])
|
||||
i = idx = 0;
|
||||
if (!conf.adc_ch[i])
|
||||
adc_stop();
|
||||
else
|
||||
ADMUX = 1<<REFS0 | 1<<ADLAR | conf.adc_ch[i];
|
||||
conf.adc_idx = idx;
|
||||
}
|
||||
|
||||
static void conf_init()
|
||||
{
|
||||
cli();
|
||||
adc_init();
|
||||
adc_start(conf.adc_idx>>4);
|
||||
dac_set(conf.dac_ramp);
|
||||
stepper_init();
|
||||
}
|
||||
|
||||
// Configuration in EEPROM
|
||||
|
||||
// Write EEPROM in interrupt.
|
||||
|
||||
static uint8_t eewr_n; // number of bytes to write
|
||||
static uint16_t eewr_a; // EEPROM conf base address
|
||||
|
||||
ISR(EE_READY_vect)
|
||||
{
|
||||
uint8_t n = eewr_n;
|
||||
while (n) {
|
||||
n--;
|
||||
EEAR = eewr_a + n;
|
||||
EECR |= 1<<EERE;
|
||||
uint8_t d = ((uint8_t*)&conf)[n];
|
||||
if (EEDR == d)
|
||||
continue;
|
||||
EEDR = d;
|
||||
EECR |= 1<<EEMWE;
|
||||
EECR |= 1<<EEWE;
|
||||
return;
|
||||
}
|
||||
EECR = 0; // clear EERIE
|
||||
return;
|
||||
}
|
||||
|
||||
static inline
|
||||
uint8_t eeprom_save(uint16_t a)
|
||||
{
|
||||
// eeprom_update_block(&conf, (void*)a, sizeof(conf));
|
||||
|
||||
if (EECR & 1<<EERIE)
|
||||
return 1;
|
||||
eewr_a = a;
|
||||
eewr_n = sizeof(conf);
|
||||
EECR = 1<<EERIE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
uint8_t eeprom_load(uint16_t a)
|
||||
{
|
||||
// eeprom_read_block(&conf, (void*)a, sizeof(conf));
|
||||
cli();
|
||||
if (EECR & 1<<EEWE) {
|
||||
sei();
|
||||
return 1;
|
||||
}
|
||||
uint8_t n = sizeof(conf);
|
||||
uint8_t *c = (uint8_t*)&conf;
|
||||
while (n--) {
|
||||
EEAR = a++;
|
||||
EECR |= 1<<EERE;
|
||||
*c++ = EEDR;
|
||||
}
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
void reg16(uint16_t *v, unsigned char *r, const unsigned char *c)
|
||||
{
|
||||
int2frame(*v, r+1);
|
||||
if (upcase(c[0]))
|
||||
*v = frame2int(c+1);
|
||||
}
|
||||
static inline
|
||||
void reg8(uint8_t *v, unsigned char *r, const unsigned char *c)
|
||||
{
|
||||
r[1] = *v;
|
||||
if (upcase(c[0]))
|
||||
*v = c[1];
|
||||
}
|
||||
|
||||
static inline
|
||||
void reg8f(uint8_t *v, unsigned char *r, const unsigned char *c)
|
||||
{
|
||||
r[1] = *v;
|
||||
*v = *v & ~c[2] | c[1];
|
||||
}
|
||||
|
||||
static inline
|
||||
void reg88(uint8_t *v1, uint8_t *v2, unsigned char *r, const unsigned char *c)
|
||||
{
|
||||
r[1] = *v1;
|
||||
r[2] = *v2;
|
||||
if (upcase(c[0])) {
|
||||
*v1 = c[1];
|
||||
*v2 = c[2];
|
||||
}
|
||||
}
|
||||
|
||||
// '0': stop dir nsteps → n_steps
|
||||
// '1': step1 dir nsteps → n_steps
|
||||
// '2': step2 dir nsteps → n_steps
|
||||
// 'a': Adc val, what
|
||||
// 'l': sLen val (STEP pulse duration)
|
||||
// 'm': limit Mask, val (LIMIT_PORT match)
|
||||
// 'o': enable set clear (RESET_PORT value)
|
||||
// 'p': ramP stepsize
|
||||
// 'q': period val (23.148µs)
|
||||
// 'r': Reset mask what (RESET_PORT mask)
|
||||
// 's': Steps n_steps → n_steps
|
||||
// 'x': conf idx, val
|
||||
// 'y': poke a, val
|
||||
// 'z': eeprom a
|
||||
|
||||
//
|
||||
// read/write:
|
||||
// Return the old value.
|
||||
// Update the value when the command is upper case.
|
||||
//
|
||||
// STEPPING:
|
||||
//
|
||||
// 'q': period val
|
||||
// read/write stepping period, in units of 23.148µs
|
||||
// reinitialize stepper when written
|
||||
// 'l': slen val
|
||||
// STEP pulse duration, in units of 23.148µs
|
||||
// reinitialize stepper when written
|
||||
// 'm': limit Mask, val
|
||||
// read/write the mask and val bytes
|
||||
// stepping stops when PIND & mask != val
|
||||
// FAULT: goes low when the motor controller is unhappy
|
||||
// LIMIT: goes low when the endswitch light bridge is blocked.
|
||||
// 'o': enable set, clear
|
||||
// set and clear bits in `enable`
|
||||
// `enable` is written to PORTD when stepping
|
||||
// `O`: write to PORTD immediately.
|
||||
// 'r': Reset mask, what
|
||||
// read/write the RESET pin mask.
|
||||
// what != 0: stop stepping
|
||||
// what[7]: reinitialize the stepper.
|
||||
// what[0]: issure a reset.
|
||||
// '0': stop dir, exp, nsteps → n_steps
|
||||
// '1': step1 dir, exp, nsteps → n_steps
|
||||
// '2': step2 dir, exp, nsteps → n_steps
|
||||
// Stop, start stepping
|
||||
// n_steps = nsteps << exp, when nonzero
|
||||
// dir,ms = dir
|
||||
// return `n_steps`
|
||||
// 's': nSteps nsteps
|
||||
// read/write n_steps.
|
||||
//
|
||||
// ADC
|
||||
//
|
||||
// 'a': Adc val, idx, what, start
|
||||
// idx: adc config index
|
||||
// start: 'a': stop ADC, 'A': start adc @ idx
|
||||
// what:
|
||||
// 1: read/write channel[idx] mux
|
||||
// 2: read/write ch_idx
|
||||
// 3: read/write idx increment
|
||||
// 4: read/write tick TIMER0 period
|
||||
// The adc_idx is 8 bits, with the 4 MSB being the index into the adc_config.
|
||||
// adc_incr advances adc_idx, but the channel mux changes only when the MSB change,
|
||||
// so that each mux may be get sampled multiple times.
|
||||
// When a channel mux config is empty, restart at idx 0.
|
||||
// When idx 0 is empty, stop.
|
||||
//
|
||||
// DAC
|
||||
//
|
||||
// 'd': Dac val
|
||||
// read/write the DAC value
|
||||
// When dac_step is not zero, ramp at TIMER0 cadence.
|
||||
// 'p': ramP dac_step
|
||||
// read/write dac_step
|
||||
//
|
||||
// MEMORY
|
||||
//
|
||||
// 'x': conf a, val
|
||||
// read/write config byte
|
||||
// 'z': eeprom a
|
||||
// write conf to eeprom at address a*4
|
||||
// 'Z': eeprom what, a
|
||||
// what=='e' or 'E": read eeprom from address a*4 to conf
|
||||
// what=='F': ignore magic/version mismatch
|
||||
// what=='E' or 'F': reinitialize with conf.
|
||||
// 'y': peek a
|
||||
// 'Y': poke a, v
|
||||
|
||||
// The mainloop runs with interrupts disabled. Interrupts are enabled
|
||||
// while waiting for a command on the SPI. As soon as SSEL toggles,
|
||||
// interrupts are disabled, so that the SPI reciever can respond to
|
||||
// all bytes in time. All commands execute fast, the master µC shall
|
||||
// not time out while waiting for the reponse. The stepping timer is
|
||||
// restarted after command execution, when any steps are commanded.
|
||||
//
|
||||
// Interrupts
|
||||
// PCINT2: disable global interrupts
|
||||
// TIMER1_COMPA: assert STEP
|
||||
// TIMER1_COMPB: deassert STEP, RESET
|
||||
// TIMER0_COMPB: ramp DAC
|
||||
// ADC: capture conversion results, advance MUX config
|
||||
// EE_READY: write conf bytes to EEPROM
|
||||
|
||||
int main()
|
||||
{
|
||||
MCUSR = 0;
|
||||
|
||||
// turn of unused IO modules
|
||||
PRR = (1<<PRPSC)|(1<<PRCAN)|(1<<PRLIN);
|
||||
|
||||
spi_slave_init_sei();
|
||||
DDRB = MISO;
|
||||
eeprom_load(0);
|
||||
adc_init();
|
||||
|
||||
if (conf.magic == MAGIC && conf.version == VERSION) {
|
||||
conf_init();
|
||||
}
|
||||
while (1) {
|
||||
unsigned char cmd[3];
|
||||
unsigned char resp[3];
|
||||
if (!stepper_status() && conf.step && conf.n_steps)
|
||||
stepper_start(0);
|
||||
if (spi_slave_Rx(cmd, 3)) {
|
||||
spi_slave_Tx((const unsigned char*)"\xff\xff\xff" "EEY", 6);
|
||||
continue;
|
||||
}
|
||||
stepper_stop();
|
||||
char up = upcase(cmd[0]);
|
||||
if (up)
|
||||
conf.step = 0;
|
||||
resp[0] = cmd[0];
|
||||
uint8_t i;
|
||||
switch (cmd[0] | 0x20) {
|
||||
default:
|
||||
resp[0] = 'E';
|
||||
resp[1] = 'E';
|
||||
resp[2] = cmd[0];
|
||||
break;
|
||||
case 'm': reg88(&conf.lmask, &conf.lval, resp, cmd); break;
|
||||
case 'q': reg16(&conf.period, resp, cmd);
|
||||
if (0) case 'l': reg16(&conf.slen, resp, cmd);
|
||||
if (up)
|
||||
stepper_init();
|
||||
break;
|
||||
case 'o': reg8f(&conf.enable, resp, cmd);
|
||||
if (up)
|
||||
RESET_PORT = conf.enable;
|
||||
break;
|
||||
case '0': conf.step = 0;
|
||||
if (0) case '1': conf.step = STEP1;
|
||||
if (0) case '2': conf.step = STEP2;
|
||||
conf.dir = cmd[1] & 0xf;
|
||||
if (cmd[2])
|
||||
conf.n_steps = cmd[2] << ((cmd[1]>>4) & 7);
|
||||
// fall through, return n_steps
|
||||
case 's': reg16(&conf.n_steps, resp, cmd); break;
|
||||
case 'r': reg8(&conf.reset, resp, cmd);
|
||||
resp[2] = cmd[2];
|
||||
if (cmd[2]) {
|
||||
conf.step = 0;
|
||||
if (cmd[2] & 0x80)
|
||||
stepper_init();
|
||||
if (cmd[2] & 1)
|
||||
stepper_start(conf.reset);
|
||||
}
|
||||
break;
|
||||
case 'd': reg16(&conf.dac, resp, cmd);
|
||||
dac_ramp(conf.dac);
|
||||
break;
|
||||
case 'p': reg16(&conf.dac_step, resp, cmd); break;
|
||||
case 'a':
|
||||
resp[2] = cmd[2];
|
||||
i = cmd[2] & 15;
|
||||
switch ((cmd[2]>>4) & 7) {
|
||||
default: int2frame(adc[i], resp+1);
|
||||
if (up) adc[i] = 0;
|
||||
break;
|
||||
case 1: reg8(conf.adc_ch+i, resp, cmd); break;
|
||||
case 2: reg8(&conf.adc_idx, resp, cmd); break;
|
||||
case 3: reg8(&conf.adc_incr, resp, cmd); break;
|
||||
case 4: reg8(&conf.adc_period, resp, cmd); break;
|
||||
}
|
||||
if (cmd[2] & 0x80)
|
||||
if (up)
|
||||
adc_start(i);
|
||||
else
|
||||
adc_stop();
|
||||
break;
|
||||
case 'z': // Load/Save conf
|
||||
resp[1] = cmd[1];
|
||||
resp[2] = sizeof(conf);
|
||||
if (!up) {
|
||||
if (eeprom_save(cmd[2]*4)) {
|
||||
resp[0] = 'E';
|
||||
resp[1] = 'W';
|
||||
resp[2] = 'Y';
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((cmd[1] | 0x20) == 'e') {
|
||||
if (eeprom_load(cmd[2]*4)) {
|
||||
resp[0] = 'E';
|
||||
resp[1] = 'R';
|
||||
resp[2] = 'Y';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conf.magic != MAGIC || conf.version != VERSION) {
|
||||
resp[0] = 'E';
|
||||
resp[2] = conf.version;
|
||||
if (cmd[1] != 'F')
|
||||
break;
|
||||
}
|
||||
if (cmd[1] && upcase(cmd[1]))
|
||||
conf_init();
|
||||
break;
|
||||
case 'x': // conf Byte
|
||||
resp[2] = cmd[1];
|
||||
if (cmd[1] >= sizeof(conf)) {
|
||||
resp[0] = 'E';
|
||||
resp[1] = sizeof(conf);
|
||||
break;
|
||||
}
|
||||
resp[1] = ((unsigned char *)(&conf))[cmd[1]];
|
||||
if (up)
|
||||
((unsigned char *)(&conf))[cmd[1]] = cmd[2];
|
||||
break;
|
||||
case 'y': // peek, poke
|
||||
resp[2] = cmd[1];
|
||||
resp[1] = *(unsigned char *)(cmd[1]+0);
|
||||
if (up)
|
||||
*(unsigned char *)(cmd[1]+0) = cmd[2];
|
||||
break;
|
||||
}
|
||||
spi_slave_Tx(resp, 3);
|
||||
}
|
||||
}
|
||||
123
spi_slave.h
123
spi_slave.h
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#define SPSR_IF (1<<SPIF)
|
||||
|
||||
static inline void spi_slave_init()
|
||||
|
|
@ -14,7 +15,8 @@ static inline unsigned char spi_slave_Rx_status()
|
|||
return SPSR & SPSR_IF && !(SPDR & 0x80);
|
||||
}
|
||||
|
||||
static inline void spi_slave_Rx(unsigned char d[], unsigned char n)
|
||||
#define spi_slave_Rx spi_slave_Rx_n
|
||||
static inline char spi_slave_Rx_n(unsigned char d[], unsigned char n)
|
||||
{
|
||||
SPSR;
|
||||
SPDR;
|
||||
|
|
@ -30,6 +32,29 @@ static inline void spi_slave_Rx(unsigned char d[], unsigned char n)
|
|||
SPDR = 0xff;
|
||||
*d++ = SPDR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline char spi_slave_Rx_cli(unsigned char d[], unsigned char n)
|
||||
{
|
||||
SPSR;
|
||||
SPDR;
|
||||
register unsigned char b;
|
||||
do {
|
||||
while (!(SPSR & SPSR_IF))
|
||||
if (!(PIND & 8))
|
||||
cli();
|
||||
SPDR = 0xff;
|
||||
b = SPDR;
|
||||
} while (b&0x80);
|
||||
cli();
|
||||
*d++ = b;
|
||||
while (--n) {
|
||||
while (!(SPSR & SPSR_IF));
|
||||
SPDR = 0xff;
|
||||
*d++ = SPDR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern volatile unsigned char wdt_tick;
|
||||
|
|
@ -53,7 +78,8 @@ static inline char spi_slave_Rx_wdt(unsigned char d[], unsigned char n)
|
|||
return wdt_tick;
|
||||
}
|
||||
|
||||
static inline void spi_slave_Tx(const unsigned char d[], unsigned char n)
|
||||
#define spi_slave_Tx spi_slave_Tx_n
|
||||
static inline void spi_slave_Tx_n(const unsigned char d[], unsigned char n)
|
||||
{
|
||||
SPSR;
|
||||
SPDR;
|
||||
|
|
@ -70,9 +96,100 @@ static inline void int2frame(unsigned int i, unsigned char *f)
|
|||
f[0] = i;
|
||||
f[1] = i>>8;
|
||||
}
|
||||
static inline unsigned int frame2int(unsigned char *f)
|
||||
static inline unsigned int frame2int(const unsigned char *f)
|
||||
{
|
||||
return f[1]<<8 | f[0];
|
||||
}
|
||||
#define INT2FRAME(i) ((unsigned char []){i, i>>8})
|
||||
|
||||
extern char spi_was_busy;
|
||||
|
||||
static inline
|
||||
void spi_busy_init()
|
||||
{
|
||||
PCMSK2 |= 8; // PIND3 PCINT19
|
||||
while (!(PIND & 8));
|
||||
PCIFR = 4;
|
||||
}
|
||||
|
||||
static inline
|
||||
unsigned char spi_busy()
|
||||
{
|
||||
// Return true if the SSEL pin became low
|
||||
unsigned char f = PCIFR & 4;
|
||||
PCIFR = 4;
|
||||
spi_was_busy = f && !(PIND & 8);
|
||||
return spi_was_busy;
|
||||
}
|
||||
|
||||
static inline
|
||||
void clear_spi_busy(const char *busy_msg)
|
||||
{
|
||||
if (spi_was_busy && !(PIND & 8))
|
||||
spi_slave_Tx((const unsigned char *)busy_msg, 6);
|
||||
spi_was_busy = 0;
|
||||
}
|
||||
|
||||
#ifdef SPI_Rx_SEI
|
||||
|
||||
#ifndef PIN_SSEL
|
||||
// ATmega32M1
|
||||
|
||||
# define PIN_SSEL (PIND & 8)
|
||||
# define PCIF_SSEL 4
|
||||
|
||||
// TODO: avoid output in a header file?
|
||||
|
||||
ISR(PCINT2_vect, ISR_NAKED)
|
||||
{
|
||||
// return without sei()
|
||||
__asm__ ("ret");
|
||||
}
|
||||
|
||||
static inline
|
||||
void spi_slave_init_sei()
|
||||
{
|
||||
spi_slave_init();
|
||||
PCMSK2 |= 8;
|
||||
PCICR |= PCIF_SSEL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef spi_slave_Rx
|
||||
#define spi_slave_Rx spi_slave_Rx_sei
|
||||
static inline
|
||||
char spi_slave_Rx_sei(unsigned char d[], unsigned char n)
|
||||
{
|
||||
if (!PIN_SSEL)
|
||||
return 1;
|
||||
if (PCIFR & PCIF_SSEL)
|
||||
return 1;
|
||||
PCIFR = PCIF_SSEL;
|
||||
sei();
|
||||
spi_slave_Rx_n(d, n);
|
||||
cli();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef spi_slave_Tx
|
||||
#define spi_slave_Tx spi_slave_Tx_sei
|
||||
static inline
|
||||
char spi_slave_Tx_sei(const unsigned char d[], unsigned char n)
|
||||
{
|
||||
SPSR;
|
||||
SPDR;
|
||||
while (n) {
|
||||
register unsigned char b = *d++;
|
||||
while (!(SPSR & SPSR_IF))
|
||||
if (!PIN_SSEL)
|
||||
return 1;
|
||||
SPDR = b;
|
||||
n--;
|
||||
}
|
||||
while (!PIN_SSEL);
|
||||
PCIFR = PCIF_SSEL;
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue