Skip to content

Commit 5e806ae

Browse files
committed
machine/stm32: add OTG FS USB driver for F4/F7
STM32F4 and F7 share the same OTG FS IP but have no USB driver in TinyGo. This adds a full device-mode driver covering CDC, HID, and MSC with working examples. - OTG FS has 4 physical EPs (0–3); virtual indices 4–7 fold onto them via physEP() so the existing machine/usb API is unchanged - F4 bypasses VBUS sensing via GCCFG.NOVBUSSENS; F7 uses the USB voltage regulator + GOTGCTL B-valid override instead - STM32F7 PLL_Q changed 2→9 to produce the 48 MHz clock required by USB/RNG/SDMMC; CK48MSEL cleared to select main PLL as source - HID and MSC descriptors remapped at init() to physical endpoint addresses (EP2/EP1 for HID, EP2/EP3 for MSC) - usb-storage example replaced machine.Flash with a FAT12 RAM disk so the host mounts without reformatting - MSC sendCSW sets queuedBytes before state transition to fix a missed byte-count on the status phase
1 parent 4204f3d commit 5e806ae

21 files changed

Lines changed: 1199 additions & 19 deletions

builder/sizes_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestBinarySize(t *testing.T) {
4444
// microcontrollers
4545
{"hifive1b", "examples/echo", 3680, 280, 0, 2252},
4646
{"microbit", "examples/serial", 2694, 342, 8, 2248},
47-
{"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248},
47+
{"wioterminal", "examples/pininterrupt", 7090, 1510, 120, 7248},
4848

4949
// TODO: also check wasm. Right now this is difficult, because
5050
// wasm binaries are run through wasm-opt and therefore the

src/examples/hid-keyboard/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func main() {
2222
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
2323

2424
kb := keyboard.Port()
25+
machine.USBDev.Configure(machine.UARTConfig{}) // no-op if already init'd by serial.usb
2526

2627
for {
2728
if !button.Get() {

src/examples/usb-storage/main.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,85 @@ import (
66
"time"
77
)
88

9+
// Disk geometry.
10+
const (
11+
sectorSize = 512
12+
diskSectors = 128 // 64 KB total
13+
)
14+
15+
var diskData [diskSectors * sectorSize]byte
16+
17+
type ramDisk struct{}
18+
19+
func (r *ramDisk) ReadAt(p []byte, off int64) (int, error) {
20+
return copy(p, diskData[off:]), nil
21+
}
22+
23+
func (r *ramDisk) WriteAt(p []byte, off int64) (int, error) {
24+
return copy(diskData[off:], p), nil
25+
}
26+
27+
func (r *ramDisk) Size() int64 { return int64(diskSectors * sectorSize) }
28+
func (r *ramDisk) WriteBlockSize() int64 { return sectorSize }
29+
func (r *ramDisk) EraseBlockSize() int64 { return sectorSize }
30+
func (r *ramDisk) EraseBlocks(start, len int64) error { return nil }
31+
32+
func init() {
33+
formatFAT12(diskData[:])
34+
}
35+
36+
// formatFAT12 writes a minimal FAT12 volume boot record and FAT tables so the
37+
// host OS can mount the disk without reformatting.
38+
func formatFAT12(d []byte) {
39+
// --- Sector 0: Volume Boot Record ---
40+
s := d[0:]
41+
s[0] = 0xEB
42+
s[1] = 0x3C
43+
s[2] = 0x90 // short JMP + NOP
44+
copy(s[3:11], "MSDOS5.0")
45+
// BPB fields (little-endian)
46+
s[11] = 0x00
47+
s[12] = 0x02 // bytesPerSector = 512
48+
s[13] = 0x01 // sectorsPerCluster = 1
49+
s[14] = 0x01
50+
s[15] = 0x00 // reservedSectors = 1
51+
s[16] = 0x02 // numFATs = 2
52+
s[17] = 0x20
53+
s[18] = 0x00 // rootEntryCount = 32
54+
s[19] = 0x80
55+
s[20] = 0x00 // totalSectors16 = 128
56+
s[21] = 0xF8 // mediaType = fixed disk
57+
s[22] = 0x01
58+
s[23] = 0x00 // sectorsPerFAT = 1
59+
s[24] = 0x80
60+
s[25] = 0x00 // sectorsPerTrack = 128
61+
s[26] = 0x01
62+
s[27] = 0x00 // numHeads = 1
63+
// hiddenSectors[28:32] = 0
64+
// totalSectors32[32:36] = 0
65+
s[38] = 0x29 // extBootSig
66+
s[39] = 0x47
67+
s[40] = 0x4F
68+
s[41] = 0x30
69+
s[42] = 0x31 // volumeID "GO01"
70+
copy(s[43:54], "TINYGO ") // volumeLabel (11 bytes)
71+
copy(s[54:62], "FAT12 ") // fsType
72+
s[510] = 0x55
73+
s[511] = 0xAA // boot sector signature
74+
75+
// --- Sector 1: FAT1 ---
76+
// Entry 0 = 0xFF8 (media byte), entry 1 = 0xFFF (EOC); all others = free.
77+
d[512] = 0xF8
78+
d[513] = 0xFF
79+
d[514] = 0xFF
80+
81+
// --- Sector 2: FAT2 (identical copy) ---
82+
copy(d[1024:1027], d[512:515])
83+
}
84+
985
func main() {
10-
msc.Port(machine.Flash)
86+
msc.Port(&ramDisk{})
87+
machine.USBDev.Configure(machine.UARTConfig{})
1188

1289
for {
1390
time.Sleep(2 * time.Second)

src/machine/flash.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build esp32c3 || nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
1+
//go:build esp32c3 || nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32f7 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
22

33
package machine
44

src/machine/machine_atsamd21_usb.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,3 +663,11 @@ func setEPINTENSET(ep uint32, val uint8) {
663663
return
664664
}
665665
}
666+
667+
func (dev *USBDevice) SetStallEPIn(ep uint32) {
668+
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_STALLRQ1)
669+
}
670+
671+
func (dev *USBDevice) SetStallEPOut(ep uint32) {
672+
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_STALLRQ0)
673+
}

src/machine/machine_atsamd51_usb.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,11 @@ func setEPINTENCLR(ep uint32, val uint8) {
494494
func setEPINTENSET(ep uint32, val uint8) {
495495
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
496496
}
497+
498+
func (dev *USBDevice) SetStallEPIn(ep uint32) {
499+
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_STALLRQ1)
500+
}
501+
502+
func (dev *USBDevice) SetStallEPOut(ep uint32) {
503+
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_STALLRQ0)
504+
}

src/machine/machine_nrf52840_usb.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,19 @@ func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
378378

379379
return b, nil
380380
}
381+
382+
func (dev *USBDevice) SetStallEPIn(ep uint32) {
383+
if ep == 0 {
384+
nrf.USBD.TASKS_EP0STALL.Set(1)
385+
return
386+
}
387+
nrf.USBD.EPSTALL.Set(uint32(ep) | nrf.USBD_EPSTALL_IO_In<<7 | nrf.USBD_EPSTALL_STALL_Stall<<8)
388+
}
389+
390+
func (dev *USBDevice) SetStallEPOut(ep uint32) {
391+
if ep == 0 {
392+
nrf.USBD.TASKS_EP0STALL.Set(1)
393+
return
394+
}
395+
nrf.USBD.EPSTALL.Set(uint32(ep) | nrf.USBD_EPSTALL_IO_Out<<7 | nrf.USBD_EPSTALL_STALL_Stall<<8)
396+
}

src/machine/machine_stm32.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ 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.
91+
func EnterBootloader() {
92+
}
93+
8994
var deviceID [12]byte
9095

9196
// DeviceID returns an identifier that is unique within

0 commit comments

Comments
 (0)