Commit 48f117e9 authored by Jake Read's avatar Jake Read

rm firmware

parent 771b21af
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
# < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
# < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#
#
# Template #1: General project. Test it using existing `platformio.ini`.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio run
#
# Template #2: The project is intended to be used as a library with examples.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# env:
# - PLATFORMIO_CI_SRC=path/to/test/file.c
# - PLATFORMIO_CI_SRC=examples/file.ino
# - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}
{
"files.associations": {
"unordered_map": "cpp"
}
}
\ No newline at end of file
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:adafruit_feather_m4]
platform = atmelsam
board = adafruit_feather_m4
framework = arduino
// for the new one! with the DIP switch!
#define CLKLIGHT_PIN 30
#define CLKLIGHT_PORT PORT->Group[1]
#define ERRLIGHT_PIN 27
#define ERRLIGHT_PORT PORT->Group[0]
#define DEBUG1PIN_PIN 13
#define DEBUG1PIN_PORT PORT->Group[0]
#define DEBUG2PIN_PIN 12
#define DEBUG2PIN_PORT PORT->Group[0]
#define DEBUG3PIN_PIN 15
#define DEBUG3PIN_PORT PORT->Group[1]
#define DEBUG4PIN_PIN 14
#define DEBUG4PIN_PORT PORT->Group[1]
#define CLKLIGHT_BM (uint32_t)(1 << CLKLIGHT_PIN)
#define CLKLIGHT_ON
//CLKLIGHT_PORT.OUTCLR.reg = CLKLIGHT_BM
#define CLKLIGHT_OFF
//CLKLIGHT_PORT.OUTSET.reg = CLKLIGHT_BM
#define CLKLIGHT_TOGGLE
//CLKLIGHT_PORT.OUTTGL.reg = CLKLIGHT_BM
#define CLKLIGHT_SETUP
//CLKLIGHT_PORT.DIRSET.reg = CLKLIGHT_BM; CLKLIGHT_OFF
#define ERRLIGHT_BM (uint32_t)(1 << ERRLIGHT_PIN)
#define ERRLIGHT_ON
//ERRLIGHT_PORT.OUTCLR.reg = ERRLIGHT_BM
#define ERRLIGHT_OFF
//ERRLIGHT_PORT.OUTSET.reg = ERRLIGHT_BM
#define ERRLIGHT_TOGGLE
//ERRLIGHT_PORT.OUTTGL.reg = ERRLIGHT_BM
#define ERRLIGHT_SETUP
//ERRLIGHT_PORT.DIRSET.reg = ERRLIGHT_BM; ERRLIGHT_OFF
#define DEBUG1PIN_BM (uint32_t)(1 << DEBUG1PIN_PIN)
#define DEBUG1PIN_ON DEBUG1PIN_PORT.OUTSET.reg = DEBUG1PIN_BM
#define DEBUG1PIN_OFF DEBUG1PIN_PORT.OUTCLR.reg = DEBUG1PIN_BM
#define DEBUG1PIN_TOGGLE DEBUG1PIN_PORT.OUTTGL.reg = DEBUG1PIN_BM
#define DEBUG1PIN_SETUP DEBUG1PIN_PORT.DIRSET.reg = DEBUG1PIN_BM; DEBUG1PIN_OFF
#define DEBUG2PIN_BM (uint32_t)(1 << DEBUG2PIN_PIN)
#define DEBUG2PIN_ON DEBUG2PIN_PORT.OUTSET.reg = DEBUG2PIN_BM
#define DEBUG2PIN_OFF DEBUG2PIN_PORT.OUTCLR.reg = DEBUG2PIN_BM
#define DEBUG2PIN_TOGGLE DEBUG2PIN_PORT.OUTTGL.reg = DEBUG2PIN_BM
#define DEBUG2PIN_SETUP DEBUG2PIN_PORT.DIRSET.reg = DEBUG2PIN_BM; DEBUG2PIN_OFF
#define DEBUG3PIN_BM (uint32_t)(1 << DEBUG3PIN_PIN)
#define DEBUG3PIN_ON DEBUG3PIN_PORT.OUTSET.reg = DEBUG3PIN_BM
#define DEBUG3PIN_OFF DEBUG3PIN_PORT.OUTCLR.reg = DEBUG3PIN_BM
#define DEBUG3PIN_TOGGLE DEBUG3PIN_PORT.OUTTGL.reg = DEBUG3PIN_BM
#define DEBUG3PIN_SETUP DEBUG3PIN_PORT.DIRSET.reg = DEBUG3PIN_BM; DEBUG3PIN_OFF
#define DEBUG4PIN_BM (uint32_t)(1 << DEBUG4PIN_PIN)
#define DEBUG4PIN_ON DEBUG4PIN_PORT.OUTSET.reg = DEBUG4PIN_BM
#define DEBUG4PIN_OFF DEBUG4PIN_PORT.OUTCLR.reg = DEBUG4PIN_BM
#define DEBUG4PIN_TOGGLE DEBUG4PIN_PORT.OUTTGL.reg = DEBUG4PIN_BM
#define DEBUG4PIN_SETUP DEBUG4PIN_PORT.DIRSET.reg = DEBUG4PIN_BM; DEBUG4PIN_OFF
\ No newline at end of file
#ifndef PERIPHERAL_NUMS_H_
#define PERIPHERAL_NUMS_H_
#define PERIPHERAL_A 0
#define PERIPHERAL_B 1
#define PERIPHERAL_C 2
#define PERIPHERAL_D 3
#define PERIPHERAL_E 4
#define PERIPHERAL_F 5
#define PERIPHERAL_G 6
#define PERIPHERAL_H 7
#define PERIPHERAL_I 8
#define PERIPHERAL_K 9
#define PERIPHERAL_L 10
#define PERIPHERAL_M 11
#define PERIPHERAL_N 12
#endif
\ No newline at end of file
/*
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_head.h"
UCBus_Head* UCBus_Head::instance = 0;
UCBus_Head* UCBus_Head::getInstance(void){
if(instance == 0){
instance = new UCBus_Head();
}
return instance;
}
// making this instance globally available, when built,
// recall extern declaration in .h
UCBus_Head* ucBusHead = UCBus_Head::getInstance();
UCBus_Head::UCBus_Head(void) {}
// uart init
void UCBus_Head::startupUART(void){
// driver output is always on at head, set HI to enable
UBH_DE_PORT.DIRSET.reg = UBH_DE_BM;
UBH_DE_PORT.OUTSET.reg = UBH_DE_BM;
// receive output is always on at head, set LO to enable
UBH_RE_PORT.DIRSET.reg = UBH_RE_BM;
UBH_RE_PORT.OUTCLR.reg = UBH_RE_BM;
// termination resistor for receipt on bus head is on, set LO to enable
UBH_TE_PORT.DIRSET.reg = UBH_TE_BM;
UBH_TE_PORT.OUTCLR.reg = UBH_TE_BM;
// rx pin setup
UBH_COMPORT.DIRCLR.reg = UBH_RXBM;
UBH_COMPORT.PINCFG[UBH_RXPIN].bit.PMUXEN = 1;
if(UBH_RXPIN % 2){
UBH_COMPORT.PMUX[UBH_RXPIN >> 1].reg |= PORT_PMUX_PMUXO(UBH_RXPERIPHERAL);
} else {
UBH_COMPORT.PMUX[UBH_RXPIN >> 1].reg |= PORT_PMUX_PMUXE(UBH_RXPERIPHERAL);
}
// tx
UBH_COMPORT.DIRCLR.reg = UBH_TXBM;
UBH_COMPORT.PINCFG[UBH_TXPIN].bit.PMUXEN = 1;
if(UBH_TXPIN % 2){
UBH_COMPORT.PMUX[UBH_TXPIN >> 1].reg |= PORT_PMUX_PMUXO(UBH_TXPERIPHERAL);
} else {
UBH_COMPORT.PMUX[UBH_TXPIN >> 1].reg |= PORT_PMUX_PMUXE(UBH_TXPERIPHERAL);
}
// ok, clocks, first line au manuel
// unmask clocks
MCLK->APBAMASK.bit.SERCOM1_ = 1;
GCLK->GENCTRL[UBH_GCLKNUM_PICK].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(UBH_GCLKNUM_PICK));
GCLK->PCHCTRL[UBH_SERCOM_CLK].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(UBH_GCLKNUM_PICK);
// then, sercom
while(UBH_SER_USART.SYNCBUSY.bit.ENABLE);
UBH_SER_USART.CTRLA.bit.ENABLE = 0;
while(UBH_SER_USART.SYNCBUSY.bit.SWRST);
UBH_SER_USART.CTRLA.bit.SWRST = 1;
while(UBH_SER_USART.SYNCBUSY.bit.SWRST);
while(UBH_SER_USART.SYNCBUSY.bit.SWRST || UBH_SER_USART.SYNCBUSY.bit.ENABLE);
// ok, well
UBH_SER_USART.CTRLA.reg = SERCOM_USART_CTRLA_MODE(1) | SERCOM_USART_CTRLA_DORD; // data order and
UBH_SER_USART.CTRLA.reg |= SERCOM_USART_CTRLA_RXPO(UBH_RXPO) | SERCOM_USART_CTRLA_TXPO(0); // rx and tx pinout options
UBH_SER_USART.CTRLA.reg |= SERCOM_USART_CTRLA_FORM(1); // turn on parity: parity is even by default (set in CTRLB), leave that
while(UBH_SER_USART.SYNCBUSY.bit.CTRLB);
UBH_SER_USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_CHSIZE(0);
// enable interrupts
NVIC_EnableIRQ(SERCOM1_2_IRQn);
NVIC_EnableIRQ(SERCOM1_0_IRQn);
// set baud
UBH_SER_USART.BAUD.reg = UBH_BAUD_VAL;
// and finally, a kickoff
while(UBH_SER_USART.SYNCBUSY.bit.ENABLE);
UBH_SER_USART.CTRLA.bit.ENABLE = 1;
// enable the rx interrupt,
UBH_SER_USART.INTENSET.bit.RXC = 1;
}
void UCBus_Head::init(void) {
// clear buffers to begin,
for(uint8_t d = 0; d < UBH_DROP_OPS; d ++){
inBufferLen[d] = 0;
inBufferRp[d] = 0;
}
startupUART();
}
void UCBus_Head::timerISR(void){
// debug zero
if(outReceiveCall == 0){
//DEBUG1PIN_TOGGLE;
}
// so, would formulate the out bytes,
// in either case we formulate the outByte and outHeader, then bit shift identically into
// the word ...
if(outBufferALen > 0){ // always transmit channel A before B
// have bytes, write word to encapsulate outBuffer[outBufferRp]; the do outBufferRp ++ and check wrap
// mask: 6 bit,
if(outBufferARp >= outBufferALen){
// this is the EOP frame,
outByte = 0;
outHeader = headerMask & (noTokenWordA | (outReceiveCall & dropIdMask));
// now it's clear,
outBufferARp = 0;
outBufferALen = 0;
} else {
// this is a regular frame
outByte = outBufferA[outBufferARp];
outHeader = headerMask & (tokenWordA | (outReceiveCall & dropIdMask));
outBufferARp ++; // increment for next byte,
}
} else if (outBufferBLen > 0){
if(outBufferBRp >= outBufferBLen){
// CHB EOP frame
outByte = 0;
outHeader = headerMask & (noTokenWordB | (outReceiveCall & dropIdMask));
// now is clear,
outBufferBRp = 0;
outBufferBLen = 0;
} else {
// ahn regular CHB frame
outByte = outBufferB[outBufferBRp];
outHeader = headerMask & (tokenWordB | (outReceiveCall & dropIdMask));
outBufferBRp ++;
}
} else {
// no token, no EOP on either channel
// alternate channels, in case spurious packets not closed on one ... ensure close
outByte = 0;
if(lastSpareEOP == 0){
outHeader = headerMask & (noTokenWordA | (outReceiveCall & dropIdMask));
lastSpareEOP = 1;
} else {
outHeader = headerMask & (noTokenWordB | (outReceiveCall & dropIdMask));
lastSpareEOP = 0;
}
}
outWord[0] = 0b00000000 | ((outHeader << 1) & 0b01110000) | (outByte >> 4);
outWord[1] = 0b10000000 | ((outHeader << 4) & 0b01110000) | (outByte & 0b00001111);
// put the UART to work before more clocks on buffer incrementing
UBH_SER_USART.DATA.reg = outWord[0];
// and setup the interrupt to handle the second,
UBH_SER_USART.INTENSET.bit.DRE = 1;
// and loop through returns,
outReceiveCall ++;
if(outReceiveCall >= UBH_DROP_OPS){
outReceiveCall = 0;
}
}
// TX Handler, for second bytes initiated by timer,
void SERCOM1_0_Handler(void){
ucBusHead->txISR();
}
void UCBus_Head::txISR(void){
UBH_SER_USART.DATA.reg = outWord[1]; // just services the next byte in the word: timer initiates
UBH_SER_USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; // turn this interrupt off
}
void SERCOM1_2_Handler(void){
ucBusHead->rxISR();
}
void UCBus_Head::rxISR(void){
// check parity bit,
uint16_t perr = UBH_SER_USART.STATUS.bit.PERR;
if(perr){
//ERRLIGHT_ON;
uint8_t clear = UBH_SER_USART.DATA.reg;
UBH_SER_USART.STATUS.bit.PERR = 1; // clear parity flag
return;
}
// cleared by reading out, but we are blind feed forward atm
uint8_t data = UBH_SER_USART.DATA.reg;
if((data >> 7) == 0){
inWord[0] = data;
} else {
inWord[1] = data;
// now decouple,
inHeader = ((inWord[0] >> 1) & 0b00111000) | ((inWord[1] >> 4) & 0b00000111);
inByte = ((inWord[0] << 4) & 0b11110000) | (inWord[1] & 0b00001111);
// the drop reporting
uint8_t drop = inHeader & dropIdMask;
if(drop > UBH_DROP_OPS) return; // unknown drop ?
// otherwise, load it
inBuffer[drop][inBufferRp[drop]] = inByte;
if(inByte == 0){ // eop cobs encoded
inBufferLen[drop] = inBufferRp[drop];
} else {
inBufferRp[drop] += 1;
}
}
}
// -------------------------------------------------------- API
boolean UCBus_Head::ctr(uint8_t drop){
if(drop >= UBH_DROP_OPS) return false;
if(inBufferLen[drop] > 0){
return true;
} else {
return false;
}
}
size_t UCBus_Head::read(uint8_t drop, uint8_t *dest){
if(!ctr(drop)) return 0;
NVIC_DisableIRQ(SERCOM1_2_IRQn);
size_t decodeLen = cobsDecode(inBuffer[drop], inBufferLen[drop], dest);
inBufferLen[drop] = 0;
inBufferRp[drop] = 0;
NVIC_EnableIRQ(SERCOM1_2_IRQn);
return decodeLen;
}
// mod cts(channel) and transmit(data, len, channel)
// then do an example for channel-b-write currents, then do drop code, then test
boolean UCBus_Head::cts_a(void){
if(outBufferALen != 0){
return false;
} else {
return true;
}
}
boolean UCBus_Head::cts_b(void){
if(outBufferBLen != 0){
return false;
} else {
return true;
}
}
void UCBus_Head::transmit_a(uint8_t *data, uint16_t len){
if(!cts_a()) return;
//size_t encLen = cobsEncode(data, len, outBuffer);
memcpy(outBufferA, data, len);
outBufferALen = len; //encLen;
outBufferARp = 0;
}
void UCBus_Head::transmit_b(uint8_t *data, uint16_t len){\
if(!cts_b()) return;
memcpy(outBufferB, data, len);
outBufferBLen = len;
outBufferBRp = 0;
}
/*
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_HEAD_H_
#define UCBUS_HEAD_H_
#include <arduino.h>
#include "indicators.h"
#include "peripheral_nums.h"
#include "utils/syserror.h"
#include "utils/clocks_d51_module.h"
#include "utils/cobs.h"
#define TIMER_A_GCLK_NUM 9
#define TIMER_B_GCLK_NUM 10
#define UBH_SER_USART SERCOM1->USART
#define UBH_SERCOM_CLK SERCOM1_GCLK_ID_CORE
#define UBH_GCLKNUM_PICK 7
#define UBH_COMPORT PORT->Group[0]
#define UBH_TXPIN 16 // x-0
#define UBH_TXBM (uint32_t)(1 << UBH_TXPIN)
#define UBH_RXPIN 18 // x-2
#define UBH_RXBM (uint32_t)(1 << UBH_RXPIN)
#define UBH_RXPO 2 // RX on SER-2
#define UBH_TXPERIPHERAL PERIPHERAL_C
#define UBH_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 UBH_BAUD_VAL 0
#define UBH_DE_PIN 16 // driver output enable: set HI to enable, LO to tri-state the driver
#define UBH_DE_BM (uint32_t)(1 << UBH_DE_PIN)
#define UBH_DE_PORT PORT->Group[1]
#define UBH_RE_PIN 19 // receiver output enable, set LO to enable the RO, set HI to tri-state RO
#define UBH_RE_BM (uint32_t)(1 << UBH_RE_PIN)
#define UBH_RE_PORT PORT->Group[0]
#define UBH_TE_PIN 17 // termination enable, drive LO to enable to internal termination resistor, HI to disable
#define UBH_TE_BM (uint32_t)(1 << UBH_TE_PIN)
#define UBH_TE_PORT PORT->Group[0]
#define UBH_BUFSIZE 1024
#define UBH_DROP_OPS 14
#define UB_AK_GOTOPOS 91
#define UB_AK_SETPOS 92
#define UB_AK_SETRPM 93
// PLEASE NOTE: this requires a 100kHz tick, use interrupt timer,
// fire the timerISR there.
class UCBus_Head {
private:
// singleton-ness
static UCBus_Head* instance;
// input is big for the head,
volatile uint8_t inWord[2];
volatile uint8_t inHeader;
volatile uint8_t inByte;
uint8_t inBuffer[UBH_DROP_OPS][UBH_BUFSIZE];
volatile uint16_t inBufferRp[UBH_DROP_OPS];
volatile uint16_t inBufferLen[UBH_DROP_OPS];
// transmit buffers for A / B Channels
uint8_t outBufferA[UBH_BUFSIZE];
volatile uint16_t outBufferARp = 0;