Skip to content

Commit 72b1955

Browse files
aykevldeadprogram
authored andcommitted
machine: add simulated PWM/timer peripherals
I will soon use these as part of the TinyGo tour, to explain the PWM peripherals on common chips.
1 parent 612a38e commit 72b1955

File tree

4 files changed

+298
-0
lines changed

4 files changed

+298
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//go:build !baremetal && (gemma_m0 || qtpy || trinket_m0 || arduino_mkr1000 || arduino_mkrwifi1010 || arduino_nano33 || arduino_zero || circuitplay_express || feather_m0_express || feather_m0 || itsybitsy_m0 || p1am_100 || xiao)
2+
3+
// Simulated atsamd21 chips.
4+
5+
package machine
6+
7+
// The timer channels/pins match the hardware, and encode the same information
8+
// as pinTimerMapping but in a more generic (less efficient) way.
9+
10+
var TCC0 = timerType{
11+
instance: 0,
12+
frequency: 48e6,
13+
bits: 24,
14+
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
15+
channelPins: [][]Pin{
16+
{PA04, PA08, PB10, PA14, PB16, PA22, PB30}, // channel 0
17+
{PA05, PA09, PB11, PA15, PB17, PA23, PB31}, // channel 1
18+
{PA10, PB12, PA12, PA16, PA18, PA20}, // channel 2
19+
{PA11, PB13, PA13, PA17, PA19, PA21}, // channel 3
20+
},
21+
}
22+
23+
var TCC1 = timerType{
24+
instance: 1,
25+
frequency: 48e6,
26+
bits: 24,
27+
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
28+
channelPins: [][]Pin{
29+
{PA06, PA10, PA30}, // channel 0
30+
{PA07, PA11, PA31}, // channel 1
31+
{PA08, PA24, PB30}, // channel 2
32+
{PA09, PA25, PB31}, // channel 3
33+
},
34+
}
35+
36+
var TCC2 = timerType{
37+
instance: 2,
38+
frequency: 48e6,
39+
bits: 16,
40+
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
41+
channelPins: [][]Pin{
42+
{PA00, PA12, PA16}, // channel 0
43+
{PA01, PA13, PA17}, // channel 1
44+
},
45+
}

src/machine/machine_generic.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,103 @@ func gpioSet(pin Pin, value bool)
4848
//export __tinygo_gpio_get
4949
func gpioGet(pin Pin) bool
5050

