mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 15:33:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13c0714fda |
@@ -172,16 +172,15 @@ commands:
|
|||||||
key: llvm-build-11-linux-v2-assert
|
key: llvm-build-11-linux-v2-assert
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run: make ASSERT=1
|
- run: |
|
||||||
|
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
||||||
|
# necessary to keep memory consumption down and avoid OOM (for a
|
||||||
|
# 2CPU/4GB executor).
|
||||||
|
GOFLAGS="-p=2" make ASSERT=1
|
||||||
- build-wasi-libc
|
- build-wasi-libc
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make ASSERT=1 test
|
command: make ASSERT=1 test
|
||||||
environment:
|
|
||||||
# Note: -p=2 limits parallelism to two jobs at a time, which is
|
|
||||||
# necessary to keep memory consumption down and avoid OOM (for a
|
|
||||||
# 2CPU/4GB executor).
|
|
||||||
GOFLAGS: -p=2
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
|
|||||||
@@ -120,22 +120,22 @@ gen-device-avr:
|
|||||||
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||||
|
|
||||||
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o $@ ./tools/gen-device-svd/
|
$(GO) build -o $@ ./tools/gen-device-svd/
|
||||||
|
|
||||||
gen-device-esp: build/gen-device-svd
|
gen-device-esp: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -only-used -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/esp
|
GO111MODULE=off $(GO) fmt ./src/device/esp
|
||||||
|
|
||||||
gen-device-nrf: build/gen-device-svd
|
gen-device-nrf: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk -only-used lib/nrfx/mdk/ src/device/nrf/
|
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||||
|
|
||||||
gen-device-nxp: build/gen-device-svd
|
gen-device-nxp: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/NXP -only-used lib/cmsis-svd/data/NXP/ src/device/nxp/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/NXP lib/cmsis-svd/data/NXP/ src/device/nxp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/nxp
|
GO111MODULE=off $(GO) fmt ./src/device/nxp
|
||||||
|
|
||||||
gen-device-sam: build/gen-device-svd
|
gen-device-sam: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel -only-used lib/cmsis-svd/data/Atmel/ src/device/sam/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel lib/cmsis-svd/data/Atmel/ src/device/sam/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/sam
|
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||||
|
|
||||||
gen-device-sifive: build/gen-device-svd
|
gen-device-sifive: build/gen-device-svd
|
||||||
@@ -143,11 +143,11 @@ gen-device-sifive: build/gen-device-svd
|
|||||||
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||||
|
|
||||||
gen-device-kendryte: build/gen-device-svd
|
gen-device-kendryte: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Kendryte-Community -only-used -interrupts=software lib/cmsis-svd/data/Kendryte-Community/ src/device/kendryte/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Kendryte-Community -interrupts=software lib/cmsis-svd/data/Kendryte-Community/ src/device/kendryte/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/kendryte
|
GO111MODULE=off $(GO) fmt ./src/device/kendryte
|
||||||
|
|
||||||
gen-device-stm32: build/gen-device-svd
|
gen-device-stm32: build/gen-device-svd
|
||||||
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd -only-used lib/stm32-svd/svd src/device/stm32/
|
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||||
|
|
||||||
|
|
||||||
@@ -341,6 +341,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(STM32), 0)
|
ifneq ($(STM32), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -352,8 +354,6 @@ ifneq ($(STM32), 0)
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l031k6 examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ require (
|
|||||||
go.bug.st/serial v1.1.2
|
go.bug.st/serial v1.1.2
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,5 +59,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c h1:vn9IPshzYmzZis10UEVrsIBRv9FpykADw6M3/tHHROg=
|
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
|
||||||
|
|||||||
+1
-14
@@ -15,7 +15,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -129,18 +128,6 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles.
|
|
||||||
// Therefore, we put a lock around builds and run everything else in parallel.
|
|
||||||
var buildLock sync.Mutex
|
|
||||||
|
|
||||||
// runBuild is a thread-safe wrapper around Build.
|
|
||||||
func runBuild(src, out string, opts *compileopts.Options) error {
|
|
||||||
buildLock.Lock()
|
|
||||||
defer buildLock.Unlock()
|
|
||||||
|
|
||||||
return Build(src, out, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
@@ -177,7 +164,7 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
|||||||
}
|
}
|
||||||
|
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = runBuild("./"+path, binary, config)
|
err = Build("./"+path, binary, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// +build arduino
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Configuration on an Arduino Uno.
|
|
||||||
pwm = machine.Timer2
|
|
||||||
pinA = machine.PB3 // pin 11 on the Uno
|
|
||||||
pinB = machine.PD3 // pin 3 on the Uno
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// +build feather_m4
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
pwm = machine.TCC0
|
|
||||||
pinA = machine.D12
|
|
||||||
pinB = machine.D13
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// +build itsybitsy_m0
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
pwm = machine.TCC0
|
|
||||||
pinA = machine.D3
|
|
||||||
pinB = machine.D4
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// +build itsybitsy_m4
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
var (
|
|
||||||
pwm = machine.TCC0
|
|
||||||
pinA = machine.D12
|
|
||||||
pinB = machine.D13
|
|
||||||
)
|
|
||||||
+54
-64
@@ -1,74 +1,64 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// This example demonstrates some features of the PWM support.
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const delayBetweenPeriods = time.Second * 5
|
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
|
||||||
|
// Change the values below to use different pins.
|
||||||
|
const (
|
||||||
|
redPin = machine.D4
|
||||||
|
greenPin = machine.D5
|
||||||
|
bluePin = machine.D6
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
// cycleColor is just a placeholder until math/rand or some equivalent is working.
|
||||||
// Delay a bit on startup to easily catch the first messages.
|
func cycleColor(color uint8) uint8 {
|
||||||
time.Sleep(time.Second * 2)
|
if color < 10 {
|
||||||
|
return color + 1
|
||||||
// Configure the PWM with the given period.
|
} else if color < 200 {
|
||||||
err := pwm.Configure(machine.PWMConfig{
|
return color + 10
|
||||||
Period: 16384e3, // 16.384ms
|
} else {
|
||||||
})
|
return 0
|
||||||
if err != nil {
|
}
|
||||||
println("failed to configure PWM")
|
}
|
||||||
return
|
|
||||||
}
|
func main() {
|
||||||
|
machine.InitPWM()
|
||||||
// The top value is the highest value that can be passed to PWMChannel.Set.
|
|
||||||
// It is usually an even number.
|
red := machine.PWM{redPin}
|
||||||
println("top:", pwm.Top())
|
err := red.Configure()
|
||||||
|
checkError(err, "failed to configure red pin")
|
||||||
// Configure the two channels we'll use as outputs.
|
|
||||||
channelA, err := pwm.Channel(pinA)
|
green := machine.PWM{greenPin}
|
||||||
if err != nil {
|
err = green.Configure()
|
||||||
println("failed to configure channel A")
|
checkError(err, "failed to configure green pin")
|
||||||
return
|
|
||||||
}
|
blue := machine.PWM{bluePin}
|
||||||
channelB, err := pwm.Channel(pinB)
|
err = blue.Configure()
|
||||||
if err != nil {
|
checkError(err, "failed to configure blue pin")
|
||||||
println("failed to configure channel B")
|
|
||||||
return
|
var rc uint8
|
||||||
}
|
var gc uint8 = 20
|
||||||
|
var bc uint8 = 30
|
||||||
// Invert one of the channels to demonstrate output polarity.
|
|
||||||
pwm.SetInverting(channelB, true)
|
for {
|
||||||
|
rc = cycleColor(rc)
|
||||||
// Test out various frequencies below, including some edge cases.
|
gc = cycleColor(gc)
|
||||||
|
bc = cycleColor(bc)
|
||||||
println("running at 0% duty cycle")
|
|
||||||
pwm.Set(channelA, 0)
|
red.Set(uint16(rc) << 8)
|
||||||
pwm.Set(channelB, 0)
|
green.Set(uint16(gc) << 8)
|
||||||
time.Sleep(delayBetweenPeriods)
|
blue.Set(uint16(bc) << 8)
|
||||||
|
|
||||||
println("running at 1")
|
time.Sleep(time.Millisecond * 500)
|
||||||
pwm.Set(channelA, 1)
|
}
|
||||||
pwm.Set(channelB, 1)
|
}
|
||||||
time.Sleep(delayBetweenPeriods)
|
|
||||||
|
func checkError(err error, msg string) {
|
||||||
println("running at 25% duty cycle")
|
if err != nil {
|
||||||
pwm.Set(channelA, pwm.Top()/4)
|
print(msg, ": ", err.Error())
|
||||||
pwm.Set(channelB, pwm.Top()/4)
|
println()
|
||||||
time.Sleep(delayBetweenPeriods)
|
|
||||||
|
|
||||||
println("running at top-1")
|
|
||||||
pwm.Set(channelA, pwm.Top()-1)
|
|
||||||
pwm.Set(channelB, pwm.Top()-1)
|
|
||||||
time.Sleep(delayBetweenPeriods)
|
|
||||||
|
|
||||||
println("running at 100% duty cycle")
|
|
||||||
pwm.Set(channelA, pwm.Top())
|
|
||||||
pwm.Set(channelB, pwm.Top())
|
|
||||||
time.Sleep(delayBetweenPeriods)
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const (
|
|||||||
|
|
||||||
// Analog Pins
|
// Analog Pins
|
||||||
const (
|
const (
|
||||||
A0 = PA02 // ADC/AIN[0]
|
A0 = PA02 // PWM available, also ADC/AIN[0]
|
||||||
A1 = PA05 // PWM available, also ADC/AIN[5]
|
A1 = PA05 // ADC/AIN[5]
|
||||||
A2 = PA06 // PWM available, also ADC/AIN[6]
|
A2 = PA06 // PWM available, also ADC/AIN[6]
|
||||||
A3 = PA07 // PWM available, also ADC/AIN[7]
|
A3 = PA07 // PWM available, also ADC/AIN[7]
|
||||||
A4 = PB03 // PORTB
|
A4 = PB03 // PORTB
|
||||||
|
|||||||
@@ -41,17 +41,13 @@ const (
|
|||||||
// LORA RFM95 Radio
|
// LORA RFM95 Radio
|
||||||
RFM95_DIO0_PIN = PC13
|
RFM95_DIO0_PIN = PC13
|
||||||
|
|
||||||
// TinyGo UART is MCU LPUSART1
|
//TinyGo UART is MCU LPUSART1
|
||||||
UART_RX_PIN = PA13
|
UART_RX_PIN = PA13
|
||||||
UART_TX_PIN = PA14
|
UART_TX_PIN = PA14
|
||||||
|
|
||||||
// TinyGo UART1 is MCU USART1
|
//TinyGo UART1 is MCU USART1
|
||||||
UART1_RX_PIN = PB6
|
UART1_RX_PIN = PB6
|
||||||
UART1_TX_PIN = PB7
|
UART1_TX_PIN = PB7
|
||||||
|
|
||||||
// MPU9250 Nine-Axis (Gyro + Accelerometer + Compass)
|
|
||||||
I2C0_SCL_PIN = PA9
|
|
||||||
I2C0_SDA_PIN = PA10
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -72,13 +68,6 @@ var (
|
|||||||
RxAltFuncSelector: 0,
|
RxAltFuncSelector: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
// MPU9250 Nine-Axis (Gyro + Accelerometer + Compass)
|
|
||||||
I2C1 = &I2C{
|
|
||||||
Bus: stm32.I2C1,
|
|
||||||
AltFuncSelector: 6,
|
|
||||||
}
|
|
||||||
I2C0 = I2C1
|
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
SPI0 = SPI{
|
SPI0 = SPI{
|
||||||
Bus: stm32.SPI1,
|
Bus: stm32.SPI1,
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
// +build nucleol031k6
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"runtime/interrupt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LED = LED_BUILTIN
|
|
||||||
LED_BUILTIN = LED_GREEN
|
|
||||||
LED_GREEN = PB3
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Arduino Pins
|
|
||||||
A0 = PA0 // ADC_IN0
|
|
||||||
A1 = PA1 // ADC_IN1
|
|
||||||
A2 = PA3 // ADC_IN3
|
|
||||||
A3 = PA4 // ADC_IN4
|
|
||||||
A4 = PA5 // ADC_IN5 || I2C1_SDA
|
|
||||||
A5 = PA6 // ADC_IN6 || I2C1_SCL
|
|
||||||
A6 = PA7 // ADC_IN7
|
|
||||||
A7 = PA2 // ADC_IN2
|
|
||||||
|
|
||||||
D0 = PA10 // USART1_TX
|
|
||||||
D1 = PA9 // USART1_RX
|
|
||||||
D2 = PA12
|
|
||||||
D3 = PB0 // TIM2_CH3
|
|
||||||
D4 = PB7
|
|
||||||
D5 = PB6 // TIM16_CH1N
|
|
||||||
D6 = PB1 // TIM14_CH1
|
|
||||||
D9 = PA8 // TIM1_CH1
|
|
||||||
D10 = PA11 // SPI_CS || TIM1_CH4
|
|
||||||
D11 = PB5 // SPI1_MOSI || TIM3_CH2
|
|
||||||
D12 = PB4 // SPI1_MISO
|
|
||||||
D13 = PB3 // SPI1_SCK
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// UART pins
|
|
||||||
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
|
||||||
UART_TX_PIN = PA2
|
|
||||||
UART_RX_PIN = PA15
|
|
||||||
|
|
||||||
// SPI
|
|
||||||
SPI1_SCK_PIN = PB3
|
|
||||||
SPI1_SDI_PIN = PB5
|
|
||||||
SPI1_SDO_PIN = PB4
|
|
||||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
|
||||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
|
||||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
|
||||||
|
|
||||||
// I2C pins
|
|
||||||
// PB6 and PB7 are mapped to CN4 pin 7 and CN4 pin 8 respectively with the
|
|
||||||
// default solder bridge settings
|
|
||||||
I2C0_SCL_PIN = PB7
|
|
||||||
I2C0_SDA_PIN = PB6
|
|
||||||
I2C0_ALT_FUNC = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
|
||||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
|
||||||
// Both UART0 and UART1 refer to USART2.
|
|
||||||
UART0 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: stm32.USART2,
|
|
||||||
TxAltFuncSelector: 4,
|
|
||||||
RxAltFuncSelector: 4,
|
|
||||||
}
|
|
||||||
UART1 = &UART0
|
|
||||||
|
|
||||||
// I2C1 is documented, alias to I2C0 as well
|
|
||||||
I2C1 = &I2C{
|
|
||||||
Bus: stm32.I2C1,
|
|
||||||
AltFuncSelector: 1,
|
|
||||||
}
|
|
||||||
I2C0 = I2C1
|
|
||||||
|
|
||||||
// SPI
|
|
||||||
SPI0 = SPI{
|
|
||||||
Bus: stm32.SPI1,
|
|
||||||
AltFuncSelector: 0,
|
|
||||||
}
|
|
||||||
SPI1 = &SPI0
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
|
||||||
}
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build atmega nrf sam stm32 fe310 k210
|
// +build atmega nrf sam stm32,!stm32l0 fe310 k210
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (p Pin) Low() {
|
|||||||
p.Set(false)
|
p.Set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PWM struct {
|
||||||
|
Pin Pin
|
||||||
|
}
|
||||||
|
|
||||||
type ADC struct {
|
type ADC struct {
|
||||||
Pin Pin
|
Pin Pin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
"runtime/interrupt"
|
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,432 +21,71 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
// InitPWM initializes the registers needed for PWM.
|
||||||
// channels (that can be connected to two fixed pins). You can set the frequency
|
func InitPWM() {
|
||||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
// use waveform generation
|
||||||
// once.
|
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||||
type PWM struct {
|
|
||||||
num uint8
|
// set timer 0 prescale factor to 64
|
||||||
|
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||||
|
|
||||||
|
// set timer 1 prescale factor to 64
|
||||||
|
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||||
|
|
||||||
|
// put timer 1 in 8-bit phase correct pwm mode
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||||
|
|
||||||
|
// set timer 2 prescale factor to 64
|
||||||
|
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||||
|
|
||||||
|
// configure timer 2 for phase correct pwm (8-bit)
|
||||||
|
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// Configure configures a PWM pin for output.
|
||||||
Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
|
func (pwm PWM) Configure() error {
|
||||||
Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
|
switch pwm.Pin / 8 {
|
||||||
Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
|
case 0: // port B
|
||||||
)
|
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
||||||
|
case 2: // port D
|
||||||
// Configure enables and configures this PWM.
|
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
||||||
//
|
|
||||||
// For the two 8 bit timers, there is only a limited number of periods
|
|
||||||
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
|
||||||
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
|
||||||
// 128µs, 1024µs, 4096µs, or 16384µs.
|
|
||||||
func (pwm PWM) Configure(config PWMConfig) error {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
|
||||||
// Calculate the timer prescaler.
|
|
||||||
// While we could configure a flexible top, that would sacrifice one of
|
|
||||||
// the PWM output compare registers and thus a PWM channel. I've chosen
|
|
||||||
// to instead limit this timer to a fixed number of frequencies.
|
|
||||||
var prescaler uint8
|
|
||||||
switch config.Period {
|
|
||||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 1
|
|
||||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 2
|
|
||||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 3
|
|
||||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 4
|
|
||||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
|
||||||
prescaler = 5
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
if pwm.num == 0 {
|
|
||||||
avr.TCCR0B.Set(prescaler)
|
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
|
||||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
|
||||||
} else {
|
|
||||||
avr.TCCR2B.Set(prescaler)
|
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
|
||||||
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
|
||||||
}
|
|
||||||
case 1: // Timer/counter 1
|
|
||||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
|
||||||
// initially picked assuming an unlimited counter top and no PWM
|
|
||||||
// prescaler.
|
|
||||||
var top uint64
|
|
||||||
if config.Period == 0 {
|
|
||||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
|
||||||
// here (0xff) for consistency with the other timers.
|
|
||||||
top = 0xff
|
|
||||||
} else {
|
|
||||||
// The formula below calculates the following formula, optimized:
|
|
||||||
// top = period * (CPUFrequency() / 1e9)
|
|
||||||
// By dividing the CPU frequency first (an operation that is easily
|
|
||||||
// optimized away) the period has less chance of overflowing.
|
|
||||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
|
||||||
|
|
||||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
|
||||||
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
|
||||||
// speed lower with a prescaler to make the top value fit the maximum
|
|
||||||
// top value.
|
|
||||||
const maxTop = 0x10000
|
|
||||||
switch {
|
|
||||||
case top <= maxTop:
|
|
||||||
avr.TCCR1B.Set(3<<3 | 1) // no prescaling
|
|
||||||
case top/8 <= maxTop:
|
|
||||||
avr.TCCR1B.Set(3<<3 | 2) // divide by 8
|
|
||||||
top /= 8
|
|
||||||
case top/64 <= maxTop:
|
|
||||||
avr.TCCR1B.Set(3<<3 | 3) // divide by 64
|
|
||||||
top /= 64
|
|
||||||
case top/256 <= maxTop:
|
|
||||||
avr.TCCR1B.Set(3<<3 | 4) // divide by 256
|
|
||||||
top /= 256
|
|
||||||
case top/1024 <= maxTop:
|
|
||||||
avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
|
|
||||||
top /= 1024
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
|
||||||
// counter counts from 0, not 1 (avoiding an off-by-one).
|
|
||||||
top -= 1
|
|
||||||
|
|
||||||
avr.ICR1H.Set(uint8(top >> 8))
|
|
||||||
avr.ICR1L.Set(uint8(top))
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPeriod updates the period of this PWM peripheral.
|
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||||
// To set a particular frequency, use the following formula:
|
// 8-bit value ranging from 0 to 255.
|
||||||
//
|
func (pwm PWM) Set(value uint16) {
|
||||||
// period = 1e9 / frequency
|
value8 := uint8(value >> 8)
|
||||||
//
|
switch pwm.Pin {
|
||||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
case PD3:
|
||||||
//
|
// connect pwm to pin on timer 2, channel B
|
||||||
// SetPeriod will not change the prescaler, but also won't change the current
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||||
// value in any of the channels. This means that you may need to update the
|
avr.OCR2B.Set(value8) // set pwm duty
|
||||||
// value for the particular channel.
|
case PD5:
|
||||||
//
|
// connect pwm to pin on timer 0, channel B
|
||||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||||
// been configured. If you want to switch between frequencies, pick the lowest
|
avr.OCR0B.Set(value8) // set pwm duty
|
||||||
// frequency (longest period) once when calling Configure and adjust the
|
case PD6:
|
||||||
// frequency here as needed.
|
// connect pwm to pin on timer 0, channel A
|
||||||
func (pwm PWM) SetPeriod(period uint64) error {
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||||
if pwm.num != 1 {
|
avr.OCR0A.Set(value8) // set pwm duty
|
||||||
return ErrPWMPeriodTooLong // TODO better error message
|
case PB1:
|
||||||
}
|
// connect pwm to pin on timer 1, channel A
|
||||||
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||||
// initially picked assuming an unlimited counter top and no PWM
|
avr.OCR1AL.Set(value8) // set pwm duty
|
||||||
// prescaler.
|
case PB2:
|
||||||
var top uint64
|
// connect pwm to pin on timer 1, channel B
|
||||||
if period == 0 {
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||||
// here (0xff) for consistency with the other timers.
|
avr.OCR1BL.Set(value8) // set pwm duty
|
||||||
top = 0xff
|
case PB3:
|
||||||
} else {
|
// connect pwm to pin on timer 2, channel A
|
||||||
// The formula below calculates the following formula, optimized:
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||||
// top = period * (CPUFrequency() / 1e9)
|
avr.OCR2A.Set(value8) // set pwm duty
|
||||||
// By dividing the CPU frequency first (an operation that is easily
|
default:
|
||||||
// optimized away) the period has less chance of overflowing.
|
panic("Invalid PWM pin")
|
||||||
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
prescaler := avr.TCCR1B.Get() & 0x7
|
|
||||||
switch prescaler {
|
|
||||||
case 1:
|
|
||||||
top /= 1
|
|
||||||
case 2:
|
|
||||||
top /= 8
|
|
||||||
case 3:
|
|
||||||
top /= 64
|
|
||||||
case 4:
|
|
||||||
top /= 256
|
|
||||||
case 5:
|
|
||||||
top /= 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
|
|
||||||
// counts from 0, not 1 (avoiding an off-by-one).
|
|
||||||
top -= 1
|
|
||||||
|
|
||||||
if top > 0xffff {
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning: this change is not atomic!
|
|
||||||
avr.ICR1H.Set(uint8(top >> 8))
|
|
||||||
avr.ICR1L.Set(uint8(top))
|
|
||||||
|
|
||||||
// ... and because of that, set the counter back to zero to avoid most of
|
|
||||||
// the effects of this non-atomicity.
|
|
||||||
avr.TCNT1H.Set(0)
|
|
||||||
avr.TCNT1L.Set(0)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
|
||||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
|
||||||
// constant.
|
|
||||||
//
|
|
||||||
// The value returned here is hardware dependent. In general, it's best to treat
|
|
||||||
// it as an opaque value that can be divided by some number and passed to Set
|
|
||||||
// (see Set documentation for more information).
|
|
||||||
func (pwm PWM) Top() uint32 {
|
|
||||||
if pwm.num == 1 {
|
|
||||||
// Timer 1 has a configurable top value.
|
|
||||||
low := avr.ICR1L.Get()
|
|
||||||
high := avr.ICR1H.Get()
|
|
||||||
return uint32(high)<<8 | uint32(low) + 1
|
|
||||||
}
|
|
||||||
// Other timers go from 0 to 0xff (0x100 or 256 in total).
|
|
||||||
return 256
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter returns the current counter value of the timer in this PWM
|
|
||||||
// peripheral. It may be useful for debugging.
|
|
||||||
func (pwm PWM) Counter() uint32 {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
return uint32(avr.TCNT0.Get())
|
|
||||||
case 1:
|
|
||||||
mask := interrupt.Disable()
|
|
||||||
low := avr.TCNT1L.Get()
|
|
||||||
high := avr.TCNT1H.Get()
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
return uint32(high)<<8 | uint32(low)
|
|
||||||
case 2:
|
|
||||||
return uint32(avr.TCNT2.Get())
|
|
||||||
}
|
|
||||||
// Unknown PWM.
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
|
||||||
// from the configured period due to rounding.
|
|
||||||
func (pwm PWM) Period() uint64 {
|
|
||||||
var prescaler uint8
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
prescaler = avr.TCCR0B.Get() & 0x7
|
|
||||||
case 1:
|
|
||||||
prescaler = avr.TCCR1B.Get() & 0x7
|
|
||||||
case 2:
|
|
||||||
prescaler = avr.TCCR2B.Get() & 0x7
|
|
||||||
}
|
|
||||||
top := uint64(pwm.Top())
|
|
||||||
switch prescaler {
|
|
||||||
case 1: // prescaler 1
|
|
||||||
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
|
|
||||||
case 2: // prescaler 8
|
|
||||||
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
|
|
||||||
case 3: // prescaler 64
|
|
||||||
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
|
|
||||||
case 4: // prescaler 256
|
|
||||||
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
|
|
||||||
case 5: // prescaler 1024
|
|
||||||
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
|
|
||||||
default: // unknown clock source
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel returns a PWM channel for the given pin.
|
|
||||||
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
|
||||||
pin.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
pin.Low()
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
switch pin {
|
|
||||||
case PD6: // channel A
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
|
||||||
return 0, nil
|
|
||||||
case PD5: // channel B
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
switch pin {
|
|
||||||
case PB1: // channel A
|
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
|
||||||
return 0, nil
|
|
||||||
case PB2: // channel B
|
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
switch pin {
|
|
||||||
case PB3: // channel A
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
|
||||||
return 0, nil
|
|
||||||
case PD3: // channel B
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, ErrInvalidOutputPin
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetInverting sets whether to invert the output of this channel.
|
|
||||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
|
||||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
|
||||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
|
||||||
// high with a duty cycle of 25%.
|
|
||||||
//
|
|
||||||
// Note: the invert state may not be applied on the AVR until the next call to
|
|
||||||
// ch.Set().
|
|
||||||
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 6) // PB6 high
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
|
||||||
}
|
|
||||||
case 1: // channel B
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 5) // PB5 high
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
|
|
||||||
// It will be set the following call to Set(), however.
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A, PB1
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 1) // PB1 high
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 1) // PB1 low
|
|
||||||
}
|
|
||||||
case 1: // channel B, PB2
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 2) // PB2 high
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 2) // PB2 low
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A
|
|
||||||
if inverting {
|
|
||||||
avr.PORTB.SetBits(1 << 3) // PB3 high
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
|
||||||
} else {
|
|
||||||
avr.PORTB.ClearBits(1 << 3) // PB3 low
|
|
||||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
|
||||||
}
|
|
||||||
case 1: // channel B
|
|
||||||
if inverting {
|
|
||||||
avr.PORTD.SetBits(1 << 3) // PD3 high
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
|
||||||
} else {
|
|
||||||
avr.PORTD.ClearBits(1 << 3) // PD3 low
|
|
||||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updates the channel value. This is used to control the channel duty
|
|
||||||
// cycle, in other words the fraction of time the channel output is high (or low
|
|
||||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
|
||||||
//
|
|
||||||
// pwm.Set(channel, pwm.Top() / 4)
|
|
||||||
//
|
|
||||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
|
||||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
|
||||||
func (pwm PWM) Set(channel uint8, value uint32) {
|
|
||||||
switch pwm.num {
|
|
||||||
case 0:
|
|
||||||
value := uint16(value)
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
|
||||||
} else {
|
|
||||||
avr.OCR0A.Set(uint8(value - 1))
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
|
||||||
}
|
|
||||||
case 1: // channel B
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
|
||||||
} else {
|
|
||||||
avr.OCR0B.Set(uint8(value) - 1)
|
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
mask := interrupt.Disable()
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A, PB1
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
|
||||||
} else {
|
|
||||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
|
||||||
avr.OCR1AH.Set(uint8(value >> 8))
|
|
||||||
avr.OCR1AL.Set(uint8(value))
|
|
||||||
if avr.PORTB.HasBits(1 << 1) { // is PB1 high?
|
|
||||||
// Yes, set the inverting bit.
|
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
|
||||||
} else {
|
|
||||||
// No, output is non-inverting.
|
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1: // channel B, PB2
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
|
||||||
} else {
|
|
||||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
|
||||||
avr.OCR1BH.Set(uint8(value >> 8))
|
|
||||||
avr.OCR1BL.Set(uint8(value))
|
|
||||||
if avr.PORTB.HasBits(1 << 2) { // is PB2 high?
|
|
||||||
// Yes, set the inverting bit.
|
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
|
||||||
} else {
|
|
||||||
// No, output is non-inverting.
|
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
case 2:
|
|
||||||
value := uint16(value)
|
|
||||||
switch channel {
|
|
||||||
case 0: // channel A
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
|
|
||||||
} else {
|
|
||||||
avr.OCR2A.Set(uint8(value - 1))
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
|
||||||
}
|
|
||||||
case 1: // channel B
|
|
||||||
if value == 0 {
|
|
||||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
|
|
||||||
} else {
|
|
||||||
avr.OCR2B.Set(uint8(value - 1))
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+171
-343
@@ -31,8 +31,8 @@ const (
|
|||||||
PinInput PinMode = 9
|
PinInput PinMode = 9
|
||||||
PinInputPullup PinMode = 10
|
PinInputPullup PinMode = 10
|
||||||
PinOutput PinMode = 11
|
PinOutput PinMode = 11
|
||||||
PinTCC PinMode = PinTimer
|
PinPWM PinMode = PinTimer
|
||||||
PinTCCAlt PinMode = PinTimerAlt
|
PinPWMAlt PinMode = PinTimerAlt
|
||||||
PinInputPulldown PinMode = 12
|
PinInputPulldown PinMode = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1421,373 +1421,201 @@ func (spi SPI) txrx24mhz(tx, rx []byte) {
|
|||||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCC is one timer/counter peripheral, which consists of a counter and multiple
|
// PWM
|
||||||
// output channels (that can be connected to actual pins). You can set the
|
const period = 0xFFFF
|
||||||
// frequency using SetPeriod, but only for all the channels in this TCC
|
|
||||||
// peripheral at once.
|
|
||||||
type TCC sam.TCC_Type
|
|
||||||
|
|
||||||
// The SAM D21 has three TCC peripherals, which have PWM as one feature.
|
// InitPWM initializes the PWM interface.
|
||||||
var (
|
func InitPWM() {
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
// turn on timer clocks used for PWM
|
||||||
TCC1 = (*TCC)(sam.TCC1)
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_ | sam.PM_APBCMASK_TCC1_ | sam.PM_APBCMASK_TCC2_)
|
||||||
TCC2 = (*TCC)(sam.TCC2)
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:inline
|
// Use GCLK0 for TCC0/TCC1
|
||||||
func (tcc *TCC) timer() *sam.TCC_Type {
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
return (*sam.TCC_Type)(tcc)
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
}
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
// Configure enables and configures this TCC.
|
|
||||||
func (tcc *TCC) Configure(config PWMConfig) error {
|
|
||||||
// Enable the clock source for this timer.
|
|
||||||
switch tcc.timer() {
|
|
||||||
case sam.TCC0:
|
|
||||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC0_)
|
|
||||||
// Use GCLK0 for TCC0/TCC1
|
|
||||||
sam.GCLK.CLKCTRL.Set((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.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
|
||||||
}
|
|
||||||
case sam.TCC1:
|
|
||||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC1_)
|
|
||||||
// Use GCLK0 for TCC0/TCC1
|
|
||||||
sam.GCLK.CLKCTRL.Set((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.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
|
||||||
}
|
|
||||||
case sam.TCC2:
|
|
||||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_TCC2_)
|
|
||||||
// Use GCLK0 for TCC2/TC3
|
|
||||||
sam.GCLK.CLKCTRL.Set((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.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable timer (if it was enabled). This is necessary because
|
// Use GCLK0 for TCC2/TC3
|
||||||
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
// only allowed when the TCC is disabled.
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure configures a PWM pin for output.
|
||||||
|
func (pwm PWM) Configure() error {
|
||||||
|
// figure out which TCCX timer for this pin
|
||||||
|
timer := pwm.getTimer()
|
||||||
|
if timer == nil {
|
||||||
|
return ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable timer
|
||||||
|
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||||
|
// Wait for synchronization
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||||
|
}
|
||||||
|
|
||||||
// Use "Normal PWM" (single-slope PWM)
|
// Use "Normal PWM" (single-slope PWM)
|
||||||
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||||
|
// Wait for synchronization
|
||||||
// Wait for synchronization of all changed registers.
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the period and prescaler.
|
// Set the period (the number to count to (TOP) before resetting timer)
|
||||||
err := tcc.setPeriod(config.Period, true)
|
//TCC0->PER.reg = period;
|
||||||
|
timer.PER.Set(period)
|
||||||
// Enable the timer.
|
// Wait for synchronization
|
||||||
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
|
||||||
|
|
||||||
// Wait for synchronization of all changed registers.
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return any error that might have occured in the tcc.setPeriod call.
|
// Set pin as output
|
||||||
return err
|
sam.PORT.DIRSET0.Set(1 << uint8(pwm.Pin))
|
||||||
}
|
// Set pin to low
|
||||||
|
sam.PORT.OUTCLR0.Set(1 << uint8(pwm.Pin))
|
||||||
|
|
||||||
// SetPeriod updates the period of this TCC peripheral.
|
// Enable the port multiplexer for pin
|
||||||
// To set a particular frequency, use the following formula:
|
pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||||
//
|
|
||||||
// period = 1e9 / frequency
|
// Connect TCCX timer to pin.
|
||||||
//
|
// we normally use the F channel aka ALT
|
||||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
pwmConfig := PinPWMAlt
|
||||||
//
|
|
||||||
// SetPeriod will not change the prescaler, but also won't change the current
|
// in the case of PA6 or PA7 we have to use E channel
|
||||||
// value in any of the channels. This means that you may need to update the
|
if pwm.Pin == 6 || pwm.Pin == 7 {
|
||||||
// value for the particular channel.
|
pwmConfig = PinPWM
|
||||||
//
|
|
||||||
// Note that you cannot pick any arbitrary period after the TCC peripheral has
|
|
||||||
// been configured. If you want to switch between frequencies, pick the lowest
|
|
||||||
// frequency (longest period) once when calling Configure and adjust the
|
|
||||||
// frequency here as needed.
|
|
||||||
func (tcc *TCC) SetPeriod(period uint64) error {
|
|
||||||
err := tcc.setPeriod(period, false)
|
|
||||||
if err == nil {
|
|
||||||
if tcc.Counter() >= tcc.Top() {
|
|
||||||
// When setting the timer to a shorter period, there is a chance
|
|
||||||
// that it passes the counter value and thus goes all the way to MAX
|
|
||||||
// before wrapping back to zero.
|
|
||||||
// To avoid this, reset the counter back to 0.
|
|
||||||
tcc.timer().COUNT.Set(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPeriod sets the period of this TCC, possibly updating the prescaler as
|
if pwm.Pin&1 > 0 {
|
||||||
// well. The prescaler can only modified when the TCC is disabled, that is, in
|
// odd pin, so save the even pins
|
||||||
// the Configure function.
|
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
|
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
var top uint64
|
|
||||||
if period == 0 {
|
|
||||||
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
|
|
||||||
top = 0xffff
|
|
||||||
} else {
|
} else {
|
||||||
// The formula below calculates the following formula, optimized:
|
// even pin, so save the odd pins
|
||||||
// period * (48e6 / 1e9)
|
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
// This assumes that the chip is running at the (default) 48MHz speed.
|
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
top = period * 6 / 125
|
|
||||||
}
|
|
||||||
|
|
||||||
maxTop := uint64(0xffffff)
|
|
||||||
if tcc.timer() == sam.TCC2 {
|
|
||||||
// TCC2 is a 16-bit timer, not a 24-bit timer.
|
|
||||||
maxTop = 0xffff
|
|
||||||
}
|
|
||||||
|
|
||||||
if updatePrescaler {
|
|
||||||
// This function was called during Configure(), with the timer disabled.
|
|
||||||
// Note that updating the prescaler can only happen while the peripheral
|
|
||||||
// is disabled.
|
|
||||||
var prescaler uint32
|
|
||||||
switch {
|
|
||||||
case top <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1
|
|
||||||
case top/2 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV2
|
|
||||||
top = top / 2
|
|
||||||
case top/4 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV4
|
|
||||||
top = top / 4
|
|
||||||
case top/8 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV8
|
|
||||||
top = top / 8
|
|
||||||
case top/16 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV16
|
|
||||||
top = top / 16
|
|
||||||
case top/64 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV64
|
|
||||||
top = top / 64
|
|
||||||
case top/256 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV256
|
|
||||||
top = top / 256
|
|
||||||
case top/1024 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1024
|
|
||||||
top = top / 1024
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
tcc.timer().CTRLA.Set((tcc.timer().CTRLA.Get() &^ sam.TCC_CTRLA_PRESCALER_Msk) | (prescaler << sam.TCC_CTRLA_PRESCALER_Pos))
|
|
||||||
} else {
|
|
||||||
// Do not update the prescaler, but use the already-configured
|
|
||||||
// prescaler. This is the normal SetPeriod case, where the prescaler
|
|
||||||
// must not be changed.
|
|
||||||
prescaler := (tcc.timer().CTRLA.Get() & sam.TCC_CTRLA_PRESCALER_Msk) >> sam.TCC_CTRLA_PRESCALER_Pos
|
|
||||||
switch prescaler {
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV1:
|
|
||||||
top /= 1 // no-op
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV2:
|
|
||||||
top /= 2
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV4:
|
|
||||||
top /= 4
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV8:
|
|
||||||
top /= 8
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV16:
|
|
||||||
top /= 16
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV64:
|
|
||||||
top /= 64
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV256:
|
|
||||||
top /= 256
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV1024:
|
|
||||||
top /= 1024
|
|
||||||
default:
|
|
||||||
// unreachable
|
|
||||||
}
|
|
||||||
if top > maxTop {
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the period (the counter top).
|
|
||||||
tcc.timer().PER.Set(uint32(top) - 1)
|
|
||||||
|
|
||||||
// Wait for synchronization of CTRLA.PRESCALER and PER registers.
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
func (pwm PWM) Set(value uint16) {
|
||||||
// constant.
|
// figure out which TCCX timer for this pin
|
||||||
//
|
timer := pwm.getTimer()
|
||||||
// The value returned here is hardware dependent. In general, it's best to treat
|
if timer == nil {
|
||||||
// it as an opaque value that can be divided by some number and passed to Set
|
// The Configure call above cannot have succeeded, so simply ignore this
|
||||||
// (see Set documentation for more information).
|
// error.
|
||||||
func (tcc *TCC) Top() uint32 {
|
return
|
||||||
return tcc.timer().PER.Get() + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter returns the current counter value of the timer in this TCC
|
|
||||||
// peripheral. It may be useful for debugging.
|
|
||||||
func (tcc *TCC) Counter() uint32 {
|
|
||||||
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
|
||||||
return tcc.timer().COUNT.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some constans to make pinTimerMapping below easier to read.
|
|
||||||
const (
|
|
||||||
pinTCC0 = 1
|
|
||||||
pinTCC1 = 2
|
|
||||||
pinTCC2 = 3
|
|
||||||
pinTimerCh0 = 0 << 3
|
|
||||||
pinTimerCh2 = 1 << 3
|
|
||||||
pinTCC0Ch0 = pinTCC0 | pinTimerCh0
|
|
||||||
pinTCC0Ch2 = pinTCC0 | pinTimerCh2
|
|
||||||
pinTCC1Ch0 = pinTCC1 | pinTimerCh0
|
|
||||||
pinTCC1Ch2 = pinTCC1 | pinTimerCh2
|
|
||||||
pinTCC2Ch0 = pinTCC2 | pinTimerCh0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mapping from pin number to TCC peripheral and channel using a special
|
|
||||||
// encoding. Note that only TCC0-TCC2 are included, not TC3 and up.
|
|
||||||
// Every byte is split in two nibbles where the low nibble describes PinTCC and
|
|
||||||
// the high nibble describes PinTCCAlt. Within a nibble, there is one bit that
|
|
||||||
// indicates Ch0/Ch1 or Ch2/Ch3, and three other bits that contain the TCC
|
|
||||||
// peripheral number plus one (to distinguish between TCC0Ch0 and 0).
|
|
||||||
//
|
|
||||||
// The encoding can be so compact because all pins are configured in pairs, so
|
|
||||||
// if you know PA00 you can infer the configuration of PA01. And only channel 0
|
|
||||||
// or 2 need to be included (taking up just one bit), because channel 0 and 2
|
|
||||||
// are only ever used on odd pins and channel 1 and 3 on even pins, again using
|
|
||||||
// the pin pair pattern to reduce the amount of information needed to be stored.
|
|
||||||
//
|
|
||||||
// Datasheet: https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf
|
|
||||||
var pinTimerMapping = [...]uint8{
|
|
||||||
// page 21
|
|
||||||
PA00 / 2: pinTCC2Ch0 | 0,
|
|
||||||
PA04 / 2: pinTCC0Ch0 | 0,
|
|
||||||
PA06 / 2: pinTCC1Ch0 | 0,
|
|
||||||
PA08 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
|
||||||
PA10 / 2: pinTCC1Ch0 | pinTCC0Ch2<<4,
|
|
||||||
// page 22
|
|
||||||
PB10 / 2: 0 | pinTCC0Ch0<<4,
|
|
||||||
PB12 / 2: 0 | pinTCC0Ch2<<4,
|
|
||||||
PA12 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
|
||||||
PA14 / 2: 0 | pinTCC0Ch0<<4,
|
|
||||||
PA16 / 2: pinTCC2Ch0 | pinTCC0Ch2<<4,
|
|
||||||
PA18 / 2: 0 | pinTCC0Ch2<<4,
|
|
||||||
PB16 / 2: 0 | pinTCC0Ch0<<4,
|
|
||||||
PA20 / 2: 0 | pinTCC0Ch2<<4,
|
|
||||||
PA22 / 2: 0 | pinTCC0Ch0<<4,
|
|
||||||
PA24 / 2: 0 | pinTCC1Ch2<<4,
|
|
||||||
// page 23
|
|
||||||
PA30 / 2: 0 | pinTCC1Ch0<<4,
|
|
||||||
PB30 / 2: pinTCC0Ch0 | pinTCC1Ch2<<4,
|
|
||||||
}
|
|
||||||
|
|
||||||
// findPinPadMapping returns the pin mode (PinTCC or PinTCCAlt) and the channel
|
|
||||||
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
|
||||||
// could be found.
|
|
||||||
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
|
||||||
mapping := pinTimerMapping[pin/2]
|
|
||||||
// evenChannel below indicates the channel 0 or 2, for the even part of the
|
|
||||||
// pin pair. The next pin will also have the next channel (1 or 3).
|
|
||||||
if mapping&0x07 == timer+1 {
|
|
||||||
// PWM output is on peripheral function E.
|
|
||||||
evenChannel := ((mapping >> 3) & 1) * 2
|
|
||||||
return PinTCC, evenChannel + uint8(pin&1)
|
|
||||||
}
|
|
||||||
if (mapping&0x70)>>4 == timer+1 {
|
|
||||||
// PWM output is on peripheral function F.
|
|
||||||
evenChannel := ((mapping >> 7) & 1) * 2
|
|
||||||
return PinTCCAlt, evenChannel + uint8(pin&1)
|
|
||||||
}
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
|
||||||
// shared between multiple pins, and so will have the same duty cycle. If this
|
|
||||||
// is not desirable, look for a different TCC peripheral or consider using a
|
|
||||||
// different pin.
|
|
||||||
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
|
||||||
var pinMode PinMode
|
|
||||||
var channel uint8
|
|
||||||
switch tcc.timer() {
|
|
||||||
case sam.TCC0:
|
|
||||||
pinMode, channel = findPinTimerMapping(0, pin)
|
|
||||||
case sam.TCC1:
|
|
||||||
pinMode, channel = findPinTimerMapping(1, pin)
|
|
||||||
case sam.TCC2:
|
|
||||||
pinMode, channel = findPinTimerMapping(2, pin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pinMode == 0 {
|
// disable output
|
||||||
// No pin could be found.
|
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||||
return 0, ErrInvalidOutputPin
|
|
||||||
|
// Wait for synchronization
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable the port multiplexer for pin
|
|
||||||
pin.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
|
||||||
|
|
||||||
if pin&1 > 0 {
|
|
||||||
// odd pin, so save the even pins
|
|
||||||
val := pin.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
|
||||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXO_Pos))
|
|
||||||
} else {
|
|
||||||
// even pin, so save the odd pins
|
|
||||||
val := pin.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
|
||||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_PMUX0_PMUXE_Pos))
|
|
||||||
}
|
|
||||||
return channel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetInverting sets whether to invert the output of this channel.
|
|
||||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
|
||||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
|
||||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
|
||||||
// high with a duty cycle of 25%.
|
|
||||||
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
|
||||||
if inverting {
|
|
||||||
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
|
||||||
} else {
|
|
||||||
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for synchronization of the WAVE register.
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updates the channel value. This is used to control the channel duty
|
|
||||||
// cycle, in other words the fraction of time the channel output is high (or low
|
|
||||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
|
||||||
//
|
|
||||||
// tcc.Set(channel, tcc.Top() / 4)
|
|
||||||
//
|
|
||||||
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
|
|
||||||
// tcc.Top()) will set the output to high, assuming the output isn't inverted.
|
|
||||||
func (tcc *TCC) Set(channel uint8, value uint32) {
|
|
||||||
// Set PWM signal to output duty cycle
|
// Set PWM signal to output duty cycle
|
||||||
switch channel {
|
pwm.setChannel(timer, uint32(value))
|
||||||
case 0:
|
|
||||||
tcc.timer().CC0.Set(value)
|
// Wait for synchronization on all channels
|
||||||
case 1:
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0 |
|
||||||
tcc.timer().CC1.Set(value)
|
sam.TCC_SYNCBUSY_CC1 |
|
||||||
case 2:
|
sam.TCC_SYNCBUSY_CC2 |
|
||||||
tcc.timer().CC2.Set(value)
|
sam.TCC_SYNCBUSY_CC3) {
|
||||||
case 3:
|
|
||||||
tcc.timer().CC3.Set(value)
|
|
||||||
default:
|
|
||||||
// invalid PWM channel, ignore.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for synchronization on all channels (or anything in this peripheral,
|
// enable
|
||||||
// really).
|
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
// Wait for synchronization
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPMux returns the value for the correct PMUX register for this pin.
|
||||||
|
func (pwm PWM) getPMux() uint8 {
|
||||||
|
return pwm.Pin.getPMux()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPMux sets the value for the correct PMUX register for this pin.
|
||||||
|
func (pwm PWM) setPMux(val uint8) {
|
||||||
|
pwm.Pin.setPMux(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||||
|
func (pwm PWM) getPinCfg() uint8 {
|
||||||
|
return pwm.Pin.getPinCfg()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||||
|
func (pwm PWM) setPinCfg(val uint8) {
|
||||||
|
pwm.Pin.setPinCfg(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(timer *sam.TCC_Type, val uint32) {
|
||||||
|
switch pwm.Pin {
|
||||||
|
case 6:
|
||||||
|
timer.CC0.Set(val)
|
||||||
|
case 7:
|
||||||
|
timer.CC1.Set(val)
|
||||||
|
case 8:
|
||||||
|
timer.CC0.Set(val)
|
||||||
|
case 9:
|
||||||
|
timer.CC1.Set(val)
|
||||||
|
case 14:
|
||||||
|
timer.CC0.Set(val)
|
||||||
|
case 15:
|
||||||
|
timer.CC1.Set(val)
|
||||||
|
case 16:
|
||||||
|
timer.CC2.Set(val)
|
||||||
|
case 17:
|
||||||
|
timer.CC3.Set(val)
|
||||||
|
case 18:
|
||||||
|
timer.CC2.Set(val)
|
||||||
|
case 19:
|
||||||
|
timer.CC3.Set(val)
|
||||||
|
case 20:
|
||||||
|
timer.CC2.Set(val)
|
||||||
|
case 21:
|
||||||
|
timer.CC3.Set(val)
|
||||||
|
default:
|
||||||
|
return // not supported on this pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+225
-319
@@ -40,9 +40,9 @@ const (
|
|||||||
PinInput PinMode = 15
|
PinInput PinMode = 15
|
||||||
PinInputPullup PinMode = 16
|
PinInputPullup PinMode = 16
|
||||||
PinOutput PinMode = 17
|
PinOutput PinMode = 17
|
||||||
PinTCCE PinMode = PinTimer
|
PinPWME PinMode = PinTimer
|
||||||
PinTCCF PinMode = PinTimerAlt
|
PinPWMF PinMode = PinTimerAlt
|
||||||
PinTCCG PinMode = PinTCCPDEC
|
PinPWMG PinMode = PinTCCPDEC
|
||||||
PinInputPulldown PinMode = 18
|
PinInputPulldown PinMode = 18
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1570,350 +1570,256 @@ const (
|
|||||||
QSPI_DATA3 = PA11
|
QSPI_DATA3 = PA11
|
||||||
)
|
)
|
||||||
|
|
||||||
// TCC is one timer peripheral, which consists of a counter and multiple output
|
// PWM
|
||||||
// channels (that can be connected to actual pins). You can set the frequency
|
const period = 0xFFFF
|
||||||
// using SetPeriod, but only for all the channels in this timer peripheral at
|
|
||||||
// once.
|
|
||||||
type TCC sam.TCC_Type
|
|
||||||
|
|
||||||
//go:inline
|
// Configure configures a PWM pin for output.
|
||||||
func (tcc *TCC) timer() *sam.TCC_Type {
|
func (pwm PWM) Configure() error {
|
||||||
return (*sam.TCC_Type)(tcc)
|
// Set pin as output
|
||||||
}
|
sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
|
||||||
|
// Set pin to low
|
||||||
|
sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(pwm.Pin))
|
||||||
|
|
||||||
// Configure enables and configures this TCC.
|
// Enable the port multiplexer for pin
|
||||||
func (tcc *TCC) Configure(config PWMConfig) error {
|
pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||||
// Enable the TCC clock to be able to use the TCC.
|
|
||||||
tcc.configureClock()
|
|
||||||
|
|
||||||
// Disable timer (if it was enabled). This is necessary because
|
// Connect timer/mux to pin.
|
||||||
// tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
|
pwmConfig := pwm.getMux()
|
||||||
// only allowed when the TCC is disabled.
|
|
||||||
tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
if pwm.Pin&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||||
|
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||||
|
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out which TCCX timer for this pin
|
||||||
|
timer := pwm.getTimer()
|
||||||
|
if timer == nil {
|
||||||
|
return ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable timer
|
||||||
|
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
|
||||||
|
// Wait for synchronization
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set prescaler to 1/256
|
||||||
|
// TCCx->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_PRESCSYNC_GCLK;
|
||||||
|
timer.CTRLA.SetBits(sam.TCC_CTRLA_PRESCALER_DIV256 | sam.TCC_CTRLA_PRESCSYNC_GCLK)
|
||||||
|
|
||||||
// Use "Normal PWM" (single-slope PWM)
|
// Use "Normal PWM" (single-slope PWM)
|
||||||
tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
|
timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
|
||||||
|
// Wait for synchronization
|
||||||
// Wait for synchronization of all changed registers.
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the period and prescaler.
|
// while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1);
|
||||||
err := tcc.setPeriod(config.Period, true)
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||||
|
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||||
// Enable the timer.
|
|
||||||
tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
|
||||||
|
|
||||||
// Wait for synchronization of all changed registers.
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return any error that might have occured in the tcc.setPeriod call.
|
// Set the initial value
|
||||||
return err
|
// TCCx->CC[tcChannel].reg = (uint32_t) value;
|
||||||
}
|
pwm.setChannel(timer, 0)
|
||||||
|
|
||||||
// SetPeriod updates the period of this TCC peripheral.
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||||
// To set a particular frequency, use the following formula:
|
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||||
//
|
|
||||||
// period = 1e9 / frequency
|
|
||||||
//
|
|
||||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
|
||||||
//
|
|
||||||
// SetPeriod will not change the prescaler, but also won't change the current
|
|
||||||
// value in any of the channels. This means that you may need to update the
|
|
||||||
// value for the particular channel.
|
|
||||||
//
|
|
||||||
// Note that you cannot pick any arbitrary period after the TCC peripheral has
|
|
||||||
// been configured. If you want to switch between frequencies, pick the lowest
|
|
||||||
// frequency (longest period) once when calling Configure and adjust the
|
|
||||||
// frequency here as needed.
|
|
||||||
func (tcc *TCC) SetPeriod(period uint64) error {
|
|
||||||
return tcc.setPeriod(period, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPeriod sets the period of this TCC, possibly updating the prescaler as
|
|
||||||
// well. The prescaler can only modified when the TCC is disabled, that is, in
|
|
||||||
// the Configure function.
|
|
||||||
func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
|
|
||||||
var top uint64
|
|
||||||
if period == 0 {
|
|
||||||
// Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
|
|
||||||
top = 0xffff
|
|
||||||
} else {
|
|
||||||
// The formula below calculates the following formula, optimized:
|
|
||||||
// period * (120e6 / 1e9)
|
|
||||||
// This assumes that the chip is running from generic clock generator 0
|
|
||||||
// at 120MHz.
|
|
||||||
top = period * 3 / 25
|
|
||||||
}
|
}
|
||||||
|
|
||||||
maxTop := uint64(0xffff)
|
// Set the period (the number to count to (TOP) before resetting timer)
|
||||||
if tcc.timer() == sam.TCC0 || tcc.timer() == sam.TCC1 {
|
//TCC0->PER.reg = period;
|
||||||
// Only TCC0 and TCC1 are 24-bit timers, the rest are 16-bit.
|
timer.PER.Set(period)
|
||||||
maxTop = 0xffffff
|
// Wait for synchronization
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if updatePrescaler {
|
// enable timer
|
||||||
// This function was called during Configure(), with the timer disabled.
|
timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
|
||||||
// Note that updating the prescaler can only happen while the peripheral
|
// Wait for synchronization
|
||||||
// is disabled.
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
|
||||||
var prescaler uint32
|
|
||||||
switch {
|
|
||||||
case top <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1
|
|
||||||
case top/2 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV2
|
|
||||||
top = top / 2
|
|
||||||
case top/4 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV4
|
|
||||||
top = top / 4
|
|
||||||
case top/8 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV8
|
|
||||||
top = top / 8
|
|
||||||
case top/16 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV16
|
|
||||||
top = top / 16
|
|
||||||
case top/64 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV64
|
|
||||||
top = top / 64
|
|
||||||
case top/256 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV256
|
|
||||||
top = top / 256
|
|
||||||
case top/1024 <= maxTop:
|
|
||||||
prescaler = sam.TCC_CTRLA_PRESCALER_DIV1024
|
|
||||||
top = top / 1024
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
tcc.timer().CTRLA.Set((tcc.timer().CTRLA.Get() &^ sam.TCC_CTRLA_PRESCALER_Msk) | (prescaler << sam.TCC_CTRLA_PRESCALER_Pos))
|
|
||||||
} else {
|
|
||||||
// Do not update the prescaler, but use the already-configured
|
|
||||||
// prescaler. This is the normal SetPeriod case, where the prescaler
|
|
||||||
// must not be changed.
|
|
||||||
prescaler := (tcc.timer().CTRLA.Get() & sam.TCC_CTRLA_PRESCALER_Msk) >> sam.TCC_CTRLA_PRESCALER_Pos
|
|
||||||
switch prescaler {
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV1:
|
|
||||||
top /= 1 // no-op
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV2:
|
|
||||||
top /= 2
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV4:
|
|
||||||
top /= 4
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV8:
|
|
||||||
top /= 8
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV16:
|
|
||||||
top /= 16
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV64:
|
|
||||||
top /= 64
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV256:
|
|
||||||
top /= 256
|
|
||||||
case sam.TCC_CTRLA_PRESCALER_DIV1024:
|
|
||||||
top /= 1024
|
|
||||||
default:
|
|
||||||
// unreachable
|
|
||||||
}
|
|
||||||
if top > maxTop {
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the period (the counter top).
|
|
||||||
tcc.timer().PER.Set(uint32(top) - 1)
|
|
||||||
|
|
||||||
// Wait for synchronization of CTRLA.PRESCALER and PER registers.
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
func (pwm PWM) Set(value uint16) {
|
||||||
// constant.
|
// figure out which TCCX timer for this pin
|
||||||
//
|
timer := pwm.getTimer()
|
||||||
// The value returned here is hardware dependent. In general, it's best to treat
|
if timer == nil {
|
||||||
// it as an opaque value that can be divided by some number and passed to
|
// The Configure call above cannot have succeeded, so simply ignore this
|
||||||
// tcc.Set (see tcc.Set for more information).
|
// error.
|
||||||
func (tcc *TCC) Top() uint32 {
|
return
|
||||||
return tcc.timer().PER.Get() + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter returns the current counter value of the timer in this TCC
|
|
||||||
// peripheral. It may be useful for debugging.
|
|
||||||
func (tcc *TCC) Counter() uint32 {
|
|
||||||
tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
|
||||||
return tcc.timer().COUNT.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constants that encode a TCC number and WO number together in a single byte.
|
|
||||||
const (
|
|
||||||
pinTCC0 = 1 << 4 // keep the value 0 usable as "no value"
|
|
||||||
pinTCC1 = 2 << 4
|
|
||||||
pinTCC2 = 3 << 4
|
|
||||||
pinTCC3 = 4 << 4
|
|
||||||
pinTCC4 = 5 << 4
|
|
||||||
pinTCC0_0 = pinTCC0 | 0
|
|
||||||
pinTCC0_1 = pinTCC0 | 1
|
|
||||||
pinTCC0_2 = pinTCC0 | 2
|
|
||||||
pinTCC0_3 = pinTCC0 | 3
|
|
||||||
pinTCC0_4 = pinTCC0 | 4
|
|
||||||
pinTCC0_5 = pinTCC0 | 5
|
|
||||||
pinTCC0_6 = pinTCC0 | 6
|
|
||||||
pinTCC1_0 = pinTCC1 | 0
|
|
||||||
pinTCC1_2 = pinTCC1 | 2
|
|
||||||
pinTCC1_4 = pinTCC1 | 4
|
|
||||||
pinTCC1_6 = pinTCC1 | 6
|
|
||||||
pinTCC2_0 = pinTCC2 | 0
|
|
||||||
pinTCC2_2 = pinTCC2 | 2
|
|
||||||
pinTCC3_0 = pinTCC3 | 0
|
|
||||||
pinTCC4_0 = pinTCC4 | 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a copy of columns F and G (the TCC columns) of table 6-1 in the
|
|
||||||
// datasheet:
|
|
||||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
|
|
||||||
// For example, "TCC0/WO[2]" is converted to pinTCC0_2.
|
|
||||||
// Only the even pin numbers are stored here. The odd pin numbers are left out,
|
|
||||||
// because their PWM output can be determined from the even number: just add one
|
|
||||||
// to the wave output (WO) number.
|
|
||||||
var pinTimerMapping = [...]struct{ F, G uint8 }{
|
|
||||||
// page 33
|
|
||||||
PC04 / 2: {pinTCC0_0, 0},
|
|
||||||
PA08 / 2: {pinTCC0_0, pinTCC1_4},
|
|
||||||
PA10 / 2: {pinTCC0_2, pinTCC1_6},
|
|
||||||
PB10 / 2: {pinTCC0_4, pinTCC1_0},
|
|
||||||
PB12 / 2: {pinTCC3_0, pinTCC0_0},
|
|
||||||
PB14 / 2: {pinTCC4_0, pinTCC0_2},
|
|
||||||
PD08 / 2: {pinTCC0_1, 0},
|
|
||||||
PD10 / 2: {pinTCC0_3, 0},
|
|
||||||
PD12 / 2: {pinTCC0_5, 0},
|
|
||||||
PC10 / 2: {pinTCC0_0, pinTCC1_4},
|
|
||||||
// page 34
|
|
||||||
PC12 / 2: {pinTCC0_2, pinTCC1_6},
|
|
||||||
PC14 / 2: {pinTCC0_4, pinTCC1_0},
|
|
||||||
PA12 / 2: {pinTCC0_6, pinTCC1_2},
|
|
||||||
PA14 / 2: {pinTCC2_0, pinTCC1_2},
|
|
||||||
PA16 / 2: {pinTCC1_0, pinTCC0_4},
|
|
||||||
PA18 / 2: {pinTCC1_2, pinTCC0_6},
|
|
||||||
PC16 / 2: {pinTCC0_0, 0},
|
|
||||||
PC18 / 2: {pinTCC0_2, 0},
|
|
||||||
PC20 / 2: {pinTCC0_4, 0},
|
|
||||||
PC22 / 2: {pinTCC0_6, 0},
|
|
||||||
PD20 / 2: {pinTCC1_0, 0},
|
|
||||||
PB16 / 2: {pinTCC3_0, pinTCC0_4},
|
|
||||||
PB18 / 2: {pinTCC1_0, 0},
|
|
||||||
// page 35
|
|
||||||
PB20 / 2: {pinTCC1_2, 0},
|
|
||||||
PA20 / 2: {pinTCC1_4, pinTCC0_0},
|
|
||||||
PA22 / 2: {pinTCC1_6, pinTCC0_2},
|
|
||||||
PA24 / 2: {pinTCC2_2, 0},
|
|
||||||
PB26 / 2: {pinTCC1_2, 0},
|
|
||||||
PB28 / 2: {pinTCC1_4, 0},
|
|
||||||
PA30 / 2: {pinTCC2_0, 0},
|
|
||||||
// page 36
|
|
||||||
PB30 / 2: {pinTCC4_0, pinTCC0_6},
|
|
||||||
PB02 / 2: {pinTCC2_2, 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
// findPinPadMapping returns the pin mode (PinTCCF or PinTCCG) and the channel
|
|
||||||
// number for a given timer and pin. A zero PinMode is returned if no mapping
|
|
||||||
// could be found.
|
|
||||||
func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
|
|
||||||
if int(pin/2) >= len(pinTimerMapping) {
|
|
||||||
return 0, 0 // invalid pin number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping := pinTimerMapping[pin/2]
|
// Wait for synchronization
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
||||||
// Check for column F in the datasheet.
|
}
|
||||||
if mapping.F>>4-1 == timer {
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||||
return PinTCCF, mapping.F&0x0f + uint8(pin)&1
|
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for column G in the datasheet.
|
// TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
|
||||||
if mapping.G>>4-1 == timer {
|
pwm.setChannelBuffer(timer, uint32(value))
|
||||||
return PinTCCG, mapping.G&0x0f + uint8(pin)&1
|
|
||||||
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
|
||||||
|
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found.
|
// TCCx->CTRLBCLR.bit.LUPD = 1;
|
||||||
return 0, 0
|
timer.CTRLBCLR.SetBits(sam.TCC_CTRLBCLR_LUPD)
|
||||||
}
|
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
|
||||||
|
|
||||||
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
|
||||||
// shared between multiple pins, and so will have the same duty cycle. If this
|
|
||||||
// is not desirable, look for a different TCC or consider using a different pin.
|
|
||||||
func (tcc *TCC) Channel(pin Pin) (uint8, error) {
|
|
||||||
pinMode, woOutput := findPinTimerMapping(tcc.timerNum(), pin)
|
|
||||||
|
|
||||||
if pinMode == 0 {
|
|
||||||
// No pin could be found.
|
|
||||||
return 0, ErrInvalidOutputPin
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert from waveform output to channel, assuming WEXCTRL.OTMX equals 0.
|
|
||||||
// See table 49-4 "Output Matrix Channel Pin Routing Configuration" on page
|
|
||||||
// 1829 of the datasheet.
|
|
||||||
// The number of channels varies by TCC instance, hence the need to switch
|
|
||||||
// over them. For TCC2-4 the number of channels is equal to the number of
|
|
||||||
// waveform outputs, so the WO number maps directly to the channel number.
|
|
||||||
// For TCC0 and TCC1 this is not the case so they will need some special
|
|
||||||
// handling.
|
|
||||||
channel := woOutput
|
|
||||||
switch tcc.timer() {
|
|
||||||
case sam.TCC0:
|
|
||||||
channel = woOutput % 6
|
|
||||||
case sam.TCC1:
|
|
||||||
channel = woOutput % 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable the port multiplexer for pin
|
|
||||||
pin.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
|
||||||
|
|
||||||
// Connect timer/mux to pin.
|
|
||||||
if pin&1 > 0 {
|
|
||||||
// odd pin, so save the even pins
|
|
||||||
val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
|
||||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
|
||||||
} else {
|
|
||||||
// even pin, so save the odd pins
|
|
||||||
val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
|
||||||
pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
return channel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetInverting sets whether to invert the output of this channel.
|
|
||||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
|
||||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
|
||||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
|
||||||
// high with a duty cycle of 25%.
|
|
||||||
func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
|
|
||||||
if inverting {
|
|
||||||
tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
|
||||||
} else {
|
|
||||||
tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for synchronization of the WAVE register.
|
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set updates the channel value. This is used to control the channel duty
|
// getPMux returns the value for the correct PMUX register for this pin.
|
||||||
// cycle, in other words the fraction of time the channel output is high (or low
|
func (pwm PWM) getPMux() uint8 {
|
||||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
return pwm.Pin.getPMux()
|
||||||
//
|
}
|
||||||
// tcc.Set(channel, tcc.Top() / 4)
|
|
||||||
//
|
// setPMux sets the value for the correct PMUX register for this pin.
|
||||||
// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
|
func (pwm PWM) setPMux(val uint8) {
|
||||||
// tcc.Top()) will set the output to high, assuming the output isn't inverted.
|
pwm.Pin.setPMux(val)
|
||||||
func (tcc *TCC) Set(channel uint8, value uint32) {
|
}
|
||||||
// Update CCBUF, which provides double buffering. The update is applied on
|
|
||||||
// the next cycle.
|
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||||
tcc.timer().CCBUF[channel].Set(value)
|
func (pwm PWM) getPinCfg() uint8 {
|
||||||
for tcc.timer().SYNCBUSY.Get() != 0 {
|
return pwm.Pin.getPinCfg()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||||
|
func (pwm PWM) setPinCfg(val uint8) {
|
||||||
|
pwm.Pin.setPinCfg(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setChannel sets the value for the correct channel for PWM on this pin.
|
||||||
|
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
||||||
|
switch pwm.Pin {
|
||||||
|
case PA14:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PA15:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PA16:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PA17:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PA18:
|
||||||
|
timer.CC[2].Set(val)
|
||||||
|
case PA19:
|
||||||
|
timer.CC[3].Set(val)
|
||||||
|
case PA20:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PA21:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PA22:
|
||||||
|
timer.CC[2].Set(val)
|
||||||
|
case PA23:
|
||||||
|
timer.CC[3].Set(val)
|
||||||
|
case PB12:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PB13:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PB14:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PB15:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PB16:
|
||||||
|
timer.CC[4].Set(val)
|
||||||
|
case PB17:
|
||||||
|
timer.CC[5].Set(val)
|
||||||
|
case PB31:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
default:
|
||||||
|
return // not supported on this pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
|
||||||
|
func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
|
||||||
|
switch pwm.Pin {
|
||||||
|
case PA14:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PA15:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PA16:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PA17:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PA18:
|
||||||
|
timer.CCBUF[2].Set(val)
|
||||||
|
case PA19:
|
||||||
|
timer.CCBUF[3].Set(val)
|
||||||
|
case PA20:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PA21:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PA22:
|
||||||
|
timer.CCBUF[2].Set(val)
|
||||||
|
case PA23:
|
||||||
|
timer.CCBUF[3].Set(val)
|
||||||
|
case PB12:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PB13:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PB14:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PB15:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PB16:
|
||||||
|
timer.CCBUF[4].Set(val)
|
||||||
|
case PB17:
|
||||||
|
timer.CCBUF[5].Set(val)
|
||||||
|
case PB31:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
default:
|
||||||
|
return // not supported on this pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMux returns the pin mode mux to be used for PWM on this pin.
|
||||||
|
func (pwm PWM) getMux() PinMode {
|
||||||
|
switch pwm.Pin {
|
||||||
|
case PA14:
|
||||||
|
return PinPWMF
|
||||||
|
case PA15:
|
||||||
|
return PinPWMF
|
||||||
|
case PA16:
|
||||||
|
return PinPWMF
|
||||||
|
case PA17:
|
||||||
|
return PinPWMF
|
||||||
|
case PA18:
|
||||||
|
return PinPWMF
|
||||||
|
case PA19:
|
||||||
|
return PinPWMF
|
||||||
|
case PA20:
|
||||||
|
return PinPWMG
|
||||||
|
case PA21:
|
||||||
|
return PinPWMG
|
||||||
|
case PA22:
|
||||||
|
return PinPWMG
|
||||||
|
case PA23:
|
||||||
|
return PinPWMG
|
||||||
|
case PB12:
|
||||||
|
return PinPWMF
|
||||||
|
case PB13:
|
||||||
|
return PinPWMF
|
||||||
|
case PB14:
|
||||||
|
return PinPWMF
|
||||||
|
case PB15:
|
||||||
|
return PinPWMF
|
||||||
|
case PB16:
|
||||||
|
return PinPWMG
|
||||||
|
case PB17:
|
||||||
|
return PinPWMG
|
||||||
|
case PB31:
|
||||||
|
return PinPWMF
|
||||||
|
default:
|
||||||
|
return 0 // not supported on this pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,37 +11,43 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00030000
|
const HSRAM_SIZE = 0x00030000
|
||||||
|
|
||||||
// This chip has three TCC peripherals, which have PWM as one feature.
|
// InitPWM initializes the PWM interface.
|
||||||
var (
|
func InitPWM() {
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
// turn on timer clocks used for PWM
|
||||||
TCC1 = (*TCC)(sam.TCC1)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
TCC2 = (*TCC)(sam.TCC2)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||||
)
|
|
||||||
|
|
||||||
func (tcc *TCC) configureClock() {
|
//use clock generator 0
|
||||||
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
switch tcc.timer() {
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
case sam.TCC0:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC1:
|
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC2:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcc *TCC) timerNum() uint8 {
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
switch tcc.timer() {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
case sam.TCC0:
|
switch pwm.Pin {
|
||||||
return 0
|
case PA14:
|
||||||
case sam.TCC1:
|
return sam.TCC2
|
||||||
return 1
|
case PA15:
|
||||||
case sam.TCC2:
|
return sam.TCC2
|
||||||
return 2
|
case PA16:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA17:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA18:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA19:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA20:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA21:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA23:
|
||||||
|
return sam.TCC0
|
||||||
default:
|
default:
|
||||||
return 0x0f // should not happen
|
return nil // not supported on this pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,49 +11,60 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00030000
|
const HSRAM_SIZE = 0x00030000
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// InitPWM initializes the PWM interface.
|
||||||
var (
|
func InitPWM() {
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
// turn on timer clocks used for PWM
|
||||||
TCC1 = (*TCC)(sam.TCC1)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
TCC2 = (*TCC)(sam.TCC2)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||||
TCC3 = (*TCC)(sam.TCC3)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
TCC4 = (*TCC)(sam.TCC4)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (tcc *TCC) configureClock() {
|
//use clock generator 0
|
||||||
// Turn on timer clocks used for the TCC and use generic clock generator 0.
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
switch tcc.timer() {
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
case sam.TCC0:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
case sam.TCC1:
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC2:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC3:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC4:
|
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcc *TCC) timerNum() uint8 {
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
switch tcc.timer() {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
case sam.TCC0:
|
switch pwm.Pin {
|
||||||
return 0
|
case PA14:
|
||||||
case sam.TCC1:
|
return sam.TCC2
|
||||||
return 1
|
case PA15:
|
||||||
case sam.TCC2:
|
return sam.TCC2
|
||||||
return 2
|
case PA16:
|
||||||
case sam.TCC3:
|
return sam.TCC1
|
||||||
return 3
|
case PA17:
|
||||||
case sam.TCC4:
|
return sam.TCC1
|
||||||
return 4
|
case PA18:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA19:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA20:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA21:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA23:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB12:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB13:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB14:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB15:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB16:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB17:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB31:
|
||||||
|
return sam.TCC4
|
||||||
default:
|
default:
|
||||||
return 0x0f // should not happen
|
return nil // not supported on this pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,49 +11,60 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00040000
|
const HSRAM_SIZE = 0x00040000
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// InitPWM initializes the PWM interface.
|
||||||
var (
|
func InitPWM() {
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
// turn on timer clocks used for PWM
|
||||||
TCC1 = (*TCC)(sam.TCC1)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
TCC2 = (*TCC)(sam.TCC2)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||||
TCC3 = (*TCC)(sam.TCC3)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
TCC4 = (*TCC)(sam.TCC4)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (tcc *TCC) configureClock() {
|
//use clock generator 0
|
||||||
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
switch tcc.timer() {
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
case sam.TCC0:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
case sam.TCC1:
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC2:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC3:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC4:
|
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcc *TCC) timerNum() uint8 {
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
switch tcc.timer() {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
case sam.TCC0:
|
switch pwm.Pin {
|
||||||
return 0
|
case PA14:
|
||||||
case sam.TCC1:
|
return sam.TCC2
|
||||||
return 1
|
case PA15:
|
||||||
case sam.TCC2:
|
return sam.TCC2
|
||||||
return 2
|
case PA16:
|
||||||
case sam.TCC3:
|
return sam.TCC1
|
||||||
return 3
|
case PA17:
|
||||||
case sam.TCC4:
|
return sam.TCC1
|
||||||
return 4
|
case PA18:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA19:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA20:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA21:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA23:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB12:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB13:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB14:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB15:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB16:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB17:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB31:
|
||||||
|
return sam.TCC4
|
||||||
default:
|
default:
|
||||||
return 0x0f // should not happen
|
return nil // not supported on this pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,49 +11,60 @@ import "device/sam"
|
|||||||
|
|
||||||
const HSRAM_SIZE = 0x00030000
|
const HSRAM_SIZE = 0x00030000
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// InitPWM initializes the PWM interface.
|
||||||
var (
|
func InitPWM() {
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
// turn on timer clocks used for PWM
|
||||||
TCC1 = (*TCC)(sam.TCC1)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
TCC2 = (*TCC)(sam.TCC2)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||||
TCC3 = (*TCC)(sam.TCC3)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
TCC4 = (*TCC)(sam.TCC4)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (tcc *TCC) configureClock() {
|
//use clock generator 0
|
||||||
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
switch tcc.timer() {
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
case sam.TCC0:
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
case sam.TCC1:
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC2:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC3:
|
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
case sam.TCC4:
|
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
|
||||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcc *TCC) timerNum() uint8 {
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
switch tcc.timer() {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
case sam.TCC0:
|
switch pwm.Pin {
|
||||||
return 0
|
case PA14:
|
||||||
case sam.TCC1:
|
return sam.TCC2
|
||||||
return 1
|
case PA15:
|
||||||
case sam.TCC2:
|
return sam.TCC2
|
||||||
return 2
|
case PA16:
|
||||||
case sam.TCC3:
|
return sam.TCC1
|
||||||
return 3
|
case PA17:
|
||||||
case sam.TCC4:
|
return sam.TCC1
|
||||||
return 4
|
case PA18:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA19:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA20:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA21:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
|
case PA23:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB12:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB13:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB14:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB15:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB16:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB17:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB31:
|
||||||
|
return sam.TCC4
|
||||||
default:
|
default:
|
||||||
return 0x0f // should not happen
|
return nil // not supported on this pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,24 @@ func (adc ADC) Get() uint16 {
|
|||||||
//export __tinygo_adc_read
|
//export __tinygo_adc_read
|
||||||
func adcRead(pin Pin) uint16
|
func adcRead(pin Pin) uint16
|
||||||
|
|
||||||
|
// InitPWM enables support for PWM peripherals.
|
||||||
|
func InitPWM() {
|
||||||
|
// Nothing to do here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure configures a PWM pin for output.
|
||||||
|
func (pwm PWM) Configure() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||||
|
func (pwm PWM) Set(value uint16) {
|
||||||
|
pwmSet(pwm.Pin, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export __tinygo_pwm_set
|
||||||
|
func pwmSet(pin Pin, value uint16)
|
||||||
|
|
||||||
// I2C is a generic implementation of the Inter-IC communication protocol.
|
// I2C is a generic implementation of the Inter-IC communication protocol.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
Bus uint8
|
Bus uint8
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
|||||||
|
|
||||||
// PWM
|
// PWM
|
||||||
var (
|
var (
|
||||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
pwmChannelPins = [3]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
pwms = [3]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2}
|
||||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
pwmChannelSequence [3]uint16
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -79,8 +79,7 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
|||||||
|
|
||||||
// PWM
|
// PWM
|
||||||
var (
|
var (
|
||||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
|
||||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
pwmChannelSequence [4]uint16
|
||||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
|
||||||
)
|
)
|
||||||
|
|||||||
+28
-193
@@ -4,7 +4,6 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -257,205 +256,41 @@ func (spi SPI) Tx(w, r []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PWM is one PWM peripheral, which consists of a counter and multiple output
|
// InitPWM initializes the registers needed for PWM.
|
||||||
// channels (that can be connected to actual pins). You can set the frequency
|
func InitPWM() {
|
||||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
return
|
||||||
// once.
|
|
||||||
type PWM struct {
|
|
||||||
PWM *nrf.PWM_Type
|
|
||||||
|
|
||||||
channelValues [4]volatile.Register16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure enables and configures this PWM.
|
// Configure configures a PWM pin for output.
|
||||||
// On the nRF52 series, the maximum period is around 0.26s.
|
func (pwm PWM) Configure() {
|
||||||
func (pwm *PWM) Configure(config PWMConfig) error {
|
|
||||||
// Enable the peripheral.
|
|
||||||
pwm.PWM.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
|
||||||
|
|
||||||
// Use up counting only. TODO: allow configuring as up-and-down.
|
|
||||||
pwm.PWM.MODE.Set(nrf.PWM_MODE_UPDOWN_Up << nrf.PWM_MODE_UPDOWN_Pos)
|
|
||||||
|
|
||||||
// Indicate there are four channels that each have a different value.
|
|
||||||
pwm.PWM.DECODER.Set(nrf.PWM_DECODER_LOAD_Individual<<nrf.PWM_DECODER_LOAD_Pos | nrf.PWM_DECODER_MODE_RefreshCount<<nrf.PWM_DECODER_MODE_Pos)
|
|
||||||
|
|
||||||
err := pwm.setPeriod(config.Period, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the EasyDMA buffer, which has 4 values (one for each channel).
|
|
||||||
pwm.PWM.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwm.channelValues[0]))))
|
|
||||||
pwm.PWM.SEQ[0].CNT.Set(4)
|
|
||||||
|
|
||||||
// SEQ[0] is not yet started, it will be started on the first
|
|
||||||
// PWMChannel.Set() call.
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPeriod updates the period of this PWM peripheral.
|
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||||
// To set a particular frequency, use the following formula:
|
func (pwm PWM) Set(value uint16) {
|
||||||
//
|
for i := 0; i < len(pwmChannelPins); i++ {
|
||||||
// period = 1e9 / frequency
|
if pwmChannelPins[i] == 0xFFFFFFFF || pwmChannelPins[i] == uint32(pwm.Pin) {
|
||||||
//
|
pwmChannelPins[i] = uint32(pwm.Pin)
|
||||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
pwmChannelSequence[i] = (value >> 2) | 0x8000 // set bit 15 to invert polarity
|
||||||
//
|
|
||||||
// SetPeriod will not change the prescaler, but also won't change the current
|
|
||||||
// value in any of the channels. This means that you may need to update the
|
|
||||||
// value for the particular channel.
|
|
||||||
//
|
|
||||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
|
||||||
// been configured. If you want to switch between frequencies, pick the lowest
|
|
||||||
// frequency (longest period) once when calling Configure and adjust the
|
|
||||||
// frequency here as needed.
|
|
||||||
func (pwm *PWM) SetPeriod(period uint64) error {
|
|
||||||
return pwm.setPeriod(period, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pwm *PWM) setPeriod(period uint64, updatePrescaler bool) error {
|
p := pwms[i]
|
||||||
const maxTop = 0x7fff // 15 bits counter
|
|
||||||
|
|
||||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
p.PSEL.OUT[0].Set(uint32(pwm.Pin))
|
||||||
// initially picked assuming an unlimited COUNTERTOP and no PWM prescaler.
|
p.PSEL.OUT[1].Set(uint32(pwm.Pin))
|
||||||
var top uint64
|
p.PSEL.OUT[2].Set(uint32(pwm.Pin))
|
||||||
if period == 0 {
|
p.PSEL.OUT[3].Set(uint32(pwm.Pin))
|
||||||
// The period is 0, which means "pick something reasonable for LEDs".
|
p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||||
top = maxTop
|
p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||||
} else {
|
p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up)
|
||||||
// The formula below calculates the following formula, optimized:
|
p.COUNTERTOP.Set(16384) // frequency
|
||||||
// period * (16e6 / 1e9)
|
p.LOOP.Set(0)
|
||||||
// The max frequency (16e6 or 16MHz) is set by the hardware.
|
p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos))
|
||||||
top = period * 2 / 125
|
p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i]))))
|
||||||
}
|
p.SEQ[0].CNT.Set(1)
|
||||||
|
p.SEQ[0].REFRESH.Set(1)
|
||||||
|
p.SEQ[0].ENDDELAY.Set(0)
|
||||||
|
p.TASKS_SEQSTART[0].Set(1)
|
||||||
|
|
||||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
break
|
||||||
// which is only 15 bits (see maxTop). Therefore, try to make the PWM clock
|
|
||||||
// speed lower with a prescaler to make the top value fit the COUNTERTOP.
|
|
||||||
if updatePrescaler {
|
|
||||||
// This function was called during Configure().
|
|
||||||
switch {
|
|
||||||
case top <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_1)
|
|
||||||
case top/2 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
|
||||||
top /= 2
|
|
||||||
case top/4 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_4)
|
|
||||||
top /= 4
|
|
||||||
case top/8 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_8)
|
|
||||||
top /= 8
|
|
||||||
case top/16 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_16)
|
|
||||||
top /= 16
|
|
||||||
case top/32 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_32)
|
|
||||||
top /= 32
|
|
||||||
case top/64 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_64)
|
|
||||||
top /= 64
|
|
||||||
case top/128 <= maxTop:
|
|
||||||
pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_128)
|
|
||||||
top /= 128
|
|
||||||
default:
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Do not update the prescaler, but use the already-configured
|
|
||||||
// prescaler. This is the normal SetPeriod case, where the prescaler
|
|
||||||
// must not be changed.
|
|
||||||
prescaler := pwm.PWM.PRESCALER.Get()
|
|
||||||
switch prescaler {
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_1:
|
|
||||||
top /= 1
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_2:
|
|
||||||
top /= 2
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_4:
|
|
||||||
top /= 4
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_8:
|
|
||||||
top /= 8
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_16:
|
|
||||||
top /= 16
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_32:
|
|
||||||
top /= 32
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_64:
|
|
||||||
top /= 64
|
|
||||||
case nrf.PWM_PRESCALER_PRESCALER_DIV_128:
|
|
||||||
top /= 128
|
|
||||||
}
|
|
||||||
if top > maxTop {
|
|
||||||
return ErrPWMPeriodTooLong
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pwm.PWM.COUNTERTOP.Set(uint32(top))
|
|
||||||
|
|
||||||
// Apparently this is needed to apply the new COUNTERTOP.
|
|
||||||
pwm.PWM.TASKS_SEQSTART[0].Set(1)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
|
||||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
|
||||||
// constant.
|
|
||||||
//
|
|
||||||
// The value returned here is hardware dependent. In general, it's best to treat
|
|
||||||
// it as an opaque value that can be divided by some number and passed to
|
|
||||||
// pwm.Set (see pwm.Set for more information).
|
|
||||||
func (pwm *PWM) Top() uint32 {
|
|
||||||
return pwm.PWM.COUNTERTOP.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel returns a PWM channel for the given pin.
|
|
||||||
func (pwm *PWM) Channel(pin Pin) (uint8, error) {
|
|
||||||
config := uint32(pin)
|
|
||||||
for ch := uint8(0); ch < 4; ch++ {
|
|
||||||
channelConfig := pwm.PWM.PSEL.OUT[ch].Get()
|
|
||||||
if channelConfig == 0xffffffff {
|
|
||||||
// Unused channel. Configure it.
|
|
||||||
pwm.PWM.PSEL.OUT[ch].Set(config)
|
|
||||||
// Configure the pin (required by the reference manual).
|
|
||||||
pin.Configure(PinConfig{Mode: PinOutput})
|
|
||||||
// Set channel to zero and non-inverting.
|
|
||||||
pwm.channelValues[ch].Set(0x8000)
|
|
||||||
return ch, nil
|
|
||||||
} else if channelConfig == config {
|
|
||||||
// This channel is already configured for this pin.
|
|
||||||
return ch, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All four pins are already in use with other pins.
|
|
||||||
return 0, ErrInvalidOutputPin
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetInverting sets whether to invert the output of this channel.
|
|
||||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
|
||||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
|
||||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
|
||||||
// high with a duty cycle of 25%.
|
|
||||||
func (pwm *PWM) SetInverting(channel uint8, inverting bool) {
|
|
||||||
ptr := &pwm.channelValues[channel]
|
|
||||||
if inverting {
|
|
||||||
ptr.Set(ptr.Get() &^ 0x8000)
|
|
||||||
} else {
|
|
||||||
ptr.Set(ptr.Get() | 0x8000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updates the channel value. This is used to control the channel duty
|
|
||||||
// cycle. For example, to set it to a 25% duty cycle, use:
|
|
||||||
//
|
|
||||||
// ch.Set(ch.Top() / 4)
|
|
||||||
//
|
|
||||||
// ch.Set(0) will set the output to low and ch.Set(ch.Top()) will set the output
|
|
||||||
// to high, assuming the output isn't inverted.
|
|
||||||
func (pwm *PWM) Set(channel uint8, value uint32) {
|
|
||||||
// Update the channel value while retaining the polarity bit.
|
|
||||||
ptr := &pwm.channelValues[channel]
|
|
||||||
ptr.Set(ptr.Get()&0x8000 | uint16(value)&0x7fff)
|
|
||||||
|
|
||||||
// Start the PWM, if it isn't already running.
|
|
||||||
pwm.PWM.TASKS_SEQSTART[0].Set(1)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build stm32l5 stm32f7 stm32l4 stm32l0
|
// +build stm32l5 stm32f7 stm32l4
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
@@ -141,6 +142,50 @@ func (p Pin) enableClock() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable peripheral clock
|
||||||
|
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||||
|
switch bus {
|
||||||
|
case unsafe.Pointer(stm32.DAC): // DAC interface clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN)
|
||||||
|
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||||
|
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||||
|
case unsafe.Pointer(stm32.USART5): // UART5 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART5EN)
|
||||||
|
case unsafe.Pointer(stm32.USART4): // UART4 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART4EN)
|
||||||
|
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
||||||
|
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
||||||
|
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_LPUART1EN)
|
||||||
|
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
||||||
|
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
||||||
|
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
||||||
|
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||||
|
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
||||||
|
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---------- UART related types and code
|
//---------- UART related types and code
|
||||||
|
|
||||||
// Configure the UART.
|
// Configure the UART.
|
||||||
@@ -240,13 +285,3 @@ func (spi SPI) configurePins(config SPIConfig) {
|
|||||||
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
||||||
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
|
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------- I2C related types and code
|
|
||||||
|
|
||||||
// Gets the value for TIMINGR register
|
|
||||||
func (i2c I2C) getFreqRange() uint32 {
|
|
||||||
// This is a 'magic' value calculated by STM32CubeMX
|
|
||||||
// for 80MHz PCLK1.
|
|
||||||
// TODO: Do calculations based on PCLK1
|
|
||||||
return 0x00303D5B
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
// +build stm32l0x1
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Peripheral abstraction layer for the stm32l0
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enable peripheral clock
|
|
||||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
|
||||||
switch bus {
|
|
||||||
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
|
||||||
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
|
||||||
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
|
||||||
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
|
||||||
case unsafe.Pointer(stm32.USART5): // UART5 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART5EN)
|
|
||||||
case unsafe.Pointer(stm32.USART4): // UART4 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART4EN)
|
|
||||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
|
||||||
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
|
||||||
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_LPUART1EN)
|
|
||||||
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
|
||||||
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
|
||||||
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
|
||||||
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
|
||||||
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
|
||||||
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
|
||||||
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
|
||||||
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
|
||||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
// +build stm32l0x2
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Peripheral abstraction layer for the stm32l0
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enable peripheral clock
|
|
||||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
|
||||||
switch bus {
|
|
||||||
case unsafe.Pointer(stm32.DAC): // DAC interface clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN)
|
|
||||||
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
|
||||||
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
|
||||||
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
|
||||||
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
|
||||||
case unsafe.Pointer(stm32.USART5): // UART5 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART5EN)
|
|
||||||
case unsafe.Pointer(stm32.USART4): // UART4 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART4EN)
|
|
||||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
|
||||||
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
|
||||||
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_LPUART1EN)
|
|
||||||
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
|
||||||
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
|
||||||
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
|
||||||
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
|
||||||
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
|
||||||
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
|
||||||
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
|
||||||
case unsafe.Pointer(stm32.ADC): // ADC clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
|
|
||||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package machine
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrPWMPeriodTooLong = errors.New("pwm: period too long")
|
|
||||||
)
|
|
||||||
|
|
||||||
// PWMConfig allows setting some configuration while configuring a PWM
|
|
||||||
// peripheral. A zero PWMConfig is ready to use for simple applications such as
|
|
||||||
// dimming LEDs.
|
|
||||||
type PWMConfig struct {
|
|
||||||
// PWM period in nanosecond. Leaving this zero will pick a reasonable period
|
|
||||||
// value for use with LEDs.
|
|
||||||
// If you want to configure a frequency instead of a period, you can use the
|
|
||||||
// following formula to calculate a period from a frequency:
|
|
||||||
//
|
|
||||||
// period = 1e9 / frequency
|
|
||||||
//
|
|
||||||
Period uint64
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build stm32l0
|
// +build stm32,stm32l0
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@@ -7,48 +7,68 @@ import (
|
|||||||
"machine"
|
"machine"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer settings used for tick and sleep.
|
||||||
|
|
||||||
|
note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock
|
||||||
|
settings above, so must be kept in sync if the clock settings are changed.
|
||||||
|
*/
|
||||||
const (
|
const (
|
||||||
RCC_SYSCLK_DIV1 = 0 // Needs SVD update (should be stm32.RCC_SYSCLK_DIV1)
|
TICK_RATE = 1000 // 1 KHz
|
||||||
|
TICK_TIMER_IRQ = stm32.IRQ_TIM7
|
||||||
|
TICK_TIMER_FREQ = 32000000 // 32 MHz
|
||||||
|
SLEEP_TIMER_IRQ = stm32.IRQ_TIM3
|
||||||
|
SLEEP_TIMER_FREQ = 32000000 // 32 MHz
|
||||||
)
|
)
|
||||||
|
|
||||||
type arrtype = uint16
|
type arrtype = uint16
|
||||||
|
|
||||||
const asyncScheduler = false
|
func init() {
|
||||||
|
initCLK()
|
||||||
|
|
||||||
|
initSleepTimer(&timerInfo{
|
||||||
|
EnableRegister: &stm32.RCC.APB1ENR,
|
||||||
|
EnableFlag: stm32.RCC_APB1ENR_TIM3EN,
|
||||||
|
Device: stm32.TIM3,
|
||||||
|
})
|
||||||
|
|
||||||
|
machine.UART0.Configure(machine.UARTConfig{})
|
||||||
|
|
||||||
|
initTickTimer(&timerInfo{
|
||||||
|
EnableRegister: &stm32.RCC.APB1ENR,
|
||||||
|
EnableFlag: stm32.RCC_APB1ENR_TIM7EN,
|
||||||
|
Device: stm32.TIM7,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
machine.UART0.WriteByte(c)
|
machine.UART0.WriteByte(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCLK() {
|
// initCLK sets clock to 32MHz
|
||||||
// Set Power Regulator to enable max performance (1.8V)
|
// SEE: https://github.com/WRansohoff/STM32x0_timer_example/blob/master/src/main.c
|
||||||
stm32.PWR.CR.ReplaceBits(1<<stm32.PWR_CR_VOS_Pos, stm32.PWR_CR_VOS_Msk, 0)
|
|
||||||
|
|
||||||
// Calibration (default 0x10)
|
func initCLK() {
|
||||||
stm32.RCC.ICSCR.ReplaceBits(0x10<<stm32.RCC_ICSCR_HSI16TRIM_Pos, stm32.RCC_ICSCR_HSI16TRIM_Msk, 0)
|
|
||||||
|
// Set the Flash ACR to use 1 wait-state
|
||||||
|
// enable the prefetch buffer and pre-read for performance
|
||||||
|
stm32.FLASH.ACR.SetBits(stm32.Flash_ACR_LATENCY | stm32.Flash_ACR_PRFTEN | stm32.Flash_ACR_PRE_READ)
|
||||||
|
|
||||||
|
// Set presaclers so half system clock (PCLKx = HCLK/2)
|
||||||
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_Div2 << stm32.RCC_CFGR_PPRE1_Pos)
|
||||||
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE2_Div2 << stm32.RCC_CFGR_PPRE2_Pos)
|
||||||
|
|
||||||
// Enable the HSI16 oscillator, since the L0 series boots to the MSI one.
|
// Enable the HSI16 oscillator, since the L0 series boots to the MSI one.
|
||||||
stm32.RCC.CR.ReplaceBits(stm32.RCC_CR_HSI16ON, stm32.RCC_CR_HSI16ON_Msk|stm32.RCC_CR_HSI16DIVEN_Msk, 0)
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSI16ON)
|
||||||
|
|
||||||
// Wait for HSI16 to be ready
|
// Wait for HSI16 to be ready
|
||||||
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSI16RDYF) {
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSI16RDYF) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable PLL
|
|
||||||
stm32.RCC.CR.ClearBits(stm32.RCC_CR_PLLON)
|
|
||||||
|
|
||||||
// Wait for PLL to be disabled
|
|
||||||
for stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the PLL to use HSI16 with a PLLDIV of 2 and PLLMUL of 4.
|
// Configure the PLL to use HSI16 with a PLLDIV of 2 and PLLMUL of 4.
|
||||||
stm32.RCC.CFGR.ReplaceBits(
|
stm32.RCC.CFGR.SetBits(0x01<<stm32.RCC_CFGR_PLLDIV_Pos | 0x01<<stm32.RCC_CFGR_PLLMUL_Pos)
|
||||||
(stm32.RCC_CFGR_PLLSRC_HSI16<<stm32.RCC_CFGR_PLLSRC_Pos)|
|
stm32.RCC.CFGR.ClearBits(0x02<<stm32.RCC_CFGR_PLLDIV_Pos | 0x0E<<stm32.RCC_CFGR_PLLMUL_Pos)
|
||||||
(stm32.RCC_CFGR_PLLMUL_Mul4<<stm32.RCC_CFGR_PLLMUL_Pos)|
|
stm32.RCC.CFGR.ClearBits(stm32.RCC_CFGR_PLLSRC)
|
||||||
(stm32.RCC_CFGR_PLLDIV_Div2<<stm32.RCC_CFGR_PLLDIV_Pos),
|
|
||||||
stm32.RCC_CFGR_PLLSRC_Msk|
|
|
||||||
stm32.RCC_CFGR_PLLMUL_Msk|
|
|
||||||
stm32.RCC_CFGR_PLLDIV_Msk,
|
|
||||||
0)
|
|
||||||
|
|
||||||
// Enable PLL
|
// Enable PLL
|
||||||
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
|
||||||
@@ -57,30 +77,9 @@ func initCLK() {
|
|||||||
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust flash latency
|
|
||||||
if FlashLatency > getFlashLatency() {
|
|
||||||
setFlashLatency(FlashLatency)
|
|
||||||
for getFlashLatency() != FlashLatency {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCLK
|
|
||||||
stm32.RCC.CFGR.ReplaceBits(RCC_SYSCLK_DIV1, stm32.RCC_CFGR_HPRE_Msk, 0)
|
|
||||||
|
|
||||||
// Use PLL As System clock
|
// Use PLL As System clock
|
||||||
stm32.RCC.CFGR.ReplaceBits(stm32.RCC_CFGR_SWS_PLL, stm32.RCC_CFGR_SW_Msk, 0)
|
stm32.RCC.CFGR.SetBits(0x3)
|
||||||
for stm32.RCC.CFGR.Get()&stm32.RCC_CFGR_SW_Msk != stm32.RCC_CFGR_SWS_PLL {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set prescalers so half system clock (PCLKx = HCLK/2)
|
|
||||||
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_Div2 << stm32.RCC_CFGR_PPRE1_Pos)
|
|
||||||
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE2_Div2 << stm32.RCC_CFGR_PPRE2_Pos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFlashLatency() uint32 {
|
const asyncScheduler = false
|
||||||
return stm32.FLASH.ACR.Get() & stm32.Flash_ACR_LATENCY_Msk
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFlashLatency(l uint32) {
|
|
||||||
stm32.FLASH.ACR.ReplaceBits(l, stm32.Flash_ACR_LATENCY_Msk, 0)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
// +build stm32l0x1
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"machine"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer settings used for tick and sleep.
|
|
||||||
|
|
||||||
note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock
|
|
||||||
settings, so must be kept in sync if the clock settings are changed.
|
|
||||||
*/
|
|
||||||
const (
|
|
||||||
TICK_RATE = 1000 // 1 KHz
|
|
||||||
TICK_TIMER_IRQ = stm32.IRQ_TIM21
|
|
||||||
TICK_TIMER_FREQ = 32000000 // 32 MHz
|
|
||||||
SLEEP_TIMER_IRQ = stm32.IRQ_TIM22
|
|
||||||
SLEEP_TIMER_FREQ = 32000000 // 32 MHz
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
FlashLatency = stm32.Flash_ACR_LATENCY_WS1
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
initCLK()
|
|
||||||
|
|
||||||
initSleepTimer(&timerInfo{
|
|
||||||
EnableRegister: &stm32.RCC.APB2ENR,
|
|
||||||
EnableFlag: stm32.RCC_APB2ENR_TIM22EN,
|
|
||||||
Device: stm32.TIM22,
|
|
||||||
})
|
|
||||||
|
|
||||||
machine.UART0.Configure(machine.UARTConfig{})
|
|
||||||
|
|
||||||
initTickTimer(&timerInfo{
|
|
||||||
EnableRegister: &stm32.RCC.APB2ENR,
|
|
||||||
EnableFlag: stm32.RCC_APB2ENR_TIM21EN,
|
|
||||||
Device: stm32.TIM21,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
// +build stm32l0x2
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/stm32"
|
|
||||||
"machine"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer settings used for tick and sleep.
|
|
||||||
|
|
||||||
note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock
|
|
||||||
settings, so must be kept in sync if the clock settings are changed.
|
|
||||||
*/
|
|
||||||
const (
|
|
||||||
TICK_RATE = 1000 // 1 KHz
|
|
||||||
TICK_TIMER_IRQ = stm32.IRQ_TIM7
|
|
||||||
TICK_TIMER_FREQ = 32000000 // 32 MHz
|
|
||||||
SLEEP_TIMER_IRQ = stm32.IRQ_TIM3
|
|
||||||
SLEEP_TIMER_FREQ = 32000000 // 32 MHz
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
FlashLatency = stm32.Flash_ACR_LATENCY_WS1
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
initCLK()
|
|
||||||
|
|
||||||
initSleepTimer(&timerInfo{
|
|
||||||
EnableRegister: &stm32.RCC.APB1ENR,
|
|
||||||
EnableFlag: stm32.RCC_APB1ENR_TIM3EN,
|
|
||||||
Device: stm32.TIM3,
|
|
||||||
})
|
|
||||||
|
|
||||||
machine.UART0.Configure(machine.UARTConfig{})
|
|
||||||
|
|
||||||
initTickTimer(&timerInfo{
|
|
||||||
EnableRegister: &stm32.RCC.APB1ENR,
|
|
||||||
EnableFlag: stm32.RCC_APB1ENR_TIM7EN,
|
|
||||||
Device: stm32.TIM7,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"inherits": ["cortex-m"],
|
|
||||||
"llvm-target": "armv6m-none-eabi",
|
|
||||||
"cflags": [
|
|
||||||
"--target=armv6m-none-eabi",
|
|
||||||
"-Qunused-arguments"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"inherits": ["cortex-m0"],
|
|
||||||
"build-tags": ["nucleol031k6", "stm32l031", "stm32l0x1", "stm32l0", "stm32"],
|
|
||||||
"linkerscript": "targets/stm32l031k6.ld",
|
|
||||||
"extra-files": [
|
|
||||||
"src/device/stm32/stm32l0x1.s"
|
|
||||||
],
|
|
||||||
"flash-method": "openocd",
|
|
||||||
"openocd-interface": "stlink",
|
|
||||||
"openocd-target": "stm32l0"
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
FLASH_TEXT (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
|
|
||||||
}
|
|
||||||
|
|
||||||
_stack_size = 2K;
|
|
||||||
|
|
||||||
INCLUDE "targets/arm.ld"
|
|
||||||
@@ -5,8 +5,6 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -15,10 +13,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/builder"
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var validName = regexp.MustCompile("^[a-zA-Z0-9_]+$")
|
var validName = regexp.MustCompile("^[a-zA-Z0-9_]+$")
|
||||||
@@ -1170,7 +1164,7 @@ Default_Handler:
|
|||||||
return w.Flush()
|
return w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func generate(indir, outdir, sourceURL, interruptSystem string, onlyUsed bool) error {
|
func generate(indir, outdir, sourceURL, interruptSystem string) error {
|
||||||
if _, err := os.Stat(indir); os.IsNotExist(err) {
|
if _, err := os.Stat(indir); os.IsNotExist(err) {
|
||||||
fmt.Fprintln(os.Stderr, "cannot find input directory:", indir)
|
fmt.Fprintln(os.Stderr, "cannot find input directory:", indir)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -1183,14 +1177,7 @@ func generate(indir, outdir, sourceURL, interruptSystem string, onlyUsed bool) e
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
sort.Strings(infiles)
|
sort.Strings(infiles)
|
||||||
|
|
||||||
targets := tinygoTargets()
|
|
||||||
for _, infile := range infiles {
|
for _, infile := range infiles {
|
||||||
if onlyUsed {
|
|
||||||
if !targets[strings.ToLower(strings.TrimSuffix(filepath.Base(infile), filepath.Ext(infile)))] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println(infile)
|
fmt.Println(infile)
|
||||||
device, err := readSVD(infile, sourceURL)
|
device, err := readSVD(infile, sourceURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1215,59 +1202,9 @@ func generate(indir, outdir, sourceURL, interruptSystem string, onlyUsed bool) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func tinygoTargets() map[string]bool {
|
|
||||||
names := []string{}
|
|
||||||
|
|
||||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
|
||||||
entries, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "could not list targets:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
if !entry.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
|
|
||||||
// Only inspect JSON files.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
path := filepath.Join(dir, entry.Name())
|
|
||||||
spec, err := compileopts.LoadTarget(path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "could not list target:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
|
|
||||||
// This doesn't look like a regular target file, but rather like
|
|
||||||
// a parent target (such as targets/cortex-m.json).
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := entry.Name()
|
|
||||||
name = name[:len(name)-5]
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
tags := map[string]bool{}
|
|
||||||
for _, tgt := range names {
|
|
||||||
options := &compileopts.Options{
|
|
||||||
Target: tgt,
|
|
||||||
}
|
|
||||||
config, err := builder.NewConfig(options)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, tag := range config.BuildTags() {
|
|
||||||
tags[tag] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
sourceURL := flag.String("source", "<unknown>", "source SVD file")
|
sourceURL := flag.String("source", "<unknown>", "source SVD file")
|
||||||
interruptSystem := flag.String("interrupts", "hardware", "interrupt system in use (software, hardware)")
|
interruptSystem := flag.String("interrupts", "hardware", "interrupt system in use (software, hardware)")
|
||||||
onlyUsed := flag.Bool("only-used", false, "generate only what is defined in target")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if flag.NArg() != 2 {
|
if flag.NArg() != 2 {
|
||||||
fmt.Fprintln(os.Stderr, "provide exactly two arguments: input directory (with .svd files) and output directory for generated files")
|
fmt.Fprintln(os.Stderr, "provide exactly two arguments: input directory (with .svd files) and output directory for generated files")
|
||||||
@@ -1276,7 +1213,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
indir := flag.Arg(0)
|
indir := flag.Arg(0)
|
||||||
outdir := flag.Arg(1)
|
outdir := flag.Arg(1)
|
||||||
err := generate(indir, outdir, *sourceURL, *interruptSystem, *onlyUsed)
|
err := generate(indir, outdir, *sourceURL, *interruptSystem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user