From 19b4476cbbdb5fd43491ea11a94df12c4507e2a0 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Mon, 28 Jan 2019 13:48:52 +0100 Subject: [PATCH] Implement PWM interface for SAMD21 (#157) * machine/atsamd21: implement PWM interface for all pins that support it * machine/atsamd21: correct PWM channel mapping for pin PA18 * machine/atsamd21: move clock init into InitPWM() to hopefully save power --- src/machine/board_itsybitsy-m0.go | 28 +- src/machine/machine_atsamd21g18.go | 620 +++++++++++++++++++---------- 2 files changed, 428 insertions(+), 220 deletions(-) diff --git a/src/machine/board_itsybitsy-m0.go b/src/machine/board_itsybitsy-m0.go index 6c369e1b..8bf35743 100644 --- a/src/machine/board_itsybitsy-m0.go +++ b/src/machine/board_itsybitsy-m0.go @@ -4,20 +4,20 @@ package machine // GPIO Pins const ( - D0 = 11 // UART0 RX: SERCOM0/PAD[3] - D1 = 10 // UART0 TX: SERCOM0/PAD[2] + D0 = 11 // UART0 RX + D1 = 10 // UART0 TX D2 = 14 - D3 = 9 - D4 = 8 - D5 = 15 - D6 = 20 + D3 = 9 // PWM available + D4 = 8 // PWM available + D5 = 15 // PWM available + D6 = 20 // PWM available D7 = 21 - D8 = 6 - D9 = 7 - D10 = 18 // UART1 TX(1): SERCOM1/PAD[2] can be used for UART1 TX - D11 = 16 // UART1 TX(2): SERCOM1/PAD[0] can be used for UART1 TX - D12 = 19 - D13 = 17 + D8 = 6 // PWM available + D9 = 7 // PWM available + D10 = 18 // can be used for PWM or UART1 RX + D11 = 16 // can be used for PWM or UART1 TX + D12 = 19 // PWM available + D13 = 17 // PWM available ) const ( @@ -32,6 +32,6 @@ const ( // I2C pins const ( - SDA_PIN = 22 // // SDA: SERCOM3/PAD[0] - SCL_PIN = 23 // // SCL: SERCOM3/PAD[1] + SDA_PIN = 22 // SDA: SERCOM3/PAD[0] + SCL_PIN = 23 // SCL: SERCOM3/PAD[1] ) diff --git a/src/machine/machine_atsamd21g18.go b/src/machine/machine_atsamd21g18.go index 4551ae08..1747672c 100644 --- a/src/machine/machine_atsamd21g18.go +++ b/src/machine/machine_atsamd21g18.go @@ -77,224 +77,22 @@ func (p GPIO) Set(high bool) { // getPMux returns the value for the correct PMUX register for this pin. func (p GPIO) getPMux() sam.RegValue8 { - pin := p.Pin >> 1 - switch pin { - case 0: - return sam.PORT.PMUX0_0 - case 1: - return sam.PORT.PMUX0_1 - case 2: - return sam.PORT.PMUX0_2 - case 3: - return sam.PORT.PMUX0_3 - case 4: - return sam.PORT.PMUX0_4 - case 5: - return sam.PORT.PMUX0_5 - case 6: - return sam.PORT.PMUX0_6 - case 7: - return sam.PORT.PMUX0_7 - case 8: - return sam.PORT.PMUX0_8 - case 9: - return sam.PORT.PMUX0_9 - case 10: - return sam.PORT.PMUX0_10 - case 11: - return sam.PORT.PMUX0_11 - case 12: - return sam.PORT.PMUX0_12 - case 13: - return sam.PORT.PMUX0_13 - case 14: - return sam.PORT.PMUX0_14 - case 15: - return sam.PORT.PMUX0_15 - default: - return 0 - } + return getPMux(p.Pin) } // setPMux sets the value for the correct PMUX register for this pin. func (p GPIO) setPMux(val sam.RegValue8) { - pin := p.Pin >> 1 - switch pin { - case 0: - sam.PORT.PMUX0_0 = val - case 1: - sam.PORT.PMUX0_1 = val - case 2: - sam.PORT.PMUX0_2 = val - case 3: - sam.PORT.PMUX0_3 = val - case 4: - sam.PORT.PMUX0_4 = val - case 5: - sam.PORT.PMUX0_5 = val - case 6: - sam.PORT.PMUX0_6 = val - case 7: - sam.PORT.PMUX0_7 = val - case 8: - sam.PORT.PMUX0_8 = val - case 9: - sam.PORT.PMUX0_9 = val - case 10: - sam.PORT.PMUX0_10 = val - case 11: - sam.PORT.PMUX0_11 = val - case 12: - sam.PORT.PMUX0_12 = val - case 13: - sam.PORT.PMUX0_13 = val - case 14: - sam.PORT.PMUX0_14 = val - case 15: - sam.PORT.PMUX0_15 = val - } + setPMux(p.Pin, val) } // getPinCfg returns the value for the correct PINCFG register for this pin. func (p GPIO) getPinCfg() sam.RegValue8 { - switch p.Pin { - case 0: - return sam.PORT.PINCFG0_0 - case 1: - return sam.PORT.PINCFG0_1 - case 2: - return sam.PORT.PINCFG0_2 - case 3: - return sam.PORT.PINCFG0_3 - case 4: - return sam.PORT.PINCFG0_4 - case 5: - return sam.PORT.PINCFG0_5 - case 6: - return sam.PORT.PINCFG0_6 - case 7: - return sam.PORT.PINCFG0_7 - case 8: - return sam.PORT.PINCFG0_8 - case 9: - return sam.PORT.PINCFG0_9 - case 10: - return sam.PORT.PINCFG0_10 - case 11: - return sam.PORT.PINCFG0_11 - case 12: - return sam.PORT.PINCFG0_12 - case 13: - return sam.PORT.PINCFG0_13 - case 14: - return sam.PORT.PINCFG0_14 - case 15: - return sam.PORT.PINCFG0_15 - case 16: - return sam.PORT.PINCFG0_16 - case 17: - return sam.PORT.PINCFG0_17 - case 18: - return sam.PORT.PINCFG0_18 - case 19: - return sam.PORT.PINCFG0_19 - case 20: - return sam.PORT.PINCFG0_20 - case 21: - return sam.PORT.PINCFG0_21 - case 22: - return sam.PORT.PINCFG0_22 - case 23: - return sam.PORT.PINCFG0_23 - case 24: - return sam.PORT.PINCFG0_24 - case 25: - return sam.PORT.PINCFG0_25 - case 26: - return sam.PORT.PINCFG0_26 - case 27: - return sam.PORT.PINCFG0_27 - case 28: - return sam.PORT.PINCFG0_28 - case 29: - return sam.PORT.PINCFG0_29 - case 30: - return sam.PORT.PINCFG0_30 - case 31: - return sam.PORT.PINCFG0_31 - default: - return 0 - } + return getPinCfg(p.Pin) } // setPinCfg sets the value for the correct PINCFG register for this pin. func (p GPIO) setPinCfg(val sam.RegValue8) { - switch p.Pin { - case 0: - sam.PORT.PINCFG0_0 = val - case 1: - sam.PORT.PINCFG0_1 = val - case 2: - sam.PORT.PINCFG0_2 = val - case 3: - sam.PORT.PINCFG0_3 = val - case 4: - sam.PORT.PINCFG0_4 = val - case 5: - sam.PORT.PINCFG0_5 = val - case 6: - sam.PORT.PINCFG0_6 = val - case 7: - sam.PORT.PINCFG0_7 = val - case 8: - sam.PORT.PINCFG0_8 = val - case 9: - sam.PORT.PINCFG0_9 = val - case 10: - sam.PORT.PINCFG0_10 = val - case 11: - sam.PORT.PINCFG0_11 = val - case 12: - sam.PORT.PINCFG0_12 = val - case 13: - sam.PORT.PINCFG0_13 = val - case 14: - sam.PORT.PINCFG0_14 = val - case 15: - sam.PORT.PINCFG0_15 = val - case 16: - sam.PORT.PINCFG0_16 = val - case 17: - sam.PORT.PINCFG0_17 = val - case 18: - sam.PORT.PINCFG0_18 = val - case 19: - sam.PORT.PINCFG0_19 = val - case 20: - sam.PORT.PINCFG0_20 = val - case 21: - sam.PORT.PINCFG0_21 = val - case 22: - sam.PORT.PINCFG0_22 = val - case 23: - sam.PORT.PINCFG0_23 = val - case 24: - sam.PORT.PINCFG0_24 = val - case 25: - sam.PORT.PINCFG0_25 = val - case 26: - sam.PORT.PINCFG0_26 = val - case 27: - sam.PORT.PINCFG0_27 = val - case 28: - sam.PORT.PINCFG0_28 = val - case 29: - sam.PORT.PINCFG0_29 = val - case 30: - sam.PORT.PINCFG0_30 = val - case 31: - sam.PORT.PINCFG0_31 = val - } + setPinCfg(p.Pin, val) } // UART on the SAMD21. @@ -698,3 +496,413 @@ func (i2c I2C) readByte() byte { } return byte(i2c.Bus.DATA) } + +// PWM +const period = 0xFFFF + +// InitPWM initializes the PWM interface. +func InitPWM() { + // turn on timer clocks used for PWM + sam.PM.APBCMASK |= sam.PM_APBCMASK_TCC0_ | sam.PM_APBCMASK_TCC1_ | sam.PM_APBCMASK_TCC2_ + + // Use GCLK0 for TCC0/TCC1 + sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 { + } + + // Use GCLK0 for TCC2/TC3 + sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 { + } +} + +// Configure configures a PWM pin for output. +func (pwm PWM) Configure() { + // figure out which TCCX timer for this pin + timer := pwm.getTimer() + + // disable timer + timer.CTRLA &^= sam.TCC_CTRLA_ENABLE + // Wait for synchronization + for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 { + } + + // Use "Normal PWM" (single-slope PWM) + timer.WAVE |= sam.TCC_WAVE_WAVEGEN_NPWM + // Wait for synchronization + for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_WAVE) > 0 { + } + + // Set the period (the number to count to (TOP) before resetting timer) + //TCC0->PER.reg = period; + timer.PER = period + // Wait for synchronization + for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_PER) > 0 { + } + + // Set pin as output + sam.PORT.DIRSET0 = (1 << pwm.Pin) + // Set pin to low + sam.PORT.OUTCLR0 = (1 << pwm.Pin) + + // Enable the port multiplexer for pin + pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN) + + // Connect TCCX timer to pin. + // we normally use the F channel aka ALT + pwmConfig := GPIO_PWM_ALT + + // in the case of PA6 or PA7 we have to use E channel + if pwm.Pin == 6 || pwm.Pin == 7 { + pwmConfig = GPIO_PWM + } + + if pwm.Pin&1 > 0 { + // odd pin, so save the even pins + val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk + pwm.setPMux(val | sam.RegValue8(pwmConfig< 0 { + } + + // Set PWM signal to output duty cycle + pwm.setChannel(sam.RegValue(value)) + + // Wait for synchronization on all channels + for (timer.SYNCBUSY & (sam.TCC_SYNCBUSY_CC0 | + sam.TCC_SYNCBUSY_CC1 | + sam.TCC_SYNCBUSY_CC2 | + sam.TCC_SYNCBUSY_CC3)) > 0 { + } + + // enable + timer.CTRLA |= sam.TCC_CTRLA_ENABLE + // Wait for synchronization + for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 { + } +} + +// getPMux returns the value for the correct PMUX register for this pin. +func (pwm PWM) getPMux() sam.RegValue8 { + return getPMux(pwm.Pin) +} + +// setPMux sets the value for the correct PMUX register for this pin. +func (pwm PWM) setPMux(val sam.RegValue8) { + setPMux(pwm.Pin, val) +} + +// getPinCfg returns the value for the correct PINCFG register for this pin. +func (pwm PWM) getPinCfg() sam.RegValue8 { + return getPinCfg(pwm.Pin) +} + +// setPinCfg sets the value for the correct PINCFG register for this pin. +func (pwm PWM) setPinCfg(val sam.RegValue8) { + setPinCfg(pwm.Pin, val) +} + +// getPMux returns the value for the correct PMUX register for this pin. +func getPMux(p uint8) sam.RegValue8 { + pin := p >> 1 + switch pin { + case 0: + return sam.PORT.PMUX0_0 + case 1: + return sam.PORT.PMUX0_1 + case 2: + return sam.PORT.PMUX0_2 + case 3: + return sam.PORT.PMUX0_3 + case 4: + return sam.PORT.PMUX0_4 + case 5: + return sam.PORT.PMUX0_5 + case 6: + return sam.PORT.PMUX0_6 + case 7: + return sam.PORT.PMUX0_7 + case 8: + return sam.PORT.PMUX0_8 + case 9: + return sam.PORT.PMUX0_9 + case 10: + return sam.PORT.PMUX0_10 + case 11: + return sam.PORT.PMUX0_11 + case 12: + return sam.PORT.PMUX0_12 + case 13: + return sam.PORT.PMUX0_13 + case 14: + return sam.PORT.PMUX0_14 + case 15: + return sam.PORT.PMUX0_15 + default: + return 0 + } +} + +// setPMux sets the value for the correct PMUX register for this pin. +func setPMux(p uint8, val sam.RegValue8) { + pin := p >> 1 + switch pin { + case 0: + sam.PORT.PMUX0_0 = val + case 1: + sam.PORT.PMUX0_1 = val + case 2: + sam.PORT.PMUX0_2 = val + case 3: + sam.PORT.PMUX0_3 = val + case 4: + sam.PORT.PMUX0_4 = val + case 5: + sam.PORT.PMUX0_5 = val + case 6: + sam.PORT.PMUX0_6 = val + case 7: + sam.PORT.PMUX0_7 = val + case 8: + sam.PORT.PMUX0_8 = val + case 9: + sam.PORT.PMUX0_9 = val + case 10: + sam.PORT.PMUX0_10 = val + case 11: + sam.PORT.PMUX0_11 = val + case 12: + sam.PORT.PMUX0_12 = val + case 13: + sam.PORT.PMUX0_13 = val + case 14: + sam.PORT.PMUX0_14 = val + case 15: + sam.PORT.PMUX0_15 = val + } +} + +// getPinCfg returns the value for the correct PINCFG register for this pin. +func getPinCfg(p uint8) sam.RegValue8 { + switch p { + case 0: + return sam.PORT.PINCFG0_0 + case 1: + return sam.PORT.PINCFG0_1 + case 2: + return sam.PORT.PINCFG0_2 + case 3: + return sam.PORT.PINCFG0_3 + case 4: + return sam.PORT.PINCFG0_4 + case 5: + return sam.PORT.PINCFG0_5 + case 6: + return sam.PORT.PINCFG0_6 + case 7: + return sam.PORT.PINCFG0_7 + case 8: + return sam.PORT.PINCFG0_8 + case 9: + return sam.PORT.PINCFG0_9 + case 10: + return sam.PORT.PINCFG0_10 + case 11: + return sam.PORT.PINCFG0_11 + case 12: + return sam.PORT.PINCFG0_12 + case 13: + return sam.PORT.PINCFG0_13 + case 14: + return sam.PORT.PINCFG0_14 + case 15: + return sam.PORT.PINCFG0_15 + case 16: + return sam.PORT.PINCFG0_16 + case 17: + return sam.PORT.PINCFG0_17 + case 18: + return sam.PORT.PINCFG0_18 + case 19: + return sam.PORT.PINCFG0_19 + case 20: + return sam.PORT.PINCFG0_20 + case 21: + return sam.PORT.PINCFG0_21 + case 22: + return sam.PORT.PINCFG0_22 + case 23: + return sam.PORT.PINCFG0_23 + case 24: + return sam.PORT.PINCFG0_24 + case 25: + return sam.PORT.PINCFG0_25 + case 26: + return sam.PORT.PINCFG0_26 + case 27: + return sam.PORT.PINCFG0_27 + case 28: + return sam.PORT.PINCFG0_28 + case 29: + return sam.PORT.PINCFG0_29 + case 30: + return sam.PORT.PINCFG0_30 + case 31: + return sam.PORT.PINCFG0_31 + default: + return 0 + } +} + +// setPinCfg sets the value for the correct PINCFG register for this pin. +func setPinCfg(p uint8, val sam.RegValue8) { + switch p { + case 0: + sam.PORT.PINCFG0_0 = val + case 1: + sam.PORT.PINCFG0_1 = val + case 2: + sam.PORT.PINCFG0_2 = val + case 3: + sam.PORT.PINCFG0_3 = val + case 4: + sam.PORT.PINCFG0_4 = val + case 5: + sam.PORT.PINCFG0_5 = val + case 6: + sam.PORT.PINCFG0_6 = val + case 7: + sam.PORT.PINCFG0_7 = val + case 8: + sam.PORT.PINCFG0_8 = val + case 9: + sam.PORT.PINCFG0_9 = val + case 10: + sam.PORT.PINCFG0_10 = val + case 11: + sam.PORT.PINCFG0_11 = val + case 12: + sam.PORT.PINCFG0_12 = val + case 13: + sam.PORT.PINCFG0_13 = val + case 14: + sam.PORT.PINCFG0_14 = val + case 15: + sam.PORT.PINCFG0_15 = val + case 16: + sam.PORT.PINCFG0_16 = val + case 17: + sam.PORT.PINCFG0_17 = val + case 18: + sam.PORT.PINCFG0_18 = val + case 19: + sam.PORT.PINCFG0_19 = val + case 20: + sam.PORT.PINCFG0_20 = val + case 21: + sam.PORT.PINCFG0_21 = val + case 22: + sam.PORT.PINCFG0_22 = val + case 23: + sam.PORT.PINCFG0_23 = val + case 24: + sam.PORT.PINCFG0_24 = val + case 25: + sam.PORT.PINCFG0_25 = val + case 26: + sam.PORT.PINCFG0_26 = val + case 27: + sam.PORT.PINCFG0_27 = val + case 28: + sam.PORT.PINCFG0_28 = val + case 29: + sam.PORT.PINCFG0_29 = val + case 30: + sam.PORT.PINCFG0_30 = val + case 31: + sam.PORT.PINCFG0_31 = val + } +} + +// getTimer returns the timer to be used for PWM on this pin +func (pwm PWM) getTimer() *sam.TCC_Type { + switch pwm.Pin { + case 6: + return sam.TCC1 + case 7: + return sam.TCC1 + case 8: + return sam.TCC1 + case 9: + return sam.TCC1 + case 14: + return sam.TCC0 + case 15: + return sam.TCC0 + case 16: + return sam.TCC0 + case 17: + return sam.TCC0 + case 18: + return sam.TCC0 + case 19: + return sam.TCC0 + case 20: + return sam.TCC0 + case 21: + return sam.TCC0 + default: + return nil // not supported on this pin + } +} + +// setChannel sets the value for the correct channel for PWM on this pin +func (pwm PWM) setChannel(val sam.RegValue) { + switch pwm.Pin { + case 6: + pwm.getTimer().CC0 = val + case 7: + pwm.getTimer().CC1 = val + case 8: + pwm.getTimer().CC0 = val + case 9: + pwm.getTimer().CC1 = val + case 14: + pwm.getTimer().CC0 = val + case 15: + pwm.getTimer().CC1 = val + case 16: + pwm.getTimer().CC2 = val + case 17: + pwm.getTimer().CC3 = val + case 18: + pwm.getTimer().CC2 = val + case 19: + pwm.getTimer().CC3 = val + case 20: + pwm.getTimer().CC2 = val + case 21: + pwm.getTimer().CC3 = val + default: + return // not supported on this pin + } +}