Compare commits

...

13 Commits

25 changed files with 6384 additions and 122 deletions
+162 -66
View File
@@ -7,6 +7,7 @@
package nxp
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
@@ -377,30 +378,6 @@ func (clk Clock) setCcm(value uint32) {
}
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// PLL configuration for ARM
type ClockConfigArmPll struct {
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
@@ -471,59 +448,178 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
setSysPfd(pfd...)
}
// PLL configuration for USB
type ClockConfigUsbPll struct {
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
Src uint8 // Pll clock source, reference _clock_pll_clk_src
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
// PHY configuration for USB HS
type ClockConfigUsbPhy struct {
Instance uint8 // USB PHY number (1 or 2)
XtalFreq uint32 // External reference clock frequency (Hz)
DCal uint32 // Decode to trim nominal 17.78mA current source
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
PllConfig ClockConfigUsbPll
}
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
// the USB +3V regulator (PMU), for use as either USB host or device.
func (cfg ClockConfigUsbPhy) Configure() {
var (
usb *USB_Type
phy *USBPHY_Type
chrgDetectReg *volatile.Register32
chrgDetectMsk uint32
)
// Select appropriate peripherals based on receiver Instance
switch cfg.Instance {
case 1:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB1_SET.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
// update PFDs after update
setUsb1Pfd(pfd...)
usb = USB1 // Select USB1 HS PHY/PLL
phy = USBPHY1 //
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
case 2:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
usb = USB2 // Select USB2 HS PHY/PLL
phy = USBPHY2 //
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
default:
panic("nxp: invalid USB PHY")
}
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
// Configure and enable USB PLL clocks
cfg.PllConfig.Configure()
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
// Release PHY from reset
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
// Enable power to USB PHY
phy.PWD.Set(0)
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
// enable support for low-speed device connection, direct and indirect (hub)
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
// Enable USB HS clocks gate
ClockIpUsbOh3.Enable(true)
// Reset USB peripheral
usb.USBCMD.SetBits(USB_USBCMD_RST)
// Add a delay after RST to ensure there is a USB D+ pullup sequence
nopDelay(400000)
// Enable USB LDO
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
// check whether we are connected to USB charger
chrgDetectReg.Set(chrgDetectMsk)
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
phy.TX.Set((phy.TX.Get() &
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
}
// PLL configuration for USB
type ClockConfigUsbPll struct {
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
}
func (cfg ClockConfigUsbPll) Configure() {
// select USB peripheral registers based on receiver's Instance
switch cfg.Instance {
case 1: // USB1 PLL
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
// update PFDs (if provided)
if nil != cfg.Pfd {
setUsb1Pfd(cfg.Pfd...)
}
}
case 2: // USB2 PLL
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
default:
panic("nxp: invalid USB PLL")
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// We cannot use the sleep timer from this context (import cycle), but we need
// an approximate method to spin CPU cycles for short periods of time.
// go:inline
func nopDelay(cycles uint32) {
for i := uint32(0); i < cycles; i++ {
arm.Asm(`nop`)
}
}
+80
View File
@@ -278,3 +278,83 @@ func enableDcache(enable bool) {
}
}
}
// FlushDcache flushes data from cache to memory
//
// Normally FlushDcache is used when metadata written to memory will be used by
// a DMA or a bus-controller peripheral. Any data in the cache is written to
// memory. A copy remains in the cache, so this is typically used with special
// fields you will want to quickly access in the future. For data transmission,
// use FlushDeleteDcache.
//go:inline
func FlushDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// DeleteDcache deletes data from the cache, without touching memory.
//
// Normally DeleteDcache is used before receiving data via DMA or from
// bus-controller peripherals which write to memory. You want to delete anything
// the cache may have stored, so your next read is certain to access the
// physical memory.
//go:inline
func DeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
// cache
//
// Normally FlushDeleteDcache is used when transmitting data via DMA or
// bus-controller peripherals which read from memory. You want any cached data
// written to memory, and then removed from the cache, because you no longer
// need to access the data after transmission.
//go:inline
func FlushDeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
View File
+251
View File
@@ -0,0 +1,251 @@
package main
import (
"machine"
"machine/usb"
"time"
)
var keyboard = machine.HID0.Keyboard()
func main() {
println("USB HID keyboard demo")
for {
time.Sleep(5 * time.Second)
// Open a new text editor
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("kate"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Use the io.Writer interface
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
time.Sleep(2 * time.Second)
// Or manually specify keycodes and Unicode codepoints
testKeys([]Key{
// Print alphabet out-of-order
{Press: usb.KeyX},
{Press: usb.KeyY},
{Press: usb.KeyZ},
{Press: usb.KeyG},
{Press: usb.KeyH},
{Press: usb.KeyI},
{Press: usb.KeyJ},
{Press: usb.KeyK},
{Press: usb.KeyL},
{Press: usb.KeyM},
{Press: usb.KeyN},
{Press: usb.KeyO},
{Press: usb.KeyP},
{Press: usb.KeyQ},
{Press: usb.KeyR},
{Press: usb.KeyS},
{Press: usb.KeyT},
{Press: usb.KeyA},
{Press: usb.KeyB},
{Press: usb.KeyC},
{Press: usb.KeyD},
{Press: usb.KeyE},
{Press: usb.KeyF},
{Press: usb.KeyU},
{Press: usb.KeyV},
{Press: usb.KeyW},
// Pause 1 second
{Time: time.Second},
// Move cursor left x3
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
// Pause 1 second
{Time: time.Second},
// Highlight 6 symbols to the left
{Down: usb.KeyModifierShift},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Up: usb.KeyModifierShift},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to beginning of line
{Press: usb.KeyHome},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Highlight 3 symbols to the right
{Down: usb.KeyModifierShift},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Up: usb.KeyModifierShift},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to end of line
{Press: usb.KeyEnd},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Newline
{Press: usb.KeyEnter},
{Press: usb.KeyEnter},
}, 150*time.Millisecond)
// Highlight all text and delete
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyA)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
keyboard.Press(usb.KeyDelete)
time.Sleep(time.Second)
// Close window
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyQ)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(2 * time.Second)
// Confirm discard file
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(5 * time.Second)
// Open a new terminal
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("konsole"))
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Open serial connection
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
time.Sleep(2 * time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(4 * time.Second)
// Write to UART
keyboard.Write([]byte("hello!"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("NO U"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close serial connection
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyX)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
keyboard.Press(usb.KeyBackslash)
time.Sleep(time.Second)
keyboard.Press(usb.KeyY)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close terminal
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(25 * time.Second)
}
}
type Key struct {
Press usb.Keycode
Down usb.Keycode
Up usb.Keycode
Time time.Duration
}
func testKeys(key []Key, delay time.Duration) {
for _, k := range key {
if 0 != k.Down {
keyboard.Down(k.Down)
}
if 0 != k.Press {
keyboard.Press(k.Press)
}
if 0 != k.Up {
keyboard.Up(k.Up)
}
if 0 != k.Time {
time.Sleep(k.Time)
} else {
time.Sleep(delay)
}
}
}
func testInternationalLayout() {
// International keyboard layouts also supported
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Numbers: 0123456789\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬­®¯°±\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Cedilla: Çç\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Ring Above: Åå\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("AE: Ææ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Thorn: Þþ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Sharp S: ß\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("O-Stroke: Øø\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Eth: Ðð\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Euro: €\n"))
}
+37
View File
@@ -0,0 +1,37 @@
// This is a echo console running on the device UART.
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
package main
import (
"machine"
"time"
)
func main() {
uart := machine.UART0
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
input := make([]byte, 4096)
i := 0
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
switch data {
case 13:
// return key
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default:
// just echo the character
uart.WriteByte(data)
input[i] = data
i++
}
}
time.Sleep(10 * time.Millisecond)
}
}
@@ -0,0 +1,53 @@
// +build sam,atsamd51,matrixportal_m4
package machine
import (
"device/sam"
"runtime/interrupt"
)
// UART on the MatrixPortal M4
var (
Serial = UART1
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
UART2 = &_UART2
_UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM4_USART_INT,
SERCOM: 4,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, _UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, _UART2.handleInterrupt)
}
// I2C on the MatrixPortal M4
var (
I2C0 = &I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI on the MatrixPortal M4
var (
SPI0 = SPI{
Bus: sam.SERCOM3_SPIM,
SERCOM: 3, // BUG: SDO on SERCOM1!
}
NINA_SPI = SPI0
SPI1 = SPI{
Bus: sam.SERCOM0_SPIM,
SERCOM: 0,
}
)
+25 -15
View File
@@ -4,6 +4,7 @@ package machine
import (
"device/nxp"
"machine/usb"
"runtime/interrupt"
)
@@ -55,21 +56,21 @@ const (
// Analog pins
const (
// = Pin // Dig | [Pad] {ADC1/ADC2}
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
// = Pin // Dig [Pad] {ADC1/ADC2}
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
)
// Default peripheral pins
@@ -104,6 +105,15 @@ func init() {
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
}
// #=====================================================#
// | USB |
// #=====================================================#
var (
// UART0 = usb.UART{Port: 0}
HID0 = usb.HID{Port: 0}
UART0 = &UART1
)
// #=====================================================#
// | UART |
// #===========#===========#=============#===============#
+1
View File
@@ -11,6 +11,7 @@ import (
// Peripheral abstraction layer for the MIMXRT1062
//go:inline
func CPUFrequency() uint32 {
return 600000000
}
+935
View File
@@ -0,0 +1,935 @@
package usb
// Implementation of 32-bit target-agnostic USB device controller driver (dcd).
import "unsafe"
func init() {
if unsafe.Sizeof(uintptr(0)) > 4 {
panic("USB device controller is only supported on 32-bit systems")
}
}
// dcdCount defines the number of USB cores to configure for device mode. It is
// computed as the sum of all declared device configuration descriptors.
const dcdCount = descCDCACMCount + descHIDCount
// dcdInstance provides statically-allocated instances of each USB device
// controller configured on this platform.
var dcdInstance [dcdCount]dcd
// dhwInstance provides statically-allocated instances of each USB hardware
// abstraction for ports configured as device on this platform.
var dhwInstance [dcdCount]dhw
// dcd implements a generic USB device controller driver (dcd) for 32-bit ARM
// targets.
type dcd struct {
*dhw // USB hardware abstraction layer
core *core // Parent USB core this instance is attached to
port int // USB port index
cc class // USB device class
id int // USB device controller index
}
// initDCD initializes and assigns a free device controller instance to the
// given USB port. Returns the initialized device controller or nil if no free
// device controller instances remain.
func initDCD(port int, class class) (*dcd, status) {
if 0 == dcdCount {
return nil, statusInvalid // Must have defined device controllers
}
switch class.id {
case classDeviceCDCACM:
if 0 == class.config || class.config > descCDCACMCount {
return nil, statusInvalid // Must have defined descriptors
}
default:
}
// Return the first instance whose assigned core is currently nil.
for i := range dcdInstance {
if nil == dcdInstance[i].core {
// Initialize device controller.
dcdInstance[i].dhw = allocDHW(port, i, &dcdInstance[i])
dcdInstance[i].core = &coreInstance[port]
dcdInstance[i].port = port
dcdInstance[i].cc = class
dcdInstance[i].id = i
return &dcdInstance[i], statusOK
}
}
return nil, statusBusy // No free device controller instances available.
}
// class returns the receiver's current device class configuration.
func (d *dcd) class() class { return d.cc }
// dcdSetupSize defines the size (bytes) of a USB standard setup packet.
const dcdSetupSize = 8 // bytes
// dcdSetup contains the USB standard setup packet used to configure a device.
type dcdSetup struct {
bmRequestType uint8
bRequest uint8
wValue uint16
wIndex uint16
wLength uint16
}
// pack returns the receiver setup packet encoded as uint64.
func (s dcdSetup) pack() uint64 {
return ((uint64(s.bmRequestType) & 0xFF) << 0) |
((uint64(s.bRequest) & 0xFF) << 8) |
((uint64(s.wValue) & 0xFFFF) << 16) |
((uint64(s.wIndex) & 0xFFFF) << 32) |
((uint64(s.wLength) & 0xFFFF) << 48)
}
// dcdEvent is used to describe virtual interrupts on the USB bus to a device
// controller.
//
// Since the device controller software is intended for use with multiple TinyGo
// targets, all of which may not have exactly the same USB bus interrupts, a
// "virtual interrupt" is defined that is common to all targets. The target's
// hardware implementation (type dhw) is responsible for translating real system
// interrupts it receives into the appropriate virtual interrupt code, defined
// below, and notifying the device controller via method (*dcd).event(dcdEvent).
type dcdEvent struct {
id uint8
setup dcdSetup
mask uint32
}
// Enumerated constants for all possible USB device controller interrupt codes.
const (
dcdEventInvalid uint8 = iota // Invalid interrupt
dcdEventStatusReset // USB reset received
dcdEventStatusRun // USB controller entered run state
dcdEventStatusSuspend // USB suspend received
dcdEventStatusError // USB error condition detected on bus
dcdEventControlSetup // USB setup received
dcdEventPeripheralReady // USB PHY powered and ready to _go_
dcdEventTransactComplete // USB transaction complete
dcdEventTimer // USB (system) timer
)
func (d *dcd) event(ev dcdEvent) {
switch ev.id {
case dcdEventInvalid:
case dcdEventStatusReset:
d.endpointMask = 0
case dcdEventPeripheralReady:
// Configure and enable control endpoint 0
d.endpointEnable(0, true, 0)
case dcdEventStatusRun:
case dcdEventStatusSuspend:
case dcdEventStatusError:
case dcdEventControlSetup:
// On control endpoint 0 setup events, the ev.setup field will be defined
switch d.controlSetup(ev.setup) {
case dcdStageSetup:
case dcdStageData:
case dcdStageStatus:
case dcdStageStall:
d.controlStall()
}
case dcdEventTransactComplete:
case dcdEventTimer:
default:
}
}
// dcdStage represents the USB transaction stage of a control request.
type dcdStage uint8
// Enumerated constants for all possible USB transaction stages.
const (
dcdStageSetup dcdStage = iota // Indicates no stage transition required
dcdStageData // IN/OUT data transfer
dcdStageStatus // Setup request complete
dcdStageStall // Unhandled or invalid request
)
// controlSetup handles setup messages on control endpoint 0.
func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
// Reset endpoint 0 notify mask
d.controlMask = 0
// First, switch on the type of request (standard, class, or vendor)
switch sup.bmRequestType & descRequestTypeTypeMsk {
// === STANDARD REQUEST ===
case descRequestTypeTypeStandard:
// Switch on the recepient and direction of the request
switch sup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- DEVICE Rx (OUT) ---
case descRequestTypeRecipientDevice | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// SET ADDRESS (0x05):
case descRequestStandardSetAddress:
d.setDeviceAddress(sup.wValue)
d.controlReceive(uintptr(0), 0, false)
return dcdStageSetup
// SET CONFIGURATION (0x09):
case descRequestStandardSetConfiguration:
d.cc.config = int(sup.wValue)
if 0 == d.cc.config || d.cc.config > dcdCount {
// Use default if invalid index received
d.cc.config = 1
}
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
d.uartConfigure()
d.controlReceive(uintptr(0), 0, false)
// HID
case classDeviceHID:
d.serialConfigure()
d.keyboardConfigure()
d.mouseConfigure()
d.joystickConfigure()
d.controlReceive(uintptr(0), 0, false)
default:
// Unhandled device class
}
return dcdStageSetup
default:
// Unhandled request
}
// --- DEVICE Tx (IN) ---
case descRequestTypeRecipientDevice | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// GET STATUS (0x00):
case descRequestStandardGetStatus:
d.controlReply[0] = 0
d.controlReply[1] = 0
d.controlTransmit(
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
return dcdStageSetup
// GET DESCRIPTOR (0x06):
case descRequestStandardGetDescriptor:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
d.controlDescriptorCDCACM(sup)
return dcdStageSetup
// HID
case classDeviceHID:
d.controlDescriptorHID(sup)
return dcdStageSetup
default:
// Unhandled device class
}
// GET CONFIGURATION (0x08):
case descRequestStandardGetConfiguration:
d.controlReply[0] = uint8(d.cc.config)
d.controlTransmit(
uintptr(unsafe.Pointer(&d.controlReply[0])), 1, false)
return dcdStageSetup
default:
// Unhandled request
}
// --- INTERFACE Tx (IN) ---
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// GET DESCRIPTOR (0x06):
case descRequestStandardGetDescriptor:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
d.controlDescriptorCDCACM(sup)
return dcdStageSetup
// HID
case classDeviceHID:
d.controlDescriptorHID(sup)
return dcdStageSetup
default:
// Unhandled device class
}
// GET DESCRIPTOR (0x06):
case descHIDRequestGetReport:
// Respond based on our device class configuration
switch d.cc.id {
// HID
case classDeviceHID:
d.controlDescriptorHID(sup)
return dcdStageSetup
default:
// Unhandled device class
}
default:
// Unhandled request
}
// --- ENDPOINT Rx (OUT) ---
case descRequestTypeRecipientEndpoint | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// CLEAR FEATURE (0x01):
case descRequestStandardClearFeature:
d.endpointClearFeature(uint8(sup.wIndex))
d.controlReceive(uintptr(0), 0, false)
return dcdStageSetup
// SET FEATURE (0x03):
case descRequestStandardSetFeature:
d.endpointSetFeature(uint8(sup.wIndex))
d.controlReceive(uintptr(0), 0, false)
return dcdStageSetup
default:
// Unhandled request
}
// --- ENDPOINT Tx (IN) ---
case descRequestTypeRecipientEndpoint | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// GET STATUS (0x00):
case descRequestStandardGetStatus:
status := d.endpointStatus(uint8(sup.wIndex))
d.controlReply[0] = uint8(status)
d.controlReply[1] = uint8(status >> 8)
d.controlTransmit(
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
return dcdStageSetup
default:
// Unhandled request
}
default:
// Unhandled request recepient or direction
}
// === CLASS REQUEST ===
case descRequestTypeTypeClass:
// Switch on the recepient and direction of the request
switch sup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// CDC | SET LINE CODING (0x20):
case descCDCRequestSetLineCoding:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
// line coding must contain exactly 7 bytes
if descCDCACMCodingSize == sup.wLength {
d.setup = sup
d.controlReceive(
uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])),
descCDCACMCodingSize, true)
// CDC Line Coding packet receipt handling occurs in method
// controlComplete().
return dcdStageSetup
}
default:
// Unhandled device class
}
// CDC | SET CONTROL LINE STATE (0x22):
case descCDCRequestSetControlLineState:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
// Determine interface destination of the request
switch sup.wIndex {
// Control/status interface:
case descCDCACMInterfaceCtrl:
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
d.uartSetLineState(0 != sup.wValue&0x01, 0 != sup.wValue&0x02)
d.controlReceive(uintptr(0), 0, false)
return dcdStageSetup
default:
// Unhandled device interface
}
default:
// Unhandled device class
}
// CDC | SEND BREAK (0x23):
case descCDCRequestSendBreak:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
d.controlReceive(uintptr(0), 0, false)
return dcdStageSetup
default:
// Unhandled device class
}
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
// Respond based on our device class configuration
switch d.cc.id {
// HID
case classDeviceHID:
if sup.wLength <= descHIDCxCount {
d.setup = sup
descHID[d.cc.config-1].cx[0] = 0xE9
d.controlReceive(
uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])),
uint32(sup.wLength), true)
return dcdStageSetup
}
default:
// Unhandled device class
}
// HID | SET IDLE (0x0A)
case descHIDRequestSetIdle:
// Respond based on our device class configuration
switch d.cc.id {
// HID
case classDeviceHID:
idleRate := sup.wValue >> 8
// TBD: do we need to handle this request? wIndex contains the target
// interface of the request.
_ = idleRate
d.controlReceive(uintptr(0), 0, false)
return dcdStageSetup
default:
// Unhandled device class
}
default:
// Unhandled request
}
// --- INTERFACE Tx (IN) ---
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// HID | GET REPORT (0x01)
case descHIDRequestGetReport:
// Respond based on our device class configuration
switch d.cc.id {
// HID
case classDeviceHID:
reportType := uint8(sup.wValue >> 8)
reportID := uint8(sup.wValue)
// TBD: do we need to handle this request? wIndex contains the target
// interface of the request.
_, _ = reportType, reportID
d.controlReply[0] = 0
d.controlReply[1] = 0
d.controlTransmit(
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
return dcdStageSetup
default:
// Unhandled device class
}
default:
// Unhandled request
}
default:
// Unhandled request recepient or direction
}
case descRequestTypeTypeVendor:
default:
// Unhandled request type
}
// All successful requests return early. If we reach this point, the request
// was invalid or unhandled. Stall the endpoint.
return dcdStageStall
}
// controlComplete handles the setup completion of control endpoint 0.
func (d *dcd) controlComplete(status uint32) {
// Reset endpoint 0 notify mask
d.controlMask = 0
// First, switch on the type of request (standard, class, or vendor)
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
// === CLASS REQUEST ===
case descRequestTypeTypeClass:
// Switch on the recepient and direction of the request
switch d.setup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch d.setup.bRequest {
// CDC | SET LINE CODING (0x20):
case descCDCRequestSetLineCoding:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
acm := &descCDCACM[d.cc.config-1]
// Determine interface destination of the request
switch d.setup.wIndex {
// CDC-ACM Control Interface:
case descCDCACMInterfaceCtrl:
// Notify PHY to handle triggers like special baud rates, which
// signal to reboot into bootloader or begin receiving OTA updates
d.uartSetLineCoding(descCDCACMLineCoding{
baud: packU32(acm.cx[:]),
stopBits: acm.cx[4],
parity: acm.cx[5],
numBits: acm.cx[6],
})
default:
// Unhandled device interface
}
default:
// Unhandled device class
}
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
// Respond based on our device class configuration
switch d.cc.id {
// HID
case classDeviceHID:
hid := &descHID[d.cc.config-1]
// Determine interface destination of the request
switch d.setup.wIndex {
// HID Keyboard Interface
case descHIDInterfaceKeyboard:
// Determine the type of descriptor being requested
switch d.setup.wValue >> 8 {
// Configuration descriptor
case descTypeConfigure:
if 1 == d.setup.wLength {
hid.keyboard.led = hid.cx[0]
d.controlTransmit(uintptr(0), 0, false)
}
default:
// Unhandled descriptor type
}
// HID Serial Interface
case descHIDInterfaceSerial:
// Determine the type of descriptor being requested
switch d.setup.wValue >> 8 {
// String descriptor
case descTypeString:
if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) {
d.enableSOF(true, descHIDInterfaceCount)
}
default:
// Unhandled descriptor type
}
default:
// Unhandled device interface
}
default:
// Unhandled device class
}
default:
// Unhandled request
}
default:
// Unhandled recepient or direction
}
default:
// Unhandled request type
}
}
func (d *dcd) controlDescriptorCDCACM(sup dcdSetup) {
acm := &descCDCACM[d.cc.config-1]
dxn := uint8(0)
// Determine the type of descriptor being requested
switch sup.wValue >> 8 {
// Device descriptor
case descTypeDevice:
dxn = descLengthDevice
_ = copy(acm.dx[:], acm.device[:dxn])
// Configuration descriptor
case descTypeConfigure:
dxn = uint8(descCDCACMConfigSize)
_ = copy(acm.dx[:], acm.config[:dxn])
// String descriptor
case descTypeString:
if 0 == len(acm.locale) {
break // No string descriptors defined!
}
var sd []uint8
if 0 == uint8(sup.wValue) {
// setup.wIndex contains an arbitrary index referring to a collection of
// strings in some given language. This case (setup.wValue = [0x03]00)
// is a string request from the host to determine what that language is.
//
// In subsequent string requests, the host will populate setup.wIndex
// with the language code we return here in this string descriptor.
//
// This way all strings returned to the host are in the same language,
// whatever language that may be.
code := int(sup.wIndex)
if code >= len(acm.locale) {
code = 0
}
sd = acm.locale[code].descriptor[sup.wValue&0xFF][:]
} else {
// setup.wIndex now contains a language code, which we specified in a
// previous request (above: setup.wValue = [0x03]00). We need to locate
// the set of strings whose language matches the language code given in
// this new setup.wIndex.
for code := range acm.locale {
if sup.wIndex == acm.locale[code].language {
// Found language, check if string descriptor at given index exists
if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) {
// Found language with a string defined at the requested index.
//
// TODO: Add API methods to device controller that allows the user
// to provide these strings at/before driver initialization.
//
// For now, we just always use the descCommon* strings.
var s string
switch uint8(sup.wValue) {
case 1:
s = descCommonManufacturer
case 2:
s = descCommonProduct + " CDC-ACM"
case 3:
s = descCommonSerialNumber
}
// Construct a string descriptor dynamically to be transmitted on
// the serial bus.
sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:]
// String descriptor format is 2-byte header + 2-bytes per rune
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
sd[1] = descTypeString // header[1] = descriptor type
// Copy UTF-8 string into string descriptor as UTF-16
for n, c := range s {
if 2+2*n >= len(sd) {
break
}
sd[2+2*n] = uint8(c)
sd[3+2*n] = 0
}
break // end search for matching language code
}
}
}
}
// Copy string descriptor into descriptor transmit buffer
if nil != sd && len(sd) >= 0 {
dxn = sd[0]
_ = copy(acm.dx[:], sd[:dxn])
}
// Device qualification descriptor
case descTypeQualification:
dxn = descLengthQualification
_ = copy(acm.dx[:], acm.qualif[:dxn])
// Alternate configuration descriptor
case descTypeOtherSpeedConfiguration:
// TODO
default:
// Unhandled descriptor type
}
if dxn > 0 {
if dxn > uint8(sup.wLength) {
dxn = uint8(sup.wLength)
}
flushCache(
uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn))
d.controlTransmit(
uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false)
}
}
func (d *dcd) controlDescriptorHID(sup dcdSetup) {
hid := &descHID[d.cc.config-1]
dxn := uint8(0)
pos := uint8(0)
// Determine the type of descriptor being requested
switch sup.wValue >> 8 {
// Device descriptor
case descTypeDevice:
dxn = descLengthDevice
_ = copy(hid.dx[:], hid.device[:dxn])
// Configuration descriptor
case descTypeConfigure:
dxn = uint8(descHIDConfigSize)
_ = copy(hid.dx[:], hid.config[:dxn])
// String descriptor
case descTypeString:
if 0 == len(hid.locale) {
break // No string descriptors defined!
}
var sd []uint8
if 0 == uint8(sup.wValue) {
// setup.wIndex contains an arbitrary index referring to a collection of
// strings in some given language. This case (setup.wValue = [0x03]00)
// is a string request from the host to determine what that language is.
//
// In subsequent string requests, the host will populate setup.wIndex
// with the language code we return here in this string descriptor.
//
// This way all strings returned to the host are in the same language,
// whatever language that may be.
code := int(sup.wIndex)
if code >= len(hid.locale) {
code = 0
}
sd = hid.locale[code].descriptor[sup.wValue&0xFF][:]
} else {
// setup.wIndex now contains a language code, which we specified in a
// previous request (above: setup.wValue = [0x03]00). We need to locate
// the set of strings whose language matches the language code given in
// this new setup.wIndex.
for code := range hid.locale {
if sup.wIndex == hid.locale[code].language {
// Found language, check if string descriptor at given index exists
if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) {
// Found language with a string defined at the requested index.
//
// TODO: Add API methods to device controller that allows the user
// to provide these strings at/before driver initialization.
//
// For now, we just always use the descCommon* strings.
var s string
switch uint8(sup.wValue) {
case 1:
s = descCommonManufacturer
case 2:
s = descCommonProduct + " HID"
case 3:
s = descCommonSerialNumber
}
// Construct a string descriptor dynamically to be transmitted on
// the serial bus.
sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:]
// String descriptor format is 2-byte header + 2-bytes per rune
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
sd[1] = descTypeString // header[1] = descriptor type
// Copy UTF-8 string into string descriptor as UTF-16
for n, c := range s {
if 2+2*n >= len(sd) {
break
}
sd[2+2*n] = uint8(c)
sd[3+2*n] = 0
}
break // end search for matching language code
}
}
}
}
// Copy string descriptor into descriptor transmit buffer
if nil != sd && len(sd) >= 0 {
dxn = sd[0]
_ = copy(hid.dx[:], sd[:dxn])
}
// Device qualification descriptor
case descTypeQualification:
dxn = descLengthQualification
_ = copy(hid.dx[:], hid.qualif[:dxn])
// Alternate configuration descriptor
case descTypeOtherSpeedConfiguration:
// TODO
// HID descriptor
case descTypeHID:
// Determine interface destination of the request
switch sup.wIndex {
case descHIDInterfaceKeyboard:
pos = descHIDConfigKeyboardPos
case descHIDInterfaceMouse:
pos = descHIDConfigMousePos
case descHIDInterfaceSerial:
pos = descHIDConfigSerialPos
case descHIDInterfaceJoystick:
pos = descHIDConfigJoystickPos
case descHIDInterfaceMediaKey:
pos = descHIDConfigMediaKeyPos
default:
// Unhandled HID interface
}
if 0 != pos {
dxn = descLengthInterface
_ = copy(hid.dx[:], hid.config[pos:pos+dxn])
}
// HID report descriptor
case descTypeHIDReport:
// Determine interface destination of the request
switch sup.wIndex {
case descHIDInterfaceKeyboard:
dxn = uint8(len(descHIDReportKeyboard))
_ = copy(hid.dx[:], descHIDReportKeyboard[:])
case descHIDInterfaceMouse:
dxn = uint8(len(descHIDReportMouse))
_ = copy(hid.dx[:], descHIDReportMouse[:])
case descHIDInterfaceSerial:
dxn = uint8(len(descHIDReportSerial))
_ = copy(hid.dx[:], descHIDReportSerial[:])
case descHIDInterfaceJoystick:
dxn = uint8(len(descHIDReportJoystick))
_ = copy(hid.dx[:], descHIDReportJoystick[:])
case descHIDInterfaceMediaKey:
dxn = uint8(len(descHIDReportMediaKey))
_ = copy(hid.dx[:], descHIDReportMediaKey[:])
default:
// Unhandled HID interface
}
default:
// Unhandled descriptor type
}
if dxn > 0 {
if dxn > uint8(sup.wLength) {
dxn = uint8(sup.wLength)
}
flushCache(
uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn))
d.controlTransmit(
uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false)
}
}
File diff suppressed because it is too large Load Diff
+604
View File
@@ -0,0 +1,604 @@
// +build mimxrt1062
package usb
// descCPUFrequencyHz defines the target CPU frequency (Hz).
const descCPUFrequencyHz = 600000000
// descCDCACMCount defines the number of USB cores that may be configured as
// CDC-ACM (single) devices.
const descCDCACMCount = 0
// descHIDCount defines the number of USB cores that may be configured as a
// composite (keyboard + mouse + joystick) human interface device (HID).
const descHIDCount = 1
// General USB device identification constants.
const (
descCommonVendorID = 0x16C0
descCommonProductID = 0x0483
descCommonReleaseID = 0x0101 // BCD (1.1)
descCommonLanguage = descLanguageEnglish
descCommonManufacturer = "TinyGo"
descCommonProduct = "USB"
descCommonSerialNumber = "00000"
)
// Constants for USB CDC-ACM device classes.
const (
descCDCACMMaxPower = 50 // 100 mA
// CDC-ACM Control Buffers
descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1)
descCDCACMCxCount = 8
// CDC-ACM Data Buffers
descCDCACMRDCount = 2 * descCDCACMEndpointCount
descCDCACMRxSize = descCDCACMDataRxPacketSize
descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount
descCDCACMTDCount = descCDCACMEndpointCount
descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize
descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount
descCDCACMTxTimeoutMs = 120 // millisec
descCDCACMTxSyncUs = 75 // microsec
// Default CDC-ACM Endpoint Configurations (High-Speed)
descCDCACMStatusInterval = descCDCACMStatusHSInterval // Status
descCDCACMStatusPacketSize = descCDCACMStatusHSPacketSize //
descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // Data Rx
descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // Data Tx
// CDC-ACM Endpoint Configurations for Full-Speed Device
descCDCACMStatusFSInterval = 5 // Status
descCDCACMStatusFSPacketSize = 16 // (full-speed)
descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed)
descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed)
// CDC-ACM Endpoint Configurations for High-Speed Device
descCDCACMStatusHSInterval = 5 // Status
descCDCACMStatusHSPacketSize = 16 // (high-speed)
descCDCACMDataRxHSPacketSize = 512 // Data Rx (high-speed)
descCDCACMDataTxHSPacketSize = 512 // Data Tx (high-speed)
)
// Constants for USB HID (keyboard, mouse, joystick) device classes.
const (
descHIDMaxPower = 50 // 100 mA
// HID Control Buffers
descHIDQHCount = 2 * (descHIDEndpointCount + 1)
descHIDCxCount = 8
// HID Serial Buffers
descHIDSerialRDCount = 8
descHIDSerialRxSize = descHIDSerialRxPacketSize
descHIDSerialRxCount = descHIDSerialRxSize * descHIDSerialRDCount
descHIDSerialTDCount = 12
descHIDSerialTxSize = descHIDSerialTxPacketSize
descHIDSerialTxCount = descHIDSerialTxSize * descHIDSerialTDCount
descHIDSerialTxTimeoutMs = 50 // millisec
descHIDSerialTxSyncUs = 75 // microsec
// HID Keyboard Buffers
descHIDKeyboardTDCount = 12
descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize
descHIDKeyboardTxCount = descHIDKeyboardTxSize * descHIDKeyboardTDCount
descHIDKeyboardTxTimeoutMs = 50 // millisec
// HID Mouse Buffers
descHIDMouseTDCount = 4
descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize
descHIDMouseTxCount = descHIDMouseTxSize * descHIDMouseTDCount
descHIDMouseTxTimeoutMs = 30 // millisec
// HID Joystick Buffers
descHIDJoystickTDCount = 4
descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize
descHIDJoystickTxCount = descHIDJoystickTxSize * descHIDJoystickTDCount
descHIDJoystickTxTimeoutMs = 30 // millisec
// Default HID Endpoint Configurations (High-Speed)
descHIDSerialRxInterval = descHIDSerialRxHSInterval // Serial Rx
descHIDSerialRxPacketSize = descHIDSerialRxHSPacketSize //
descHIDSerialTxInterval = descHIDSerialTxHSInterval // Serial Tx
descHIDSerialTxPacketSize = descHIDSerialTxHSPacketSize //
descHIDKeyboardTxInterval = descHIDKeyboardTxHSInterval // Keyboard
descHIDKeyboardTxPacketSize = descHIDKeyboardTxHSPacketSize //
descHIDMediaKeyTxInterval = descHIDMediaKeyTxHSInterval // Keyboard Media Keys
descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxHSPacketSize //
descHIDMouseTxInterval = descHIDMouseTxHSInterval // Mouse
descHIDMouseTxPacketSize = descHIDMouseTxHSPacketSize //
descHIDJoystickTxInterval = descHIDJoystickTxHSInterval // Joystick
descHIDJoystickTxPacketSize = descHIDJoystickTxHSPacketSize //
// HID Endpoint Configurations for Full-Speed Device
descHIDSerialRxFSInterval = 2 // Serial Rx
descHIDSerialRxFSPacketSize = 8 // (full-speed)
descHIDSerialTxFSInterval = 1 // Serial Tx
descHIDSerialTxFSPacketSize = 16 // (full-speed)
descHIDKeyboardTxFSInterval = 4 // Keyboard
descHIDKeyboardTxFSPacketSize = 8 // (full-speed)
descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys
descHIDMediaKeyTxFSPacketSize = 8 // (full-speed)
descHIDMouseTxFSInterval = 4 // Mouse
descHIDMouseTxFSPacketSize = 8 // (full-speed)
descHIDJoystickTxFSInterval = 4 // Joystick
descHIDJoystickTxFSPacketSize = 12 // (full-speed)
// HID Endpoint Configurations for High-Speed Device
descHIDSerialRxHSInterval = 2 // Serial
descHIDSerialRxHSPacketSize = 32 // (high-speed)
descHIDSerialTxHSInterval = 1 // Serial Tx
descHIDSerialTxHSPacketSize = 64 // (high-speed)
descHIDKeyboardTxHSInterval = 1 // Keyboard
descHIDKeyboardTxHSPacketSize = 8 // (high-speed)
descHIDMediaKeyTxHSInterval = 4 // Keyboard Media Keys
descHIDMediaKeyTxHSPacketSize = 8 // (high-speed)
descHIDMouseTxHSInterval = 1 // Mouse
descHIDMouseTxHSPacketSize = 8 // (high-speed)
descHIDJoystickTxHSInterval = 2 // Joystick
descHIDJoystickTxHSPacketSize = 12 // (high-speed)
)
// descCDCACM0QH is an array of endpoint queue heads, which is where all
// transfers for a given endpoint are managed, for the default CDC-ACM (single)
// device class configuration (index 1).
//
// From the iMXRT1062 Reference Manual:
//
// Software must ensure that no interface data structure reachable
// by the Device Controller spans a 4K-page boundary.
//
// The [queue head] is a 48-byte data structure, but must be aligned on
// 64-byte boundaries.
//
// Endpoint queue heads are arranged in an array in a continuous area of
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
// device queue heads in the list support receive endpoints (OUT/SETUP) and
// the odd-numbered queue heads in the list are used for transmit endpoints
// (IN/INTERRUPT). The device controller will index into this array based upon
// the endpoint number received from the USB bus. All information necessary to
// respond to transactions for all primed transfers is contained in this list
// so the Device Controller can readily respond to incoming requests without
// having to traverse a linked list.
//go:align 4096
var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint
// descCDCACM0CD is the transfer descriptor for messages transmitted or received
// on the status/control endpoint 0 for the default CDC-ACM (single) device
// class configuration (index 1).
//go:align 32
var descCDCACM0CD dhwTransfer
// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of
// the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Cx [descCDCACMCxCount]uint8
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
// transmitted or received on the status/control endpoint 0 for the default
// CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDCACM0AD dhwTransfer
// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0
// for the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Dx [descCDCACMConfigSize]uint8
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
// which describe to the device controller the location and quantity of data
// being received for a given transfer, for the default CDC-ACM (single) device
// class configuration (index 1).
//go:align 32
var descCDCACM0RD [descCDCACMRDCount]dhwTransfer
// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Rx [descCDCACMRxCount]uint8
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
// which describe to the device controller the location and quantity of data
// being transmitted for a given transfer, for the default CDC-ACM (single)
// device class configuration (index 1).
//go:align 32
var descCDCACM0TD [descCDCACMTDCount]dhwTransfer
// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Tx [descCDCACMTxCount]uint8
var descCDCACM0RDNum [descCDCACMRDCount]uint16
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16
// descCDCACMClassData holds the buffers and control states for all CDC-ACM
// (single) device class configurations, ordered by index (offset by -1), for
// iMXRT1062 targets only.
//
// Instances of this type (elements of descCDCACMData) are embedded in elements
// of the common/target-agnostic CDC-ACM class configurations (descCDCACM).
// Methods defined on this type implement target-specific functionality, and
// some of these methods are required by the common device controller driver.
// Thus, this type functions as a hardware abstraction layer (HAL).
type descCDCACMClassData struct {
// CDC-ACM Control Buffers
qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
// CDC-ACM Data Buffers
rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors
tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer
rxCount *[descCDCACMRDCount]uint16
rxIndex *[descCDCACMRDCount]uint16
rxQueue *[(descCDCACMRDCount + 1)]uint16
sxSize uint16
rxSize uint16
txSize uint16
txHead uint8
txFree uint16
txPrev bool
rxHead uint8
rxTail uint8
rxFree uint16
}
// descCDCACMData holds statically-allocated instances for each of the target-
// specific (iMXRT1062) CDC-ACM (single) device class configurations' control
// and data structures, ordered by configuration index (offset by -1). Each
// element is embedded in a corresponding element of descCDCACM.
//go:align 64
var descCDCACMData = [dcdCount]descCDCACMClassData{
{ // -- CDC-ACM (single) Class Configuration Index 1 --
// CDC-ACM Control Buffers
qh: &descCDCACM0QH,
cd: &descCDCACM0CD,
cx: &descCDCACM0Cx,
ad: &descCDCACM0AD,
dx: &descCDCACM0Dx,
// CDC-ACM Data Buffers
rd: &descCDCACM0RD,
rx: &descCDCACM0Rx,
td: &descCDCACM0TD,
tx: &descCDCACM0Tx,
rxCount: &descCDCACM0RDNum,
rxIndex: &descCDCACM0RDIdx,
rxQueue: &descCDCACM0RDQue,
sxSize: descCDCACMStatusPacketSize,
rxSize: descCDCACMDataRxPacketSize,
txSize: descCDCACMDataTxPacketSize,
},
}
// descHID0QH is an array of endpoint queue heads, which is where all transfers
// for a given endpoint are managed, for the default HID device class
// configuration (index 1).
//
// From the iMXRT1062 Reference Manual:
//
// Software must ensure that no interface data structure reachable
// by the Device Controller spans a 4K-page boundary.
//
// The [queue head] is a 48-byte data structure, but must be aligned on
// 64-byte boundaries.
//
// Endpoint queue heads are arranged in an array in a continuous area of
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
// device queue heads in the list support receive endpoints (OUT/SETUP) and
// the odd-numbered queue heads in the list are used for transmit endpoints
// (IN/INTERRUPT). The device controller will index into this array based upon
// the endpoint number received from the USB bus. All information necessary to
// respond to transactions for all primed transfers is contained in this list
// so the Device Controller can readily respond to incoming requests without
// having to traverse a linked list.
//go:align 4096
var descHID0QH [descHIDQHCount]dhwEndpoint
// descHID0CD is the transfer descriptor for messages transmitted or received on
// the status/control endpoint 0 for the default HID device class configuration
// (index 1).
//go:align 32
var descHID0CD dhwTransfer
// descHID0Cx is the buffer for control/status data received on endpoint 0 of
// the default HID device class configuration (index 1).
//go:align 32
var descHID0Cx [descHIDCxCount]uint8
// descHID0AD is the transfer descriptor for ackowledgement (ACK) messages
// transmitted or received on the status/control endpoint 0 for the default HID
// device class configuration (index 1).
//go:align 32
var descHID0AD dhwTransfer
// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for
// the default HID device class configuration (index 1).
//go:align 32
var descHID0Dx [descHIDConfigSize]uint8
// descHID0SerialRD is an array of transfer descriptors for serial Rx (OUT)
// transfers, which describe to the device controller the location and quantity
// of data being received for a given transfer, for the default HID device class
// configuration (index 1).
//go:align 32
var descHID0SerialRD [descHIDSerialRDCount]dhwTransfer
// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
var descHID0SerialRx [descHIDSerialRxCount]uint8
// descHID0SerialTD is an array of transfer descriptors for serial Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0SerialTD [descHIDSerialTDCount]dhwTransfer
// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
var descHID0SerialTx [descHIDSerialTxCount]uint8
var descHID0SerialRDIdx [descHIDSerialRDCount]uint16
var descHID0SerialRDQue [(descHIDSerialRDCount + 1)]uint16
// descHID0KeyboardTD is an array of transfer descriptors for keyboard Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0KeyboardTD [descHIDKeyboardTDCount]dhwTransfer
// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the
// default HID device class configuration (index 1).
//go:align 32
var descHID0KeyboardTx [descHIDKeyboardTxCount]uint8
// descHID0KeyboardTp is the keyboard HID report transmit (Tx) transfer buffer
// for the default HID device class configuration (index 1).
//go:align 32
var descHID0KeyboardTp [descHIDKeyboardTxPacketSize]uint8
//go:align 32
var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8
//go:align 32
var descHID0KeyboardTxCon [hidKeyboardConCount]uint16
//go:align 32
var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8
// descHID0MouseTD is an array of transfer descriptors for mouse Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0MouseTD [descHIDMouseTDCount]dhwTransfer
// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
var descHID0MouseTx [descHIDMouseTxCount]uint8
// descHID0JoystickTD is an array of transfer descriptors for joystick Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0JoystickTD [descHIDJoystickTDCount]dhwTransfer
// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the
// default HID device class configuration (index 1).
//go:align 32
var descHID0JoystickTx [descHIDJoystickTxCount]uint8
// descHID0Keyboard is the Keyboard instance with which the user may interact
// when using the default HID device class configuration (index 1).
//go:align 64
var descHID0Keyboard = Keyboard{
key: &descHID0KeyboardTxKey,
con: &descHID0KeyboardTxCon,
sys: &descHID0KeyboardTxSys,
}
// descHIDClassData holds the buffers and control states for all of the HID
// device class configurations, ordered by index (offset by -1), for iMXRT1062
// targets only.
//
// Instances of this type (elements of descHIDData) are embedded in elements
// of the common/target-agnostic HID class configurations (descHID).
// Methods defined on this type implement target-specific functionality, and
// some of these methods are required by the common device controller driver.
// Thus, this type functions as a hardware abstraction layer (HAL).
type descHIDClassData struct {
// HID Control Buffers
qh *[descHIDQHCount]dhwEndpoint // endpoint queue heads
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
cx *[descHIDCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
// HID Serial Buffers
rdSerial *[descHIDSerialRDCount]dhwTransfer // interrupt endpoint serial Rx (OUT) transfer descriptors
rxSerial *[descHIDSerialRxCount]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer
tdSerial *[descHIDSerialTDCount]dhwTransfer // interrupt endpoint serial Tx (IN) transfer descriptors
txSerial *[descHIDSerialTxCount]uint8 // interrupt endpoint serial Tx (IN) transfer buffer
rxSerialIndex *[descHIDSerialRDCount]uint16
rxSerialQueue *[(descHIDSerialRDCount + 1)]uint16
rxSerialSize uint16
txSerialSize uint16
txSerialHead uint8
txSerialFree uint16
txSerialPrev bool
rxSerialHead uint8
rxSerialTail uint8
rxSerialFree uint16
// HID Keyboard Buffers
tdKeyboard *[descHIDKeyboardTDCount]dhwTransfer // interrupt endpoint keyboard Tx (IN) transfer descriptors
txKeyboard *[descHIDKeyboardTxCount]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer
tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer
txKeyboardSize uint16
txKeyboardHead uint8
txKeyboardPrev bool
// HID Mouse Buffers
tdMouse *[descHIDMouseTDCount]dhwTransfer // interrupt endpoint mouse Tx (IN) transfer descriptors
txMouse *[descHIDMouseTxCount]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer
txMouseSize uint16
txMouseHead uint8
txMousePrev bool
// HID Joystick Buffers
tdJoystick *[descHIDJoystickTDCount]dhwTransfer // interrupt endpoint joystick Tx (IN) transfer descriptors
txJoystick *[descHIDJoystickTxCount]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer
txJoystickSize uint16
txJoystickHead uint8
txJoystickPrev bool
// HID Device Instances
keyboard *Keyboard
}
// descHIDData holds statically-allocated instances for each of the target-
// specific (iMXRT1062) HID device class configurations' control and data
// structures, ordered by configuration index (offset by -1). Each element is
// embedded in a corresponding element of descHID.
//go:align 64
var descHIDData = [dcdCount]descHIDClassData{
{ // -- HID Class Configuration Index 1 --
// HID Control Buffers
qh: &descHID0QH,
cd: &descHID0CD,
cx: &descHID0Cx,
ad: &descHID0AD,
dx: &descHID0Dx,
// HID Serial Buffers
rdSerial: &descHID0SerialRD,
rxSerial: &descHID0SerialRx,
tdSerial: &descHID0SerialTD,
txSerial: &descHID0SerialTx,
rxSerialIndex: &descHID0SerialRDIdx,
rxSerialQueue: &descHID0SerialRDQue,
rxSerialSize: descHIDSerialRxPacketSize,
txSerialSize: descHIDSerialTxPacketSize,
// HID Keyboard Buffers
tdKeyboard: &descHID0KeyboardTD,
txKeyboard: &descHID0KeyboardTx,
tpKeyboard: &descHID0KeyboardTp,
txKeyboardSize: descHIDKeyboardTxPacketSize,
// HID Mouse Buffers
tdMouse: &descHID0MouseTD,
txMouse: &descHID0MouseTx,
txMouseSize: descHIDMouseTxPacketSize,
// HID Joystick Buffers
tdJoystick: &descHID0JoystickTD,
txJoystick: &descHID0JoystickTx,
txJoystickSize: descHIDJoystickTxPacketSize,
// HID Device Instances
keyboard: &descHID0Keyboard,
},
}
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
package usb
// Implementation of 32-bit target-agnostic USB host controller driver (hcd).
// hcdCount defines the number of USB cores to configure for host mode. It is
// computed as the sum of all declared host configuration descriptors.
const hcdCount = 0 // + ...
// hcdInstance provides statically-allocated instances of each USB host
// controller configured on this platform.
var hcdInstance [hcdCount]hcd
// hhwInstance provides statically-allocated instances of each USB hardware
// abstraction for ports configured as host on this platform.
var hhwInstance [hcdCount]hhw
// hcd implements USB host controller driver (hcd) interface.
type hcd struct {
*hhw // USB hardware abstraction layer
core *core // Parent USB core this instance is attached to
port int // USB port index
cc class // USB host class
id int // USB host controller index
}
// initHCD initializes and assigns a free host controller instance to the given
// USB port. Returns the initialized host controller or nil if no free host
// controller instances remain.
func initHCD(port int, class class) (*hcd, status) {
if 0 == hcdCount {
return nil, statusInvalid // Must have defined host controllers
}
switch class.id {
default:
}
// Return the first instance whose assigned core is currently nil.
for i := range hcdInstance {
if nil == hcdInstance[i].core {
// Initialize host controller.
hcdInstance[i].hhw = allocHHW(port, i, &hcdInstance[i])
hcdInstance[i].core = &coreInstance[port]
hcdInstance[i].port = port
hcdInstance[i].cc = class
hcdInstance[i].id = i
return &hcdInstance[i], statusOK
}
}
return nil, statusBusy // No free host controller instances available.
}
// class returns the receiver's current host class configuration.
func (h *hcd) class() class { return h.cc }
+59
View File
@@ -0,0 +1,59 @@
// +build mimxrt1062
package usb
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
import (
"device/nxp"
"runtime/interrupt"
)
// hcdInterruptPriority defines the priority for all USB host interrupts.
const hcdInterruptPriority = 3
// hcd implements USB host controller driver (hcd) interface.
type hhw struct {
*hcd // USB host controller driver
bus *nxp.USB_Type // USB core register
phy *nxp.USBPHY_Type // USB PHY register
irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062
}
// allocHHW returns a reference to the USB hardware abstraction for the given
// host controller driver. Should be called only one time and during host
// controller initialization.
func allocHHW(port, instance int, hc *hcd) *hhw {
switch port {
case 0:
hhwInstance[instance].hcd = hc
hhwInstance[instance].bus = nxp.USB1
hhwInstance[instance].phy = nxp.USBPHY1
case 1:
hhwInstance[instance].hcd = hc
hhwInstance[instance].bus = nxp.USB2
hhwInstance[instance].phy = nxp.USBPHY2
}
return &hhwInstance[instance]
}
// init configures the USB port for host mode operation by initializing all
// endpoint and transfer descriptor data structures, initializing core registers
// and interrupts, resetting the USB PHY, and enabling power on the bust.
func (h *hhw) init() status {
return statusOK
}
// enable causes the USB core to enter (or exit) the normal run state and
// enables/disables all interrupts on the receiver's USB port.
func (h *hhw) enable(enable bool) {
if enable {
h.irq.Enable() // Enable USB interrupts
} else {
h.irq.Disable() // Disable USB interrupts
}
}
+42
View File
@@ -0,0 +1,42 @@
package usb
import (
"errors"
)
var (
ErrHIDInvalidPort = errors.New("invalid USB port")
ErrHIDInvalidCore = errors.New("invalid USB core")
ErrHIDReportTransfer = errors.New("failed to transfer HID report")
)
type HID struct {
Port int
core *core
}
type HIDConfig struct {
// Port is the MCU's native USB core number. If in doubt, leave it
// uninitialized for default (0).
Port int
}
func (hid *HID) Configure(config HIDConfig) error {
if config.Port >= CoreCount || config.Port >= dcdCount {
return ErrHIDInvalidPort
}
hid.Port = config.Port
// verify we have a free USB port and take ownership of it
var st status
hid.core, st = initCore(hid.Port, class{id: classDeviceHID, config: 1})
if !st.ok() {
return ErrHIDInvalidPort
}
return nil
}
func (hid *HID) Keyboard() *Keyboard {
return hid.core.dc.keyboard()
}
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
package usb
import (
"errors"
)
var (
ErrUARTInvalidPort = errors.New("invalid USB port")
ErrUARTInvalidCore = errors.New("invalid USB core")
ErrUARTEmptyBuffer = errors.New("USB receive buffer empty")
ErrUARTWriteFailed = errors.New("USB write failure")
)
// UART represents a virtual serial (UART) device emulation using the USB
// CDC-ACM device class driver.
type UART struct {
Port int
core *core
}
type UARTConfig struct {
// Port is the MCU's native USB core number. If in doubt, leave it
// uninitialized for default (0).
Port int
}
func (uart *UART) Configure(config UARTConfig) error {
if config.Port >= CoreCount || config.Port >= dcdCount {
return ErrUARTInvalidPort
}
uart.Port = config.Port
// verify we have a free USB port and take ownership of it
var st status
uart.core, st = initCore(uart.Port, class{id: classDeviceCDCACM, config: 1})
if !st.ok() {
return ErrUARTInvalidPort
}
return nil
}
// Buffered returns the number of bytes currently stored in the RX buffer.
func (uart UART) Buffered() int {
return uart.core.dc.uartAvailable()
}
// ReadByte reads a single byte from the RX buffer.
// If there is no data in the buffer, returns an error.
func (uart UART) ReadByte() (byte, error) {
n, ok := uart.core.dc.uartReadByte()
if !ok {
return 0, ErrUARTEmptyBuffer
}
return n, nil
}
// Read from the RX buffer.
func (uart UART) Read(data []byte) (n int, err error) {
return uart.core.dc.uartRead(data), nil
}
// WriteByte writes a single byte of data to the UART interface.
func (uart UART) WriteByte(c byte) error {
if !uart.core.dc.uartWriteByte(c) {
return ErrUARTWriteFailed
}
return nil
}
// Write data to the UART.
func (uart UART) Write(data []byte) (n int, err error) {
return uart.core.dc.uartWrite(data), nil
}
+143
View File
@@ -0,0 +1,143 @@
package usb
// Hardware abstraction for USB ports configured as either host or device.
// CoreCount defines the total number of USB cores to configure in device or
// host mode.
const CoreCount = dcdCount + hcdCount
// coreInstance provides statically-allocated instances of each USB core
// configured on this platform.
var coreInstance [CoreCount]core
// core represents the core of a USB port configured as either host or device.
type core struct {
port int
mode int
dc *dcd
hc *hcd
}
// Constant definitions for USB core operating modes.
const (
modeIdle = 0 // USB port has not been configured
modeDevice = 1
modeHost = 2
)
// initCore initializes a free USB core with given operating mode on the USB
// port at given index, if available. Returns a reference to the initialized
// core or nil if the core is unavailable.
func initCore(port int, class class) (*core, status) {
iv := disableInterrupts()
defer enableInterrupts(iv)
if port < 0 || port >= CoreCount || 0 == class.config {
return nil, statusInvalid
}
if modeIdle != coreInstance[port].mode {
// Check if requested port is already configured as requested class. If so,
// just return a reference to the existing core instead of an error.
//
// This will allow, for instance, TinyGo examples that try to reconfigure
// the USB (CDC-ACM) UART port (which is already configured by the runtime)
// to continue without error.
if coreInstance[port].mode == class.mode() {
switch class.mode() {
case modeDevice:
if coreInstance[port].dc.class().equals(class) {
return &coreInstance[port], statusOK
}
case modeHost:
if coreInstance[port].hc.class().equals(class) {
return &coreInstance[port], statusOK
}
}
}
return nil, statusBusy
}
switch class.mode() {
case modeDevice:
// Allocate a free device controller and install interrupts
dc, st := initDCD(port, class)
if !st.ok() {
return nil, st
}
// Initialize buffers and device descriptors
if st = dc.init(); !st.ok() {
return nil, st
}
coreInstance[port].port = port
coreInstance[port].mode = modeDevice
coreInstance[port].dc = dc
dc.enable(true) // Enable interrupts and enter runtime
case modeHost:
// Allocate a free host controller and install interrupts
hc, st := initHCD(port, class)
if !st.ok() {
return nil, st
}
// Initialize buffers and device descriptors
if st = hc.init(); !st.ok() {
return nil, st
}
coreInstance[port].port = port
coreInstance[port].mode = modeHost
coreInstance[port].hc = hc
hc.enable(true) // Enable interrupts and enter runtime
default:
return nil, statusInvalid
}
return &coreInstance[port], statusOK
}
// class represents the type of a host/device and its class configuration index.
// The first valid configuration index is 1. Index 0 is reserved and invalid.
type class struct {
id int
config int
}
// Enumerated constants for all host/device class configurations.
const (
classDeviceCDCACM = 0
classDeviceHID = 1
)
// mode returns the USB core operating mode of the receiver class c.
//go:inline
func (c class) mode() int {
switch c.id {
case classDeviceCDCACM, classDeviceHID:
return modeDevice
default:
return modeIdle
}
}
// equals returns true if and only if all fields of the given class are equal to
// those of the receiver c.
//go:inline
func (c class) equals(class class) bool {
return c.id == class.id && c.config == class.config
}
// status represents the return code of a subroutine.
type status uint8
// Constant definitions for all status codes used within the package.
const (
statusOK status = iota // Success
statusBusy // Busy
statusInvalid // Invalid argument
)
// ok returns true if and only if the receiver st equals statusOK.
//go:inline
func (s status) ok() bool { return statusOK == s }
+271
View File
@@ -0,0 +1,271 @@
package usb
//go:linkname ticks runtime.ticks
func ticks() int64
// leU64 returns a slice containing 8 bytes from the given uint64 u.
//
// The returned bytes have little-endian ordering; that is, the first element
// at index 0 is the least-significant byte in u and index 7 is the most-
// significant byte.
//go:inline
func leU64(u uint64) []uint8 {
if u == 0 {
// skip all processing for the common case (u = 0)
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
}
return []uint8{
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
uint8(u >> 32), uint8(u >> 40), uint8(u >> 48), uint8(u >> 56),
}
}
// leU32 returns a slice containing 4 bytes from the given uint32 u.
//
// The returned bytes have little-endian ordering; that is, the first element
// at index 0 is the least-significant byte in u and index 3 is the most-
// significant byte.
//go:inline
func leU32(u uint32) []uint8 {
if u == 0 {
// skip all processing for the common case (u = 0)
return []uint8{0, 0, 0, 0}
}
return []uint8{
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
}
}
// leU16 returns a slice containing 2 bytes from the given uint16 u.
//
// The returned bytes have little-endian ordering; that is, the first element
// at index 0 is the least-significant byte in u and index 1 is the most-
// significant byte.
//go:inline
func leU16(u uint16) []uint8 {
if u == 0 {
// skip all processing for the common case (u = 0)
return []uint8{0, 0}
}
return []uint8{
uint8(u), uint8(u >> 8),
}
}
// beU64 returns a slice containing 8 bytes from the given uint64 u.
//
// The returned bytes have big-endian ordering; that is, the first element at
// index 0 is the most-significant byte in u and index 7 is the least-
// significant byte.
//go:inline
func beU64(u uint64) []uint8 {
if u == 0 {
// skip all processing for the common case (u = 0)
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
}
return []uint8{
uint8(u >> 56), uint8(u >> 48), uint8(u >> 40), uint8(u >> 32),
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
}
}
// beU32 returns a slice containing 4 bytes from the given uint32 u.
//
// The returned bytes have big-endian ordering; that is, the first element at
// index 0 is the most-significant byte in u and index 3 is the least-
// significant byte.
//go:inline
func beU32(u uint32) []uint8 {
if u == 0 {
// skip all processing for the common case (u = 0)
return []uint8{0, 0, 0, 0}
}
return []uint8{
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
}
}
// beU16 returns a slice containing 2 bytes from the given uint16 u.
//
// The returned bytes have big-endian ordering; that is, the first element at
// index 0 is the most-significant byte in u and index 1 is the least-
// significant byte.
//go:inline
func beU16(u uint16) []uint8 {
if u == 0 {
// skip all processing for the common case (u = 0)
return []uint8{0, 0}
}
return []uint8{
uint8(u >> 8), uint8(u),
}
}
// revU64 returns the given uint64 u with bytes in the reverse order.
//go:inline
func revU64(u uint64) uint64 {
if u == 0 {
// skip all processing for the common case (u = 0)
return 0
}
return ((u & 0x00000000000000FF) << 56) |
((u & 0x000000000000FF00) << 40) |
((u & 0x0000000000FF0000) << 24) |
((u & 0x00000000FF000000) << 8) |
((u & 0x000000FF00000000) >> 8) |
((u & 0x0000FF0000000000) >> 24) |
((u & 0x00FF000000000000) >> 40) |
((u & 0xFF00000000000000) >> 56)
}
// revU32 returns the given uint32 u with bytes in the reverse order.
//go:inline
func revU32(u uint32) uint32 {
if u == 0 {
// skip all processing for the common case (u = 0)
return 0
}
return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) |
((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24)
}
// revU16 returns the given uint16 u with bytes in the reverse order.
//go:inline
func revU16(u uint16) uint16 {
if u == 0 {
// skip all processing for the common case (u = 0)
return 0
}
return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8)
}
// packU64 returns a uint64 constructed by concatenating the bytes in slice b.
//
// The least-significant byte in the returned value is the first element at
// index 0 in b and the most significant byte is index 7, if given. If fewer
// than 8 elements are given in b, the corresponding bytes in the returned value
// are all 0.
//go:inline
func packU64(b []uint8) (u uint64) {
for i := 0; i < 8 && i < len(b); i++ {
u |= uint64(b[i]) << (i * 8)
}
return
}
// packU32 returns a uint32 constructed by concatenating the bytes in slice b.
//
// The least-significant byte in the returned value is the first element at
// index 0 in b and the most significant byte is index 3, if given. If fewer
// than 4 elements are given in b, the corresponding bytes in the returned value
// are all 0.
//go:inline
func packU32(b []uint8) (u uint32) {
for i := 0; i < 4 && i < len(b); i++ {
u |= uint32(b[i]) << (i * 8)
}
return
}
// packU16 returns a uint16 constructed by concatenating the bytes in slice b.
//
// The least-significant byte in the returned value is the first element at
// index 0 in b and the most significant byte is index 1, if given. If fewer
// than 2 elements are given in b, the corresponding bytes in the returned value
// are all 0.
//go:inline
func packU16(b []uint8) (u uint16) {
for i := 0; i < 2 && i < len(b); i++ {
u |= uint16(b[i]) << (i * 8)
}
return
}
// msU8 returns the most-significant byte of u.
//go:inline
func msU8(u uint16) uint8 { return uint8(u >> 8) }
// lsU8 returns the least-significant byte of u.
//go:inline
func lsU8(u uint16) uint8 { return uint8(u) }
// cycles converts the given number of microseconds to CPU cycles for a CPU with
// given frequency.
//go:inline
func cycles(microsec, cpuFreqHz uint32) uint32 {
return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000)
}
//go:inline
func unpackEndpoint(address uint8) (number, direction uint8) {
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos,
(address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos
}
//go:inline
func rxEndpoint(number uint8) uint8 {
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut
}
//go:inline
func txEndpoint(number uint8) uint8 {
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionIn
}
//go:inline
func endpointIndex(address uint8) uint8 {
return ((address & descEndptAddrNumberMsk) << 1) |
((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos)
}
// The following buffLo and buffHi are helper methods for slice definitions from
// potentially zero-length arrays (depending on compile-time constants).
//
// For example, if we have an array containing a 5-element buffer for three
// instances of some device class (15 total elements), partitioned as follows,
// then we compute the indices for instance 2 as usual:
//
// Index: 01234 56789 ABCDE
// Array: [ 1 | 2 | 3 ]
//
// Lo: (n-1) * size => (2-1) * 5 => 5
// Hi: (n) * size => (2) * 5 => 10 (0xA)
//
// However, if we have specified (via const definition) that 0 instances of some
// device class be allocated, then the associated device class buffer arrays
// will all be zero-length arrays, and the arithmetic to compute the slice
// indices used above will result in out-of-bounds indices:
//
// Index:
// Array: []
//
// Lo: (n-1) * size => (2-1) * 5 => 5 [Error!]
// Hi: (n) * size => (2) * 5 => 10 (0xA) [Error!]
//
//
// I couldn't figure out a straight-forward way to resolve these slice indices
// using only arithmetic, so I've resorted to simple conditionals. If the number
// of instances for some given class is zero (count=0), defined via compile-time
// constant, then just use the empty slice range [0:0].
// buffLo returns the starting array slice index for the n'th region of size
// elements from an array containing count regions of size elements.
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
// argument equals 0.
func buffLo(n, count, size uint16) uint16 {
if 0 == n || 0 == count || 0 == size {
return 0
}
return (n - 1) * size
}
// buffHi returns the ending array slice index for the n'th region of size
// elements from an array containing count regions of size elements.
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
// argument equals 0.
func buffHi(n, count, size uint16) uint16 {
if 0 == n || 0 == count || 0 == size {
return 0
}
return n * size
}
+24
View File
@@ -0,0 +1,24 @@
// +build arm
package usb
import "device/arm"
// udelay waits for the given number of microseconds before returning.
// We cannot use the sleep timer from this context (import cycle), but we need
// an approximate method to spin CPU cycles for short periods of time.
//go:inline
func udelay(microsec uint32) {
n := cycles(microsec, descCPUFrequencyHz)
for i := uint32(0); i < n; i++ {
arm.Asm(`nop`)
}
}
func disableInterrupts() uintptr {
return arm.DisableInterrupts()
}
func enableInterrupts(mask uintptr) {
arm.EnableInterrupts(mask)
}
@@ -35,7 +35,7 @@ func handleHardFault(sp *interruptStack) {
if fault.Mem().WhileUnstackingException() {
print(" while unstacking exception")
}
if fault.Mem().WileStackingException() {
if fault.Mem().WhileStackingException() {
print(" while stacking exception")
}
if fault.Mem().DuringFPLazyStatePres() {
@@ -161,13 +161,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
return fs&arm.SCB_CFSR_MUNSTKERR != 0
}
// WileStackingException: stacking for an exception entry has caused one or more
// WhileStackingException: stacking for an exception entry has caused one or more
// access violations
//
// "When this bit is 1, the SP is still adjusted but the values in the context
// area on the stack might be incorrect. The processor has not written a fault
// address to the MMAR."
func (fs MemFaultStatus) WileStackingException() bool {
func (fs MemFaultStatus) WhileStackingException() bool {
return fs&arm.SCB_CFSR_MSTKERR != 0
}
+11 -3
View File
@@ -6,6 +6,7 @@ import (
"device/arm"
"device/nxp"
"machine"
"machine/usb"
"math/bits"
"unsafe"
)
@@ -36,7 +37,7 @@ func main() {
// initialize cache and MPU
initCache()
// enable SysTick, GPIO, and peripherals
// enable SysTick, GPIO, USB, and other peripherals
initPeripherals()
// reenable interrupts
@@ -108,7 +109,8 @@ func initPeripherals() {
initPins() // configure GPIO
enablePeripheralClocks() // activate peripheral clock gates
initUART() // configure UART (initialized first for debugging)
initUSB() // configure USB CDC-ACM (UART0)
initUART() // configure hardware UART (UART1)
}
func initPins() {
@@ -123,8 +125,14 @@ func initUART() {
machine.UART1.Configure(machine.UARTConfig{})
}
func initUSB() {
machine.HID0.Configure(usb.HIDConfig{})
// machine.UART0.Configure(usb.UARTConfig{})
}
func putchar(c byte) {
machine.UART1.WriteByte(c)
// machine.UART0.WriteByte(c) // print to USB UART
machine.UART1.WriteByte(c) // print to hardware UART
}
func exit(code int) {
+52 -34
View File
@@ -35,15 +35,31 @@ var (
Denominator: 1, // 30-bit DENOM of fractional loop divider
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
}
Usb1PllConfig = nxp.ClockConfigUsbPll{
Instance: 1, // USB PLL instance
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
Usb1PhyConfig = nxp.ClockConfigUsbPhy{
Instance: 1, // USB PHY number (1 or 2)
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
DCal: 0xC, // Decode to trim nominal 17.78mA current source
TxCal45DP: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D+
TxCal45DM: 0x6, // Decode to trim nominal 45-Ohm series Rp on USB D-
PllConfig: nxp.ClockConfigUsbPll{
Instance: 1, // USB PLL number (1 or 2)
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
},
}
Usb2PllConfig = nxp.ClockConfigUsbPll{
Instance: 2, // USB PLL instance
LoopDivider: 0, // PLL loop divider, Fout=Fin*20
Src: 0, // bypass clock source, 0=OSC24M, 1=CLK1_P & CLK1_N
Usb2PhyConfig = nxp.ClockConfigUsbPhy{
Instance: 2, // USB PHY number (1 or 2)
XtalFreq: OSC_FREQ, // External reference clock frequency (Hz)
DCal: 0xC, // Decode to trim the nominal 17.78mA current source
TxCal45DP: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D+
TxCal45DM: 0x6, // Decode to trim the nominal 45-Ohm series Rp on USB D-
PllConfig: nxp.ClockConfigUsbPll{
Instance: 2, // USB PLL number (1 or 2)
LoopDivider: 0, // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
Src: 0, // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
Pfd: nil, // Phase fractional divisors (len=4, or nil for boot default)
},
}
)
@@ -85,7 +101,7 @@ func initClocks() {
// set VDD_SOC to 1.275V, necessary to config AHB to 600 MHz
nxp.DCDC.REG3.Set((nxp.DCDC.REG3.Get() & ^uint32(nxp.DCDC_REG3_TRG_Msk)) |
((13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
((0x13 << nxp.DCDC_REG3_TRG_Pos) & nxp.DCDC_REG3_TRG_Msk))
// wait until DCDC_STS_DC_OK bit is asserted
for !nxp.DCDC.REG0.HasBits(nxp.DCDC_REG0_STS_DC_OK_Msk) {
@@ -104,6 +120,8 @@ func initClocks() {
nxp.DivIpArm.Div(1) // divide ARM_PODF (DIV2)
nxp.DivIpPeriphClk2.Div(0) // divide PERIPH_CLK2_PODF (DIV1)
nxp.ClockIpUsbOh3.Enable(false) // disable USB
nxp.ClockIpGpt1.Enable(false) // disable GPT/PIT
nxp.ClockIpGpt1S.Enable(false) //
nxp.ClockIpGpt2.Enable(false) //
@@ -112,6 +130,11 @@ func initClocks() {
nxp.DivIpPerclk.Div(0) // divide PERCLK_PODF (DIV1)
nxp.ClockIpGpio1.Enable(false) // disable GPIO
nxp.ClockIpGpio2.Enable(false) //
nxp.ClockIpGpio3.Enable(false) //
nxp.ClockIpGpio4.Enable(false) //
nxp.ClockIpUsdhc1.Enable(false) // disable USDHC1
nxp.DivIpUsdhc1.Div(1) // divide USDHC1_PODF (DIV2)
nxp.MuxIpUsdhc1.Mux(1) // USDHC1 select PLL2_PFD0
@@ -120,9 +143,9 @@ func initClocks() {
nxp.MuxIpUsdhc2.Mux(1) // USDHC2 select PLL2_PFD0
nxp.ClockIpSemc.Enable(false) // disable SEMC
nxp.DivIpSemc.Div(1) // divide SEMC_PODF (DIV2)
nxp.DivIpSemc.Div(7) // divide SEMC_PODF (DIV8)
nxp.MuxIpSemcAlt.Mux(0) // SEMC_ALT select PLL2_PFD2
nxp.MuxIpSemc.Mux(1) // SEMC select SEMC_ALT
nxp.MuxIpSemc.Mux(0) // SEMC select PERIPH_CLK
if false {
// TODO: external flash is on this bus, configured via DCD block
@@ -191,7 +214,7 @@ func initClocks() {
nxp.ClockIpLcdPixel.Enable(false) // disable LCDIF
nxp.DivIpLcdifPre.Div(1) // divide LCDIF_PRED (DIV2)
nxp.DivIpLcdif.Div(3) // divide LCDIF_CLK_PODF (DIV4)
nxp.MuxIpLcdifPre.Mux(5) // LCDIF_PRE select PLL3_PFD1
nxp.MuxIpLcdifPre.Mux(4) // LCDIF_PRE select PLL2_PFD1
nxp.ClockIpSpdif.Enable(false) // disable SPDIF
nxp.DivIpSpdif0Pre.Div(1) // divide SPDIF0_CLK_PRED (DIV2)
@@ -209,43 +232,43 @@ func initClocks() {
nxp.MuxIpPll3Sw.Mux(0) // PLL3_SW select PLL3_MAIN
// Disable Audio/Video/Ethernet PLLs
nxp.CCM_ANALOG.PLL_AUDIO.Set(nxp.CCM_ANALOG_PLL_AUDIO_POWERDOWN_Msk)
nxp.CCM_ANALOG.PLL_VIDEO.Set(nxp.CCM_ANALOG_PLL_VIDEO_POWERDOWN_Msk)
nxp.CCM_ANALOG.PLL_ENET.Set(nxp.CCM_ANALOG_PLL_ENET_POWERDOWN_Msk)
ArmPllConfig.Configure() // init ARM PLL
// SYS PLL (PLL2) @ 528 MHz
// PFD0 = 396 MHz -> USDHC1/USDHC2(DIV2)=198 MHz
// PFD1 = 594 MHz -> (currently unused)
// PFD2 = 327.72 MHz -> SEMC(DIV2)=163.86 MHz, FlexSPI/FlexSPI2=327.72 MHz
// PFD3 = 454.73 MHz -> (currently unused)
// PFD2 = 327.72 MHz -> FlexSPI/FlexSPI2=327.72 MHz
// PFD3 = 594 MHz -> (currently unused)
SysPllConfig.Configure(24, 16, 29, 16) // init SYS PLL and PFDs
// USB1 PLL (PLL3) @ 480 MHz
// PFD0 -> (currently unused)
// PFD1 -> (currently unused)
// PFD2 -> (currently unused)
// PFD3 -> (currently unused)
Usb1PllConfig.Configure() // init USB1 PLL and PFDs
Usb2PllConfig.Configure() // init USB2 PLL
Usb1PhyConfig.Configure() // init USB1 HS PHY/PLL
Usb2PhyConfig.Configure() // init USB2 HS PHY/PLL
nxp.MuxIpPrePeriph.Mux(3) // PRE_PERIPH select ARM_PLL
nxp.MuxIpPeriph.Mux(0) // PERIPH select PRE_PERIPH
nxp.MuxIpPeriphClk2.Mux(1) // PERIPH_CLK2 select OSC
nxp.MuxIpPerclk.Mux(1) // PERCLK select OSC
// set LVDS1 clock source
// set LVDS1 clock source (ARM_PLL)
nxp.CCM_ANALOG.MISC1.Set((nxp.CCM_ANALOG.MISC1.Get() & ^uint32(nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk)) |
((0 << nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Pos) & nxp.CCM_ANALOG_MISC1_LVDS1_CLK_SEL_Msk))
// set CLOCK_OUT1 divider
// set CLOCK_OUT1 divider (DIV1)
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_DIV_Msk)) |
((0 << nxp.CCM_CCOSR_CLKO1_DIV_Pos) & nxp.CCM_CCOSR_CLKO1_DIV_Msk))
// set CLOCK_OUT1 source
// set CLOCK_OUT1 source (PLL2/DIV2)
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO1_SEL_Msk)) |
((1 << nxp.CCM_CCOSR_CLKO1_SEL_Pos) & nxp.CCM_CCOSR_CLKO1_SEL_Msk))
// set CLOCK_OUT2 divider
// set CLOCK_OUT2 divider (DIV1)
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_DIV_Msk)) |
((0 << nxp.CCM_CCOSR_CLKO2_DIV_Pos) & nxp.CCM_CCOSR_CLKO2_DIV_Msk))
// set CLOCK_OUT2 source
// set CLOCK_OUT2 source (SPDIF0)
nxp.CCM.CCOSR.Set((nxp.CCM.CCOSR.Get() & ^uint32(nxp.CCM_CCOSR_CLKO2_SEL_Msk)) |
((18 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
((29 << nxp.CCM_CCOSR_CLKO2_SEL_Pos) & nxp.CCM_CCOSR_CLKO2_SEL_Msk))
nxp.CCM.CCOSR.ClearBits(nxp.CCM_CCOSR_CLK_OUT_SEL_Msk) // set CLK_OUT1 drives CLK_OUT
nxp.CCM.CCOSR.SetBits(nxp.CCM_CCOSR_CLKO1_EN_Msk) // enable CLK_OUT1
@@ -253,15 +276,10 @@ func initClocks() {
nxp.ClockIpIomuxcGpr.Enable(false) // disable IOMUXC_GPR
nxp.ClockIpIomuxc.Enable(false) // disable IOMUXC
// set GPT1 High frequency reference clock source
// set GPT1 High frequency reference clock source (PERCLK)
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT1_Msk)
// set GPT2 High frequency reference clock source
// set GPT2 High frequency reference clock source (PERCLK)
nxp.IOMUXC_GPR.GPR5.ClearBits(nxp.IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_Msk)
nxp.ClockIpGpio1.Enable(false) // disable GPIO
nxp.ClockIpGpio2.Enable(false) //
nxp.ClockIpGpio3.Enable(false) //
nxp.ClockIpGpio4.Enable(false) //
}
func enableTimerClocks() {
+1 -1
View File
@@ -141,7 +141,7 @@ func timerSleep(cycles uint32) bool {
nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE) // enable interrupts
nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
for {
//arm.Asm("wfi") // TODO: causes hardfault! why?
waitForEvents()
if pitActive.Get() == 0 {
return true
}
+1
View File
@@ -9,6 +9,7 @@ MEMORY
ENTRY(Reset_Handler);
_stack_size = 4K;
_heap_size = 512K;
SECTIONS
{