From e32d74628054225370ec46f79a52acf9b7be8bde Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Wed, 29 May 2024 15:12:31 +0200 Subject: [PATCH 1/8] TX working --- AltSoftSerial.cpp | 63 +++++++++++++++++++++++++ config/AltSoftSerial_Boards.h | 5 ++ config/AltSoftSerial_Timers.h | 89 +++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) diff --git a/AltSoftSerial.cpp b/AltSoftSerial.cpp index 916d056..fe0eb5d 100644 --- a/AltSoftSerial.cpp +++ b/AltSoftSerial.cpp @@ -67,6 +67,16 @@ static volatile uint8_t tx_buffer[TX_BUFFER_SIZE]; #define MAX_COUNTS_PER_BIT 6241 // 65536 / 10.5 +#if defined(ALTSS_SAMD) +enum MATCH_MODE{ + NORMAL, + SET, + CLEAR +}; + +static volatile MATCH_MODE match_mode = NORMAL; +#endif + void AltSoftSerial::init(uint32_t cycles_per_bit) { //Serial.printf("cycles_per_bit = %d\n", cycles_per_bit); @@ -135,8 +145,12 @@ void AltSoftSerial::writeByte(uint8_t b) head = tx_buffer_head + 1; if (head >= TX_BUFFER_SIZE) head = 0; while (tx_buffer_tail == head) ; // wait until space in buffer +#if defined(ALTSS_SAMD) + __disable_irq(); +#else intr_state = SREG; cli(); +#endif if (tx_state) { tx_buffer[head] = b; tx_buffer_head = head; @@ -148,12 +162,53 @@ void AltSoftSerial::writeByte(uint8_t b) CONFIG_MATCH_CLEAR(); SET_COMPARE_A(GET_TIMER_COUNT() + 16); } +#if defined(ALTSS_SAMD) + __enable_irq(); +#else SREG = intr_state; +#endif } +#if defined(ALTSS_USE_SAMD_TIMER3) +void COMPARE_A_ISR(); +void COMPARE_B_ISR(); +void TC3_Handler() +{ + uint8_t status = TC3->COUNT16.INTFLAG.reg; + uint8_t clear = 0; + if (status & TC_INTFLAG_MC0){ + clear = TC_INTFLAG_MC0; + COMPARE_A_ISR(); + } else if (status & TC_INTFLAG_MC1){ + clear = TC_INTFLAG_MC1; + COMPARE_B_ISR(); + } else { // unknown interrupt -> clear all + clear = status; + } + TC3->COUNT16.INTFLAG.reg = clear; +} + +#endif + +#if defined(ALTSS_SAMD) +void COMPARE_A_ISR() +{ + timer_request(); // request current timer value & save until read via `GET_COMPARE_A()` macro + switch(match_mode){ + case NORMAL: + break; + case SET: + digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH); + break; + case CLEAR: + digitalWrite(OUTPUT_COMPARE_A_PIN, LOW); + break; + }; +#else ISR(COMPARE_A_INTERRUPT) { +#endif uint8_t state, byte, bit, head, tail; uint16_t target; @@ -219,7 +274,11 @@ void AltSoftSerial::flushOutput(void) /** Reception **/ /****************************************/ +#if defined(ALTSS_SAMD) +void INPUT_PIN_ISR() +#else ISR(CAPTURE_INTERRUPT) +#endif { uint8_t state, bit, head; uint16_t capture, target; @@ -272,7 +331,11 @@ ISR(CAPTURE_INTERRUPT) //if (GET_TIMER_COUNT() - capture > ticks_per_bit) AltSoftSerial::timing_error = true; } +#if defined(ALTSS_SAMD) +void COMPARE_B_ISR() +#else ISR(COMPARE_B_INTERRUPT) +#endif { uint8_t head, state, bit; diff --git a/config/AltSoftSerial_Boards.h b/config/AltSoftSerial_Boards.h index 48a3d68..fd21de9 100644 --- a/config/AltSoftSerial_Boards.h +++ b/config/AltSoftSerial_Boards.h @@ -140,6 +140,11 @@ #define OUTPUT_COMPARE_B_PIN 12 // unusable PWM +#elif defined(ARDUINO_ARCH_SAMD) + #define ALTSS_SAMD + #define ALTSS_USE_SAMD_TIMER3 + #define INPUT_CAPTURE_PIN A2 // receive + #define OUTPUT_COMPARE_A_PIN 2 // transmit // Unknown board #else diff --git a/config/AltSoftSerial_Timers.h b/config/AltSoftSerial_Timers.h index 16df5b8..b5aa5e0 100644 --- a/config/AltSoftSerial_Timers.h +++ b/config/AltSoftSerial_Timers.h @@ -183,5 +183,94 @@ #endif #define ISR(f) static void f (void) +#elif defined(ALTSS_USE_SAMD_TIMER3) +// Library works with 16 bit timer (implizitly) + +void INPUT_PIN_ISR(); + +// Request the current value of the COUNT register +inline void timer_request(){ + // the current value of COUNT will be read even later (except, when COUNT register is written to) + TC3->COUNT16.READREQ.reg = TC_READREQ_RREQ | TC_READREQ_ADDR(TC_COUNT16_COUNT_OFFSET); +}; + +// Read COUNT register (from point in time when request was mad) +inline uint16_t timer_read(){ + // Wait for read-synchronization (if not already done) + while(TC3->COUNT16.STATUS.bit.SYNCBUSY); + return TC3->COUNT16.COUNT.reg; +}; + +// Request & Read COUNT register +inline uint16_t timer_request_read(){ + timer_request(); + return timer_read(); +}; + +inline void init_timer(uint8_t TC_CTRLA_PRESCALER_Val){ + const uint16_t GCLK_ID = 3; // use Generic Clock 3 + + // Setup GCLK (Generic Clock Controller) for TC3 + GCLK->GENDIV.bit.ID = GCLK_ID; // Select Generic Clock ID + GCLK->GENDIV.bit.DIV = 1; // Setup Divison - GCLK = 48MHz / DIV + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + GCLK->GENCTRL.bit.ID = GCLK_ID; // Select Generic Clock ID + GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val; // Set the 48MHz clock source + GCLK->GENCTRL.bit.GENEN = 1; // Enable GCLK + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + GCLK->CLKCTRL.bit.GEN = GCLK_ID; // Select the GCLK + GCLK->CLKCTRL.bit.ID = GCLK_CLKCTRL_ID_TCC2_TC3_Val; // Feed the GCLK to TCC2 and TC3 + GCLK->CLKCTRL.bit.CLKEN = 1; // Enable + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + // Select 16-bit timer counter mode (TC3) + // Software reset of TC3; Initialization only possible when TC is disabled -> do a reset + TC3->COUNT16.CTRLA.bit.SWRST = 1; + while(TC3->COUNT16.CTRLA.bit.SWRST); // Wait for reset to complete + +// TC3->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val; // Counter in 32-bit mode +// TC3->COUNT16.CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; // WaveGen Mode: Normal Frequency (Top Value is Max) + TC3->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_Val; // Set prescaler bits + while(TC3->COUNT16.STATUS.bit.SYNCBUSY); + + + // Enable TC3 interrupt (there is only one vector for all TC3 interrupts) + NVIC_SetPriority(TC3_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest) + NVIC_EnableIRQ(TC3_IRQn); // Connect TC3 to Nested Vector Interrupt Controller (NVIC) + + // Enable TC3 + TC3->COUNT16.CTRLA.bit.ENABLE = 1; + while(TC3->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization +}; + +#define CONFIG_TIMER_NOPRESCALE() (init_timer(TC_CTRLA_PRESCALER_DIV1_Val)) +#define CONFIG_TIMER_PRESCALE_8() (init_timer(TC_CTRLA_PRESCALER_DIV8_Val)) +#define CONFIG_TIMER_PRESCALE_256() (init_timer(TC_CTRLA_PRESCALER_DIV256_Val)) + +#define SET_COMPARE_A(val) (TC3->COUNT16.CC[0].reg = val) +#define SET_COMPARE_B(val) (TC3->COUNT16.CC[1].reg = val) + +#define ENABLE_INT_COMPARE_A() (TC3->COUNT16.INTENSET.bit.MC0 = 1) +#define ENABLE_INT_COMPARE_B() (TC3->COUNT16.INTENSET.bit.MC1 = 1) +#define DISABLE_INT_COMPARE_A() (TC3->COUNT16.INTENCLR.bit.MC0 = 1) +#define DISABLE_INT_COMPARE_B() (TC3->COUNT16.INTENCLR.bit.MC1 = 1) + +#define CONFIG_MATCH_NORMAL() {match_mode = NORMAL;} +#define CONFIG_MATCH_SET() {match_mode = SET;} +#define CONFIG_MATCH_CLEAR() {match_mode = CLEAR;} + +#define GET_TIMER_COUNT() (timer_request_read()) +#define GET_INPUT_CAPTURE() (timer_request_read()) +#define GET_COMPARE_A() (timer_read()) + +#define DETACH_PIN_ISR() (detachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN))) +#define ATTACH_PIN_ISR(MODE) {DETACH_PIN_ISR(); \ + attachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN), INPUT_PIN_ISR, MODE);} +#define ENABLE_INT_INPUT_CAPTURE() (ATTACH_PIN_ISR(CHANGE)) +#define DISABLE_INT_INPUT_CAPTURE() (DETACH_PIN_ISR()) +#define CONFIG_CAPTURE_FALLING_EDGE() (ATTACH_PIN_ISR(FALLING)) +#define CONFIG_CAPTURE_RISING_EDGE() (ATTACH_PIN_ISR(RISING)) #endif From d8bffa4a6a6c8ed25f95299dc62d7f10731a180d Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Wed, 29 May 2024 20:34:53 +0200 Subject: [PATCH 2/8] Use more descriptive Defines --- AltSoftSerial.cpp | 66 ++++++++------- config/AltSoftSerial_Boards.h | 3 + config/AltSoftSerial_Timers.h | 151 ++++++++++++++++++---------------- 3 files changed, 121 insertions(+), 99 deletions(-) diff --git a/AltSoftSerial.cpp b/AltSoftSerial.cpp index fe0eb5d..673c744 100644 --- a/AltSoftSerial.cpp +++ b/AltSoftSerial.cpp @@ -67,7 +67,7 @@ static volatile uint8_t tx_buffer[TX_BUFFER_SIZE]; #define MAX_COUNTS_PER_BIT 6241 // 65536 / 10.5 -#if defined(ALTSS_SAMD) +#if defined(ALTSS_TX_DIGITALWRITE) enum MATCH_MODE{ NORMAL, SET, @@ -146,6 +146,7 @@ void AltSoftSerial::writeByte(uint8_t b) if (head >= TX_BUFFER_SIZE) head = 0; while (tx_buffer_tail == head) ; // wait until space in buffer #if defined(ALTSS_SAMD) + // SAMD architecture uses different Macros __disable_irq(); #else intr_state = SREG; @@ -163,38 +164,26 @@ void AltSoftSerial::writeByte(uint8_t b) SET_COMPARE_A(GET_TIMER_COUNT() + 16); } #if defined(ALTSS_SAMD) + // SAMD architecture uses different Macros than AVR __enable_irq(); #else SREG = intr_state; #endif } -#if defined(ALTSS_USE_SAMD_TIMER3) -void COMPARE_A_ISR(); -void COMPARE_B_ISR(); - -void TC3_Handler() -{ - uint8_t status = TC3->COUNT16.INTFLAG.reg; - uint8_t clear = 0; - if (status & TC_INTFLAG_MC0){ - clear = TC_INTFLAG_MC0; - COMPARE_A_ISR(); - } else if (status & TC_INTFLAG_MC1){ - clear = TC_INTFLAG_MC1; - COMPARE_B_ISR(); - } else { // unknown interrupt -> clear all - clear = status; - } - TC3->COUNT16.INTFLAG.reg = clear; -} - -#endif - #if defined(ALTSS_SAMD) void COMPARE_A_ISR() { - timer_request(); // request current timer value & save until read via `GET_COMPARE_A()` macro + // SAMD architecutre: request current timer value & save until read via `GET_COMPARE_A()` macro + timer_request(); + +#else +ISR(COMPARE_A_INTERRUPT) +{ +#endif + +#if defined(ALTSS_TX_DIGITALWRITE) + // Use Arduino function for TX pin switch(match_mode){ case NORMAL: break; @@ -205,10 +194,8 @@ void COMPARE_A_ISR() digitalWrite(OUTPUT_COMPARE_A_PIN, LOW); break; }; -#else -ISR(COMPARE_A_INTERRUPT) -{ #endif + uint8_t state, byte, bit, head, tail; uint16_t target; @@ -274,7 +261,7 @@ void AltSoftSerial::flushOutput(void) /** Reception **/ /****************************************/ -#if defined(ALTSS_SAMD) +#if defined(ALTSS_RX_ATTACHINTERRUPT) void INPUT_PIN_ISR() #else ISR(CAPTURE_INTERRUPT) @@ -421,3 +408,26 @@ void ftm0_isr(void) } #endif + +/****************************************/ +/** SAMD Architecture ISR **/ +/****************************************/ + +#if defined(ALTSS_SAMD) + // SAMD architecture uses only one ISR for all timer events +void ALTSS_SAMD_TIMER_HANDLER() +{ + uint8_t status = ALTSS_SAMD_TC->COUNT16.INTFLAG.reg; + uint8_t clear = 0; + if (status & TC_INTFLAG_MC1){ + clear = TC_INTFLAG_MC1; + COMPARE_B_ISR(); + } else if (status & TC_INTFLAG_MC0){ + clear = TC_INTFLAG_MC0; + COMPARE_A_ISR(); + } else { // unknown interrupt -> clear all set flags + clear = status; + } + ALTSS_SAMD_TC->COUNT16.INTFLAG.reg = clear; +} +#endif \ No newline at end of file diff --git a/config/AltSoftSerial_Boards.h b/config/AltSoftSerial_Boards.h index fd21de9..343b357 100644 --- a/config/AltSoftSerial_Boards.h +++ b/config/AltSoftSerial_Boards.h @@ -142,6 +142,9 @@ #elif defined(ARDUINO_ARCH_SAMD) #define ALTSS_SAMD + #define ALTSS_TX_DIGITALWRITE + #define ALTSS_RX_ATTACHINTERRUPT + #define ALTSS_USE_SAMD_TIMER3 #define INPUT_CAPTURE_PIN A2 // receive #define OUTPUT_COMPARE_A_PIN 2 // transmit diff --git a/config/AltSoftSerial_Timers.h b/config/AltSoftSerial_Timers.h index b5aa5e0..1e8b8df 100644 --- a/config/AltSoftSerial_Timers.h +++ b/config/AltSoftSerial_Timers.h @@ -184,93 +184,102 @@ #define ISR(f) static void f (void) #elif defined(ALTSS_USE_SAMD_TIMER3) -// Library works with 16 bit timer (implizitly) + #define ALTSS_SAMD_TC TC3 + #define ALTSS_SAMD_TIMER_HANDLER TC3_Handler + #define ALTSS_SAMD_TIMER_IRQn TC3_IRQn + #define ALTSS_SAMD_GCLK_ID 3 // use Generic Clock 3 + #define ALTSS_SAMD_GCLK_CLKCTRL_ID GCLK_CLKCTRL_ID_TCC2_TC3_Val + +#endif -void INPUT_PIN_ISR(); -// Request the current value of the COUNT register -inline void timer_request(){ - // the current value of COUNT will be read even later (except, when COUNT register is written to) - TC3->COUNT16.READREQ.reg = TC_READREQ_RREQ | TC_READREQ_ADDR(TC_COUNT16_COUNT_OFFSET); -}; +#if defined(ALTSS_RX_ATTACHINTERRUPT) + // Use Arduino functions for RX pin + void INPUT_PIN_ISR(); -// Read COUNT register (from point in time when request was mad) -inline uint16_t timer_read(){ - // Wait for read-synchronization (if not already done) - while(TC3->COUNT16.STATUS.bit.SYNCBUSY); - return TC3->COUNT16.COUNT.reg; -}; + #define DETACH_PIN_ISR() (detachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN))) + #define ATTACH_PIN_ISR(MODE) {DETACH_PIN_ISR(); \ + attachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN), INPUT_PIN_ISR, MODE);} + #define ENABLE_INT_INPUT_CAPTURE() (ATTACH_PIN_ISR(CHANGE)) + #define DISABLE_INT_INPUT_CAPTURE() (DETACH_PIN_ISR()) + #define CONFIG_CAPTURE_FALLING_EDGE() (ATTACH_PIN_ISR(FALLING)) + #define CONFIG_CAPTURE_RISING_EDGE() (ATTACH_PIN_ISR(RISING)) -// Request & Read COUNT register -inline uint16_t timer_request_read(){ - timer_request(); - return timer_read(); -}; + #define CONFIG_MATCH_NORMAL() {match_mode = NORMAL;} + #define CONFIG_MATCH_SET() {match_mode = SET;} + #define CONFIG_MATCH_CLEAR() {match_mode = CLEAR;} -inline void init_timer(uint8_t TC_CTRLA_PRESCALER_Val){ - const uint16_t GCLK_ID = 3; // use Generic Clock 3 +#endif - // Setup GCLK (Generic Clock Controller) for TC3 - GCLK->GENDIV.bit.ID = GCLK_ID; // Select Generic Clock ID - GCLK->GENDIV.bit.DIV = 1; // Setup Divison - GCLK = 48MHz / DIV - while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization - - GCLK->GENCTRL.bit.ID = GCLK_ID; // Select Generic Clock ID - GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val; // Set the 48MHz clock source - GCLK->GENCTRL.bit.GENEN = 1; // Enable GCLK - while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization +#if defined (ALTSS_SAMD) + // Request the current value of the COUNT register + inline void timer_request(){ + // the current value of COUNT will be read even later (except, when COUNT register is written to) + ALTSS_SAMD_TC->COUNT16.READREQ.reg = TC_READREQ_RREQ | TC_READREQ_ADDR(TC_COUNT16_COUNT_OFFSET); + }; - GCLK->CLKCTRL.bit.GEN = GCLK_ID; // Select the GCLK - GCLK->CLKCTRL.bit.ID = GCLK_CLKCTRL_ID_TCC2_TC3_Val; // Feed the GCLK to TCC2 and TC3 - GCLK->CLKCTRL.bit.CLKEN = 1; // Enable - while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + // Read COUNT register (from point in time when request was mad) + inline uint16_t timer_read(){ + // Wait for read-synchronization (if not already done) + while(ALTSS_SAMD_TC->COUNT16.STATUS.bit.SYNCBUSY); + return ALTSS_SAMD_TC->COUNT16.COUNT.reg; + }; - // Select 16-bit timer counter mode (TC3) - // Software reset of TC3; Initialization only possible when TC is disabled -> do a reset - TC3->COUNT16.CTRLA.bit.SWRST = 1; - while(TC3->COUNT16.CTRLA.bit.SWRST); // Wait for reset to complete + // Request & Read COUNT register + inline uint16_t timer_request_read(){ + timer_request(); + return timer_read(); + }; -// TC3->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val; // Counter in 32-bit mode -// TC3->COUNT16.CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; // WaveGen Mode: Normal Frequency (Top Value is Max) - TC3->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_Val; // Set prescaler bits - while(TC3->COUNT16.STATUS.bit.SYNCBUSY); + inline void init_timer(uint8_t TC_CTRLA_PRESCALER_Val){ + // Setup GCLK (Generic Clock Controller) for ALTSS_SAMD_TC + GCLK->GENDIV.bit.ID = ALTSS_SAMD_GCLK_ID; // Select Generic Clock ID + GCLK->GENDIV.bit.DIV = 1; // Setup Divison - GCLK = 48MHz / DIV + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + GCLK->GENCTRL.bit.ID = ALTSS_SAMD_GCLK_ID; // Select Generic Clock ID + GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val; // Set the 48MHz clock source + GCLK->GENCTRL.bit.GENEN = 1; // Enable GCLK + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization - - // Enable TC3 interrupt (there is only one vector for all TC3 interrupts) - NVIC_SetPriority(TC3_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest) - NVIC_EnableIRQ(TC3_IRQn); // Connect TC3 to Nested Vector Interrupt Controller (NVIC) + GCLK->CLKCTRL.bit.GEN = ALTSS_SAMD_GCLK_ID; // Select the GCLK + GCLK->CLKCTRL.bit.ID = ALTSS_SAMD_GCLK_CLKCTRL_ID; // Feed the GCLK to TCC2 and TC3 + GCLK->CLKCTRL.bit.CLKEN = 1; // Enable + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization - // Enable TC3 - TC3->COUNT16.CTRLA.bit.ENABLE = 1; - while(TC3->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization -}; + // Select 16-bit timer counter mode (TC3) + // Software reset of TC3; Initialization only possible when TC is disabled -> do a reset + ALTSS_SAMD_TC->COUNT16.CTRLA.bit.SWRST = 1; + while(ALTSS_SAMD_TC->COUNT16.CTRLA.bit.SWRST); // Wait for reset to complete -#define CONFIG_TIMER_NOPRESCALE() (init_timer(TC_CTRLA_PRESCALER_DIV1_Val)) -#define CONFIG_TIMER_PRESCALE_8() (init_timer(TC_CTRLA_PRESCALER_DIV8_Val)) -#define CONFIG_TIMER_PRESCALE_256() (init_timer(TC_CTRLA_PRESCALER_DIV256_Val)) + // TC3->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val; // Counter in 32-bit mode + // TC3->COUNT16.CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; // WaveGen Mode: Normal Frequency (Top Value is Max) + ALTSS_SAMD_TC->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_Val; // Set prescaler bits + while(ALTSS_SAMD_TC->COUNT16.STATUS.bit.SYNCBUSY); -#define SET_COMPARE_A(val) (TC3->COUNT16.CC[0].reg = val) -#define SET_COMPARE_B(val) (TC3->COUNT16.CC[1].reg = val) + + // Enable TC3 interrupt (there is only one vector for all TC3 interrupts) + NVIC_SetPriority(ALTSS_SAMD_TIMER_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest) + NVIC_EnableIRQ(ALTSS_SAMD_TIMER_IRQn); // Connect TC3 to Nested Vector Interrupt Controller (NVIC) -#define ENABLE_INT_COMPARE_A() (TC3->COUNT16.INTENSET.bit.MC0 = 1) -#define ENABLE_INT_COMPARE_B() (TC3->COUNT16.INTENSET.bit.MC1 = 1) -#define DISABLE_INT_COMPARE_A() (TC3->COUNT16.INTENCLR.bit.MC0 = 1) -#define DISABLE_INT_COMPARE_B() (TC3->COUNT16.INTENCLR.bit.MC1 = 1) + // Enable TC3 + ALTSS_SAMD_TC->COUNT16.CTRLA.bit.ENABLE = 1; + while(ALTSS_SAMD_TC->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + }; -#define CONFIG_MATCH_NORMAL() {match_mode = NORMAL;} -#define CONFIG_MATCH_SET() {match_mode = SET;} -#define CONFIG_MATCH_CLEAR() {match_mode = CLEAR;} + #define CONFIG_TIMER_NOPRESCALE() (init_timer(TC_CTRLA_PRESCALER_DIV1_Val)) + #define CONFIG_TIMER_PRESCALE_8() (init_timer(TC_CTRLA_PRESCALER_DIV8_Val)) + #define CONFIG_TIMER_PRESCALE_256() (init_timer(TC_CTRLA_PRESCALER_DIV256_Val)) -#define GET_TIMER_COUNT() (timer_request_read()) -#define GET_INPUT_CAPTURE() (timer_request_read()) -#define GET_COMPARE_A() (timer_read()) + #define SET_COMPARE_A(val) (ALTSS_SAMD_TC->COUNT16.CC[0].reg = val) + #define SET_COMPARE_B(val) (ALTSS_SAMD_TC->COUNT16.CC[1].reg = val) -#define DETACH_PIN_ISR() (detachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN))) -#define ATTACH_PIN_ISR(MODE) {DETACH_PIN_ISR(); \ - attachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN), INPUT_PIN_ISR, MODE);} -#define ENABLE_INT_INPUT_CAPTURE() (ATTACH_PIN_ISR(CHANGE)) -#define DISABLE_INT_INPUT_CAPTURE() (DETACH_PIN_ISR()) -#define CONFIG_CAPTURE_FALLING_EDGE() (ATTACH_PIN_ISR(FALLING)) -#define CONFIG_CAPTURE_RISING_EDGE() (ATTACH_PIN_ISR(RISING)) + #define ENABLE_INT_COMPARE_A() (ALTSS_SAMD_TC->COUNT16.INTENSET.reg = TC_INTENSET_MC0) + #define ENABLE_INT_COMPARE_B() (ALTSS_SAMD_TC->COUNT16.INTENSET.reg = TC_INTENSET_MC1) + #define DISABLE_INT_COMPARE_A() (ALTSS_SAMD_TC->COUNT16.INTENCLR.reg = TC_INTENSET_MC0) + #define DISABLE_INT_COMPARE_B() (ALTSS_SAMD_TC->COUNT16.INTENCLR.reg = TC_INTENCLR_MC1) + #define GET_TIMER_COUNT() (timer_request_read()) + #define GET_INPUT_CAPTURE() (timer_request_read()) + #define GET_COMPARE_A() (timer_read()) #endif From 17bfe69bc4c8d4eecba84fe5e0f01e62b4370a69 Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Fri, 31 May 2024 16:55:40 +0200 Subject: [PATCH 3/8] TX & RX working --- AltSoftSerial.cpp | 13 +++++++------ config/AltSoftSerial_Timers.h | 23 +++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/AltSoftSerial.cpp b/AltSoftSerial.cpp index 673c744..c18d2cb 100644 --- a/AltSoftSerial.cpp +++ b/AltSoftSerial.cpp @@ -176,7 +176,7 @@ void COMPARE_A_ISR() { // SAMD architecutre: request current timer value & save until read via `GET_COMPARE_A()` macro timer_request(); - + #else ISR(COMPARE_A_INTERRUPT) { @@ -263,10 +263,11 @@ void AltSoftSerial::flushOutput(void) #if defined(ALTSS_RX_ATTACHINTERRUPT) void INPUT_PIN_ISR() +{ #else ISR(CAPTURE_INTERRUPT) -#endif { +#endif uint8_t state, bit, head; uint16_t capture, target; uint16_t offset, offset_overflow; @@ -419,12 +420,12 @@ void ALTSS_SAMD_TIMER_HANDLER() { uint8_t status = ALTSS_SAMD_TC->COUNT16.INTFLAG.reg; uint8_t clear = 0; - if (status & TC_INTFLAG_MC1){ - clear = TC_INTFLAG_MC1; - COMPARE_B_ISR(); - } else if (status & TC_INTFLAG_MC0){ + if (status & TC_INTFLAG_MC0){ clear = TC_INTFLAG_MC0; COMPARE_A_ISR(); + } else if (status & TC_INTFLAG_MC1){ + clear = TC_INTFLAG_MC1; + COMPARE_B_ISR(); } else { // unknown interrupt -> clear all set flags clear = status; } diff --git a/config/AltSoftSerial_Timers.h b/config/AltSoftSerial_Timers.h index 1e8b8df..efaa071 100644 --- a/config/AltSoftSerial_Timers.h +++ b/config/AltSoftSerial_Timers.h @@ -192,22 +192,23 @@ #endif +#if defined(ALTSS_TX_DIGITALWRITE) + #define CONFIG_MATCH_NORMAL() {match_mode = NORMAL;} + #define CONFIG_MATCH_SET() {match_mode = SET;} + #define CONFIG_MATCH_CLEAR() {match_mode = CLEAR;} +#endif #if defined(ALTSS_RX_ATTACHINTERRUPT) // Use Arduino functions for RX pin void INPUT_PIN_ISR(); #define DETACH_PIN_ISR() (detachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN))) - #define ATTACH_PIN_ISR(MODE) {DETACH_PIN_ISR(); \ - attachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN), INPUT_PIN_ISR, MODE);} - #define ENABLE_INT_INPUT_CAPTURE() (ATTACH_PIN_ISR(CHANGE)) + #define ATTACH_PIN_ISR(MODE) {attachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN), INPUT_PIN_ISR, MODE);} + #define ENABLE_INT_INPUT_CAPTURE() (ATTACH_PIN_ISR(FALLING)) #define DISABLE_INT_INPUT_CAPTURE() (DETACH_PIN_ISR()) #define CONFIG_CAPTURE_FALLING_EDGE() (ATTACH_PIN_ISR(FALLING)) #define CONFIG_CAPTURE_RISING_EDGE() (ATTACH_PIN_ISR(RISING)) - #define CONFIG_MATCH_NORMAL() {match_mode = NORMAL;} - #define CONFIG_MATCH_SET() {match_mode = SET;} - #define CONFIG_MATCH_CLEAR() {match_mode = CLEAR;} #endif @@ -252,8 +253,6 @@ ALTSS_SAMD_TC->COUNT16.CTRLA.bit.SWRST = 1; while(ALTSS_SAMD_TC->COUNT16.CTRLA.bit.SWRST); // Wait for reset to complete - // TC3->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val; // Counter in 32-bit mode - // TC3->COUNT16.CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; // WaveGen Mode: Normal Frequency (Top Value is Max) ALTSS_SAMD_TC->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_Val; // Set prescaler bits while(ALTSS_SAMD_TC->COUNT16.STATUS.bit.SYNCBUSY); @@ -274,10 +273,10 @@ #define SET_COMPARE_A(val) (ALTSS_SAMD_TC->COUNT16.CC[0].reg = val) #define SET_COMPARE_B(val) (ALTSS_SAMD_TC->COUNT16.CC[1].reg = val) - #define ENABLE_INT_COMPARE_A() (ALTSS_SAMD_TC->COUNT16.INTENSET.reg = TC_INTENSET_MC0) - #define ENABLE_INT_COMPARE_B() (ALTSS_SAMD_TC->COUNT16.INTENSET.reg = TC_INTENSET_MC1) - #define DISABLE_INT_COMPARE_A() (ALTSS_SAMD_TC->COUNT16.INTENCLR.reg = TC_INTENSET_MC0) - #define DISABLE_INT_COMPARE_B() (ALTSS_SAMD_TC->COUNT16.INTENCLR.reg = TC_INTENCLR_MC1) + #define ENABLE_INT_COMPARE_A() {ALTSS_SAMD_TC->COUNT16.INTENSET.bit.MC0 = 1 ; ALTSS_SAMD_TC->COUNT16.INTFLAG.bit.MC0 = 1;} + #define ENABLE_INT_COMPARE_B() {ALTSS_SAMD_TC->COUNT16.INTENSET.bit.MC1 = 1 ; ALTSS_SAMD_TC->COUNT16.INTFLAG.bit.MC1 = 1;} + #define DISABLE_INT_COMPARE_A() (ALTSS_SAMD_TC->COUNT16.INTENCLR.bit.MC0 = 1) + #define DISABLE_INT_COMPARE_B() (ALTSS_SAMD_TC->COUNT16.INTENCLR.bit.MC1 = 1) #define GET_TIMER_COUNT() (timer_request_read()) #define GET_INPUT_CAPTURE() (timer_request_read()) From 2f7fc02a41a1b35ebe6e265e04e73d276e56b2d4 Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Fri, 31 May 2024 17:30:02 +0200 Subject: [PATCH 4/8] Add some more comments --- config/AltSoftSerial_Boards.h | 2 +- config/AltSoftSerial_Timers.h | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/config/AltSoftSerial_Boards.h b/config/AltSoftSerial_Boards.h index 343b357..a0cc8b0 100644 --- a/config/AltSoftSerial_Boards.h +++ b/config/AltSoftSerial_Boards.h @@ -146,7 +146,7 @@ #define ALTSS_RX_ATTACHINTERRUPT #define ALTSS_USE_SAMD_TIMER3 - #define INPUT_CAPTURE_PIN A2 // receive + #define INPUT_CAPTURE_PIN 3 // receive #define OUTPUT_COMPARE_A_PIN 2 // transmit // Unknown board diff --git a/config/AltSoftSerial_Timers.h b/config/AltSoftSerial_Timers.h index efaa071..0bba61b 100644 --- a/config/AltSoftSerial_Timers.h +++ b/config/AltSoftSerial_Timers.h @@ -187,17 +187,29 @@ #define ALTSS_SAMD_TC TC3 #define ALTSS_SAMD_TIMER_HANDLER TC3_Handler #define ALTSS_SAMD_TIMER_IRQn TC3_IRQn - #define ALTSS_SAMD_GCLK_ID 3 // use Generic Clock 3 + #define ALTSS_SAMD_GCLK_ID 3 // use Generic Clock 3 #define ALTSS_SAMD_GCLK_CLKCTRL_ID GCLK_CLKCTRL_ID_TCC2_TC3_Val #endif +/* ========================== + * AltSoftSerial - use TX digitalWrite + * - TX pin can be set to any pin + * - should work on all Arduino Boards + * (introduced for SAMD boards, work up to 19200 baud) + */ #if defined(ALTSS_TX_DIGITALWRITE) #define CONFIG_MATCH_NORMAL() {match_mode = NORMAL;} #define CONFIG_MATCH_SET() {match_mode = SET;} #define CONFIG_MATCH_CLEAR() {match_mode = CLEAR;} #endif +/* ========================== + * AltSoftSerial - use RX attachInterrupt + * - RX pin can be set to any pin + * - should work on all Arduino Boards + * (introduced for SAMD boards, work up to 19200 baud) + */ #if defined(ALTSS_RX_ATTACHINTERRUPT) // Use Arduino functions for RX pin void INPUT_PIN_ISR(); @@ -212,6 +224,11 @@ #endif +/* ========================== + * AltSoftSerial - SAMD Timer Setup + * - should work on all SAMD boards, tested only on `Arduino MKR Zero` + * - using MACROS which where originally designed for AVR boards + */ #if defined (ALTSS_SAMD) // Request the current value of the COUNT register inline void timer_request(){ From 5288fb7b1bbd5435bb1b0394d6db61829a49568d Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Fri, 31 May 2024 19:33:17 +0200 Subject: [PATCH 5/8] Add configurable Pins for SAMD architecture & invertible output --- AltSoftSerial.cpp | 16 +++++++++------- AltSoftSerial.h | 13 ++++++++++++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/AltSoftSerial.cpp b/AltSoftSerial.cpp index c18d2cb..f59ff8b 100644 --- a/AltSoftSerial.cpp +++ b/AltSoftSerial.cpp @@ -32,8 +32,6 @@ #include "AltSoftSerial.h" -#include "config/AltSoftSerial_Boards.h" -#include "config/AltSoftSerial_Timers.h" /****************************************/ /** Initialization **/ @@ -60,6 +58,10 @@ static volatile uint8_t tx_buffer_tail; #define TX_BUFFER_SIZE 68 static volatile uint8_t tx_buffer[TX_BUFFER_SIZE]; +volatile PinStatus AltSoftSerial::uart_HIGH = HIGH; +volatile PinStatus AltSoftSerial::uart_LOW = LOW; +volatile pin_size_t AltSoftSerial::tx_pin = OUTPUT_COMPARE_A_PIN; +volatile pin_size_t AltSoftSerial::rx_pin = INPUT_CAPTURE_PIN; #ifndef INPUT_PULLUP #define INPUT_PULLUP INPUT @@ -111,9 +113,9 @@ void AltSoftSerial::init(uint32_t cycles_per_bit) } ticks_per_bit = cycles_per_bit; rx_stop_ticks = cycles_per_bit * 37 / 4; - pinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP); - digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH); - pinMode(OUTPUT_COMPARE_A_PIN, OUTPUT); + pinMode(rx_pin, INPUT_PULLUP); + digitalWrite(tx_pin, uart_HIGH); + pinMode(tx_pin, OUTPUT); rx_state = 0; rx_buffer_head = 0; rx_buffer_tail = 0; @@ -188,10 +190,10 @@ ISR(COMPARE_A_INTERRUPT) case NORMAL: break; case SET: - digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH); + digitalWrite(AltSoftSerial::tx_pin, AltSoftSerial::uart_HIGH); break; case CLEAR: - digitalWrite(OUTPUT_COMPARE_A_PIN, LOW); + digitalWrite(AltSoftSerial::tx_pin, AltSoftSerial::uart_LOW); break; }; #endif diff --git a/AltSoftSerial.h b/AltSoftSerial.h index 7860fdd..eb8a690 100644 --- a/AltSoftSerial.h +++ b/AltSoftSerial.h @@ -26,6 +26,10 @@ #include +#include +#include "config/AltSoftSerial_Boards.h" +#include "config/AltSoftSerial_Timers.h" + #if ARDUINO >= 100 #include "Arduino.h" #else @@ -61,13 +65,20 @@ class AltSoftSerial : public Stream static void flushInput(); static void flushOutput(); // for drop-in compatibility with NewSoftSerial, rxPin & txPin ignored - AltSoftSerial(uint8_t rxPin, uint8_t txPin, bool inverse = false) { (void)rxPin; (void)txPin; (void)inverse; } + AltSoftSerial(uint8_t rxPin, uint8_t txPin, bool inverse = false) // WARNING: only applicable for SAMD boards & `inverse` only affects TX polarity + { tx_pin = txPin; rx_pin = rxPin; if(inverse) {uart_HIGH = LOW; uart_LOW = HIGH;} } bool listen() { return false; } bool isListening() { return true; } bool overflow() { bool r = timing_error; timing_error = false; return r; } static int library_version() { return 1; } static void enable_timer0(bool enable) { (void)enable; } static bool timing_error; + + // used by ISR (must be public) + static volatile PinStatus uart_HIGH; + static volatile PinStatus uart_LOW; + static volatile pin_size_t tx_pin; + static volatile pin_size_t rx_pin; private: static void init(uint32_t cycles_per_bit); static void writeByte(uint8_t byte); From 5691223e9884e2c244a7c69e18bde21e730f66a2 Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Mon, 3 Jun 2024 18:32:41 +0200 Subject: [PATCH 6/8] Fix: ATTACH_ISR macros where not using new pin definition --- config/AltSoftSerial_Timers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/AltSoftSerial_Timers.h b/config/AltSoftSerial_Timers.h index 0bba61b..255034b 100644 --- a/config/AltSoftSerial_Timers.h +++ b/config/AltSoftSerial_Timers.h @@ -214,8 +214,8 @@ // Use Arduino functions for RX pin void INPUT_PIN_ISR(); - #define DETACH_PIN_ISR() (detachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN))) - #define ATTACH_PIN_ISR(MODE) {attachInterrupt(digitalPinToInterrupt(INPUT_CAPTURE_PIN), INPUT_PIN_ISR, MODE);} + #define DETACH_PIN_ISR() (detachInterrupt(digitalPinToInterrupt(AltSoftSerial::rx_pin))) + #define ATTACH_PIN_ISR(MODE) {attachInterrupt(digitalPinToInterrupt(AltSoftSerial::rx_pin), INPUT_PIN_ISR, MODE);} #define ENABLE_INT_INPUT_CAPTURE() (ATTACH_PIN_ISR(FALLING)) #define DISABLE_INT_INPUT_CAPTURE() (DETACH_PIN_ISR()) #define CONFIG_CAPTURE_FALLING_EDGE() (ATTACH_PIN_ISR(FALLING)) From b9eb964fb6a00d20a9ce85e5d707256d9b053959 Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Wed, 3 Jul 2024 20:23:10 +0200 Subject: [PATCH 7/8] Bug-fix: AltSoftSerial need INPUT_PULLUP define, but the MKR Arduino lib does use an PinMode enum. --- config/AltSoftSerial_Boards.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config/AltSoftSerial_Boards.h b/config/AltSoftSerial_Boards.h index a0cc8b0..cda2556 100644 --- a/config/AltSoftSerial_Boards.h +++ b/config/AltSoftSerial_Boards.h @@ -144,6 +144,7 @@ #define ALTSS_SAMD #define ALTSS_TX_DIGITALWRITE #define ALTSS_RX_ATTACHINTERRUPT + #define INPUT_PULLUP PinMode::INPUT_PULLUP // an enum is used, but AltSoftSerial.cpp does need a define #define ALTSS_USE_SAMD_TIMER3 #define INPUT_CAPTURE_PIN 3 // receive From fa16e88bf9e1936d503f4517924c49c57b9dd2f3 Mon Sep 17 00:00:00 2001 From: simon-77 <30923077+simon-77@users.noreply.github.com> Date: Wed, 3 Jul 2024 20:25:44 +0200 Subject: [PATCH 8/8] Bug fix: for SAMD architecture needs to check interrupt-enable additionally to interrupt-flag --- AltSoftSerial.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/AltSoftSerial.cpp b/AltSoftSerial.cpp index f59ff8b..bbcefe6 100644 --- a/AltSoftSerial.cpp +++ b/AltSoftSerial.cpp @@ -420,17 +420,20 @@ void ftm0_isr(void) // SAMD architecture uses only one ISR for all timer events void ALTSS_SAMD_TIMER_HANDLER() { - uint8_t status = ALTSS_SAMD_TC->COUNT16.INTFLAG.reg; + uint8_t int_flag = ALTSS_SAMD_TC->COUNT16.INTFLAG.reg; // flags are also set when interrupt is not enabled + uint8_t int_ena = ALTSS_SAMD_TC->COUNT16.INTENSET.reg; // get interrupt enable flags + uint8_t isr_trigger = int_flag & int_ena; // interrupt triggers uint8_t clear = 0; - if (status & TC_INTFLAG_MC0){ - clear = TC_INTFLAG_MC0; - COMPARE_A_ISR(); - } else if (status & TC_INTFLAG_MC1){ - clear = TC_INTFLAG_MC1; - COMPARE_B_ISR(); - } else { // unknown interrupt -> clear all set flags - clear = status; - } - ALTSS_SAMD_TC->COUNT16.INTFLAG.reg = clear; + + if (isr_trigger & TC_INTFLAG_MC0){ + clear |= TC_INTFLAG_MC0; // Use bitwise OR to accumulate flags + COMPARE_A_ISR(); + } + if (isr_trigger & TC_INTFLAG_MC1){ + clear |= TC_INTFLAG_MC1; // Use bitwise OR to accumulate flags + COMPARE_B_ISR(); + } + + ALTSS_SAMD_TC->COUNT16.INTFLAG.reg = clear; // clear interrupt triggers } #endif \ No newline at end of file