// References / Resources / Inspiration // https://gist.github.com/nonsintetic/ad13e70f164801325f5f552f84306d6f // Varous system includes #include #include "wiring_private.h" // Various library includes #include // Buffer for serial data -> i2c CircularBuffer uartBufferOutbound; // Buffer for serial data <- i2c CircularBuffer uartBufferInbound; // 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); // 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; // 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 // 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() { rpiUART0.IrqHandler(); } void SERCOM4_Handler() { rpiUART5.IrqHandler(); } // Misc uart functions (defined after setup/loop) void setupUARTs(); // Setup stuff needed for build void setup() { // Setup board LED pinMode(PIN_BOARD_LED, OUTPUT); // Timer setup timerConfigure(TIMER_MILLIS); timerStartCounter(); // Setup UARTS setupUARTs(); // Assign SERCOM functionality pinPeripheral(10, PIO_SERCOM); pinPeripheral(11, PIO_SERCOM); } // Main function int serialIncomingByte; void loop() { while (uartConfig.Serial && Serial.available()) { uartBufferOutbound.push(Serial.read()); if (uartConfig.Serial1) { Serial1.write(uartBufferOutbound.last()); } if (uartConfig.rpiUART0) { rpiUART0.write(uartBufferOutbound.last()); } if (uartConfig.rpiUART5) { rpiUART5.write(uartBufferOutbound.last()); } } while (uartConfig.Serial1 && Serial1.available()) { uartBufferInbound.push(Serial1.read()); if (uartConfig.Serial) { Serial.write(uartBufferInbound.last()); } } while (uartConfig.rpiUART0 && rpiUART0.available()) { uartBufferInbound.push(rpiUART0.read()); if (uartConfig.Serial) { Serial.write(uartBufferInbound.last()); } } while (uartConfig.rpiUART5 && rpiUART5.available()) { uartBufferInbound.push(rpiUART5.read()); 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); } } // 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()); }