mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-15 08:23:41 +00:00
rp2350: add pll generalized solution; fix ADC handles; pwm period fix
This commit is contained in:
@@ -523,6 +523,8 @@ smoketest: testchdir
|
|||||||
# regression test for #2563
|
# regression test for #2563
|
||||||
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
|
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
|
||||||
# test all examples (except pwm)
|
# test all examples (except pwm)
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pga2350 examples/echo
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
//go:build pga2350
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// PGA2350 pin definitions.
|
||||||
|
const (
|
||||||
|
GP0 = GPIO0
|
||||||
|
GP1 = GPIO1
|
||||||
|
GP2 = GPIO2
|
||||||
|
GP3 = GPIO3
|
||||||
|
GP4 = GPIO4
|
||||||
|
GP5 = GPIO5
|
||||||
|
GP6 = GPIO6
|
||||||
|
GP7 = GPIO7
|
||||||
|
GP8 = GPIO8
|
||||||
|
GP9 = GPIO9
|
||||||
|
GP10 = GPIO10
|
||||||
|
GP11 = GPIO11
|
||||||
|
GP12 = GPIO12
|
||||||
|
GP13 = GPIO13
|
||||||
|
GP14 = GPIO14
|
||||||
|
GP15 = GPIO15
|
||||||
|
GP16 = GPIO16
|
||||||
|
GP17 = GPIO17
|
||||||
|
GP18 = GPIO18
|
||||||
|
GP19 = GPIO19
|
||||||
|
GP20 = GPIO20
|
||||||
|
GP21 = GPIO21
|
||||||
|
GP22 = GPIO22
|
||||||
|
GP26 = GPIO26
|
||||||
|
GP27 = GPIO27
|
||||||
|
GP28 = GPIO28
|
||||||
|
GP29 = GPIO29
|
||||||
|
GP30 = GPIO30 // peripherals: PWM7 channel A
|
||||||
|
GP31 = GPIO31 // peripherals: PWM7 channel B
|
||||||
|
GP32 = GPIO32 // peripherals: PWM8 channel A
|
||||||
|
GP33 = GPIO33 // peripherals: PWM8 channel B
|
||||||
|
GP34 = GPIO34 // peripherals: PWM9 channel A
|
||||||
|
GP35 = GPIO35 // peripherals: PWM9 channel B
|
||||||
|
GP36 = GPIO36 // peripherals: PWM10 channel A
|
||||||
|
GP37 = GPIO37 // peripherals: PWM10 channel B
|
||||||
|
GP38 = GPIO38 // peripherals: PWM11 channel A
|
||||||
|
GP39 = GPIO39 // peripherals: PWM11 channel B
|
||||||
|
GP40 = GPIO40 // peripherals: PWM8 channel A
|
||||||
|
GP41 = GPIO41 // peripherals: PWM8 channel B
|
||||||
|
GP42 = GPIO42 // peripherals: PWM9 channel A
|
||||||
|
GP43 = GPIO43 // peripherals: PWM9 channel B
|
||||||
|
GP44 = GPIO44 // peripherals: PWM10 channel A
|
||||||
|
GP45 = GPIO45 // peripherals: PWM10 channel B
|
||||||
|
GP46 = GPIO46 // peripherals: PWM11 channel A
|
||||||
|
GP47 = GPIO47 // peripherals: PWM11 channel B
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultUART = UART0
|
||||||
|
|
||||||
|
// Peripheral defaults.
|
||||||
|
const (
|
||||||
|
xoscFreq = 12 // MHz
|
||||||
|
|
||||||
|
I2C0_SDA_PIN = GP4
|
||||||
|
I2C0_SCL_PIN = GP5
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = GP2
|
||||||
|
I2C1_SCL_PIN = GP3
|
||||||
|
|
||||||
|
// Default Serial Clock Bus 0 for SPI communications
|
||||||
|
SPI0_SCK_PIN = GPIO18
|
||||||
|
// Default Serial Out Bus 0 for SPI communications
|
||||||
|
SPI0_SDO_PIN = GPIO19 // Tx
|
||||||
|
// Default Serial In Bus 0 for SPI communications
|
||||||
|
SPI0_SDI_PIN = GPIO16 // Rx
|
||||||
|
|
||||||
|
// Default Serial Clock Bus 1 for SPI communications
|
||||||
|
SPI1_SCK_PIN = GPIO10
|
||||||
|
// Default Serial Out Bus 1 for SPI communications
|
||||||
|
SPI1_SDO_PIN = GPIO11 // Tx
|
||||||
|
// Default Serial In Bus 1 for SPI communications
|
||||||
|
SPI1_SDI_PIN = GPIO12 // Rx
|
||||||
|
|
||||||
|
UART0_TX_PIN = GPIO0
|
||||||
|
UART0_RX_PIN = GPIO1
|
||||||
|
UART1_TX_PIN = GPIO8
|
||||||
|
UART1_RX_PIN = GPIO9
|
||||||
|
UART_TX_PIN = UART0_TX_PIN
|
||||||
|
UART_RX_PIN = UART0_RX_PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "PGA2350"
|
||||||
|
usb_STRING_MANUFACTURER = "Pimoroni"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2E8A
|
||||||
|
usb_PID uint16 = 0x000A
|
||||||
|
)
|
||||||
@@ -33,16 +33,6 @@ const (
|
|||||||
rp.PADS_BANK0_GPIO0_ISO_Msk
|
rp.PADS_BANK0_GPIO0_ISO_Msk
|
||||||
)
|
)
|
||||||
|
|
||||||
// Analog pins on RP2350.
|
|
||||||
const (
|
|
||||||
ADC0 Pin = GPIO26
|
|
||||||
ADC1 Pin = GPIO27
|
|
||||||
ADC2 Pin = GPIO28
|
|
||||||
ADC3 Pin = GPIO29
|
|
||||||
|
|
||||||
thermADC = 30
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PinOutput PinMode = iota
|
PinOutput PinMode = iota
|
||||||
PinInput
|
PinInput
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//go:build rp2350 && !rp2350b
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Analog pins on RP2350a.
|
||||||
|
const (
|
||||||
|
ADC0 Pin = GPIO26
|
||||||
|
ADC1 Pin = GPIO27
|
||||||
|
ADC2 Pin = GPIO28
|
||||||
|
ADC3 Pin = GPIO29
|
||||||
|
|
||||||
|
// fifth ADC channel.
|
||||||
|
thermADC = 30
|
||||||
|
)
|
||||||
@@ -35,7 +35,7 @@ const (
|
|||||||
ADC5 Pin = GPIO45
|
ADC5 Pin = GPIO45
|
||||||
ADC6 Pin = GPIO46
|
ADC6 Pin = GPIO46
|
||||||
ADC7 Pin = GPIO47
|
ADC7 Pin = GPIO47
|
||||||
|
// Ninth ADC channel.
|
||||||
thermADC = 48
|
thermADC = 48
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pllsysFB, pllsysPD1, pllsysPD2 uint32 = 125, 6, 2 // RP2040 running 125MHz with 1500MHz VCO.
|
||||||
|
|
||||||
// init initializes the clock hardware.
|
// init initializes the clock hardware.
|
||||||
//
|
//
|
||||||
// Must be called before any other clock function.
|
// Must be called before any other clock function.
|
||||||
@@ -163,8 +165,8 @@ func (clks *clocksType) init() {
|
|||||||
// REF FBDIV VCO POSTDIV
|
// REF FBDIV VCO POSTDIV
|
||||||
// pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
|
// pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
|
||||||
// pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
|
// pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
|
||||||
pllSys.init(1, 1500*MHz, 6, 2)
|
pllSys.init(1, uint32(pllsysFB), uint32(pllsysPD1), uint32(pllsysPD2))
|
||||||
pllUSB.init(1, 480*MHz, 5, 2)
|
pllUSB.init(1, 40, 5, 2)
|
||||||
|
|
||||||
// Configure clocks
|
// Configure clocks
|
||||||
// clkRef = xosc (12MHz) / 1 = 12MHz
|
// clkRef = xosc (12MHz) / 1 = 12MHz
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/rp"
|
"device/rp"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"math/bits"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -29,12 +32,11 @@ var (
|
|||||||
// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
|
// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
|
||||||
//
|
//
|
||||||
// Post Divider 2, postDiv2 with range 1-7.
|
// Post Divider 2, postDiv2 with range 1-7.
|
||||||
func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
|
func (pll *pll) init(refdiv, fbdiv, postDiv1, postDiv2 uint32) {
|
||||||
refFreq := xoscFreq / refdiv
|
refFreq := xoscFreq / refdiv
|
||||||
|
|
||||||
// What are we multiplying the reference clock by to get the vco freq
|
// What are we multiplying the reference clock by to get the vco freq
|
||||||
// (The regs are called div, because you divide the vco output and compare it to the refclk)
|
// (The regs are called div, because you divide the vco output and compare it to the refclk)
|
||||||
fbdiv := vcoFreq / (refFreq * MHz)
|
|
||||||
|
|
||||||
// Check fbdiv range
|
// Check fbdiv range
|
||||||
if !(fbdiv >= 16 && fbdiv <= 320) {
|
if !(fbdiv >= 16 && fbdiv <= 320) {
|
||||||
@@ -54,13 +56,14 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
|
|||||||
panic("postdiv1 should be greater than or equal to postdiv2")
|
panic("postdiv1 should be greater than or equal to postdiv2")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reference frequency is no greater than vco / 16
|
// Check that reference frequency is no greater than vcoFreq / 16
|
||||||
|
vcoFreq := calcVCO(xoscFreq, fbdiv, refdiv)
|
||||||
if refFreq > vcoFreq/16 {
|
if refFreq > vcoFreq/16 {
|
||||||
panic("reference frequency should not be greater than vco frequency divided by 16")
|
panic("reference frequency should not be greater than vco frequency divided by 16")
|
||||||
}
|
}
|
||||||
|
|
||||||
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
|
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
|
||||||
pdiv := postDiv1<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | postDiv2<<rp.PLL_SYS_PRIM_POSTDIV2_Pos
|
pdiv := uint32(postDiv1)<<rp.PLL_SYS_PRIM_POSTDIV1_Pos | uint32(postDiv2)<<rp.PLL_SYS_PRIM_POSTDIV2_Pos
|
||||||
|
|
||||||
if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) &&
|
if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) &&
|
||||||
refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk &&
|
refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk &&
|
||||||
@@ -98,3 +101,179 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
|
|||||||
pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)
|
pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errVCOOverflow = errors.New("VCO calculation overflow; use lower MHz")
|
||||||
|
|
||||||
|
// pllSearch enables searching for a good PLL configuration.
|
||||||
|
// Example for 12MHz crystal and RP2040:
|
||||||
|
//
|
||||||
|
// fbdiv, refdiv, pd1, pd2, _ := pllSearch{LockRefDiv:1}.CalcDivs(12*MHz, 125*MHz, MHz)
|
||||||
|
//
|
||||||
|
// Example for 12MHz crystal and RP2350:
|
||||||
|
//
|
||||||
|
// fbdiv, refdiv, pd1, pd2, _ := pllSearch{LockRefDiv:1}.CalcDivs(12*MHz, 133*MHz, MHz)
|
||||||
|
type pllSearch struct {
|
||||||
|
LowerVCO bool
|
||||||
|
LockRefDiv uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps pllSearch) CalcDivs(xoscRef, targetFreq, MHz uint64) (fbdiv uint64, refdiv, pd1, pd2 uint8, err error) {
|
||||||
|
genTable()
|
||||||
|
var bestFreq, bestFbdiv uint64
|
||||||
|
var bestRefdiv, bestpd1, bestpd2 uint8
|
||||||
|
maxVCO, minVCO := 1600*MHz, 750*MHz
|
||||||
|
var bestMargin int64 = int64(maxVCO)
|
||||||
|
iters := 0
|
||||||
|
for refdiv = 1; refdiv < 64; refdiv++ {
|
||||||
|
if ps.LockRefDiv != 0 && refdiv != ps.LockRefDiv {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
firstFBDiv := minVCO * uint64(refdiv) / xoscRef
|
||||||
|
for fbdiv = firstFBDiv; fbdiv < 321; fbdiv++ {
|
||||||
|
overflow, vco := bits.Mul64(xoscRef, fbdiv)
|
||||||
|
vco /= uint64(refdiv)
|
||||||
|
if overflow != 0 {
|
||||||
|
return fbdiv, refdiv, pd1, pd2, errVCOOverflow
|
||||||
|
} else if vco > maxVCO {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
calcPD12 := vco / targetFreq
|
||||||
|
if calcPD12 < 1 {
|
||||||
|
calcPD12 = 1
|
||||||
|
} else if calcPD12 > 49 {
|
||||||
|
calcPD12 = 49
|
||||||
|
}
|
||||||
|
iters++
|
||||||
|
pd1 = pdTable[calcPD12].hivco[0]
|
||||||
|
pd2 = pdTable[calcPD12].hivco[1]
|
||||||
|
fout, err := pllFreqOutPostdiv(xoscRef, fbdiv, MHz, refdiv, pd1, pd2)
|
||||||
|
found := false
|
||||||
|
margin := abs(int64(fout) - int64(targetFreq))
|
||||||
|
if err == nil && margin <= bestMargin {
|
||||||
|
found = true
|
||||||
|
bestFreq = fout
|
||||||
|
bestFbdiv = fbdiv
|
||||||
|
bestpd1 = pd1
|
||||||
|
bestpd2 = pd2
|
||||||
|
bestRefdiv = refdiv
|
||||||
|
bestMargin = margin
|
||||||
|
}
|
||||||
|
pd1 = pdTable[calcPD12].lovco[0]
|
||||||
|
pd2 = pdTable[calcPD12].lovco[1]
|
||||||
|
fout, err = pllFreqOutPostdiv(xoscRef, fbdiv, MHz, refdiv, pd1, pd2)
|
||||||
|
margin = abs(int64(fout) - int64(targetFreq))
|
||||||
|
if err == nil && margin <= bestMargin {
|
||||||
|
found = true
|
||||||
|
bestFreq = fout
|
||||||
|
bestFbdiv = fbdiv
|
||||||
|
bestpd1 = pd1
|
||||||
|
bestpd2 = pd2
|
||||||
|
bestRefdiv = refdiv
|
||||||
|
bestMargin = margin
|
||||||
|
}
|
||||||
|
if found && ps.LowerVCO {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bestFreq == 0 {
|
||||||
|
return fbdiv, refdiv, pd1, pd2, errors.New("no best frequency found")
|
||||||
|
}
|
||||||
|
return bestFbdiv, bestRefdiv, bestpd1, bestpd2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(a int64) int64 {
|
||||||
|
if a == math.MinInt64 {
|
||||||
|
return math.MaxInt64
|
||||||
|
} else if a < 0 {
|
||||||
|
return -a
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func pllFreqOutPostdiv(xosc, fbdiv, MHz uint64, refdiv, postdiv1, postdiv2 uint8) (foutpostdiv uint64, err error) {
|
||||||
|
// testing grounds.
|
||||||
|
const (
|
||||||
|
mhz = 1
|
||||||
|
cfref = 12 * mhz // given by crystal oscillator selection.
|
||||||
|
crefd = 1
|
||||||
|
cfbdiv = 100
|
||||||
|
cvco = cfref * cfbdiv / crefd
|
||||||
|
cpd1 = 6
|
||||||
|
cpd2 = 2
|
||||||
|
foutpd = (cfref / crefd) * cfbdiv / (cpd1 * cpd2)
|
||||||
|
)
|
||||||
|
refFreq := xosc / uint64(refdiv)
|
||||||
|
overflow, vco := bits.Mul64(xosc, fbdiv)
|
||||||
|
vco /= uint64(refdiv)
|
||||||
|
foutpostdiv = vco / uint64(postdiv1*postdiv2)
|
||||||
|
switch {
|
||||||
|
case refdiv < 1 || refdiv > 63:
|
||||||
|
err = errors.New("reference divider out of range")
|
||||||
|
case fbdiv < 16 || fbdiv > 320:
|
||||||
|
err = errors.New("feedback divider out of range")
|
||||||
|
case postdiv1 < 1 || postdiv1 > 7:
|
||||||
|
err = errors.New("postdiv1 out of range")
|
||||||
|
case postdiv2 < 1 || postdiv2 > 7:
|
||||||
|
err = errors.New("postdiv2 out of range")
|
||||||
|
case postdiv1 < postdiv2:
|
||||||
|
err = errors.New("user error: use higher value for postdiv1 for lower power consumption")
|
||||||
|
case vco < 750*MHz || vco > 1600*MHz:
|
||||||
|
err = errors.New("VCO out of range")
|
||||||
|
case refFreq < 5*MHz:
|
||||||
|
err = errors.New("minimum reference frequency breach")
|
||||||
|
case refFreq > vco/16:
|
||||||
|
err = errors.New("maximum reference frequency breach")
|
||||||
|
case vco > 1200*MHz && vco < 1600*MHz && xosc < 75*MHz && refdiv != 1:
|
||||||
|
err = errors.New("refdiv should be 1 for given VCO and reference frequency")
|
||||||
|
case overflow != 0:
|
||||||
|
err = errVCOOverflow
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return foutpostdiv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcVCO(xoscFreq, fbdiv, refdiv uint32) uint32 {
|
||||||
|
const maxXoscMHz = math.MaxUint32 / 320 / MHz // 13MHz maximum xosc apparently.
|
||||||
|
if fbdiv > 320 || xoscFreq > math.MaxUint32/320 {
|
||||||
|
panic("invalid VCO calculation args")
|
||||||
|
}
|
||||||
|
return xoscFreq * fbdiv / refdiv
|
||||||
|
}
|
||||||
|
|
||||||
|
var pdTable = [50]struct {
|
||||||
|
hivco [2]uint8
|
||||||
|
lovco [2]uint8
|
||||||
|
}{}
|
||||||
|
|
||||||
|
func genTable() {
|
||||||
|
if pdTable[1].hivco[1] != 0 {
|
||||||
|
return // Already generated.
|
||||||
|
}
|
||||||
|
for product := 1; product < len(pdTable); product++ {
|
||||||
|
bestProdhi := 255
|
||||||
|
bestProdlo := 255
|
||||||
|
for pd1 := 7; pd1 > 0; pd1-- {
|
||||||
|
for pd2 := pd1; pd2 > 0; pd2-- {
|
||||||
|
gotprod := pd1 * pd2
|
||||||
|
if abs(int64(gotprod-product)) < abs(int64(bestProdlo-product)) {
|
||||||
|
bestProdlo = gotprod
|
||||||
|
pdTable[product].lovco[0] = uint8(pd1)
|
||||||
|
pdTable[product].lovco[1] = uint8(pd2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for pd1 := 1; pd1 < 8; pd1++ {
|
||||||
|
for pd2 := 1; pd2 <= pd1; pd2++ {
|
||||||
|
gotprod := pd1 * pd2
|
||||||
|
if abs(int64(gotprod-product)) < abs(int64(bestProdhi-product)) {
|
||||||
|
bestProdhi = gotprod
|
||||||
|
pdTable[product].hivco[0] = uint8(pd1)
|
||||||
|
pdTable[product].hivco[1] = uint8(pd2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -146,13 +146,14 @@ func (p *pwmGroup) Counter() uint32 {
|
|||||||
|
|
||||||
// Period returns the used PWM period in nanoseconds.
|
// Period returns the used PWM period in nanoseconds.
|
||||||
func (p *pwmGroup) Period() uint64 {
|
func (p *pwmGroup) Period() uint64 {
|
||||||
freq := CPUFrequency()
|
// Lines below can overflow if operations done without care.
|
||||||
|
// maxInt=255, maxFrac=15, maxTop=65536, maxPHC=1 => maxProduct= (16*255+15) * (65536*2*1e9) = 5.3673e17 < MaxUint64=1.8e19 (close call.)
|
||||||
|
const compileTimeCheckPeriod uint64 = (255*16 + 15) * (65535 + 1) * 2 * 1e9
|
||||||
|
freq := uint64(CPUFrequency())
|
||||||
top := p.getWrap()
|
top := p.getWrap()
|
||||||
phc := p.getPhaseCorrect()
|
phc := p.getPhaseCorrect()
|
||||||
Int, frac := p.getClockDiv()
|
Int, frac := p.getClockDiv()
|
||||||
// Lines below can overflow if operations done without care.
|
return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*1e9) / (16 * freq) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||||
term2 := 16 * uint64((top+1)*(phc+1)) * 1e9 / uint64(freq) // 1e9/freq == CPU period in nanoseconds.
|
|
||||||
return (uint64(Int) + uint64(frac)) * term2 / 16 // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetInverting sets whether to invert the output of this channel.
|
// SetInverting sets whether to invert the output of this channel.
|
||||||
@@ -267,6 +268,9 @@ func (pwm *pwmGroup) setPeriod(period uint64) error {
|
|||||||
// Maximum Period is 268369920ns on rp2040, given by (16*255+15)*8*(1+0xffff)*(1+1)/16
|
// Maximum Period is 268369920ns on rp2040, given by (16*255+15)*8*(1+0xffff)*(1+1)/16
|
||||||
// With no phase shift max period is half of this value.
|
// With no phase shift max period is half of this value.
|
||||||
maxPeriod = 268 * milliseconds
|
maxPeriod = 268 * milliseconds
|
||||||
|
// This will be a compile time error if this method is at risk of overflowing. cpufreq=155MHz for typical RP2350.
|
||||||
|
maxCPUFreq = 4 * GHz // Can go up to 4GHz without overflowing :)
|
||||||
|
compileTimeCheckSetPeriod uint64 = 16 * maxPeriod * maxCPUFreq
|
||||||
)
|
)
|
||||||
|
|
||||||
if period > maxPeriod || period < 8 {
|
if period > maxPeriod || period < 8 {
|
||||||
@@ -280,11 +284,13 @@ func (pwm *pwmGroup) setPeriod(period uint64) error {
|
|||||||
// DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) // DIV_FRAC/16 is always 0 in this equation
|
// DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) // DIV_FRAC/16 is always 0 in this equation
|
||||||
// where cycles must be converted to time:
|
// where cycles must be converted to time:
|
||||||
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
|
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
|
||||||
freq := uint64(CPUFrequency())
|
var (
|
||||||
phc := uint64(pwm.getPhaseCorrect())
|
freq = uint64(CPUFrequency())
|
||||||
rhs := 16e9 * period / ((1 + phc) * freq * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided
|
phc = uint64(pwm.getPhaseCorrect())
|
||||||
whole := rhs / 16
|
rhs = 16 * period * freq / ((1 + phc) * 1e9 * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided
|
||||||
frac := rhs % 16
|
whole = rhs / 16
|
||||||
|
frac = rhs % 16
|
||||||
|
)
|
||||||
switch {
|
switch {
|
||||||
case whole > 0xff:
|
case whole > 0xff:
|
||||||
whole = 0xff
|
whole = 0xff
|
||||||
@@ -297,7 +303,7 @@ func (pwm *pwmGroup) setPeriod(period uint64) error {
|
|||||||
|
|
||||||
// Step 2 is acquiring a better top value. Clearing the equation:
|
// Step 2 is acquiring a better top value. Clearing the equation:
|
||||||
// TOP = cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1
|
// TOP = cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1
|
||||||
top := 16e9*period/((16*whole+frac)*freq*(1+phc)) - 1
|
top := 16*period*freq/((1+phc)*1e9*(16*whole+frac)) - 1
|
||||||
if top > maxTop {
|
if top > maxTop {
|
||||||
top = maxTop
|
top = maxTop
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"inherits": ["rp2350b"],
|
||||||
|
"build-tags": ["pga2350"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user