51+
// Generic PWM/timer peripheral. Properties can be configured depending on the
52+
// hardware.
53+
type timerType struct {
54+
// Static properties.
55+
instance int32
56+
frequency uint64
57+
bits int
58+
prescalers []int
59+
channelPins [][]Pin
60+
61+
// Configured 'top' value.
62+
top uint32
63+
}
64+
65+
// Configure the PWM/timer peripheral.
66+
func (t *timerType) Configure(config PWMConfig) error {
67+
// Note: for very large period values, this multiplication will overflow.
68+
top := config.Period * t.frequency / 1e9
69+
if config.Period == 0 {
70+
top = 0xffff // default for LEDs
71+
}
72+
73+
// The maximum value that can be stored with the given number of bits in
74+
// this timer.
75+
maxTop := uint64(1)<<uint64(t.bits) - 1
76+
77+
// Look for an appropriate prescaler value.
78+
var prescaler int
79+
for _, div := range t.prescalers {
80+
if top/uint64(div) <= maxTop {
81+
prescaler = div
82+
top = top / uint64(div)
83+
break
84+
}
85+
}
86+
if prescaler == 0 {
87+
return ErrPWMPeriodTooLong
88+
}
89+
90+
// Set these values as the configuration.
91+
t.top = uint32(top)
92+
pwmConfigure(t.instance, float64(t.frequency)/float64(prescaler), uint32(top))
93+
94+
return nil
95+
}
96+
97+
// Channel returns a PWM channel for the given pin. Note that one channel may be
98+
// shared between multiple pins, and so will have the same duty cycle. If this
99+
// is not desirable, look for a different PWM/timer peripheral or consider using
100+
// a different pin.
101+
func (t *timerType) Channel(pin Pin) (uint8, error) {
102+
for ch, pins := range t.channelPins {
103+
// For nrf52xxx chips specifically we can assign any channel to any pin.
104+
// We use a similar (identical?) logic to the hardware implementation,
105+
// and pick the first empty channel.
106+
if pins == nil {
107+
t.channelPins[ch] = []Pin{pin}
108+
pwmChannelConfigure(t.instance, int32(ch), pin)
109+
return uint8(ch), nil
110+
}
111+
112+
// Check whether the pin can be used on this channel.
113+
for _, p := range pins {
114+
if p == pin {
115+
pwmChannelConfigure(t.instance, int32(ch), pin)
116+
return uint8(ch), nil
117+
}
118+
}
119+
}
120+
121+
return 0, ErrInvalidOutputPin
122+
}
123+
124+
func (t *timerType) Set(channel uint8, value uint32) {
125+
pwmChannelSet(t.instance, channel, value)
126+
}
127+
128+
// Top returns the current counter top, for use in duty cycle calculation. It
129+
// will only change with a call to Configure or SetPeriod, otherwise it is
130+
// constant.
131+
//
132+
// The value returned here is hardware dependent. In general, it's best to treat
133+
// it as an opaque value that can be divided by some number and passed to Set
134+
// (see Set documentation for more information).
135+
func (t *timerType) Top() uint32 {
136+
return t.top
137+
}
138+
139+
//export __tinygo_pwm_configure
140+
func pwmConfigure(instance int32, frequency float64, top uint32)
141+
142+
//export __tinygo_pwm_channel_configure
143+
func pwmChannelConfigure(instance, channel int32, pin Pin)
144+
145+
//export __tinygo_pwm_channel_set
146+
func pwmChannelSet(instance int32, channel uint8, value uint32)
147+
51148
type SPI struct {
52149
Bus uint8
53150
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//go:build !baremetal && (bluemicro840 || circuitplay_bluefruit || clue_alpha || feather_nrf52840_sense || feather_nrf52840 || itsybitsy_nrf52840 || mdbt50qrx || nano_33_ble || nicenano || nrf52840_mdk || particle_3rd_gen || pca10056 || pca10059 || rak4631 || reelboard || xiao_ble)
2+
3+
// Simulator support for nrf52840 based boards.
4+
5+
package machine
6+
7+
// Channel values below are nil, so that they get filled in on the first use.
8+
// This is the same as what happens on baremetal.
9+
10+
var PWM0 = timerType{
11+
instance: 0,
12+
frequency: 16e6,
13+
bits: 15,
14+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
15+
channelPins: [][]Pin{
16+
nil, // channel 0
17+
nil, // channel 1
18+
nil, // channel 2
19+
nil, // channel 3
20+
},
21+
}
22+
23+
var PWM1 = timerType{
24+
instance: 1,
25+
frequency: 16e6,
26+
bits: 15,
27+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
28+
channelPins: [][]Pin{
29+
nil, // channel 0
30+
nil, // channel 1
31+
nil, // channel 2
32+
nil, // channel 3
33+
},
34+
}
35+
36+
var PWM2 = timerType{
37+
instance: 2,
38+
frequency: 16e6,
39+
bits: 15,
40+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
41+
channelPins: [][]Pin{
42+
nil, // channel 0
43+
nil, // channel 1
44+
nil, // channel 2
45+
nil, // channel 3
46+
},
47+
}
48+
49+
var PWM3 = timerType{
50+
instance: 3,
51+
frequency: 16e6,
52+
bits: 15,
53+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
54+
channelPins: [][]Pin{
55+
nil, // channel 0
56+
nil, // channel 1
57+
nil, // channel 2
58+
nil, // channel 3
59+
},
60+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//go:build !baremetal && (ae_rp2040 || badger2040_w || badger2040 || challenger_rp2040 || elecrow_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || trinkey_qt2040 || tufty2040 || waveshare_rp2040_tiny || waveshare_rp2040_zero || xiao_rp2040)
2+
3+
// Simulator support for the RP2040.
4+
//
5+
// This is *only* for the RP2040. RP2350 is a different chip with slightly
6+
// different characteristics.
7+
8+
package machine
9+
10+
var PWM0 = timerType{
11+
instance: 0,
12+
frequency: 200e6,
13+
bits: 16,
14+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256}, // actually a continuing range, TODO
15+
channelPins: [][]Pin{
16+
{GPIO0, GPIO16}, // channel A (0)
17+
{GPIO1, GPIO17}, // channel B (1)
18+
},
19+
}
20+
21+
var PWM1 = timerType{
22+
instance: 0,
23+
frequency: 200e6,
24+
bits: 16,
25+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
26+
channelPins: [][]Pin{
27+
{GPIO2, GPIO18}, // channel A (0)
28+
{GPIO3, GPIO19}, // channel B (1)
29+
},
30+
}
31+
32+
var PWM2 = timerType{
33+
instance: 0,
34+
frequency: 200e6,
35+
bits: 16,
36+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
37+
channelPins: [][]Pin{
38+
{GPIO4, GPIO20}, // channel A (0)
39+
{GPIO5, GPIO21}, // channel B (1)
40+
},
41+
}
42+
43+
var PWM3 = timerType{
44+
instance: 0,
45+
frequency: 200e6,
46+
bits: 16,
47+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
48+
channelPins: [][]Pin{
49+
{GPIO6, GPIO22}, // channel A (0)
50+
{GPIO7, GPIO23}, // channel B (1)
51+
},
52+
}
53+
54+
var PWM4 = timerType{
55+
instance: 0,
56+
frequency: 200e6,
57+
bits: 16,
58+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
59+
channelPins: [][]Pin{
60+
{GPIO8, GPIO24}, // channel A (0)
61+
{GPIO9, GPIO25}, // channel B (1)
62+
},
63+
}
64+
65+
var PWM5 = timerType{
66+
instance: 0,
67+
frequency: 200e6,
68+
bits: 16,
69+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
70+
channelPins: [][]Pin{
71+
{GPIO10, GPIO26}, // channel A (0)
72+
{GPIO11, GPIO27}, // channel B (1)
73+
},
74+
}
75+
76+
var PWM6 = timerType{
77+
instance: 0,
78+
frequency: 200e6,
79+
bits: 16,
80+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
81+
channelPins: [][]Pin{
82+
{GPIO12, GPIO28}, // channel A (0)
83+
{GPIO13, GPIO29}, // channel B (1)
84+
},
85+
}
86+
87+
var PWM7 = timerType{
88+
instance: 0,
89+
frequency: 200e6,
90+
bits: 16,
91+
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
92+
channelPins: [][]Pin{
93+
{GPIO14}, // channel A (0)
94+
{GPIO15}, // channel B (1)
95+
},
96+
}

0 commit comments

Comments
 (0)