From f80486609505876fb0f103c3c1d03aba8f6a0f79 Mon Sep 17 00:00:00 2001 From: John Whittington Date: Tue, 2 Jan 2018 17:35:47 +0000 Subject: [PATCH] SAMD Cortex M0 device support (Zero, MKR1000, etc) Compilation improvements by adding variant as other obj but not working on device Arduino Zero devices with OpenOCD working Created ARM_TOOLS_DIR and define arm toolchain executables in Sam.mk Check avr-gcc on last AVR_TOOLS_DIR detect and indenting formatting GDB debugging and programming added Documentation updates and define ARDMK_VENDOR rather than include Sam.mk Expand all parse_boards when defined rather than when used Trim extra defines regex working on both macOS and Linux but need better fix Print USB ids and added debug usage to readme Add note on Arduino package dir and made board.txt work Do ARM ARDUINO_ARCH define in Arduino.mk] Add MZeroBlink to non-testable examples for now Remove \B from extra defines grep Add ARDUINO_PACKAGE_DIR for board support files Fix a typo in the README Fix typo in arduino-mk-vars.md Prevent re-including Arduino.mk from Sam.mk when make restarts for upload Add catrina to ARD_REST_OPTS if/else Remove realpath in Sam.mk for cygwin compatability SAMD bootloader support in ard-reset using --zero Enters bootloader using open/close of port at 1200 BAUD, then polls the attached devices for new port enumerating (bootloader). This is how the Arduino IDE operates Bossa support for Zero, MKR1000 etc Re-word Arm README section after Native USB development Reset for zero refactored like IDE Zero bootloader reset tested on macOS and comments added Re-word ARM bootloader and remove imports from testing Patch changes ARDMK_VENDOR->ARCHITECHTURE, show_config_vars, ignore CORE_VER if emtpy Common.mk header guard, openocd/bossac avoid separator, typos Documentation update for patch changes Move ARM tools to Sam.mk and auto-detect include Correct accidental commit of Blink Makefile change Lib fix with alternative core and documentation Append zero to ARD_RESET_OPTS rather than set Prioritise package ARM upload tools over path installed Add note in README on ARM tools versions Move openocd variant config script flag to OPTS --- Arduino.mk | 188 +++++++---- Common.mk | 6 +- HISTORY.md | 1 + README.md | 71 ++++- Sam.mk | 481 +++++++++++++++++++++++++++++ Teensy.mk | 2 +- arduino-mk-vars.md | 355 +++++++++++++++++++-- bin/ard-reset-arduino | 90 +++++- examples/MZeroBlink/MZeroBlink.ino | 19 ++ examples/MZeroBlink/Makefile | 24 ++ examples/ZeroBlink/Makefile | 30 ++ examples/ZeroBlink/ZeroBlink.ino | 19 ++ tests/script/runtests.sh | 2 +- 13 files changed, 1206 insertions(+), 82 deletions(-) create mode 100644 Sam.mk create mode 100644 examples/MZeroBlink/MZeroBlink.ino create mode 100644 examples/MZeroBlink/Makefile create mode 100644 examples/ZeroBlink/Makefile create mode 100644 examples/ZeroBlink/ZeroBlink.ino diff --git a/Arduino.mk b/Arduino.mk index 3473166..865b0ef 100644 --- a/Arduino.mk +++ b/Arduino.mk @@ -261,7 +261,9 @@ else endif # include Common.mk now we know where it is -include $(ARDMK_DIR)/Common.mk +ifndef COMMON_INCLUDED + include $(ARDMK_DIR)/Common.mk +endif # show_config_variable macro is available now. So let's print config details for ARDMK_DIR ifndef ARDMK_DIR_MSG @@ -317,11 +319,7 @@ else ifeq ($(ARCHITECTURE),avr) ARDUINO_ARCH_FLAG = -DARDUINO_ARCH_AVR else - ifeq ($(ARCHITECTURE),sam) - ARDUINO_ARCH_FLAG = -DARDUINO_ARCH_SAM - else - ARDUINO_ARCH_FLAG = -DARDUINO_ARCH_$(shell echo $(ARCHITECTURE) | tr '[:lower:]' '[:upper:]') - endif + ARDUINO_ARCH_FLAG = -DARDUINO_ARCH_$(shell echo $(ARCHITECTURE) | tr '[:lower:]' '[:upper:]') endif endif @@ -454,7 +452,14 @@ ifndef AVR_TOOLS_DIR AVR_TOOLS_DIR = $(SYSTEMPATH_AVR_TOOLS_DIR) $(call show_config_variable,AVR_TOOLS_DIR,[AUTODETECTED],(found in $$PATH)) else - echo $(error No AVR tools directory found) + # One last attempt using avr-gcc in case using arm + SYSTEMPATH_AVR_TOOLS_DIR := $(call dir_if_exists,$(abspath $(dir $(shell which $(avr-gcc)))/..)) + ifdef SYSTEMPATH_AVR_TOOLS_DIR + AVR_TOOLS_DIR = $(SYSTEMPATH_AVR_TOOLS_DIR) + $(call show_config_variable,AVR_TOOLS_DIR,[AUTODETECTED],(found in $$PATH)) + else + echo $(error No AVR tools directory found) + endif endif # SYSTEMPATH_AVR_TOOLS_DIR endif # BUNDLED_AVR_TOOLS_DIR @@ -644,6 +649,9 @@ ifeq ($(strip $(NO_CORE)),) USB_PID := $(call PARSE_BOARD,$(BOARD_TAG),menu.(chip|cpu).$(BOARD_SUB).build.pid) endif endif + + # add caterina flag to ARD_RESET_OPTS + ARD_RESET_OPTS += --caterina endif # normal programming info @@ -745,34 +753,21 @@ endif # Reset ifndef RESET_CMD - ARD_RESET_ARDUINO := $(shell which ard-reset-arduino 2> /dev/null) - ifndef ARD_RESET_ARDUINO - # same level as *.mk in bin directory when checked out from git - # or in $PATH when packaged - ARD_RESET_ARDUINO = $(ARDMK_DIR)/bin/ard-reset-arduino - endif - ifneq ($(CATERINA),) - ifneq (,$(findstring CYGWIN,$(shell uname -s))) - # confirm user is using default cygwin unix Python (which uses ttySx) and not Windows Python (which uses COMx) - ifeq ($(shell which python),/usr/bin/python) - RESET_CMD = $(ARD_RESET_ARDUINO) --caterina $(ARD_RESET_OPTS) $(DEVICE_PATH) - else - RESET_CMD = $(ARD_RESET_ARDUINO) --caterina $(ARD_RESET_OPTS) $(call get_monitor_port) - endif - else - RESET_CMD = $(ARD_RESET_ARDUINO) --caterina $(ARD_RESET_OPTS) $(call get_monitor_port) - endif + ARD_RESET_ARDUINO := $(shell which ard-reset-arduino 2> /dev/null) + ifndef ARD_RESET_ARDUINO + # same level as *.mk in bin directory when checked out from git + # or in $PATH when packaged + ARD_RESET_ARDUINO = $(ARDMK_DIR)/bin/ard-reset-arduino + endif + ifneq (,$(findstring CYGWIN,$(shell uname -s))) + # confirm user is using default cygwin unix Python (which uses ttySx) and not Windows Python (which uses COMx) + ifeq ($(shell which python),/usr/bin/python) + RESET_CMD = $(ARD_RESET_ARDUINO) $(ARD_RESET_OPTS) $(DEVICE_PATH) + else + RESET_CMD = $(ARD_RESET_ARDUINO) $(ARD_RESET_OPTS) $(call get_monitor_port) + endif else - ifneq (,$(findstring CYGWIN,$(shell uname -s))) - # confirm user is using default cygwin unix Python (which uses ttySx) and not Windows Python (which uses COMx) - ifeq ($(shell which python),/usr/bin/python) - RESET_CMD = $(ARD_RESET_ARDUINO) $(ARD_RESET_OPTS) $(DEVICE_PATH) - else - RESET_CMD = $(ARD_RESET_ARDUINO) $(ARD_RESET_OPTS) $(call get_monitor_port) - endif - else - RESET_CMD = $(ARD_RESET_ARDUINO) $(ARD_RESET_OPTS) $(call get_monitor_port) - endif + RESET_CMD = $(ARD_RESET_ARDUINO) $(ARD_RESET_OPTS) $(call get_monitor_port) endif endif @@ -815,7 +810,7 @@ ifeq ($(strip $(CHK_SOURCES)),) $(call show_config_info,No .pde or .ino files found. If you are compiling .c or .cpp files then you need to explicitly include Arduino header files) else #TODO: Support more than one file. https://github.com/sudar/Arduino-Makefile/issues/49 - $(error Need exactly one .pde or .ino file. This makefile doesn't support multiple .ino/.pde files yet) + $(error Need exactly one .pde or .ino file. This makefile doesn\'t support multiple .ino/.pde files yet) endif endif @@ -830,13 +825,26 @@ ifeq ($(strip $(NO_CORE)),) CORE_CPP_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.cpp) CORE_AS_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.S) + # USB Core if samd or sam + ifeq ($(findstring sam, $(strip $(ARCHITECTURE))), sam) + CORE_C_SRCS += $(wildcard $(ARDUINO_CORE_PATH)/USB/*.c) + CORE_CPP_SRCS += $(wildcard $(ARDUINO_CORE_PATH)/USB/*.cpp) + endif + ifneq ($(strip $(NO_CORE_MAIN_CPP)),) CORE_CPP_SRCS := $(filter-out %main.cpp, $(CORE_CPP_SRCS)) $(call show_config_info,NO_CORE_MAIN_CPP set so core library will not include main.cpp,[MANUAL]) endif + # Put alt core variant file for M0 devices in OTHER_OJBS + ifdef ALT_CORE_CPP_SRCS + ALT_CORE_OBJ_FILES = $(ALT_CORE_C_SRCS:.c=.c.o) $(ALT_CORE_CPP_SRCS:.cpp=.cpp.o) $(ALT_CORE_AS_SRCS:.S=.S.o) + OTHER_OBJS := $(patsubst $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/%, \ + $(OBJDIR)/core/%,$(ALT_CORE_OBJ_FILES)) + endif + CORE_OBJ_FILES = $(CORE_C_SRCS:.c=.c.o) $(CORE_CPP_SRCS:.cpp=.cpp.o) $(CORE_AS_SRCS:.S=.S.o) - CORE_OBJS = $(patsubst $(ARDUINO_CORE_PATH)/%, \ + CORE_OBJS += $(patsubst $(ARDUINO_CORE_PATH)/%, \ $(OBJDIR)/core/%,$(CORE_OBJ_FILES)) endif else @@ -908,6 +916,7 @@ endif TARGET_HEX = $(OBJDIR)/$(TARGET).hex TARGET_ELF = $(OBJDIR)/$(TARGET).elf TARGET_EEP = $(OBJDIR)/$(TARGET).eep +TARGET_BIN = $(OBJDIR)/$(TARGET).bin CORE_LIB = $(OBJDIR)/libcore.a # Names of executables - chipKIT needs to override all to set paths to PIC32 @@ -1023,6 +1032,7 @@ endif # SoftwareSerial requires -Os (some delays are tuned for this optimization level) %SoftwareSerial.cpp.o : OPTIMIZATION_FLAGS = -Os +%Uart.cpp.o : OPTIMIZATION_FLAGS = -Os ifndef MCU_FLAG_NAME MCU_FLAG_NAME = mmcu @@ -1094,7 +1104,7 @@ ifeq ($(shell expr $(CC_VERNUM) '>' 490), 1) endif LDFLAGS += -$(MCU_FLAG_NAME)=$(MCU) -Wl,--gc-sections -O$(OPTIMIZATION_LEVEL) ifeq ($(shell expr $(CC_VERNUM) '>' 490), 1) - LDFLAGS += -flto -fuse-linker-plugin + LDFLAGS += -flto -fuse-linker-plugin endif SIZEFLAGS ?= --mcu=$(MCU) -C @@ -1302,7 +1312,24 @@ $(OBJDIR)/core/%.S.o: $(ARDUINO_CORE_PATH)/%.S $(COMMON_DEPS) | $(OBJDIR) @$(MKDIR) $(dir $@) $(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@ +# alt core files +$(OBJDIR)/core/%.c.o: $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/%.c $(COMMON_DEPS) | $(OBJDIR) + @$(MKDIR) $(dir $@) + $(CC) -MMD -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +$(OBJDIR)/core/%.cpp.o: $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/%.cpp $(COMMON_DEPS) | $(OBJDIR) + @$(MKDIR) $(dir $@) + $(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +$(OBJDIR)/core/%.S.o: $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/%.S $(COMMON_DEPS) | $(OBJDIR) + @$(MKDIR) $(dir $@) + $(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@ + # various object conversions +$(OBJDIR)/%.bin: $(OBJDIR)/%.elf $(COMMON_DEPS) + @$(MKDIR) $(dir $@) + -$(OBJCOPY) -O binary $< $@ + $(OBJDIR)/%.hex: $(OBJDIR)/%.elf $(COMMON_DEPS) @$(MKDIR) $(dir $@) $(OBJCOPY) -O ihex -R .eeprom $< $@ @@ -1442,12 +1469,21 @@ endif # so we do not set it by default. AVRDUDE_ISP_OPTS = -c $(ISP_PROG) -b $(AVRDUDE_ISP_BAUDRATE) -ifndef $(ISP_PORT) +ifndef ISP_PORT ifneq ($(strip $(ISP_PROG)),$(filter $(ISP_PROG), atmelice_isp usbasp usbtiny gpio linuxgpio avrispmkii dragon_isp dragon_dw)) - AVRDUDE_ISP_OPTS += -P $(call get_isp_port) + # switch for sam devices as bootloader will be on usb serial if using stk500_v2 + ifeq ($(findstring sam, $(strip $(ARCHITECTURE))), sam) + AVRDUDE_ISP_OPTS += -P $(call get_monitor_port) + else + AVRDUDE_ISP_OPTS += -P $(call get_isp_port) + endif endif else - AVRDUDE_ISP_OPTS += -P $(call get_isp_port) + ifeq ($(CURRENT_OS), WINDOWS) + AVRDUDE_ISP_OPT += -P ISP_PORT + else + AVRDUDE_ISP_OPTS += -P $(call get_isp_port) + endif endif ifndef ISP_EEPROM @@ -1465,7 +1501,7 @@ endif ######################################################################## # Explicit targets start here -all: $(TARGET_EEP) $(TARGET_HEX) +all: $(TARGET_EEP) $(TARGET_BIN) $(TARGET_HEX) # Rule to create $(OBJDIR) automatically. All rules with recipes that # create a file within it, but do not already depend on a file within it @@ -1479,8 +1515,15 @@ $(OBJDIR): pre-build pre-build: $(call runscript_if_exists,$(PRE_BUILD_HOOK)) +# copied from arduino with start-group, end-group $(TARGET_ELF): $(LOCAL_OBJS) $(CORE_LIB) $(OTHER_OBJS) +# sam devices need start and end group +ifeq ($(findstring sam, $(strip $(ARCHITECTURE))), sam) + $(CC) $(LINKER_SCRIPTS) -Wl,-Map=$(OBJDIR)/$(TARGET).map -o $@ $(LOCAL_OBJS) $(OTHER_OBJS) $(OTHER_LIBS) $(LDFLAGS) $(CORE_LIB) -Wl,--end-group +# otherwise traditional +else $(CC) $(LDFLAGS) -o $@ $(LOCAL_OBJS) $(CORE_LIB) $(OTHER_OBJS) $(OTHER_LIBS) -lc -lm $(LINKER_SCRIPTS) +endif $(CORE_LIB): $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS) $(AR) rcs $@ $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS) @@ -1488,21 +1531,47 @@ $(CORE_LIB): $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS) error_on_caterina: $(ERROR_ON_CATERINA) - # Use submake so we can guarantee the reset happens # before the upload, even with make -j upload: $(TARGET_HEX) verify_size +ifeq ($(findstring sam, $(strip $(ARCHITECTURE))), sam) +# do reset toggle at 1200 BAUD to enter bootloader if using avrdude or bossa +ifeq ($(strip $(UPLOAD_TOOL)), avrdude) + $(MAKE) reset +else ifeq ($(findstring bossac, $(strip $(UPLOAD_TOOL))), bossac) + $(MAKE) reset +endif + $(MAKE) do_sam_upload +else $(MAKE) reset $(MAKE) do_upload +endif raw_upload: $(TARGET_HEX) verify_size +ifeq ($(findstring sam, $(strip $(ARCHITECTURE))), sam) + $(MAKE) do_sam_upload +else $(MAKE) error_on_caterina $(MAKE) do_upload +endif do_upload: $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ARD_OPTS) \ $(AVRDUDE_UPLOAD_HEX) +do_sam_upload: $(TARGET_BIN) verify_size +ifeq ($(findstring openocd, $(strip $(UPLOAD_TOOL))), openocd) + $(OPENOCD) $(OPENOCD_OPTS) -c "telnet_port disabled; program {{$(TARGET_BIN)}} verify reset $(BOOTLOADER_SIZE); shutdown" +else ifeq ($(findstring bossac, $(strip $(UPLOAD_TOOL))), bossac) + $(BOSSA) $(BOSSA_OPTS) $(TARGET_BIN) +else ifeq ($(findstring gdb, $(strip $(UPLOAD_TOOL))), gdb) + $(GDB) $(GDB_UPLOAD_OPTS) +else ifeq ($(strip $(UPLOAD_TOOL)), avrdude) + $(MAKE) ispload +else + @$(ECHO) "$(BOOTLOADER_UPLOAD_TOOL) not currently supported!\n\n" +endif + do_eeprom: $(TARGET_EEP) $(TARGET_HEX) $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ARD_OPTS) \ $(AVRDUDE_UPLOAD_EEP) @@ -1535,14 +1604,22 @@ ispload: $(TARGET_EEP) $(TARGET_HEX) verify_size $(AVRDUDE_ISPLOAD_OPTS) burn_bootloader: -ifneq ($(strip $(AVRDUDE_ISP_FUSES_PRE)),) - $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) -e $(AVRDUDE_ISP_FUSES_PRE) -endif -ifneq ($(strip $(AVRDUDE_ISP_BURN_BOOTLOADER)),) - $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) $(AVRDUDE_ISP_BURN_BOOTLOADER) -endif -ifneq ($(strip $(AVRDUDE_ISP_FUSES_POST)),) - $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) $(AVRDUDE_ISP_FUSES_POST) +ifeq ($(findstring sam, $(strip $(ARCHITECTURE))), sam) + ifeq ($(strip $(BOOTLOADER_UPLOAD_TOOL)), openocd) + $(OPENOCD) $(OPENOCD_OPTS) -c "telnet_port disabled; init; halt; $(BOOTLOADER_UNPROTECT); program {{$(BOOTLOADER_PARENT)/$(BOOTLOADER_FILE)}} verify reset; shutdown" + else + @$(ECHO) "$(BOOTLOADER_UPLOAD_TOOL) not currently supported!\n\n" + endif +else + ifneq ($(strip $(AVRDUDE_ISP_FUSES_PRE)),) + $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) -e $(AVRDUDE_ISP_FUSES_PRE) + endif + ifneq ($(strip $(AVRDUDE_ISP_BURN_BOOTLOADER)),) + $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) $(AVRDUDE_ISP_BURN_BOOTLOADER) + endif + ifneq ($(strip $(AVRDUDE_ISP_FUSES_POST)),) + $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) $(AVRDUDE_ISP_FUSES_POST) + endif endif set_fuses: @@ -1580,6 +1657,12 @@ else $(MONITOR_CMD) $(call get_monitor_port) $(MONITOR_BAUDRATE) endif +debug_init: + $(OPENOCD) + +debug: + $(GDB) $(GDB_OPTS) + disasm: $(OBJDIR)/$(TARGET).lss @$(ECHO) "The compiled ELF file has been disassembled to $(OBJDIR)/$(TARGET).lss\n\n" @@ -1599,7 +1682,6 @@ generate_assembly: $(OBJDIR)/$(TARGET).s generated_assembly: generate_assembly @$(ECHO) "\"generated_assembly\" target is deprecated. Use \"generate_assembly\" target instead\n\n" -.PHONY: tags tags: ifneq ($(words $(wildcard $(TAGS_FILE))), 0) rm -f $(TAGS_FILE) @@ -1634,6 +1716,8 @@ help: make show_boards - list all the boards defined in boards.txt\n\ make show_submenu - list all board submenus defined in boards.txt\n\ make monitor - connect to the Arduino's serial port\n\ + make debug_init - start openocd gdb server\n\ + make debug - connect to gdb target and begin debugging\n\ make size - show the size of the compiled output (relative to\n\ resources, if you have a patched avr-size).\n\ make verify_size - verify that the size of the final file is less than\n\ @@ -1655,7 +1739,7 @@ help: .PHONY: all upload raw_upload raw_eeprom error_on_caterina reset reset_stty ispload \ clean depends size show_boards monitor disasm symbol_sizes generated_assembly \ - generate_assembly verify_size burn_bootloader help pre-build + generate_assembly verify_size burn_bootloader help pre-build tags debug debug_init # added - in the beginning, so that we don't get an error if the file is not present -include $(DEPS) diff --git a/Common.mk b/Common.mk index a8bd46a..5f30fba 100644 --- a/Common.mk +++ b/Common.mk @@ -1,3 +1,4 @@ +COMMON_INCLUDED = TRUE # Useful functions # Returns the first argument (typically a directory), if the file or directory # named by concatenating the first and optionally second argument @@ -26,8 +27,11 @@ show_config_variable = $(call show_config_info,$(1) = $($(1)) $(3),$(2)) # Just a nice simple visual separator show_separator = $(call arduino_output,-------------------------) +# Master Arduino Makefile include (after user Makefile) +ardmk_include = $(shell basename $(word 2,$(MAKEFILE_LIST))) + $(call show_separator) -$(call arduino_output,Arduino.mk Configuration:) +$(call arduino_output,$(call ardmk_include) Configuration:) ######################################################################## # diff --git a/HISTORY.md b/HISTORY.md index a6aa14f..b4f4200 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,6 +18,7 @@ I tried to give credit whenever possible. If I have missed anyone, kindly add it - New: Add generation of tags file using ctags, which automatically includes project libs and Arduino core. (https://github.com/tuna-f1sh) - New: Add template Makefile and project boilerplate initialise script, `ardmk-init`. (https://github.com/tuna-f1sh) - New: Support atmelice_isp JTAG tool as ISP programmer. (https://github.com/tuna-f1sh) +- New: Support Arduino ARM-based (SAM/SAMD) devices. (https://github.com/tuna-f1sh) ### 1.6.0 (2017-07-11) - Fix: Allowed for SparkFun's weird usb pid/vid submenu shenanigans (issue #499). (https://github.com/sej7278) diff --git a/README.md b/README.md index 953d067..f3b5e20 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ This is a very simple Makefile which knows how to build Arduino sketches. It def - Very robust - Highly customizable - Supports all official AVR-based Arduino boards +- Supports official ARM-based Arduino boards using Atmel SAM chip family + (Cortex M0) and includes on-device debugging targets. - Supports chipKIT - Supports Teensy 3.x (via Teensyduino) - Works on all three major OS (Mac, Linux, Windows) @@ -313,6 +315,72 @@ See examples/BlinkOpenCM for example usage. For large Robotis projects, [libmaple](https://github.com/Rhoban/Maple) may be more appropriate, as the OpenCM IDE uses a very old compiler release. +## Arduino ARM Boards + +For Arduino boards using ARM architechure, specifically the Atmel SAM series +(Arduino M0 [Pro], Zero, MKR1000, Feather M0, etc.), first +install the board support package from the IDE or other distribution channels. + +Define`ARDUINO_PACKAGE_DIR` as the root path containing the ARM support +package (the manufacturer folder) and the `BOARD_TAG` (see `make show_boards` +for help) within your project Makefile. Include 'Sam.mk' rather than + 'Arduino.mk' at the end of your file - see examples/ZeroBlink and + examples/MZeroBlink for example usage. + +**Note**: The Arduino IDE does not install board support packages to +the base Arduino installation directory (the directory that will work with AVR +Makefiles). They are generally installed to a '.arduino15/packages' folder in +the users home directory. This is the reason for the new `ARDUINO_PACKAGE_DIR` +define. On Windows, the package directory is often in the user home directory +so advice is to create a symblic link to avoid slash/space in path problems. +You can also manually install support packages in your Sketchbook 'hardware' +folder, then define ARDUINO_PACKAGE_DIR as this path. + +If using a SAM board from a manufacturer other than Arduino, one must still +install the Arduino board support as above (unless using externally defined +toolchain) and then define the location of the manufacturer board support core +using the ALTERNATIVE_CORE_PATH define. For example: `ALTERNATE_CORE_PATH = +$(ARDUINO_SKETCHBOOK)/hardware/sparkfun/samd` + +The programing method will auto-detect based on the `BOARD_TAG` settings read +from boards.txt: + +Programming using OpenOCD CMSIS-DAP with the Programming/debug USB is +currently supported (the same method used by the IDE), including burning +bootloaders. External CMSIS tools such as Atmel Ice will also work with this +method. Black Magic Probe (BMP) support is also included using GDB for both +uploading and debugging. + +Native USB programing using Bossa (Zero, MKR1000, Feather style bootloaders) +and avrdude (M0 bootloaders) is supported. The bootloaders on these devices +requires a double press of the reset button or open/closing the serial port at +1200 BAUD. The automatic entry of the bootloader is attempted using +`ard-reset-arduino` when using the general `make upload` target by polling +attached devices until the bootloader port re-attaches (same method as the +IDE). On Windows, the USB enumerates as differnt COM ports for the CDC Serial +and bootloader and these must be defined. On encountering problems, one can +manually enter the bootloader then upload using the `make raw_upload` target. +Note that the `make reset` target will enter the bootloader on these devices; +there is no way to reset over USB. + +If using system installed tools, be aware that `openocd` and `bossa` were +orginally forked for Arduino support and system distributions may not be up +to date with merged changes. `bossa` must be version 1.7->. `openocd` should +work but there may be problems at run time +[ref](https://github.com/pda/arduino-zero-without-ide). Ideally, use the +support packaged version or compile and install the Arduino fork. + +With the ARM chipset and using a CMSIS-DAP tool, on-device debugging is made available: + +* `debug_init` and `debug` targets for on-device debugging using GDB. To use + this, one must start the GDB server with `make debug_init &`, followed by + connecting to the target with `make debug`. If using a Black Magic Probe, + one can just use `make debug`. At the moment, a system wide `arm-none-eabi-gdb` must be + installed as the one supplied with the Arduino toolchain + does not appear to work. +* Example usage: https://asciinema.org/a/Jarz7Pr3gD6mqaZvCACQBzqix +* See the examples/MZeroBlink Makefile for a commented example. + ## Versioning The current version of the makefile is `1.6.0`. You can find the full history in the [HISTORY.md](HISTORY.md) file @@ -332,9 +400,8 @@ Also checkout the [contribution guide](CONTRIBUTING.md) for more details. If you are looking for ideas to work on, then check out the following TODO items or the [issue tracker](https://github.com/sudar/Arduino-Makefile/issues/). -## Limitations / Know Issues / TODO's +## Limitations / Known Issues / TODO's -- Doesn't support SAM boards yet. - Since it doesn't do any pre processing like Arduino IDE, you have to declare all methods before you use them ([issue #59](https://github.com/sudar/Arduino-Makefile/issues/59)) - More than one .ino or .pde file is not supported yet ([issue #49](https://github.com/sudar/Arduino-Makefile/issues/49)) - When you compile for the first time, it builds all libs inside Arduino directory even if it is not needed. But while linking only the relevant files are linked. ([issue #29](https://github.com/sudar/Arduino-Makefile/issues/29)). Even Arduino IDE does the same thing though. diff --git a/Sam.mk b/Sam.mk new file mode 100644 index 0000000..8b93ee4 --- /dev/null +++ b/Sam.mk @@ -0,0 +1,481 @@ +######################################################################## +# +# Support for Arduino Atmel SAM boards (sam and samd) +# +# You must install a SAM board hardware support package (such as Arduino Zero) +# to use this, then define ARDUINO_PACKAGE_DIR as the path to the root +# directory containing the support package. +# +# 2018 John Whittington @j_whittington +# +######################################################################## + +arduino_output = +# When output is not suppressed and we're in the top-level makefile, +# running for the first time (i.e., not after a restart after +# regenerating the dependency file), then output the configuration. +ifndef ARDUINO_QUIET + ifeq ($(MAKE_RESTARTS),) + ifeq ($(MAKELEVEL),0) + arduino_output = $(info $(1)) + endif + endif +endif + +ifndef ARDMK_DIR + ARDMK_DIR := $(realpath $(dir $(realpath $(lastword $(MAKEFILE_LIST))))) +endif + +# include Common.mk now we know where it is +ifndef COMMON_INCLUDED + include $(ARDMK_DIR)/Common.mk +endif + +ifndef ARDUINO_PACKAGE_DIR + # attempt to find based on Linux, macOS and Windows default + ARDUINO_PACKAGE_DIR := $(firstword \ + $(call dir_if_exists,$(HOME)/.arduino15/packages) \ + $(call dir_if_exists,$(ARDUINO_DIR)/packages) \ + $(call dir_if_exists,$(HOME)/Library/Arduino15/packages) \ + $(call dir_if_exists,$(USERPROFILE)/AppData/Local/Arduino15/packages) ) + $(call show_config_variable,ARDUINO_PACKAGE_DIR,[AUTODETECTED],(from DEFAULT)) +else + $(call show_config_variable,ARDUINO_PACKAGE_DIR,[USER]) +endif + +ifndef ARDMK_VENDOR + ARDMK_VENDOR := arduino +endif + +ifndef ARCHITECTURE + ARCHITECTURE := samd +endif + +ifndef CORE_VER + CORE_VER := $(wildcard $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/hardware/$(ARCHITECTURE)/1.*) + ifdef CORE_VER + CORE_VER := $(shell basename $(CORE_VER)) + $(call show_config_variable,CORE_VER,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) + endif +else + $(call show_config_variable,CORE_VER,[USER]) +endif + +ifndef CMSIS_VER + CMSIS_VER := $(shell basename $(wildcard $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/CMSIS/4.*)) + $(call show_config_variable,CMSIS_VER,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) +else + $(call show_config_variable,CMSIS_VER,[USER]) +endif + +ifndef CMSIS_ATMEL_VER + CMSIS_ATMEL_VER := $(shell basename $(wildcard $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/CMSIS-Atmel/1.*)) + $(call show_config_variable,CMSIS_ATMEL_VER,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) +else + $(call show_config_variable,CMSIS_ATMEL_VER,[USER]) +endif + +ifndef CMSIS_DIR + CMSIS_DIR := $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/CMSIS/$(CMSIS_VER)/CMSIS + $(call show_config_variable,CMSIS_DIR,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) +else + $(call show_config_variable,CMSIS_DIR,[USER]) +endif + +ifndef CMSIS_ATMEL_DIR + CMSIS_ATMEL_DIR := $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/CMSIS-Atmel/$(CMSIS_ATMEL_VER)/CMSIS + $(call show_config_variable,CMSIS_ATMEL_DIR,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) +else + $(call show_config_variable,CMSIS_ATMEL_DIR,[USER]) +endif + +# Arduino Settings (will get shown in Arduino.mk as computed) +ifndef ALTERNATE_CORE_PATH + ifdef CORE_VER + ALTERNATE_CORE_PATH = $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/hardware/$(ARCHITECTURE)/$(CORE_VER) + else + ALTERNATE_CORE_PATH = $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/hardware/$(ARCHITECTURE) + endif +endif +ifndef ARDUINO_CORE_PATH + ARDUINO_CORE_PATH = $(ALTERNATE_CORE_PATH)/cores/arduino +endif +ifndef BOARD_TXT + BOARDS_TXT = $(ALTERNATE_CORE_PATH)/boards.txt +endif + +# Check boards file exists before continuing as parsing non-existant file can create problems +ifneq ($(findstring boards.txt, $(wildcard $(ALTERNATE_CORE_PATH)/*.txt)), boards.txt) + echo $(error $(CORE_VER) Cannot find boards file $(BOARDS_TXT). Check ARDUINO_PACKAGE_DIR path: $(ARDUINO_PACKAGE_DIR) and board support installed) +endif + +# add CMSIS includes +CPPFLAGS += -I$(CMSIS_DIR)/Include/ +CPPFLAGS += -I$(CMSIS_ATMEL_DIR)/Device/ATMEL +# path for Cortex library +LIB_PATH = $(CMSIS_DIR)/Lib/GCC +BOOTLOADER_PARENT = $(ALTERNATE_CORE_PATH)/bootloaders + +# Utility from ard-mk to parse boards.txt for flags +ifndef PARSE_BOARD + PARSE_BOARD = $(shell grep -Ev '^\#' $(BOARDS_TXT) | grep -E '^[ \t]*$(1).$(2)=' | cut -d = -f 2 | cut -d : -f 2) +endif + +ifndef VARIANT + VARIANT := $(call PARSE_BOARD,$(BOARD_TAG),menu.(chip|cpu).$(BOARD_SUB).build.variant) + ifndef VARIANT + VARIANT := $(call PARSE_BOARD,$(BOARD_TAG),build.variant) + endif +endif + +# grab any sources in the variant core path (variant.cpp defines pin/port mapping on SAM devices) +ALT_CORE_C_SRCS := $(wildcard $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/*.c) +ALT_CORE_CPP_SRCS := $(wildcard $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/*.cpp) +ALT_CORE_S_SRCS := $(wildcard $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/*.S) + +# Use arm-toolchain from Arduino install if exists and user has not defined global version +ifndef ARM_TOOLS_DIR + ARM_TOOLS_DIR = $(call dir_if_exists,$(wildcard $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/arm-none-eabi-gcc/*)) + $(call show_config_variable,ARM_TOOLS_DIR,[COMPUTED],(from ARDUINO_PACKAGE_DIR)) +else + $(call show_config_variable,ARM_TOOLS_DIR,[USER]) +endif + +# define plaform lib dir from Arduino ARM support +ifndef ARDUINO_PLATFORM_LIB_PATH + ARDUINO_PLATFORM_LIB_PATH := $(ALTERNATE_CORE_PATH)/libraries + $(call show_config_variable,ARDUINO_PLATFORM_LIB_PATH,[COMPUTED],(from ARDUINO_PACKAGE_DIR)) +endif + +######################################################################## +# command names + +ifndef CC_NAME + CC_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.gcc) + ifndef CC_NAME + CC_NAME := arm-none-eabi-gcc + else + $(call show_config_variable,CC_NAME,[COMPUTED]) + endif +endif + +ifndef CXX_NAME + CXX_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.g++) + ifndef CXX_NAME + CXX_NAME := arm-none-eabi-g++ + else + $(call show_config_variable,CXX_NAME,[COMPUTED]) + endif +endif + +ifndef AS_NAME + AS_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.as) + ifndef AS_NAME + AS_NAME := arm-none-eabi-gcc-as + else + $(call show_config_variable,AS_NAME,[COMPUTED]) + endif +endif + +ifndef OBJCOPY_NAME + OBJCOPY_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.objcopy) + ifndef OBJCOPY_NAME + OBJCOPY_NAME := arm-none-eabi-objcopy + else + $(call show_config_variable,OBJCOPY_NAME,[COMPUTED]) + endif +endif + +ifndef OBJDUMP_NAME + OBJDUMP_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.objdump) + ifndef OBJDUMP_NAME + OBJDUMP_NAME := arm-none-eabi-objdump + else + $(call show_config_variable,OBJDUMP_NAME,[COMPUTED]) + endif +endif + +ifndef AR_NAME + AR_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.ar) + ifndef AR_NAME + AR_NAME := arm-none-eabi-ar + else + $(call show_config_variable,AR_NAME,[COMPUTED]) + endif +endif + +ifndef SIZE_NAME + SIZE_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.size) + ifndef SIZE_NAME + SIZE_NAME := arm-none-eabi-size + else + $(call show_config_variable,SIZE_NAME,[COMPUTED]) + endif +endif + +ifndef NM_NAME + NM_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.nm) + ifndef NM_NAME + NM_NAME := arm-none-eabi-gcc-nm + else + $(call show_config_variable,NM_NAME,[COMPUTED]) + endif +endif + +ifndef GDB_NAME + GDB_NAME := $(call PARSE_BOARD,$(BOARD_TAG),build.command.gdb) + ifndef GDB_NAME + GDB_NAME := arm-none-eabi-gdb + else + $(call show_config_variable,GDB_NAME,[COMPUTED]) + endif +endif + +ifndef UPLOAD_TOOL + UPLOAD_TOOL := $(call PARSE_BOARD,$(BOARD_TAG),upload.tool) + ifndef UPLOAD_TOOL + UPLOAD_TOOL := openocd + else + $(call show_config_variable,UPLOAD_TOOL,[COMPUTED]) + endif +endif + +ifndef BOOTLOADER_UPLOAD_TOOL + BOOTLOADER_UPLOAD_TOOL := $(call PARSE_BOARD,$(BOARD_TAG),bootloader.tool) + ifndef BOOTLOADER_UPLOAD_TOOL + BOOTLOADER_UPLOAD_TOOL := openocd + else + $(call show_config_variable,BOOTLOADER_UPLOAD_TOOL,[COMPUTED]) + endif +endif + +# processor stuff +ifndef MCU + MCU := $(call PARSE_BOARD,$(BOARD_TAG),build.mcu) +endif + +ifndef MCU_FLAG_NAME + MCU_FLAG_NAME=mcpu +endif + +# native port emulates an AVR chip to use AVRDUDE +ifndef AVRDUDE_MCU + AVRDUDE_MCU := $(call PARSE_BOARD,$(BOARD_TAG),build.emu.mcu) +endif + +# GDP settings +ifndef GDB_PORT + # default to localhost default OpenOCD port + GDB_PORT = localhost:3333 +endif + +ifndef GDB_OPTS + # if using BMP do a scan and attach + ifeq ($(findstring /dev/tty, $(strip $(GDB_PORT))), /dev/tty) + GDB_OPTS = -ex "target extended-remote $(GDB_PORT)" -ex "monitor swdp_scan" -ex "attach 1" -ex "load" -d $(OBJDIR) $(TARGET_ELF) + else + GDB_OPTS = -ex "target extended-remote $(GDB_PORT)" -ex "load" -d $(OBJDIR) $(TARGET_ELF) + endif +endif + +ifndef GDB_UPLOAD_OPTS + GDB_UPLOAD_OPTS = $(GDB_OPTS) -ex "set confirm off" -ex "set target-async off" -ex "set remotetimeout 30" -ex "detach" -ex "kill" -ex "quit" +endif + +######################################################################## +# OpenOCD for SAM devices + +ifndef OPENOCD + BUNDLED_OPENOCD_DIR := $(call dir_if_exists,$(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/openocd) + # Try Arduino support package first + ifdef BUNDLED_OPENOCD_DIR + OPENOCD_VER := $(shell basename $(wildcard $(BUNDLED_OPENOCD_DIR)/*)) + OPENOCD = $(BUNDLED_OPENOCD_DIR)/$(OPENOCD_VER)/bin/openocd -s $(BUNDLED_OPENOCD_DIR)/$(OPENOCD_VER)/share/openocd/scripts/ + $(call show_config_variable,OPENOCD,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) + else + # Otherwise look on user path + OPENOCD := $(shell which openocd 2>/dev/null) + ifdef OPENOCD + $(call show_config_variable,OPENOCD,[AUTODETECTED],(found in $$PATH)) + endif + endif +else + $(call show_config_variable,OPENOCD,[USER]) +endif + +ifndef OPENOCD_OPTS + OPENOCD_OPTS += -d2 -f $(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/$(OPENOCD_SCRIPT) +endif + +######################################################################## +# Bossa for SAM devices + +ifndef BOSSA + BUNDLED_BOSSA_DIR := $(call dir_if_exists,$(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/bossac) + # Try Arduino support package first + ifdef BUNDLED_BOSSA_DIR + BOSSA_VER := $(shell basename $(wildcard $(BUNDLED_BOSSA_DIR)/*)) + BOSSA = $(BUNDLED_BOSSA_DIR)/$(BOSSA_VER)/bossac + $(call show_config_variable,BOSSA,[AUTODETECTED],(from ARDUINO_PACKAGE_DIR)) + else + # Otherwise look on user path + BOSSA := $(shell which bossac 2>/dev/null) + ifdef BOSSA + $(call show_config_variable,BOSSA,[AUTODETECTED],(found in $$PATH)) + endif + endif +else + $(call show_config_variable,BOSSA,[USER]) +endif + +ifndef BOSSA_OPTS + BOSSA_OPTS += -d --info --erase --write --verify --reset +endif + +get_bootloader = $(shell $(RESET_CMD) | tail -1) + +# if not bootloader port defined (ISP_PORT), automatically grab first port after reset +# if not on windows +ifndef ISP_PORT + ifeq ($(CURRENT_OS), WINDOWS) + BOSSA_OPTS += --port=$(COM_STYLE_MONITOR_PORT) + else + BOSSA_OPTS += --port=$(call get_monitor_port) + endif +else + BOSSA_OPTS += --port=$(ISP_PORT) +endif + +######################################################################## +# EXECUTABLES +# Define them here to use ARM_TOOLS_PATH and allow auto finding of AVR_TOOLS_PATH +OVERRIDE_EXECUTABLES = 1 + +ARM_TOOLS_PATH := $(ARM_TOOLS_DIR)/bin +CC = $(ARM_TOOLS_PATH)/$(CC_NAME) +CXX = $(ARM_TOOLS_PATH)/$(CXX_NAME) +AS = $(ARM_TOOLS_PATH)/$(AS_NAME) +OBJCOPY = $(ARM_TOOLS_PATH)/$(OBJCOPY_NAME) +OBJDUMP = $(ARM_TOOLS_PATH)/$(OBJDUMP_NAME) +AR = $(ARM_TOOLS_PATH)/$(AR_NAME) +SIZE = $(ARM_TOOLS_PATH)/$(SIZE_NAME) +NM = $(ARM_TOOLS_PATH)/$(NM_NAME) +#GDB = $(ARM_TOOLS_PATH)/$(GDB_NAME) +# Use system gdb for now as Arduino supplied has lib error? +GDB = $(GDB_NAME) + +######################################################################## +# FLAGS + +ifndef USB_TYPE + USB_TYPE = USBCON +endif + +ifndef USB_PRODUCT + USB_PRODUCT := $(call PARSE_BOARD,$(BOARD_TAG),build.usb_product) + ifdef USB_PRODUCT + $(call show_config_variable,USB_PRODUCT,[COMPUTED]) + endif +endif + +ifndef USB_MANUFACTURER + USB_MANUFACTURER := $(call PARSE_BOARD,$(BOARD_TAG),build.usb_manufacturer) + ifndef USB_MANUFACTURER + USB_MANUFACTURER = "Unknown" + else + $(call show_config_variable,USB_MANUFACTURER,[COMPUTED]) + endif +endif + +ifndef USB_VID + USB_VID := $(call PARSE_BOARD,$(BOARD_TAG),build.vid) + ifdef USB_VID + $(call show_config_variable,USB_VID,[COMPUTED]) + endif +endif + +ifndef USB_PID + USB_PID := $(call PARSE_BOARD,$(BOARD_TAG),build.pid) + ifdef USB_PID + $(call show_config_variable,USB_PID,[COMPUTED]) + endif +endif + +# Bootloader settings +ifndef BOOTLOADER_SIZE + BOOTLOADER_SIZE := $(call PARSE_BOARD,$(BOARD_TAG),bootloader.size) + ifndef BOOTLOADER_SIZE + BOOTLOADER_SIZE := 0x2000 + else + $(call show_config_variable,BOOTLOADER_SIZE,[COMPUTED]) + endif +endif + +ifndef BOOTLOADER_UNPROTECT + BOOTLOADER_UNPROTECT := $(call PARSE_BOARD,$(BOARD_TAG),bootloader.cmd_unprotect) + ifndef BOOTLOADER_UNPROTECT + BOOTLOADER_UNPROTECT := at91samd bootloader 0 + else + $(call show_config_variable,BOOTLOADER_UNPROTECT,[COMPUTED]) + endif +endif + +ifndef BOOTLOADER_PROTECT + BOOTLOADER_PROTECT := $(call PARSE_BOARD,$(BOARD_TAG),bootloader.cmd_protect) + ifndef BOOTLOADER_PROTECT + BOOTLOADER_PROTECT := at91samd bootloader 16384 + else + $(call show_config_variable,BOOTLOADER_PROTECT,[COMPUTED]) + endif +endif + +ifndef BOOTLOADER_PROTECT_VERIFY + BOOTLOADER_PROTECT_VERIFY := $(call PARSE_BOARD,$(BOARD_TAG),bootloader.cmd_protect_verify) + ifndef BOOTLOADER_PROTECT_VERIFY + BOOTLOADER_PROTECT_VERIFY := at91samd bootloader + else + $(call show_config_variable,BOOTLOADER_PROTECT_VERIFY,[COMPUTED]) + endif +endif + +# C99 with GNU extensions required for C sources using old compiler +CC_VERNUM = $(shell $(CC) -dumpversion | sed 's/\.//g') +ifneq ($(shell expr $(CC_VERNUM) '>' 490), 1) + CFLAGS_STD = -std=gnu99 +endif + +CPPFLAGS += -DMD -D$(USB_TYPE) '-DUSB_PRODUCT=$(USB_PRODUCT)' '-DUSB_MANUFACTURER=$(USB_MANUFACTURER)' + +# Get extra define flags from boards.txt +EXFLAGS := $(shell echo $(call PARSE_BOARD,$(BOARD_TAG),build.extra_flags) | grep -oE '(-D)\w+') + +# Strip only defines from extra flags as boards file appends user {build.usb} +CPPFLAGS += $(EXFLAGS) +CPPFLAGS += -DUSB_VID=$(USB_VID) +CPPFLAGS += -DUSB_PID=$(USB_PID) +# Cortex compiler flags +CPPFLAGS += -mthumb -nostdlib --param max-inline-insns-single=500 -fno-exceptions -Wl,-Map=$(OBJDIR)/$(TARGET).map +CXXFLAGS += -fno-rtti -fno-threadsafe-statics -std=gnu++11 + +AMCU := $(call PARSE_BOARD,$(BOARD_TAG),build.mcu) +BOARD_LINKER_SCRIPT := $(call PARSE_BOARD,$(BOARD_TAG),build.ldscript) +OPENOCD_SCRIPT := $(call PARSE_BOARD,$(BOARD_TAG),build.openocdscript) +# TODO Hard defines Cortex M0 math lib - should be dynamic +LDFLAGS += --specs=nano.specs --specs=nosys.specs -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group -L$(LIB_PATH) -larm_cortexM0l_math -lm +LINKER_SCRIPTS := -T$(ALTERNATE_CORE_PATH)/variants/$(VARIANT)/$(BOARD_LINKER_SCRIPT) +OTHER_LIBS := $(call PARSE_BOARD,$(BOARD_TAG),build.flags.libs) + +# OpenOCD reset command only for now +ifeq ($(strip $(UPLOAD_TOOL)), openocd) + RESET_CMD = $(OPENOCD) $(OPENOCD_OPTS) -c "telnet_port disabled; init; targets; reset run; shutdown" +else + # Set zero flag for ard-reset for 1200 baud boot to bootloader + ARD_RESET_OPTS += --zero +endif + +######################################################################## +# automatially include Arduino.mk for the user + +$(call show_separator) +$(call arduino_output,Arduino.mk Configuration:) +include $(ARDMK_DIR)/Arduino.mk diff --git a/Teensy.mk b/Teensy.mk index 8bd822b..3d0114c 100644 --- a/Teensy.mk +++ b/Teensy.mk @@ -146,7 +146,7 @@ endif # processor stuff ifndef MCU - MCU := $(call PARSE_TEENSY,$(BOARD_TAG),build.cpu) + MCU := $(call PARSE_TEENSY,$(BOARD_TAG),build.mcu) endif ifndef MCU_FLAG_NAME diff --git a/arduino-mk-vars.md b/arduino-mk-vars.md index d19d3d6..e3b6f07 100644 --- a/arduino-mk-vars.md +++ b/arduino-mk-vars.md @@ -11,6 +11,7 @@ The following are the different variables that can be overwritten in the user ma * [Avrdude setting variables](#avrdude-setting-variables) * [Bootloader variables](#bootloader-variables) * [ChipKIT variables](#chipkit-variables) +* [ARM variables](#arm-variables) * [Ctags variables](#ctags-variables) ## Global variables @@ -73,6 +74,29 @@ AVR_TOOLS_DIR = /usr/share/arduino/hardware/tools/avr ---- +### ARM_TOOLS_DIR + +**Description:** + +Directory where the arm toolchain is installed. `arm-none-eabi-*` should be +within a /bin subdirectory. + +Can usually be detected from `$ARDUINO_PACKAGE_DIR` /tools subdirectory when ARM +device support is installed. + +**Example:** + +```Makefile +ARM_TOOLS_DIR = /usr +# or +ARM_TOOLS_DIR = +/usr/share/arduino/hardware/tools/arm-none-eabi-gcc/VERSION +``` + +**Requirement:** *Optional* + +---- + ### RESET_CMD **Description:** @@ -114,6 +138,27 @@ ARDUINO_DIR = /Applications/Arduino.app/Contents/Java ---- +### ARDUINO_PACKAGE_DIR + +**Description:** + +Directory where the Arduino package support files are stored. Can auto-detect based on default OS IDE locations. + +**Example:** + +```Makefile +# Linux +ARDUINO_PACKAGE_DIR = $(HOME)/.arduino15/packages +# Mac OS X +ARDUINO_PACKAGE_DIR = $(HOME)/Library/Arduino15/packages +# Windows +ARDUINO_PACKAGE_DIR = $(USERPROFILE)/AppData/Local/Arduino15/packages +``` + +**Requirement:** *Optional* + +---- + ### ARDUINO_PLATFORM_LIB_PATH **Description:** @@ -155,12 +200,13 @@ ARDUINO_VERSION = 105 Architecture for Arduino 1.5+ -Defaults to unset for 1.0 or `avr` for 1.5+ +Defaults to unset for 1.0 or `avr` for 1.5+. This value is not literally the chip architecture but will often be +the chip series within a vendor's 'hardware' folder. For example, will default to `samd` if using Sam.mk. **Example:** ```Makefile -ARCHITECTURE = sam +ARCHITECTURE = arm ``` **Requirement:** *Optional* @@ -171,9 +217,9 @@ ARCHITECTURE = sam **Description:** -Board vendor/maintainer. +Board vendor/maintainer/series. -Defaults to `arduino` +Defaults to `arduino`. **Example:** @@ -658,7 +704,8 @@ ISP_PROG = stk500v1 **Description:** -Device path to ArduinoISP. Not needed for hardware ISP's. +Device path to ArduinoISP. Not needed for hardware ISP's. Also used to define +bootloader port on SAMD devices. **Example:** @@ -871,6 +918,18 @@ NM_NAME = pic32-nm ---- +### GDB_NAME + +**Description:** + +GDB utility. + +Defaults to `arm-none-eabi-gdb` + +**Requirement:** *Optional* + +---- + ### OPTIMIZATION_LEVEL **Description:** @@ -1044,7 +1103,7 @@ ASFLAGS += -my-as-only-flag **Description:** -Flags passed to the C pre-processor (for C, C++ and assembly source flies). Add +Flags passed to the C pre-processor (for C, C++ and assembly source files). Add more flags to this variable using `+=`. Defaults to all flags required for a typical build. @@ -1229,6 +1288,54 @@ ALTERNATE_CORE_PATH = $(HOME)/sketchbook/hardware/arduino-tiny/cores/tiny ---- +### CORE_VER + +**Description:** + +Alternate core release version. The Arduino board support packages are within +a sub-directory indicated by this define. + +Defaults to package current release. + +**Example:** + +```Makefile +CORE_VER = 1.6.17 +``` + +**Requirement:** *Optional* + +---- + +### CMSIS_DIR + +**Description:** + +Path to ARM CMSIS. Normally installed as part of ARM board support. + +Defaults to `ARDUINO_PACKAGE_DIR/tools/CMSIS/4.5.0/CMSIS` + +**Example:** + +```Makefile +CMSIS_DIR = /usr/share/CMSIS +``` + +**Requirement:** *Optional* + +---- + +### CMSIS_ATMEL_DIR + +**Description:** + +Path to CMSIS-Atmel directory. Installed with ARM support package. + +Defaults to `ARDUINO_PACKAGE_DIR/tools/CMSIS-Atmel/1.1.0/CMSIS` + +**Requirement:** *Optional* +---- + ### BOARDS_TXT **Description:** @@ -1385,6 +1492,67 @@ Would result in an absolute path to the bootloader hex file of `$(HOME)/sketchbo ---- +### BOOTLOADER_SIZE + +**Description:** + +Size of bootloader on ARM devices, ensures correct start address when flashing +application area. Normally parsed from boards.txt + +Defaults to `0x2000` + +**Requirement:** *Optional* + +---- + +### BOOTLOADER_UNPROTECT + +**Description:** + +Bootloader unprotect sequence for upload tool. Normally parsed from boards.txt + +Defaults to `at91samd bootloader 0` + +**Requirement:** *Optional* + +---- + +### BOOTLOADER_PROTECT + +**Description:** + +Bootloader protect sequence for upload tool. Normally parsed from boards.txt + +Defaults to `at91samd bootloader 16384` + +**Requirement:** *Optional* + +---- + +### BOOTLOADER_PROTECT_VERIFY + +**Description:** + +Bootloader protect and verify sequence for upload tool. Normally parsed from boards.txt + +Defaults to `at91samd bootloader` + +**Requirement:** *Optional* + +---- + +### BOOTLOADER_UPLOAD_TOOL + +**Description:** + +Bootloader upload binary to use. Normally parsed from boards.txt. + +Defaults to `openocd` + +**Requirement:** *Optional* + +---- + ## ChipKIT variables ### MPIDE_DIR @@ -1403,6 +1571,163 @@ MPIDE_DIR = $(HOME)/mpide **Requirement:** *Optional* +---- + +### MPIDE_PREFERENCES_PATH + +**Description:** + +Path to chipKIT `preferences.txt` file. + +Usually can be auto-detected as `AUTO_MPIDE_PREFERENCES_PATH` from the defaults `$(HOME)/.mpide/preferences.txt` (Linux) or `$(HOME)/Library/Mpide/preferences.txt` (OSX) + +**Example:** + +```Makefile +MPIDE_PREFERENCES_PATH = $(HOME)/chipkit/preferences.txt +``` + +**Requirement:** *Optional* + +---- + +## ARM variables + +### UPLOAD_TOOL + +**Description:** + +Tool to upload binary to device. Normally parsed from boards.txt. + +Defaults to `openocd` + +**Example:** + +```Makefile +UPLOAD_TOOL = gdb +``` + +**Requirement:** *Optional* + +---- + +### DEBUG + +**Description:** + +Define to set `DEBUG_FLAGS` and allow stepping of code using GDB. + +Defaults to undefined. + +**Example:** + +```Makefile +DEBUG = 1 +``` + +**Requirement:** *Optional* + +---- + +### GDB_PORT + +**Description:** + +Server port to use for GDB debugging or upload. Default assumes server running +on localhost but can re-define to use Black Magic Probe serial port. + +Defaults to `localhost:3333` + +**Example:** + +```Makefile +GDB_PORT = /dev/ttyACM0 +``` + +**Requirement:** *Optional* + +---- + +### GDB_OPTS + +**Description:** + +Optional arguments to parse to GDB command. + +Defaults to `-ex "target extended-remote $(GDB_PORT)" -ex "monitor swdp_scan" -ex "attach 1" -ex "load" -d $(OBJDIR) $(TARGET_ELF)` + +**Requirement:** *Optional* + +---- + +### GDB_UPLOAD_OPTS + +**Description:** + +Optional arguments to parse to GDB command when uploading binary only. + +Defaults to `GDB_UPLOAD_OPTS = $(GDB_OPTS) -ex "set confirm off" -ex "set target-async off" -ex "set remotetimeout 30" -ex "detach" -ex "kill" -ex "quit"` + +**Requirement:** *Optional* + +---- + +### BOSSA + +**Description:** + +Path to bossac binary. + +Can usually be detected from `$ARDUINO_PACKAGE_DIR` /tools subdirectory when ARM +device support is installed. + +**Requirement:** *Optional* + +---- + +### BOSSA_OPTS + +**Description:** + +Flags to pass to bossac command. + +Defaults to `-d --info --erase --write --verify --reset` + +**Requirement:** *Optional* + +---- + +### OPENOCD + +**Description:** + +Path to openocd binary. + +Can usually be detected from `$ARDUINO_PACKAGE_DIR` /tools subdirectory when ARM +device support is installed. + +**Requirement:** *Optional* + +---- + +### OPENOCD_OPTS + +**Description:** + +Flags to pass to openocd command. If using openocd from non-Arduino +distributions, one should define this with the path to the Arduino openocd script. + +Defaults to `-d2` + +Example: + +```Makefile +OPENOCD_OPTS = $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/tools/openocd/0.9.0-arduino6-static/share/openocd/scripts/ -f $(ARDUINO_PACKAGE_DIR)/$(ARDMK_VENDOR)/hardware/samd/1.6.17/variants/$(VARIANT)/$(OPENOCD_SCRIPT) +``` + +**Requirement:** *Optional* + + ---- ## Ctags variables @@ -1453,21 +1778,3 @@ CTAGS_CMD = /usr/local/bin/ ``` **Requirement:** *Optional* - ----- - -### MPIDE_PREFERENCES_PATH - -**Description:** - -Path to chipKIT `preferences.txt` file. - -Usually can be auto-detected as `AUTO_MPIDE_PREFERENCES_PATH` from the defaults `$(HOME)/.mpide/preferences.txt` (Linux) or `$(HOME)/Library/Mpide/preferences.txt` (OSX) - -**Example:** - -```Makefile -MPIDE_PREFERENCES_PATH = $(HOME)/chipkit/preferences.txt -``` - -**Requirement:** *Optional* diff --git a/bin/ard-reset-arduino b/bin/ard-reset-arduino index 1f2c7e9..d6f974f 100755 --- a/bin/ard-reset-arduino +++ b/bin/ard-reset-arduino @@ -2,6 +2,7 @@ from __future__ import print_function import serial +import serial.tools.list_ports import os.path import argparse from time import sleep @@ -13,13 +14,100 @@ except: pyserial_version = 2 # less than 2.3 parser = argparse.ArgumentParser(description='Reset an Arduino') +parser.add_argument('--zero', action='store_true', help='Reset Arduino Zero or similar Native USB to enter bootloader') parser.add_argument('--caterina', action='store_true', help='Reset a Leonardo, Micro, Robot or LilyPadUSB.') parser.add_argument('--verbose', action='store_true', help="Watch what's going on on STDERR.") parser.add_argument('--period', default=0.1, help='Specify the DTR pulse width in seconds.') parser.add_argument('port', nargs=1, help='Serial device e.g. /dev/ttyACM0') args = parser.parse_args() -if args.caterina: + +def list_ports(output=False): + """ Lists serial ports attached + + :returns + A list of paths to serial ports on system + """ + ports = serial.tools.list_ports.comports() + connected = [port[0] for port in ports] + if output: + print(connected) + + return connected + + +def new_port(old, new): + """ Checks if a new port has attached + + Args: + old: previous list of ports to check + new: current list of ports to check + Returns: + index of port in 'new' if new port found, otherwise -1 + """ + new_port = -1 + + for port in new: + if port not in old: + new_port = new.index(port) + break + + return new_port + + +if args.zero: + # number of trys to attempt + zero_attempts = 20 # ~2 seconds + initial_ports = list_ports(args.verbose) + + if args.verbose: + print('Attempting to enter bootloader using 1200 bps open/close on port %s' % args.port[0]) + + ser = serial.Serial(args.port[0], 57600) + ser.close() + + if pyserial_version < 3: + ser.setBaudrate(1200) + else: + ser.baudrate = 1200 + + # do the open/close at 1200 BAUD + ser.open() + ser.close() + + if args.verbose: + print('Done. Waiting for bootloader port to attach...') + + # get new list of ports + reset_ports = list_ports(args.verbose) + + # wait for new port or port to return + port_index = new_port(initial_ports, reset_ports) + + # keep checking until new port appears or timeout + while port_index < 0: + # count down attempts and leave if expired + zero_attempts -= 1 + if zero_attempts < 0: + break + sleep(0.1) + # get list of ports after bootloader toggle performed + reset_ports = list_ports(args.verbose) + # if a port drops, set initial ports to reset ports so that + # next attached device will be new port + if (len(reset_ports) < len(initial_ports)): + initial_ports = reset_ports + # check if a new port has attached and return the index if it has + port_index = new_port(initial_ports, reset_ports) + # return the new port if detected, otherwise return passed port + if port_index is -1: + bootloader_port = args.port[0] + else: + bootloader_port = reset_ports[port_index] + + # print so that `tail -1` can be piped for bootloader port + print(bootloader_port) +elif args.caterina: if args.verbose: print('Forcing reset using 1200bps open/close on port %s' % args.port[0]) ser = serial.Serial(args.port[0], 57600) ser.close() diff --git a/examples/MZeroBlink/MZeroBlink.ino b/examples/MZeroBlink/MZeroBlink.ino new file mode 100644 index 0000000..f9a59a9 --- /dev/null +++ b/examples/MZeroBlink/MZeroBlink.ino @@ -0,0 +1,19 @@ +/* + Blink + Turns on an LED on for one second, then off for one second, repeatedly. + + This example code is in the public domain. + */ + +void setup() { + // initialize the digital pin as an output. + // Pin 13 has an LED connected on most Arduino boards: + pinMode(13, OUTPUT); +} + +void loop() { + digitalWrite(13, HIGH); // set the LED on + delay(1000); // wait for a second + digitalWrite(13, LOW); // set the LED off + delay(1000); // wait for a second +} diff --git a/examples/MZeroBlink/Makefile b/examples/MZeroBlink/Makefile new file mode 100644 index 0000000..29cb90b --- /dev/null +++ b/examples/MZeroBlink/Makefile @@ -0,0 +1,24 @@ +# Arduino M0 Pro Programming Port +BOARD_TAG = mzero_pro_bl_dbg + +# Define debug if you want to use gdb +#DEBUG = 1 + +# Define port if using Black Magic Probe rather than default localhost:3333 +#GDB_PORT = /dev/ttyACM0 + +# Define ARM toolchain dir if not using Arduino supplied +#ARM_TOOLS_DIR = /usr + +# Define AVR toolchain dir if not using Arduino supplied and using native port +#AVR_TOOLS_DIR = /usr + +# Define Arduino support package installation path where SAM device support has been installed +# Linux +# ARDUINO_PACKAGE_DIR := $(HOME)/.arduino15/packages +# macOS +# ARDUINO_PACKAGE_DIR := $(HOME)/Library/Arduino15/packages +# Windows +# ARDUINO_PACKAGE_DIR := "C:/Users/$(USER)/AppData/Local/Arduino15/packages" + +include ../../Sam.mk diff --git a/examples/ZeroBlink/Makefile b/examples/ZeroBlink/Makefile new file mode 100644 index 0000000..60c8435 --- /dev/null +++ b/examples/ZeroBlink/Makefile @@ -0,0 +1,30 @@ +# Arduino Zero Native Port (should work with Feather, MKR1000 etc.) +# BOOTLOADER: The bootloader on these devices loaded when reset is pressed twice +# or the port is opened/closed at 1200 BAUD. If there is no program on the device, +# you may have to manually enter bootloader by toggling reset twice. +# see http://www.avdweb.nl/arduino/samd21/virus.html +BOARD_TAG = arduino_zero_native + +# Define alternative core path if using another board supplier +#ALTERNATE_CORE_PATH = $(HOME)/Arduino/hardware/sparkfun/samd + +# Define monitor port and isp port (bootloader port). +# Will automatically detect if Linux/macOS but MUST be defined on Windows +#MONITOR_PORT = com40 # CDC serial +#ISP_PORT = com39 # bootloader + +# Define ARM toolchain dir if not using Arduino supplied +#ARM_TOOLS_DIR = /usr + +# Define AVR toolchain dir if not using Arduino supplied and using native port +#AVR_TOOLS_DIR = /usr + +# Define Arduino support package installation path where SAM device support has been installed +# Linux +#ARDUINO_PACKAGE_DIR := $(HOME)/.arduino15/packages +# macOS +#ARDUINO_PACKAGE_DIR := $(HOME)/Library/Arduino15/packages +# Windows +#ARDUINO_PACKAGE_DIR := "C:/Users/$(USER)/AppData/Local/Arduino15/packages" + +include ../../Sam.mk diff --git a/examples/ZeroBlink/ZeroBlink.ino b/examples/ZeroBlink/ZeroBlink.ino new file mode 100644 index 0000000..f9a59a9 --- /dev/null +++ b/examples/ZeroBlink/ZeroBlink.ino @@ -0,0 +1,19 @@ +/* + Blink + Turns on an LED on for one second, then off for one second, repeatedly. + + This example code is in the public domain. + */ + +void setup() { + // initialize the digital pin as an output. + // Pin 13 has an LED connected on most Arduino boards: + pinMode(13, OUTPUT); +} + +void loop() { + digitalWrite(13, HIGH); // set the LED on + delay(1000); // wait for a second + digitalWrite(13, LOW); // set the LED off + delay(1000); // wait for a second +} diff --git a/tests/script/runtests.sh b/tests/script/runtests.sh index 7792186..648a0cf 100755 --- a/tests/script/runtests.sh +++ b/tests/script/runtests.sh @@ -7,7 +7,7 @@ failures=() # These examples cannot be tested easily at the moment as they require # alternate cores. The MakefileExample doesn't actually contain any source code # to compile. -NON_TESTABLE_EXAMPLES=(ATtinyBlink MakefileExample TinySoftWareSerial BlinkOpenCM BlinkTeensy BlinkNetworkRPi BlinkInAVRC) +NON_TESTABLE_EXAMPLES=(ATtinyBlink MakefileExample TinySoftWareSerial BlinkOpenCM BlinkTeensy BlinkNetworkRPi BlinkInAVRC MZeroBlink ZeroBlink) for dir in $TESTS_DIR/*/ do