2020-09-11 02:28:31 +00:00
|
|
|
// References / Resources / Inspiration
|
|
|
|
// https://gist.github.com/nonsintetic/ad13e70f164801325f5f552f84306d6f
|
|
|
|
|
|
|
|
// Varous system includes
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include "wiring_private.h"
|
|
|
|
|
|
|
|
// Various library includes
|
|
|
|
#include <CircularBuffer.h>
|
|
|
|
|
|
|
|
// Buffer for serial data -> i2c
|
2020-09-20 08:19:04 +00:00
|
|
|
CircularBuffer<byte, 16 * 1024> uartBufferOutbound;
|
|
|
|
|
|
|
|
// Buffer for serial data <- i2c
|
|
|
|
CircularBuffer<byte, 1024> uartBufferInbound;
|
2020-09-11 02:28:31 +00:00
|
|
|
|
|
|
|
// Board LED Pin
|
|
|
|
#define PIN_BOARD_LED 13
|
|
|
|
|
|
|
|
// Blinking timer setup
|
|
|
|
#define TIMER_MILLIS 500
|
|
|
|
bool ledState = false;
|
|
|
|
void TC5_Handler(void);
|
|
|
|
void timerConfigure(int sampleRate);
|
|
|
|
|
2020-09-20 20:14:06 +00:00
|
|
|
// General UART configuration struct
|
|
|
|
struct {
|
|
|
|
// Whether or not the USB is used for serial communications (stand alone builds)
|
|
|
|
bool Serial = true;
|
|
|
|
// Whether or not the i2c is used for serial communication (_controller builds)
|
|
|
|
bool uartI2C = false;
|
|
|
|
|
|
|
|
// Use the generic uart setup with the rpi
|
|
|
|
bool Serial1 = true;
|
|
|
|
// Use uart0 on the rpi
|
|
|
|
bool rpiUART0 = true;
|
|
|
|
// Use uart5 on the rpi
|
|
|
|
bool rpiUART5 = true;
|
|
|
|
} uartConfig;
|
|
|
|
|
2020-09-11 02:28:31 +00:00
|
|
|
// Additional serial ports for communication <> RPI
|
|
|
|
// https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports?view=all#creating-a-new-serial
|
2020-09-20 08:19:04 +00:00
|
|
|
// D11 (RX) -- GPIO 8 (TX)
|
|
|
|
// D10 (TX) -- GPIO 10 (RX)
|
|
|
|
// MISO (RX) -- GPIO 32 (TX)
|
|
|
|
// MOSI (TX) -- GPIO 33 (RX)
|
|
|
|
Uart rpiUART0 (&sercom1, 10, 11, SERCOM_RX_PAD_0, UART_TX_PAD_2);
|
|
|
|
Uart rpiUART5 (&sercom4, PIN_SPI_MISO, PIN_SPI_MOSI, SERCOM_RX_PAD_0, UART_TX_PAD_2);
|
|
|
|
void SERCOM1_Handler() {
|
2020-09-11 02:28:31 +00:00
|
|
|
rpiUART0.IrqHandler();
|
|
|
|
}
|
2020-09-20 08:19:04 +00:00
|
|
|
void SERCOM4_Handler() {
|
2020-09-11 02:28:31 +00:00
|
|
|
rpiUART5.IrqHandler();
|
|
|
|
}
|
|
|
|
|
2020-09-20 20:14:06 +00:00
|
|
|
// Misc uart functions (defined after setup/loop)
|
|
|
|
void setupUARTs();
|
|
|
|
|
2020-09-11 02:28:31 +00:00
|
|
|
// Setup stuff needed for build
|
|
|
|
void setup() {
|
|
|
|
// Setup board LED
|
|
|
|
pinMode(PIN_BOARD_LED, OUTPUT);
|
|
|
|
|
|
|
|
// Timer setup
|
|
|
|
timerConfigure(TIMER_MILLIS);
|
|
|
|
timerStartCounter();
|
|
|
|
|
2020-09-20 20:14:06 +00:00
|
|
|
// Setup UARTS
|
|
|
|
setupUARTs();
|
2020-09-20 08:19:04 +00:00
|
|
|
|
|
|
|
// Assign SERCOM functionality
|
|
|
|
pinPeripheral(10, PIO_SERCOM);
|
|
|
|
pinPeripheral(11, PIO_SERCOM);
|
2020-09-11 02:28:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Main function
|
2020-09-20 08:19:04 +00:00
|
|
|
int serialIncomingByte;
|
2020-09-11 02:28:31 +00:00
|
|
|
void loop() {
|
2020-09-20 20:14:06 +00:00
|
|
|
while (uartConfig.Serial && Serial.available()) {
|
2020-09-20 08:19:04 +00:00
|
|
|
uartBufferOutbound.push(Serial.read());
|
2020-09-20 20:14:06 +00:00
|
|
|
if (uartConfig.Serial1) {
|
|
|
|
Serial1.write(uartBufferOutbound.last());
|
|
|
|
}
|
|
|
|
if (uartConfig.rpiUART0) {
|
|
|
|
rpiUART0.write(uartBufferOutbound.last());
|
|
|
|
}
|
|
|
|
if (uartConfig.rpiUART5) {
|
|
|
|
rpiUART5.write(uartBufferOutbound.last());
|
|
|
|
}
|
2020-09-20 08:19:04 +00:00
|
|
|
}
|
2020-09-20 20:14:06 +00:00
|
|
|
while (uartConfig.Serial1 && Serial1.available()) {
|
2020-09-20 08:19:04 +00:00
|
|
|
uartBufferInbound.push(Serial1.read());
|
2020-09-20 20:14:06 +00:00
|
|
|
if (uartConfig.Serial) {
|
|
|
|
Serial.write(uartBufferInbound.last());
|
|
|
|
}
|
2020-09-20 08:19:04 +00:00
|
|
|
}
|
2020-09-20 20:14:06 +00:00
|
|
|
while (uartConfig.rpiUART0 && rpiUART0.available()) {
|
2020-09-20 08:19:04 +00:00
|
|
|
uartBufferInbound.push(rpiUART0.read());
|
2020-09-20 20:14:06 +00:00
|
|
|
if (uartConfig.Serial) {
|
|
|
|
Serial.write(uartBufferInbound.last());
|
|
|
|
}
|
2020-09-20 08:19:04 +00:00
|
|
|
}
|
2020-09-20 20:14:06 +00:00
|
|
|
while (uartConfig.rpiUART5 && rpiUART5.available()) {
|
2020-09-20 08:19:04 +00:00
|
|
|
uartBufferInbound.push(rpiUART5.read());
|
2020-09-20 20:14:06 +00:00
|
|
|
if (uartConfig.Serial) {
|
|
|
|
Serial.write(uartBufferInbound.last());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method to setup / reset the uarts
|
|
|
|
void setupUARTs() {
|
|
|
|
// Close ALL serial ports and re-enable per config (config can change during runtime)
|
|
|
|
Serial.end();
|
|
|
|
Serial1.end();
|
|
|
|
rpiUART0.end();
|
|
|
|
rpiUART5.end();
|
|
|
|
|
|
|
|
// Setup serial ports
|
|
|
|
if (uartConfig.Serial) {
|
|
|
|
Serial.begin(115200);
|
|
|
|
}
|
|
|
|
if (uartConfig.Serial1) {
|
|
|
|
Serial1.begin(115200);
|
|
|
|
}
|
|
|
|
if (uartConfig.rpiUART0) {
|
|
|
|
rpiUART0.begin(115200);
|
|
|
|
}
|
|
|
|
if (uartConfig.rpiUART5) {
|
|
|
|
rpiUART5.begin(115200);
|
2020-09-20 08:19:04 +00:00
|
|
|
}
|
2020-09-11 02:28:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Timer handler
|
|
|
|
void TC5_Handler (void) {
|
|
|
|
if(ledState == true) {
|
|
|
|
digitalWrite(PIN_BOARD_LED, HIGH);
|
|
|
|
} else {
|
|
|
|
digitalWrite(PIN_BOARD_LED, LOW);
|
|
|
|
}
|
|
|
|
ledState = !ledState;
|
|
|
|
TC5->COUNT16.INTFLAG.bit.MC0 = 1; //Writing a 1 to INTFLAG.bit.MC0 clears the interrupt so that it will run again
|
|
|
|
}
|
|
|
|
|
|
|
|
//Configures the TC to generate output events at the sample frequency.
|
|
|
|
//Configures the TC in Frequency Generation mode, with an event output once
|
|
|
|
//each time the audio sample frequency period expires.
|
|
|
|
void timerConfigure(int sampleRate) {
|
|
|
|
// select the generic clock generator used as source to the generic clock multiplexer
|
|
|
|
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ;
|
|
|
|
while (GCLK->STATUS.bit.SYNCBUSY);
|
|
|
|
|
|
|
|
timerReset(); //reset TC5
|
|
|
|
|
|
|
|
// Set Timer counter 5 Mode to 16 bits, it will become a 16bit counter ('mode1' in the datasheet)
|
|
|
|
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
|
|
|
|
// Set TC5 waveform generation mode to 'match frequency'
|
|
|
|
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
|
|
|
|
//set prescaler
|
|
|
|
//the clock normally counts at the GCLK_TC frequency, but we can set it to divide that frequency to slow it down
|
|
|
|
//you can use different prescaler divisons here like TC_CTRLA_PRESCALER_DIV1 to get a different range
|
|
|
|
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024 | TC_CTRLA_ENABLE; //it will divide GCLK_TC frequency by 1024
|
|
|
|
//set the compare-capture register.
|
|
|
|
//The counter will count up to this value (it's a 16bit counter so we use uint16_t)
|
|
|
|
//this is how we fine-tune the frequency, make it count to a lower or higher value
|
|
|
|
//system clock should be 1MHz (8MHz/8) at Reset by default
|
|
|
|
TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / sampleRate);
|
|
|
|
while (timerIsSyncing());
|
|
|
|
|
|
|
|
// Configure interrupt request
|
|
|
|
NVIC_DisableIRQ(TC5_IRQn);
|
|
|
|
NVIC_ClearPendingIRQ(TC5_IRQn);
|
|
|
|
NVIC_SetPriority(TC5_IRQn, 0);
|
|
|
|
NVIC_EnableIRQ(TC5_IRQn);
|
|
|
|
|
|
|
|
// Enable the TC5 interrupt request
|
|
|
|
TC5->COUNT16.INTENSET.bit.MC0 = 1;
|
|
|
|
while (timerIsSyncing()); //wait until TC5 is done syncing
|
|
|
|
}
|
|
|
|
|
|
|
|
//Function that is used to check if TC5 is done syncing
|
|
|
|
//returns true when it is done syncing
|
|
|
|
bool timerIsSyncing() {
|
|
|
|
return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
//This function enables TC5 and waits for it to be ready
|
|
|
|
void timerStartCounter() {
|
|
|
|
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; //set the CTRLA register
|
|
|
|
while (timerIsSyncing()); //wait until snyc'd
|
|
|
|
}
|
|
|
|
|
|
|
|
//Reset TC5
|
|
|
|
void timerReset() {
|
|
|
|
TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
|
|
|
|
while (timerIsSyncing());
|
|
|
|
while (TC5->COUNT16.CTRLA.bit.SWRST);
|
|
|
|
}
|
|
|
|
|
|
|
|
//disable TC5
|
|
|
|
void timerDisable() {
|
|
|
|
TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
|
|
|
|
while (timerIsSyncing());
|
|
|
|
}
|