Skip to content

Commit e1472f6

Browse files
author
Konstantin Sharlaimov
committed
feat(machine/stm32): add STM32H7 and NUCLEO-H753ZI support
1 parent a5d4ea3 commit e1472f6

27 files changed

Lines changed: 2576 additions & 11 deletions

GNUmakefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,8 @@ endif
807807
@$(MD5SUM) test.hex
808808
$(TINYGO) build -size short -o test.hex -target=pico -gc=leaking examples/blinky1
809809
@$(MD5SUM) test.hex
810+
$(TINYGO) build -size short -o test.hex -target=pico-w examples/blinky1
811+
@$(MD5SUM) test.hex
810812
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
811813
@$(MD5SUM) test.hex
812814
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/blinky1
@@ -845,8 +847,14 @@ endif
845847
@$(MD5SUM) test.hex
846848
$(TINYGO) build -size short -o test.hex -target=pico2 examples/blinky1
847849
@$(MD5SUM) test.hex
850+
$(TINYGO) build -size short -o test.hex -target=pico2-w examples/blinky1
851+
@$(MD5SUM) test.hex
848852
$(TINYGO) build -size short -o test.hex -target=tiny2350 examples/blinky1
849853
@$(MD5SUM) test.hex
854+
$(TINYGO) build -size short -o test.hex -target=badger2350 examples/blinky1
855+
@$(MD5SUM) test.hex
856+
$(TINYGO) build -size short -o test.hex -target=blinky2350 examples/blinky1
857+
@$(MD5SUM) test.hex
850858
$(TINYGO) build -size short -o test.hex -target=pico-plus2 examples/blinky1
851859
@$(MD5SUM) test.hex
852860
$(TINYGO) build -size short -o test.hex -target=metro-rp2350 examples/blinky1
@@ -888,6 +896,8 @@ ifneq ($(STM32), 0)
888896
@$(MD5SUM) test.hex
889897
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
890898
@$(MD5SUM) test.hex
899+
$(TINYGO) build -size short -o test.hex -target=nucleo-h753zi examples/blinky1
900+
@$(MD5SUM) test.hex
891901
$(TINYGO) build -size short -o test.hex -target=nucleo-l031k6 examples/blinky1
892902
@$(MD5SUM) test.hex
893903
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1

src/examples/pwm/nucleo-h753zi.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build stm32h7
2+
3+
package main
4+
5+
import "machine"
6+
7+
var (
8+
pwm = &machine.TIM1
9+
pinA = machine.PA8
10+
pinB = machine.PA9
11+
)

src/examples/wwdg/main.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"machine"
5+
"time"
6+
)
7+
8+
func main() {
9+
time.Sleep(2 * time.Second)
10+
11+
println("configuring window watchdog")
12+
config := machine.WindowWatchdogConfig{
13+
TimeoutMicros: 100000, // 100ms
14+
WindowPercent: 50, // 50ms to 100ms refresh window
15+
}
16+
17+
machine.WindowWatchdog.Configure(config)
18+
machine.WindowWatchdog.Start()
19+
20+
println("updating wwdg for 1 second")
21+
for i := 0; i < 10; i++ {
22+
time.Sleep(75 * time.Millisecond) // middle of the window
23+
machine.WindowWatchdog.Update()
24+
println("alive")
25+
}
26+
27+
println("entering tight loop (will reset)")
28+
for {
29+
time.Sleep(10 * time.Millisecond)
30+
}
31+
}

