diff --git a/Makefile b/Makefile index ad09d9e4..50aa13e0 100644 --- a/Makefile +++ b/Makefile @@ -226,6 +226,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1 + @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display diff --git a/README.md b/README.md index 56708f00..74d90b6f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ You can compile TinyGo programs for microcontrollers, WebAssembly and Linux. The following 22 microcontroller boards are currently supported: * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) +* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Feather M0](https://www.adafruit.com/product/2772) * [Adafruit Feather M4](https://www.adafruit.com/product/3857) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) diff --git a/builder/build.go b/builder/build.go index f9577801..fa4c689f 100644 --- a/builder/build.go +++ b/builder/build.go @@ -207,7 +207,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri } else if outext == ".uf2" { // Get UF2 from the .elf file. tmppath = filepath.Join(dir, "main"+outext) - err := convertELFFileToUF2File(executable, tmppath) + err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID) if err != nil { return err } diff --git a/builder/uf2.go b/builder/uf2.go index 3ff0a045..24417a02 100644 --- a/builder/uf2.go +++ b/builder/uf2.go @@ -11,26 +11,33 @@ import ( "bytes" "encoding/binary" "io/ioutil" + "strconv" ) // convertELFFileToUF2File converts an ELF file to a UF2 file. -func convertELFFileToUF2File(infile, outfile string) error { +func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error { // Read the .text segment. targetAddress, data, err := extractROM(infile) if err != nil { return err } - output, _ := convertBinToUF2(data, uint32(targetAddress)) + output, _, err := convertBinToUF2(data, uint32(targetAddress), uf2FamilyID) + if err != nil { + return err + } return ioutil.WriteFile(outfile, output, 0644) } // convertBinToUF2 converts the binary bytes in input to UF2 formatted data. -func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) { +func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byte, int, error) { blocks := split(input, 256) output := make([]byte, 0) - bl := newUF2Block(targetAddr) + bl, err := newUF2Block(targetAddr, uf2FamilyID) + if err != nil { + return nil, 0, err + } bl.SetNumBlocks(len(blocks)) for i := 0; i < len(blocks); i++ { @@ -41,7 +48,7 @@ func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) { bl.IncrementAddress(bl.payloadSize) } - return output, len(blocks) + return output, len(blocks), nil } const ( @@ -65,18 +72,32 @@ type uf2Block struct { } // newUF2Block returns a new uf2Block struct that has been correctly populated -func newUF2Block(targetAddr uint32) *uf2Block { +func newUF2Block(targetAddr uint32, uf2FamilyID string) (*uf2Block, error) { + var flags uint32 + var familyID uint32 + if uf2FamilyID != "" { + flags |= flagFamilyIDPresent + v, err := strconv.ParseUint(uf2FamilyID, 0, 32) + if err != nil { + return nil, err + } + familyID = uint32(v) + } return &uf2Block{magicStart0: uf2MagicStart0, magicStart1: uf2MagicStart1, magicEnd: uf2MagicEnd, targetAddr: targetAddr, - flags: 0x0, - familyID: 0x0, + flags: flags, + familyID: familyID, payloadSize: 256, data: make([]byte, 476), - } + }, nil } +const ( + flagFamilyIDPresent = 0x00002000 +) + // Bytes converts the uf2Block to a slice of bytes that can be written to file. func (b *uf2Block) Bytes() []byte { buf := bytes.NewBuffer(make([]byte, 0, 512)) diff --git a/compileopts/target.go b/compileopts/target.go index 3bc05da4..2e989ac5 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -43,6 +43,7 @@ type TargetSpec struct { FlashMethod string `json:"flash-method"` FlashVolume string `json:"msd-volume-name"` FlashFilename string `json:"msd-firmware-name"` + UF2FamilyID string `json:"uf2-family-id"` OpenOCDInterface string `json:"openocd-interface"` OpenOCDTarget string `json:"openocd-target"` OpenOCDTransport string `json:"openocd-transport"` @@ -109,6 +110,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) { if spec2.FlashFilename != "" { spec.FlashFilename = spec2.FlashFilename } + if spec2.UF2FamilyID != "" { + spec.UF2FamilyID = spec2.UF2FamilyID + } if spec2.OpenOCDInterface != "" { spec.OpenOCDInterface = spec2.OpenOCDInterface } diff --git a/src/machine/board_circuitplay_bluefruit.go b/src/machine/board_circuitplay_bluefruit.go new file mode 100644 index 00000000..e33bc81a --- /dev/null +++ b/src/machine/board_circuitplay_bluefruit.go @@ -0,0 +1,73 @@ +// +build circuitplay_bluefruit + +package machine + +const HasLowFrequencyCrystal = true + +// GPIO Pins +const ( + D0 = P0_30 + D1 = P0_14 + D2 = P0_05 + D3 = P0_04 + D4 = P1_02 + D5 = P1_15 + D6 = P0_02 + D7 = P1_06 + D8 = P0_13 + D9 = P0_29 + D10 = P0_03 + D11 = P1_04 + D12 = P0_26 + D13 = P1_14 +) + +// Analog Pins +const ( + A1 = P0_02 + A2 = P0_29 + A3 = P0_03 + A4 = P0_04 + A5 = P0_05 + A6 = P0_30 + A7 = P0_14 + A8 = P0_28 + A9 = P0_31 +) + +const ( + LED = D13 + NEOPIXELS = D8 + + BUTTONA = D4 + BUTTONB = D5 + SLIDER = D7 // built-in slide switch + + BUTTON = BUTTONA + BUTTON1 = BUTTONB + + LIGHTSENSOR = A8 + TEMPSENSOR = A9 +) + +// UART0 pins (logical UART1) +const ( + UART_TX_PIN = P0_14 // PORTB + UART_RX_PIN = P0_30 // PORTB +) + +// I2C pins +const ( + SDA_PIN = P0_05 // I2C0 external + SCL_PIN = P0_04 // I2C0 external + + SDA1_PIN = P0_00 // I2C1 internal + SCL1_PIN = P0_01 // I2C1 internal +) + +// SPI pins (internal flash) +const ( + SPI0_SCK_PIN = P0_19 // SCK + SPI0_MOSI_PIN = P0_21 // MOSI + SPI0_MISO_PIN = P0_23 // MISO +) diff --git a/src/machine/machine_nrf52840.go b/src/machine/machine_nrf52840.go index e192567d..a9dc60cc 100644 --- a/src/machine/machine_nrf52840.go +++ b/src/machine/machine_nrf52840.go @@ -11,6 +11,58 @@ func CPUFrequency() uint32 { return 64000000 } +// Hardware pins +const ( + P0_00 Pin = 0 + P0_01 Pin = 1 + P0_02 Pin = 2 + P0_03 Pin = 3 + P0_04 Pin = 4 + P0_05 Pin = 5 + P0_06 Pin = 6 + P0_07 Pin = 7 + P0_08 Pin = 8 + P0_09 Pin = 9 + P0_10 Pin = 10 + P0_11 Pin = 11 + P0_12 Pin = 12 + P0_13 Pin = 13 + P0_14 Pin = 14 + P0_15 Pin = 15 + P0_16 Pin = 16 + P0_17 Pin = 17 + P0_18 Pin = 18 + P0_19 Pin = 19 + P0_20 Pin = 20 + P0_21 Pin = 21 + P0_22 Pin = 22 + P0_23 Pin = 23 + P0_24 Pin = 24 + P0_25 Pin = 25 + P0_26 Pin = 26 + P0_27 Pin = 27 + P0_28 Pin = 28 + P0_29 Pin = 29 + P0_30 Pin = 30 + P0_31 Pin = 31 + P1_00 Pin = 32 + P1_01 Pin = 33 + P1_02 Pin = 34 + P1_03 Pin = 35 + P1_04 Pin = 36 + P1_05 Pin = 37 + P1_06 Pin = 38 + P1_07 Pin = 39 + P1_08 Pin = 40 + P1_09 Pin = 41 + P1_10 Pin = 42 + P1_11 Pin = 43 + P1_12 Pin = 44 + P1_13 Pin = 45 + P1_14 Pin = 46 + P1_15 Pin = 47 +) + // Get peripheral and pin number for this GPIO pin. func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) { if p >= 32 { diff --git a/targets/circuitplay-bluefruit.json b/targets/circuitplay-bluefruit.json new file mode 100644 index 00000000..e948115b --- /dev/null +++ b/targets/circuitplay-bluefruit.json @@ -0,0 +1,9 @@ +{ + "inherits": ["nrf52840"], + "build-tags": ["circuitplay_bluefruit"], + "flash-method": "msd", + "msd-volume-name": "CPLAYBTBOOT", + "msd-firmware-name": "firmware.uf2", + "uf2-family-id": "0xADA52840", + "linkerscript": "targets/circuitplay-bluefruit.ld" +} diff --git a/targets/circuitplay-bluefruit.ld b/targets/circuitplay-bluefruit.ld new file mode 100644 index 00000000..a5befb8c --- /dev/null +++ b/targets/circuitplay-bluefruit.ld @@ -0,0 +1,10 @@ + +MEMORY +{ + FLASH_TEXT (rw) : ORIGIN = 0x00000000+0x26000, LENGTH = 0xED000-0x26000 /* SoftDevice S140. See https://learn.adafruit.com/introducing-the-adafruit-nrf52840-feather/hathach-memory-map. Application starts at 0x26000; user data starts at 0xED000 */ + RAM (xrw) : ORIGIN = 0x20004180, LENGTH = 37K +} + +_stack_size = 2K; + +INCLUDE "targets/arm.ld"