Commit 28c1a6a7 authored by Jake Read's avatar Jake Read
Browse files

stepper with osape, stable with xyz

parent 48691d4e
[submodule "firmware/osape-smoothieroll-drop-stepper/src/osape"]
path = firmware/osape-smoothieroll-drop-stepper/src/osape
url = ssh://git@gitlab.cba.mit.edu:846/jakeread/osape.git
......@@ -58,7 +58,7 @@ Sheet="1"
Type="Board Editor"
Number=2
File="2020-06_ucbus-stepper-melted.brd"
View="20.0133 22.7202 31.9216 35.358"
View="15.3304 3.43831 50.8884 41.1746"
WireWidths=" 0.8 0.9 0.1 0.05 0.5 0 0.3 0.2032 0.1524"
PadDiameters=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 6.4516 0"
PadDrills=" 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.65 0.7 0.75 0.8 0.85 0.9 1 0.6"
......
#ifndef CONFIG_H_
#define CONFIG_H_
//#define UCBUS_IS_HEAD
#define UCBUS_IS_DROP
#endif
\ No newline at end of file
......@@ -18,7 +18,7 @@ is; no warranty is provided, and users accept all liability.
#include <arduino.h>
#include "indicators.h"
#include "utils/syserror.h"
#include "../osape/utils/syserror.h"
// scrape https://github.com/adafruit/ArduinoCore-samd/blob/master/cores/arduino/wiring_analog.c
// scrape https://github.com/adafruit/ArduinoCore-samd/blob/master/cores/arduino/startup.c (clock)
......
// DIPs
#include "dip_ucbus_config.h"
void dip_init(void){
// set direction in,
DIP_PORT.DIRCLR.reg = D_BM(D0_PIN) | D_BM(D1_PIN) | D_BM(D2_PIN) | D_BM(D3_PIN) | D_BM(D4_PIN) | D_BM(D5_PIN) | D_BM(D6_PIN) | D_BM(D7_PIN);
// enable in,
DIP_PORT.PINCFG[D0_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D1_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D2_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D3_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D4_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D5_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D6_PIN].bit.INEN = 1;
DIP_PORT.PINCFG[D7_PIN].bit.INEN = 1;
// enable pull,
DIP_PORT.PINCFG[D0_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D1_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D2_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D3_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D4_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D5_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D6_PIN].bit.PULLEN = 1;
DIP_PORT.PINCFG[D7_PIN].bit.PULLEN = 1;
// 'pull' references the value set in the 'out' register, so to pulldown:
DIP_PORT.OUTCLR.reg = D_BM(D0_PIN) | D_BM(D1_PIN) | D_BM(D2_PIN) | D_BM(D3_PIN) | D_BM(D4_PIN) | D_BM(D5_PIN) | D_BM(D6_PIN) | D_BM(D7_PIN);
}
uint8_t dip_read_lower_five(void){
uint32_t bits[5] = {0,0,0,0,0};
if(DIP_PORT.IN.reg & D_BM(D7_PIN)) { bits[0] = 1; }
if(DIP_PORT.IN.reg & D_BM(D6_PIN)) { bits[1] = 1; }
if(DIP_PORT.IN.reg & D_BM(D5_PIN)) { bits[2] = 1; }
if(DIP_PORT.IN.reg & D_BM(D4_PIN)) { bits[3] = 1; }
if(DIP_PORT.IN.reg & D_BM(D3_PIN)) { bits[4] = 1; }
/*
bits[0] = (DIP_PORT.IN.reg & D_BM(D7_PIN)) >> D7_PIN;
bits[1] = (DIP_PORT.IN.reg & D_BM(D6_PIN)) >> D6_PIN;
bits[2] = (DIP_PORT.IN.reg & D_BM(D5_PIN)) >> D5_PIN;
bits[3] = (DIP_PORT.IN.reg & D_BM(D4_PIN)) >> D4_PIN;
bits[4] = (DIP_PORT.IN.reg & D_BM(D3_PIN)) >> D3_PIN;
*/
uint32_t word = 0;
word = word | (bits[4] << 4) | (bits[3] << 3) | (bits[2] << 2) | (bits[1] << 1) | (bits[0] << 0);
return (uint8_t)word;
}
boolean dip_read_pin_0(void){
return DIP_PORT.IN.reg & D_BM(D0_PIN);
}
boolean dip_read_pin_1(void){
return DIP_PORT.IN.reg & D_BM(D1_PIN);
}
\ No newline at end of file
// DIP switch HAL macros
// pardon the mis-labeling: on board, and in the schem, these are 1-8,
// here they will be 0-7
// note: these are 'on' hi by default, from the factory.
// to set low, need to turn the internal pulldown on
#include <Arduino.h>
#define D0_PIN 5
#define D1_PIN 4
#define D2_PIN 3
#define D3_PIN 2
#define D4_PIN 1
#define D5_PIN 0
#define D6_PIN 31
#define D7_PIN 30
#define DIP_PORT PORT->Group[1]
#define D_BM(val) ((uint32_t)(1 << val))
void dip_init(void);
uint8_t dip_read_lower_five(void);
boolean dip_read_pin_0(void); // bus-head (hi) or bus-drop (lo)
boolean dip_read_pin_1(void); // if bus-drop, te-enable (hi) or no (lo)
\ No newline at end of file
......@@ -19,7 +19,7 @@ is; no warranty is provided, and users accept all liability.
#include "dacs.h"
#include "indicators.h"
#include "utils/syserror.h"
#include "../osape/utils/syserror.h"
// C_SCALE
// 1: DACs go 0->512 (of 4096, peak current is 1.6A at 4096): 0.2A
......
/*
osap/drivers/ucbus_head.cpp
beginnings of a uart-based clock / bus combo protocol
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2019
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the squidworks and ponyo
projects. Copyright is retained and must be preserved. The work is provided as
is; no warranty is provided, and users accept all liability.
*/
#include "ucbus_drop.h"
UCBus_Drop* UCBus_Drop::instance = 0;
UCBus_Drop* UCBus_Drop::getInstance(void){
if(instance == 0){
instance = new UCBus_Drop();
}
return instance;
}
// making this instance globally available, when built,
// recall extern declaration in .h
UCBus_Drop* ucBusDrop = UCBus_Drop::getInstance();
UCBus_Drop::UCBus_Drop(void) {}
// so, first thing, I've some confusion about what might be the best way (again) to implement
// multiple similar drivers. I did this before w/ the cobserialport, I might again want to
// do it, but by writing more static hardware drivers that another parent class (the bus) forwards to
// so the init codes / etc could all be verbatim with hardware registers, instead of this infinite list of defines
// yeah, this kind of stuff is morning work: focus, tracking little details. go to bed.
void UCBus_Drop::init(boolean useDipPick, uint8_t ID) {
dip_init();
if(useDipPick){
// set our id,
id = dip_read_lower_five();
} else {
id = ID;
}
if(id > 14){
id = 14;
}
// set driver output LO to start: tri-state
UBD_DE_PORT.DIRSET.reg = UBD_DE_BM;
UBD_DRIVER_DISABLE;
// set receiver output on, forever: LO to set on
UBD_RE_PORT.DIRSET.reg = UBD_RE_BM;
UBD_RE_PORT.OUTCLR.reg = UBD_RE_BM;
// termination resistor should be set only on one drop,
// or none and physically with a 'tail' cable, or something?
UBD_TE_PORT.DIRSET.reg = UBD_TE_BM;
if(dip_read_pin_1()){
UBD_TE_PORT.OUTCLR.reg = UBD_TE_BM;
} else {
UBD_TE_PORT.OUTSET.reg = UBD_TE_BM;
}
// rx pin setup
UBD_COMPORT.DIRCLR.reg = UBD_RXBM;
UBD_COMPORT.PINCFG[UBD_RXPIN].bit.PMUXEN = 1;
if(UBD_RXPIN % 2){
UBD_COMPORT.PMUX[UBD_RXPIN >> 1].reg |= PORT_PMUX_PMUXO(UBD_RXPERIPHERAL);
} else {
UBD_COMPORT.PMUX[UBD_RXPIN >> 1].reg |= PORT_PMUX_PMUXE(UBD_RXPERIPHERAL);
}
// tx
UBD_COMPORT.DIRCLR.reg = UBD_TXBM;
UBD_COMPORT.PINCFG[UBD_TXPIN].bit.PMUXEN = 1;
if(UBD_TXPIN % 2){
UBD_COMPORT.PMUX[UBD_TXPIN >> 1].reg |= PORT_PMUX_PMUXO(UBD_TXPERIPHERAL);
} else {
UBD_COMPORT.PMUX[UBD_TXPIN >> 1].reg |= PORT_PMUX_PMUXE(UBD_TXPERIPHERAL);
}
// ok, clocks, first line au manuel
// unmask clocks
MCLK->APBAMASK.bit.SERCOM1_ = 1;
GCLK->GENCTRL[UBD_GCLKNUM_PICK].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(UBD_GCLKNUM_PICK));
GCLK->PCHCTRL[UBD_SERCOM_CLK].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(UBD_GCLKNUM_PICK);
// then, sercom
while(UBD_SER_USART.SYNCBUSY.bit.ENABLE);
UBD_SER_USART.CTRLA.bit.ENABLE = 0;
while(UBD_SER_USART.SYNCBUSY.bit.SWRST);
UBD_SER_USART.CTRLA.bit.SWRST = 1;
while(UBD_SER_USART.SYNCBUSY.bit.SWRST);
while(UBD_SER_USART.SYNCBUSY.bit.SWRST || UBD_SER_USART.SYNCBUSY.bit.ENABLE);
// ok, well
UBD_SER_USART.CTRLA.reg = SERCOM_USART_CTRLA_MODE(1) | SERCOM_USART_CTRLA_DORD;
UBD_SER_USART.CTRLA.reg |= SERCOM_USART_CTRLA_RXPO(UBD_RXPO) | SERCOM_USART_CTRLA_TXPO(0);
UBD_SER_USART.CTRLA.reg |= SERCOM_USART_CTRLA_FORM(1); // enable even parity
while(UBD_SER_USART.SYNCBUSY.bit.CTRLB);
UBD_SER_USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_CHSIZE(0);
// enable interrupts
NVIC_EnableIRQ(SERCOM1_2_IRQn); // rx,
NVIC_EnableIRQ(SERCOM1_1_IRQn); // txc ?
NVIC_EnableIRQ(SERCOM1_0_IRQn); // tx,
// set baud
UBD_SER_USART.BAUD.reg = UBD_BAUD_VAL;
// and finally, a kickoff
while(UBD_SER_USART.SYNCBUSY.bit.ENABLE);
UBD_SER_USART.CTRLA.bit.ENABLE = 1;
// enable rx interrupt
UBD_SER_USART.INTENSET.bit.RXC = 1;
}
void SERCOM1_2_Handler(void){
//ERRLIGHT_TOGGLE;
ucBusDrop->rxISR();
}
void UCBus_Drop::rxISR(void){
// check parity bit,
uint16_t perr = UBD_SER_USART.STATUS.bit.PERR;
if(perr){
//DEBUGPIN_ON;
uint8_t clear = UBD_SER_USART.DATA.reg;
UBD_SER_USART.STATUS.bit.PERR = 1; // clear parity error
return;
}
// cleared by reading out, but we are blind feed forward atm
uint8_t data = UBD_SER_USART.DATA.reg;
// for now, just running the clk, on every 0th bit
if((data >> 7) == 0){ // timer bit, on every 0th bit, and beginning of word
inWord[0] = data;
timeTick ++;
timeBlink ++;
if(timeBlink >= blinkTime){
CLKLIGHT_TOGGLE;
timeBlink = 0;
}
onRxISR(); // on start of each word
} else { // end of word on every 0th bit
inWord[1] = data;
// now decouple,
inHeader = ((inWord[0] >> 1) & 0b00111000) | ((inWord[1] >> 4) & 0b00000111);
inByte = ((inWord[0] << 4) & 0b11110000) | (inWord[1] & 0b00001111);
// now check incoming data,
if((inHeader & 0b00110000) == 0b00100000){ // has-token, CHA
lastWordAHadToken = true;
if(inBufferALen != 0){ // in this case, we didn't read-out of the buffer in time,
inBufferALen = 0; // so we reset it, missing the last packet !
inBufferARp = 0;
}
inBufferA[inBufferARp] = inByte;
inBufferARp ++;
} else if ((inHeader & 0b00110000) == 0b00000000) { // no-token, CHA
if(lastWordAHadToken){
inBufferALen = inBufferARp - 1;
onPacketARx();
}
lastWordAHadToken = false;
} else if ((inHeader & 0b00110000) == 0b00110000) { // has-token, CHB
//DEBUG1PIN_ON;
lastWordBHadToken = true;
if(inBufferBLen != 0){
inBufferBLen = 0;
inBufferBRp = 0;
}
inBufferB[inBufferBRp] = inByte;
inBufferBRp ++;
} else if ((inHeader & 0b00110000) == 0b00010000) { // no-token, CHB
if(lastWordBHadToken){
inBufferBLen = inBufferARp - 1;
//onPacketBRx(); // b-channel handled in loop, yah
}
lastWordBHadToken = false;
}
// now check if outgoing tick is ours,
if((inHeader & dropIdMask) == id){
// our transmit
if(outBufferLen > 0){
outByte = outBuffer[outBufferRp];
outHeader = headerMask & (tokenWord | (id & 0b00011111));
} else {
outByte = 0;
outHeader = headerMask & (noTokenWord | (id & 0b00011111));
}
outWord[0] = 0b00000000 | ((outHeader << 1) & 0b01110000) | (outByte >> 4);
outWord[1] = 0b10000000 | ((outHeader << 4) & 0b01110000) | (outByte & 0b00001111);
// now transmit,
UBD_DRIVER_ENABLE;
UBD_SER_USART.DATA.reg = outWord[0];
UBD_SER_USART.INTENSET.bit.DRE = 1;
// now do this,
if(outBufferLen > 0){
outBufferRp ++;
if(outBufferRp >= outBufferLen){
outBufferRp = 0;
outBufferLen = 0;
}
}
} else if ((inHeader & dropIdMask) == UBD_CLOCKRESET){
// reset
timeTick = 0;
}
} // end 1th bit case,
// do every-tick stuff
}
void SERCOM1_0_Handler(void){
ucBusDrop->dreISR();
}
void UCBus_Drop::dreISR(void){
UBD_SER_USART.DATA.reg = outWord[1]; // tx the next word,
UBD_SER_USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; // clear this interrupt
UBD_SER_USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC; // now watch transmit complete
}
void SERCOM1_1_Handler(void){
ucBusDrop->txcISR();
}
void UCBus_Drop::txcISR(void){
UBD_SER_USART.INTFLAG.bit.TXC = 1; // clear the flag by writing 1
UBD_SER_USART.INTENCLR.reg = SERCOM_USART_INTENCLR_TXC; // turn off the interrupt
UBD_DRIVER_DISABLE; // turn off the driver,
//DEBUGPIN_TOGGLE;
}
// -------------------------------------------------------- ASYNC API
boolean UCBus_Drop::ctr_a(void){
if(inBufferALen > 0){
return true;
} else {
return false;
}
}
boolean UCBus_Drop::ctr_b(void){
if(inBufferBLen > 0){
return true;
} else {
return false;
}
}
size_t UCBus_Drop::read_a(uint8_t *dest){
if(!ctr_a()) return 0;
NVIC_DisableIRQ(SERCOM1_2_IRQn);
// copy out, irq disabled, TODO safety on missing an interrupt in this case?? clock jitter?
memcpy(dest, inBufferA, inBufferALen);
size_t len = inBufferALen;
inBufferALen = 0;
inBufferARp = 0;
NVIC_EnableIRQ(SERCOM1_2_IRQn);
return len;
}
size_t UCBus_Drop::read_b(uint8_t *dest){
if(!ctr_b()) return 0;
NVIC_DisableIRQ(SERCOM1_2_IRQn);
memcpy(dest, inBufferB, inBufferBLen);
size_t len = inBufferBLen;
inBufferBLen = 0;
inBufferBRp = 0;
NVIC_EnableIRQ(SERCOM1_2_IRQn);
return len;
}
boolean UCBus_Drop::cts(void){
if(outBufferLen > 0){
return false;
} else {
return true;
}
}
void UCBus_Drop::transmit(uint8_t *data, uint16_t len){
if(!cts()) return;
size_t encLen = cobsEncode(data, len, outBuffer);
outBufferLen = encLen;
outBufferRp = 0;
}
\ No newline at end of file
/*
osap/drivers/ucbus_head.h
beginnings of a uart-based clock / bus combo protocol
Jake Read at the Center for Bits and Atoms
(c) Massachusetts Institute of Technology 2019
This work may be reproduced, modified, distributed, performed, and
displayed for any purpose, but must acknowledge the squidworks and ponyo
projects. Copyright is retained and must be preserved. The work is provided as
is; no warranty is provided, and users accept all liability.
*/
#ifndef UCBUS_DROP_H_
#define UCBUS_DROP_H_
#include <arduino.h>
#include "indicators.h"
#include "dip_ucbus_config.h"
#include "peripheral_nums.h"
#include "utils/syserror.h"
#include "utils/cobs.h"
#define UBD_SER_USART SERCOM1->USART
#define UBD_SERCOM_CLK SERCOM1_GCLK_ID_CORE
#define UBD_GCLKNUM_PICK 7
#define UBD_COMPORT PORT->Group[0]
#define UBD_TXPIN 16 // x-0
#define UBD_TXBM (uint32_t)(1 << UBD_TXPIN)
#define UBD_RXPIN 18 // x-2
#define UBD_RXBM (uint32_t)(1 << UBD_RXPIN)
#define UBD_RXPO 2
#define UBD_TXPERIPHERAL PERIPHERAL_C
#define UBD_RXPERIPHERAL PERIPHERAL_C
// baud bb baud
// 63019 for a very safe 115200
// 54351 for a go-karting 512000
// 43690 for a trotting pace of 1MHz
// 21845 for the E30 2MHz
// 0 for max-speed 3MHz
#define UBD_BAUD_VAL 0
#define UBD_DE_PIN 16 // driver output enable: set HI to enable, LO to tri-state the driver
#define UBD_DE_BM (uint32_t)(1 << UBD_DE_PIN)
#define UBD_DE_PORT PORT->Group[1]
#define UBD_DRIVER_ENABLE UBD_DE_PORT.OUTSET.reg = UBD_DE_BM
#define UBD_DRIVER_DISABLE UBD_DE_PORT.OUTCLR.reg = UBD_DE_BM
#define UBD_RE_PIN 19 // receiver output enable, set LO to enable the RO, set HI to tri-state RO
#define UBD_RE_BM (uint32_t)(1 << UBD_RE_PIN)
#define UBD_RE_PORT PORT->Group[0]
#define UBD_TE_PIN 17 // termination enable, drive LO to enable to internal termination resistor, HI to disable
#define UBD_TE_BM (uint32_t)(1 << UBD_TE_PIN)
#define UBD_TE_PORT PORT->Group[0]
#define UBD_BUFSIZE 1024
#define UBD_CLOCKRESET 15
#define UB_AK_GOTOPOS 91
#define UB_AK_SETPOS 92
#define UB_AK_SETRPM 93
class UCBus_Drop {
private:
// singleton-ness
static UCBus_Drop* instance;
// timing,
volatile uint64_t timeBlink = 0;
uint16_t blinkTime = 10000;
// input buffer & word
volatile uint8_t inWord[2];
volatile uint8_t inHeader;
volatile uint8_t inByte;
volatile boolean lastWordAHadToken = false;
uint8_t inBufferA[UBD_BUFSIZE];
volatile uint16_t inBufferARp = 0;
volatile uint16_t inBufferALen = 0; // writes at terminal zero,
volatile boolean lastWordBHadToken = false;
uint8_t inBufferB[UBD_BUFSIZE];
volatile uint16_t inBufferBRp = 0;
volatile uint16_t inBufferBLen = 0;
// output,
volatile uint8_t outWord[2];
volatile uint8_t outHeader;
volatile uint8_t outByte;
uint8_t outBuffer[UBD_BUFSIZE];
volatile uint16_t outBufferRp = 0;
volatile uint16_t outBufferLen = 0;
const uint8_t headerMask = 0b00111111;
const uint8_t dropIdMask = 0b00001111;
const uint8_t tokenWord = 0b00100000;
const uint8_t noTokenWord = 0b00000000;
public:
UCBus_Drop();
static UCBus_Drop* getInstance(void);
// available time count,
volatile uint16_t timeTick = 0;
// isrs
void rxISR(void);
void dreISR(void);
void txcISR(void);
// handlers
void onRxISR(void);
void onPacketARx(void);
//void onPacketBRx(void);
// our physical bus address,
volatile uint8_t id = 0;
// the api, eh
void init(boolean useDipPick, uint8_t ID);
boolean ctr_a(void); // return true if RX complete / buffer ready
boolean ctr_b(void);
size_t read_a(uint8_t *dest); // ship les bytos
size_t read_b(uint8_t *dest);
boolean cts(void); // true if tx buffer empty,
void transmit(uint8_t *data, uint16_t len);
};
extern UCBus_Drop* ucBusDrop;
#endif
\ No newline at end of file
#include <Arduino.h>
#include "drivers/indicators.h"
#include "utils/cobs.h"
#include "osap/osap.h"
OSAP* osap = new OSAP("stepper motor drop");
#include "drivers/ucbus_drop.h"
#include "drivers/step_a4950.h"
#include "osape/osap/osap.h"
#include "osape/osap/vport_ucbus_drop.h"
union chunk_float32 {
uint8_t bytes[4];
float f;
};
union chunk_float64 {
uint8_t bytes[8];
double f;
};
// C_SCALE
// 1: DACs go 0->512 (of 4096, peak current is 1.6A at 4096): 0.2A
// 2: DACs go 0->1024,
// ...
// 8: DACs go full width
OSAP* osap = new OSAP("stepper motor drop");
VPort_UCBus_Drop* vPortUcBusDrop = new VPort_UCBus_Drop();
// clank cz:
// Z: Bus Drop 5, Axis Pick 2, Invert ?, SPU 1386.6666667
......@@ -32,13 +15,10 @@ union chunk_float64 {
// YR: Bus Drop 2, Invert false
// X: Bus Drop 3, Invert false
#define BUS_DROP 3
#define IS_LAST_DROP false
#define AXIS_PICK 0 // Z: 2, Y: 1, X: 0
#define AXIS_PICK 0 // E: 3Z: 2, Y: 1, X: 0
#define AXIS_INVERT false // Z: ?, YL: ?, YR: ?, X: ?
#define SPU 320.0F //1386.6666667F // always posiive! z: 3200, xy: 400, clank-cz: xy: 320
#define C_SCALE 0.3F // 0-1, floating
#define C_SCALE 0.4F // 0-1, floating
#define TICKS_PER_PACKET 20.0F