src/machine/board_nucleoh753zi.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//go:build nucleoh753zi
2+
3+
package machine
4+
5+
import (
6+
"device/stm32"
7+
"runtime/interrupt"
8+
)
9+
10+
const xtalHz = 8_000_000
11+
const hseBypass = true
12+
13+
const (
14+
// Arduino Pins
15+
A0 = PA3
16+
A1 = PC0
17+
A2 = PC3
18+
A3 = PB1
19+
A4 = PC2
20+
A5 = PF10
21+
22+
D0 = PG9
23+
D1 = PG14
24+
D2 = PF15
25+
D3 = PE13
26+
D4 = PF14
27+
D5 = PE11
28+
D6 = PE9
29+
D7 = PF13
30+
D8 = PF12
31+
D9 = PD15
32+
D10 = PD14
33+
D11 = PA7
34+
D12 = PA6
35+
D13 = PA5
36+
D14 = PB9
37+
D15 = PB8
38+
)
39+
40+
const (
41+
LED = LED_BUILTIN
42+
LED_BUILTIN = LED_GREEN
43+
LED_GREEN = PB0
44+
LED_YELLOW = PE1
45+
LED_RED = PB14
46+
)
47+
48+
const (
49+
BUTTON = BUTTON_USER
50+
BUTTON_USER = PC13
51+
)
52+
53+
// UART pins
54+
const (
55+
// PD8 and PD9 are connected to the ST-Link Virtual Com Port (VCP)
56+
UART_TX_PIN = PD8
57+
UART_RX_PIN = PD9
58+
UART_ALT_FN = AF7_SPI2_3_USART1_2_3_UART5_SPDIFRX
59+
)
60+
61+
var (
62+
// USART3 is the hardware serial port connected to the onboard ST-LINK
63+
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
64+
UART1 = &_UART1
65+
_UART1 = UART{
66+
Buffer: NewRingBuffer(),
67+
Bus: stm32.USART3,
68+
TxAltFuncSelector: UART_ALT_FN,
69+
RxAltFuncSelector: UART_ALT_FN,
70+
}
71+
DefaultUART = UART1
72+
)
73+
74+
func init() {
75+
UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, _UART1.handleInterrupt)
76+
}
77+
78+
// SPI pins
79+
const (
80+
SPI0_SCK_PIN = PA5
81+
SPI0_SDI_PIN = PA6
82+
SPI0_SDO_PIN = PA7
83+
)
84+
85+
var (
86+
SPI1 = &SPI{
87+
Bus: stm32.SPI1,
88+
AltFuncSelector: AF5_SPI1_2_3_4_5_6_I2S,
89+
}
90+
SPI0 = SPI1
91+
)
92+
93+
// I2C pins
94+
const (
95+
I2C0_SCL_PIN = PB8
96+
I2C0_SDA_PIN = PB9
97+
)
98+
99+
var (
100+
I2C1 = &I2C{
101+
Bus: stm32.I2C1,
102+
AltFuncSelector: AF4_I2C1_2_3_4_USART1,
103+
}
104+
I2C0 = I2C1
105+
)

src/machine/machine_stm32.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,6 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
8686
return &port.BSRR.Reg, 1 << (pin + 16)
8787
}
8888

89-
// EnterBootloader resets the chip into the bootloader.
90-
// This is currently a stub for STM32, required to satisfy machine.EnterBootloader
91-
// called by machine/usb/cdc.
92-
func EnterBootloader() {
93-
}
94-
9589
var deviceID [12]byte
9690

9791
// DeviceID returns an identifier that is unique within
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
//go:build stm32h7
2+
3+
package machine
4+
5+
import (
6+
"device/arm"
7+
"device/stm32"
8+
)
9+
10+
// InitADC initializes the registers needed for ADC1 and ADC3.
11+
func InitADC() {
12+
// 1. Enable ADC bus clocks
13+
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_ADC12EN)
14+
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_ADC3EN)
15+
16+
// 2. Configure ADC clock mode (Async from kernel clock)
17+
// CCR is at offset 0x308 from ADC base.
18+
stm32.ADC12_Common.CR.ReplaceBits(0x0, 0x3, 16) // CKMODE = 00
19+
stm32.ADC3_Common.CR.ReplaceBits(0x0, 0x3, 16) // CKMODE = 00
20+
21+
// 3. Exit deep power-down mode
22+
stm32.ADC1.CR.ClearBits(stm32.ADC_CR_DEEPPWD)
23+
stm32.ADC3.CR.ClearBits(stm32.ADC_CR_DEEPPWD)
24+
25+
// 4. Enable voltage regulators
26+
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADVREGEN)
27+
stm32.ADC3.CR.SetBits(stm32.ADC_CR_ADVREGEN)
28+
// Wait for T_ADCVREG_STUP (min 10us). The nop keeps the compiler from
29+
// eliminating the loop as free of side effects.
30+
for i := 0; i < 10000; i++ {
31+
arm.Asm("nop")
32+
}
33+
34+
// Set BOOST[1:0]=0b11 for ADC kernel clock >25MHz (RM0433 §25.4.3 Table 121).
35+
// The kernel clock is 80MHz (PLL2_P), so BOOST must be enabled.
36+
stm32.ADC1.CR.ReplaceBits(0b11, 0x3, stm32.ADC_CR_BOOST_Pos)
37+
stm32.ADC3.CR.ReplaceBits(0b11, 0x3, stm32.ADC_CR_BOOST_Pos)
38+
39+
// 5. Calibration
40+
// ADC1
41+
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADCAL | stm32.ADC_CR_ADCALLIN)
42+
for stm32.ADC1.CR.HasBits(stm32.ADC_CR_ADCAL) {
43+
}
44+
// ADC3
45+
stm32.ADC3.CR.SetBits(stm32.ADC_CR_ADCAL | stm32.ADC_CR_ADCALLIN)
46+
for stm32.ADC3.CR.HasBits(stm32.ADC_CR_ADCAL) {
47+
}
48+
49+
// 6. Enable ADCs
50+
// ADC1
51+
stm32.ADC1.ISR.SetBits(stm32.ADC_ISR_ADRDY) // Clear ADRDY by writing 1
52+
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADEN)
53+
for !stm32.ADC1.ISR.HasBits(stm32.ADC_ISR_ADRDY) {
54+
}
55+
// ADC3
56+
stm32.ADC3.ISR.SetBits(stm32.ADC_ISR_ADRDY) // Clear ADRDY by writing 1
57+
stm32.ADC3.CR.SetBits(stm32.ADC_CR_ADEN)
58+
for !stm32.ADC3.ISR.HasBits(stm32.ADC_ISR_ADRDY) {
59+
}
60+
61+
// 7. Configure resolution (16-bit)
62+
// RES[2:0] is at bits 4:2 in CFGR. 000: 16-bit.
63+
stm32.ADC1.CFGR.ReplaceBits(0x0, 0x7, 2)
64+
stm32.ADC3.CFGR.ReplaceBits(0x0, 0x7, 2)
65+
}
66+
67+
// Configure configures an ADC pin to be able to read analog data.
68+
func (a ADC) Configure(config ADCConfig) {
69+
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
70+
71+
// Set sampling time.
72+
// H7 has SMPR1 (channels 0-9) and SMPR2 (channels 10-19).
73+
// Each channel has 3 bits.
74+
ch := a.getChannel()
75+
adc, _ := a.getPeripheral()
76+
const smpVal = 0x2 // 8.5 cycles
77+
if ch <= 9 {
78+
adc.SMPR1.ReplaceBits(uint32(smpVal), 0x7, uint8(ch)*3)
79+
} else {
80+
adc.SMPR2.ReplaceBits(uint32(smpVal), 0x7, uint8(ch-10)*3)
81+
}
82+
}
83+
84+
// Get returns the current value of an ADC pin in the range 0..0xffff.
85+
func (a ADC) Get() uint16 {
86+
ch := uint32(a.getChannel())
87+
adc, ok := a.getPeripheral()
88+
if !ok {
89+
return 0
90+
}
91+
92+
// Select channel (PCSEL register)
93+
// Refer to RM0433 §25.4.12: Only one PCSELx bit must be set at a time.
94+
adc.PCSEL.Set(1 << ch)
95+
96+
// Set rank 1 to channel
97+
// SQ1[4:0] at bits 10:6. L[3:0] at bits 3:0.
98+
adc.SQR1.ReplaceBits(ch, 0x1F, 6)
99+
adc.SQR1.ReplaceBits(0x0, 0xF, 0) // L=0 (1 conversion)
100+
101+
// Start conversion
102+
adc.CR.SetBits(stm32.ADC_CR_ADSTART)
103+
104+
// Wait for end of conversion
105+
for !adc.ISR.HasBits(stm32.ADC_ISR_EOC) {
106+
}
107+
108+
// Read 16-bit result
109+
result := uint16(adc.DR.Get())
110+
111+
// Clear EOC
112+
adc.ISR.SetBits(stm32.ADC_ISR_EOC)
113+
114+
// Deselect channel
115+
adc.PCSEL.Set(0)
116+
117+
return result
118+
}
119+
120+
func (a ADC) getPeripheral() (*stm32.ADC_Type, bool) {
121+
switch a.Pin {
122+
case PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10:
123+
return stm32.ADC3, true
124+
default:
125+
// Assume ADC1 for PA/PB/PC pins
126+
return stm32.ADC1, true
127+
}
128+
}
129+
130+
// getChannel returns the ADC channel number for a given GPIO pin.
131+
// Mapping for STM32H743 per RM0433 and DS12110.
132+
func (a ADC) getChannel() uint8 {
133+
switch a.Pin {
134+
case PA0:
135+
return 16
136+
case PA1:
137+
return 17
138+
case PA2:
139+
return 14
140+
case PA3:
141+
return 15
142+
case PA4:
143+
return 18
144+
case PA5:
145+
return 19
146+
case PA6:
147+
return 3
148+
case PA7:
149+
return 7
150+
case PB0:
151+
return 9
152+
case PB1:
153+
return 5
154+
case PC0:
155+
return 10
156+
case PC1:
157+
return 11
158+
case PC2:
159+
return 12
160+
case PC3:
161+
return 13
162+
case PC4:
163+
return 4
164+
case PC5:
165+
return 8
166+
case PF3:
167+
return 5
168+
case PF4:
169+
return 9
170+
case PF5:
171+
return 4
172+
case PF6:
173+
return 8
174+
case PF7:
175+
return 3
176+
case PF8:
177+
return 7
178+
case PF9:
179+
return 2
180+
case PF10:
181+
return 6
182+
}
183+
return 0
184+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build stm32 && !stm32h7
2+
3+
package machine
4+
5+
// EnterBootloader resets the chip into the bootloader.
6+
// This is currently a stub for STM32, required to satisfy machine.EnterBootloader
7+
// called by machine/usb/cdc.
8+
func EnterBootloader() {
9+
}

src/machine/machine_stm32_exti_syscfg.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0
1+
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0 && !stm32h7
22

33
package machine
44

0 commit comments

Comments
 (0)