├── .gitignore ├── 99-openocd.rules ├── Makefile ├── README.md ├── arduino └── samd21_sam_ba.bin ├── config └── conf_spi.h ├── errno.c ├── errno.h ├── extract-asf.sh ├── ili9341.c ├── ili9341.h ├── ili9341_cmd.h ├── main.c ├── openocd.cfg └── samd21g18a_flash.ld /.gitignore: -------------------------------------------------------------------------------- 1 | /*.bin 2 | /*.elf 3 | /*.hex 4 | /*.lst 5 | /*.map 6 | /*.o 7 | -------------------------------------------------------------------------------- /99-openocd.rules: -------------------------------------------------------------------------------- 1 | # Copy this file to /etc/udev/rules.d/ 2 | 3 | ACTION!="add|change", GOTO="openocd_rules_end" 4 | SUBSYSTEM!="usb|tty|hidraw", GOTO="openocd_rules_end" 5 | 6 | # Arduino Zero EDBG (debug/programming) interface 7 | ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2157", MODE="664", GROUP="uucp" 8 | 9 | # CMSIS-DAP compatible adapters 10 | #ATTRS{product}=="*CMSIS-DAP*", MODE="664", GROUP="plugdev" 11 | 12 | LABEL="openocd_rules_end" 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJ_NAME = main 2 | 3 | CC=arm-none-eabi-gcc 4 | LD=arm-none-eabi-ld 5 | OBJCOPY=arm-none-eabi-objcopy 6 | OBJDUMP=arm-none-eabi-objdump 7 | 8 | CFLAGS = -mcpu=cortex-m0plus -mthumb 9 | CFLAGS += -Wall -Werror -std=c11 10 | CFLAGS += -O0 11 | CFLAGS += -g 12 | CFLAGS += -ffunction-sections -fdata-sections # cargo-cult 13 | CFLAGS += -Wl,--gc-sections -Wl,-Map=$(PROJ_NAME).map 14 | CFLAGS += -T samd21g18a_flash.ld 15 | 16 | CFLAGS += -I ./config 17 | CFLAGS += -I asf/common/utils 18 | CFLAGS += -I asf/common2/services/delay 19 | CFLAGS += -I asf/common2/services/delay/sam0 20 | CFLAGS += -I asf/sam0/drivers/port 21 | CFLAGS += -I asf/sam0/drivers/sercom 22 | CFLAGS += -I asf/sam0/drivers/sercom/spi 23 | CFLAGS += -I asf/sam0/drivers/system 24 | CFLAGS += -I asf/sam0/drivers/system/clock 25 | CFLAGS += -I asf/sam0/drivers/system/clock/clock_samd21_r21_da 26 | CFLAGS += -I asf/sam0/drivers/system/clock/clock_samd21_r21_da/module_config 27 | CFLAGS += -I asf/sam0/drivers/system/interrupt 28 | CFLAGS += -I asf/sam0/drivers/system/interrupt/system_interrupt_samd21 29 | CFLAGS += -I asf/sam0/drivers/system/pinmux 30 | CFLAGS += -I asf/sam0/drivers/system/power/power_sam_d_r 31 | CFLAGS += -I asf/sam0/drivers/system/reset/reset_sam_d_r 32 | CFLAGS += -I asf/sam0/utils 33 | CFLAGS += -I asf/sam0/utils/cmsis/samd21/include 34 | CFLAGS += -I asf/sam0/utils/cmsis/samd21/source 35 | CFLAGS += -I asf/sam0/utils/header_files 36 | CFLAGS += -I asf/sam0/utils/preprocessor 37 | CFLAGS += -I asf/thirdparty/CMSIS/Include 38 | 39 | CFLAGS += -D __SAMD21G18A__ 40 | CFLAGS += -D SYSTICK_MODE 41 | 42 | SRCS = $(PROJ_NAME).c 43 | SRCS += ili9341.c 44 | SRCS += errno.c 45 | SRCS += asf/common/utils/interrupt/interrupt_sam_nvic.c 46 | SRCS += asf/common2/services/delay/sam0/systick_counter.c 47 | SRCS += asf/sam0/drivers/port/port.c 48 | SRCS += asf/sam0/drivers/sercom/sercom.c 49 | SRCS += asf/sam0/drivers/sercom/spi/spi.c 50 | SRCS += asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock.c 51 | SRCS += asf/sam0/drivers/system/clock/clock_samd21_r21_da/gclk.c 52 | SRCS += asf/sam0/drivers/system/pinmux/pinmux.c 53 | SRCS += asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c 54 | SRCS += asf/sam0/utils/cmsis/samd21/source/system_samd21.c 55 | 56 | .PHONY: all 57 | all: $(PROJ_NAME).bin 58 | 59 | $(PROJ_NAME).bin: $(PROJ_NAME).elf $(PROJ_NAME).lst 60 | $(OBJCOPY) $< $@ -O binary 61 | 62 | $(PROJ_NAME).lst: $(PROJ_NAME).elf 63 | $(OBJDUMP) -D $^ > $@ 64 | 65 | $(PROJ_NAME).elf: $(SRCS) 66 | $(CC) $(CFLAGS) $^ -o $@ 67 | 68 | 69 | .PHONY: clean 70 | clean: 71 | rm -f -- *.bin *.elf *.o *.lst 72 | 73 | .PHONY: upload 74 | upload: 75 | openocd -d2 -f openocd.cfg -c "program {{main.bin}} verify reset 0x00002000; shutdown" 76 | 77 | .PHONY: bossa 78 | bossa: 79 | bossac --port=ttyACM0 --info --erase --write --verify --reset main.bin 80 | 81 | .PHONY: bootloader 82 | bootloader: 83 | openocd -d2 -f openocd.cfg -c "init; halt; at91samd bootloader 0; program {{arduino/samd21_sam_ba.bin}} verify reset; shutdown" 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | arduino-zero-without-ide 2 | ======================== 3 | 4 | Messing with an Arduino Zero board without using the Arduino IDE, from a ThinkPad running Arch Linux. 5 | 6 | Installed packages 7 | ------------------ 8 | 9 | ``` 10 | arm-none-eabi-binutils 11 | arm-none-eabi-gcc 12 | arm-none-eabi-gdb 13 | arm-none-eabi-newlib 14 | gdb 15 | make 16 | openocd 17 | ``` 18 | 19 | BOSSA 20 | ----- 21 | 22 | Atmel SAM (ARM core) can be programmed using [BOSSA]; the equivalent of programming an Atmel AVR using [avrdude]. On the Arduino Zero, the "native USB" port connects directly to the SAMD21 MCU (PA24,25; SERCOM 3) and is suitable for BOSSA. 23 | 24 | BOSSA mainline (including Arch's `bossa-bin` AUR package) currently doesn't support Arduino Zero; 25 | 26 | ``` 27 | $ bossac --port=ttyACM0 --info 28 | No device found on ttyACM0 29 | ``` 30 | 31 | Arduino uses a `bossac` binary built from the `arduino` branch at https://github.com/shumatech/BOSSA/tree/arduino (it took me a long time, and recompiling the `cdc_acm` kernel module with debug enabled, to figure that out). 32 | 33 | ``` 34 | $ bossac --port=ttyACM0 --info 35 | Atmel SMART device 0x10010005 found 36 | Device : ATSAMD21G18A 37 | Chip ID : 10010005 38 | Version : v1.1 [Arduino:XYZ] Jun 10 2015 11:08:10 39 | Address : 8192 40 | Pages : 3968 41 | Page Size : 64 bytes 42 | Total Size : 248KB 43 | Planes : 1 44 | Lock Regions : 16 45 | Locked : none 46 | Security : false 47 | Boot Flash : true 48 | BOD : true 49 | BOR : true 50 | Arduino : FAST_CHIP_ERASE 51 | Arduino : FAST_MULTI_PAGE_WRITE 52 | Arduino : CAN_CHECKSUM_MEMORY_BUFFER 53 | ``` 54 | 55 | Install BOSSA Arduino branch from source: 56 | 57 | ``` 58 | git clone --branch arduino https://github.com/shumatech/BOSSA.git ~/code/bossa 59 | cd ~/code/bossa 60 | make 61 | mkdir -p ~/bin && ln -s ~/code/BOSSA/bin/bossa* ~/bin/ 62 | ``` 63 | 64 | Writing program to flash: 65 | 66 | ``` 67 | $ bossac -p ttyACM0 -e -w blink.bin -R 68 | Atmel SMART device 0x10010005 found 69 | Erase flash 70 | done in 0.824 seconds 71 | 72 | Write 2916 bytes to flash (46 pages) 73 | [==============================] 100% (46/46 pages) 74 | done in 0.025 seconds 75 | CPU reset. 76 | ``` 77 | 78 | BOSSA only works on the native USB port, and only when in the bootloader by double-clicking the reset button (<= 500ms) or triggering a reset via the serial port. This means manual button presses to upload code, and swapping plugs to debug. Using OpenOCD on the Programming/Debug port is probably a better option. 79 | 80 | 81 | OpenOCD 82 | ------- 83 | 84 | [OpenOCD] is a software interface to On Chip Debuggers including the Atmel EDBG chip on the Arduino Zero's programming/debug USB port. It can be used instead of BOSSA for flashing the device, and additionally for remote debugging with GDB etc. 85 | 86 | To give non-root access to the `/dev/ttyACM0` interface, OpenOCD ships with an example udev configuration at `/usr/share/openocd/contrib/99-openocd.rules`. There's a stripped down version in this repo, using the `uucp` group instead of `plugdev`, and only for the Atmel EDBG device. Drop it into `/etc/udev/rules.d/` or just run `openocd` as root if you really want. 87 | 88 | As with BOSSA, Arduino has forked OpenOCD at https://github.com/arduino/OpenOCD/tree/arduino and the original does not work with Arduino Zero. The official branch/package may partially work, but the program generally wont run after programming. The difference seems to be in https://github.com/arduino/OpenOCD/commit/d4b767947e867989e461a45626c17108dcb73f61 ... I modified `PKGBUILD` from https://aur.archlinux.org/packages/openocd-git/ to use the `arduino` repo/branch, and that worked better. 89 | 90 | The local `openocd.cfg` is based on [`arduino_zero.cfg`][OpenOCD config]. 91 | 92 | ``` 93 | $ openocd 94 | Open On-Chip Debugger 0.9.0 (2015-05-19-13:50) 95 | Licensed under GNU GPL v2 96 | For bug reports, read 97 | http://openocd.org/doc/doxygen/bugs.html 98 | Info : only one transport option; autoselect 'swd' 99 | adapter speed: 500 kHz 100 | adapter_nsrst_delay: 100 101 | cortex_m reset_config sysresetreq 102 | Info : CMSIS-DAP: SWD Supported 103 | Info : CMSIS-DAP: Interface Initialised (SWD) 104 | Info : CMSIS-DAP: FW Version = 02.01.0157 105 | Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 1 106 | Info : CMSIS-DAP: Interface ready 107 | Info : clock speed 500 kHz 108 | Info : SWD IDCODE 0x0bc11477 109 | Info : at91samd21g18.cpu: hardware has 4 breakpoints, 2 watchpoints 110 | ``` 111 | 112 | ``` 113 | $ telnet 0 4444 114 | Trying 0.0.0.0... 115 | Connected to 0. 116 | Escape character is '^]'. 117 | Open On-Chip Debugger 118 | > halt 119 | target state: halted 120 | target halted due to debug-request, current mode: Thread 121 | xPSR: 0x01000000 pc: 0x00002244 msp: 0x20002418 122 | > reset 123 | > exit 124 | Connection closed by foreign host. 125 | ``` 126 | 127 | ``` 128 | $ arm-none-eabi-gdb -ex 'target remote localhost:3333' blink.elf 129 | Reading symbols from blink.elf...done. 130 | Remote debugging using localhost:3333 131 | >>> 132 | ``` 133 | 134 | 135 | 136 | Atmel ASF and ARM CMSIS 137 | ----------------------- 138 | 139 | * ARM's [CMSIS - Cortex Microcontroller Software Interface Standard][CMSIS] is a thing. 140 | * Atmel's [Atmel Software Framework (ASF)][ASF] [supports CMSIS][ASF-CMSIS]. 141 | * ASF (421 MB): [asf-standalone-archive-3.30.0.43.zip][ASF download] 142 | * CMSIS (97 MB): [CMSIS-SP-00300-r4p5-00rel0/CMSIS-SP-00300-r4p5-00rel0.zip][CMSIS download] 143 | 144 | ASF seems more useful for an Atmel microcontroller, and ships with a vendored copy of CMSIS v4.00. 145 | 146 | Run `./extract-asf.sh` to download (421 MB) and extract (1.5 GB) the relevant parts of ASF, excluding components for non-ARM architectures (avr32, mega, xmega). 147 | 148 | 149 | Hardware notes 150 | -------------- 151 | 152 | Arduino Zero 153 | 154 | ``` 155 | A0: PA02 156 | A1: PB08 157 | A2: PB09 158 | A3: PA04 159 | A4: PA05 160 | A5: PB02 161 | D0: PA11 SERCOM0 PAD3 mux C (SPI: unused) 162 | D1: PA10 SERCOM0 PAD2 mux C (SPI: MISO) 163 | D2: PA14 164 | D3: PA09 SERCOM0 PAD1 mux C (SPI: SCK) 165 | D4: PA08 SERCOM0 PAD0 mux C (SPI: MOSI) 166 | D5: PA15 167 | D6: PA20 168 | D7: PA21 169 | D8: PA06 170 | D9: PA07 171 | D10: PA18 172 | D11: PA16 173 | D12: PA19 174 | D13: PA17 (LED) 175 | ``` 176 | 177 | Sparkfun SAMD21 178 | 179 | * Blue LED ("pin 13") is `PA17` 180 | * `TX_LED` (green) is `PA27` 181 | * `RX_LED` (yellow) is `PB03` 182 | 183 | 2.2" TFT SPA 240x320 generic board I have, probably ILI9341 or similar: 184 | 185 | ``` 186 | 1: VCC 187 | 2: GND 188 | 3: CS PA15 (D5) 189 | 4: RESET PA21 (D7) 190 | 5: DC/RS PA14 (D2) 191 | 6: SDI/MOSI PA08 (D4) 192 | 7: SCK PA09 (D3) 193 | 8: LED PA20 (D6) 194 | 9: SDO/MISO PA10 (D1) 195 | ``` 196 | 197 | 198 | [ASF download]: http://www.atmel.com/images/asf-standalone-archive-3.30.0.43.zip 199 | [ASF-CMSIS]: http://asf.atmel.com/docs/latest/cmsis.html 200 | [ASF]: http://asf.atmel.com/docs/latest/index.html 201 | [BOSSA]: http://www.shumatech.com/web/products/bossa 202 | [CMSIS download]: https://silver.arm.com/download/ARM_and_AMBA_Architecture/CMSIS-SP-00300-r4p5-00rel0/CMSIS-SP-00300-r4p5-00rel0.zip 203 | [CMSIS]: http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php 204 | [OpenOCD config]: https://github.com/arduino/OpenOCD/blob/arduino/tcl/board/arduino_zero.cfg 205 | [OpenOCD]: http://openocd.org/ 206 | [avrdude]: http://www.nongnu.org/avrdude/ 207 | -------------------------------------------------------------------------------- /arduino/samd21_sam_ba.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pda/arduino-zero-without-ide/38b12a166376908fd04d562d4540580982cddc3d/arduino/samd21_sam_ba.bin -------------------------------------------------------------------------------- /config/conf_spi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define CONF_SPI_MASTER_ENABLE true 3 | #define CONF_SPI_SLAVE_ENABLE false 4 | -------------------------------------------------------------------------------- /errno.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "errno.h" 7 | 8 | void error(uint8_t errno) { 9 | while (true) { 10 | for (uint8_t i = 0; i < errno; i++) { 11 | port_pin_set_output_level(ERRNO_PIN, true); 12 | delay_ms(100); 13 | port_pin_set_output_level(ERRNO_PIN, false); 14 | delay_ms(200); 15 | } 16 | delay_ms(1000); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /errno.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ERRNO_PIN PIN_PA17 4 | 5 | #define ERRNO_SPI_INIT 2 6 | #define ERRNO_SPI_NOT_READY 3 7 | #define ERRNO_SPI_SELECT_UNSUPPORTED 4 8 | #define ERRNO_SPI_SELECT_BUSY 5 9 | #define ERRNO_SPI_WRITE_ABORTED 6 10 | #define ERRNO_SPI_WRITE_INVALID_ARG 7 11 | #define ERRNO_SPI_WRITE_TIMEOUT 8 12 | 13 | void error(uint8_t errno); 14 | -------------------------------------------------------------------------------- /extract-asf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Download Atmel Software Framework (ASF), extract files needed for SAMD21, 4 | # including the bundled ARM CMSIS. 5 | 6 | set -eou pipefail 7 | 8 | ZIPFILE="asf-standalone-archive-3.30.0.43.zip" 9 | ZIPSHA256="951453ea953d38bc73474746d43772822654b3da4f1600d0018fb93a2a51c303" 10 | 11 | main() { 12 | acquire 13 | extract 14 | rearrange 15 | } 16 | 17 | acquire() { 18 | if [[ ! -e "$ZIPFILE" ]]; then 19 | wget "http://www.atmel.com/images/$ZIPFILE" 20 | fi 21 | sha256sum -c <(echo $ZIPSHA256 $ZIPFILE) 22 | } 23 | 24 | extract() { 25 | unzip -q $ZIPFILE \ 26 | -x xdk-asf-3.30.0/{avr32,mega,xmega}/\* 27 | } 28 | 29 | rearrange() { 30 | mv --no-target-directory --no-clobber xdk-asf-3.30.0 asf 31 | } 32 | 33 | main 34 | -------------------------------------------------------------------------------- /ili9341.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "errno.h" 8 | #include "ili9341.h" 9 | #include "ili9341_cmd.h" 10 | 11 | static void set_output(const uint8_t pin); 12 | static void ili9341_brightness(struct ili9341_ctx *, uint8_t percent); 13 | static void ili9341_cmd(struct ili9341_ctx *, uint8_t cmd); 14 | static void ili9341_reset(struct ili9341_ctx *); 15 | static void ili9341_select(struct ili9341_ctx *, const bool select); 16 | static void ili9341_write16(struct ili9341_ctx *, uint16_t data); 17 | 18 | uint16_t retry_ttl; 19 | 20 | static const uint8_t init_commands[] = { 21 | 4, ILI9341_UNDOCUMENTED_EF, 0x03, 0x80, 0x02, 22 | 4, ILI9341_POWERB, 0x00, 0XC1, 0X30, 23 | 5, ILI9341_POWER_SEQ, 0x64, 0x03, 0X12, 0X81, 24 | 4, ILI9341_DTCA, 0x85, 0x00, 0x78, 25 | 6, ILI9341_POWERA, 0x39, 0x2C, 0x00, 0x34, 0x02, 26 | 2, ILI9341_PRC, 0x20, 27 | 3, ILI9341_DTCB, 0x00, 0x00, 28 | 2, ILI9341_POWER1, 0x23, 29 | 2, ILI9341_POWER2, 0x10, 30 | 3, ILI9341_VCOM1, 0x3e, 0x28, 31 | 2, ILI9341_VCOM2, 0x86, 32 | 2, ILI9341_MAC, 0x48, 33 | 2, ILI9341_PIXEL_FORMAT, 0x55, 34 | 3, ILI9341_FRMCTR1, 0x00, 0x18, 35 | 4, ILI9341_DFC, 0x08, 0x82, 0x27, 36 | 2, ILI9341_3GAMMA_EN, 0x00, 37 | 2, ILI9341_GAMMA, 0x01, 38 | 16, ILI9341_PGAMMA, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 39 | 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, 40 | 16, ILI9341_NGAMMA, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 41 | 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, 42 | 0 43 | }; 44 | 45 | 46 | void ili9341_init(struct ili9341_ctx * ctx) { 47 | set_output(ctx->pin_dc); 48 | set_output(ctx->pin_led); 49 | set_output(ctx->pin_reset); 50 | 51 | struct spi_slave_inst_config dev_cfg; 52 | spi_slave_inst_get_config_defaults(&dev_cfg); 53 | dev_cfg.ss_pin = ctx->pin_cs; 54 | spi_attach_slave(&ctx->dev, &dev_cfg); 55 | 56 | ili9341_reset(ctx); 57 | ili9341_select(ctx, true); 58 | 59 | // thanks https://github.com/PaulStoffregen/ILI9341_t3 60 | const uint8_t *addr = init_commands; 61 | while (1) { 62 | uint8_t count = *addr++; 63 | if (count == 0) { 64 | break; 65 | } 66 | ili9341_cmd(ctx, *addr++); 67 | uint8_t args = count - 1; 68 | spi_write_buffer_wait(ctx->host, addr, args); 69 | addr += args; 70 | } 71 | 72 | ili9341_cmd(ctx, ILI9341_SLEEP_OUT); 73 | delay_ms(100); 74 | ili9341_cmd(ctx, ILI9341_DISPLAY_ON); 75 | 76 | ili9341_brightness(ctx, 50); 77 | 78 | ili9341_select(ctx, false); 79 | } 80 | 81 | void ili9341_window(struct ili9341_ctx * ctx, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { 82 | ctx->window_height = height; 83 | ctx->window_width = width; 84 | ctx->window_x1 = x; 85 | ctx->window_x2 = x + width - 1; 86 | ctx->window_y1 = y; 87 | ctx->window_y2 = y + height - 1; 88 | ctx->window_pixels = width * height; 89 | ctx->window_bytes = width * height * 2; 90 | ili9341_select(ctx, true); 91 | ili9341_cmd(ctx, ILI9341_COLUMN_ADDR); 92 | ili9341_write16(ctx, x); 93 | ili9341_write16(ctx, x + width - 1); 94 | ili9341_cmd(ctx, ILI9341_PAGE_ADDR); 95 | ili9341_write16(ctx, y); 96 | ili9341_write16(ctx, y + height - 1); 97 | ili9341_select(ctx, false); 98 | } 99 | 100 | void ili9341_blank(struct ili9341_ctx * ctx, uint16_t color) { 101 | ili9341_select(ctx, true); 102 | uint8_t hi = color >> 8; 103 | uint8_t lo = color & 0xFF; 104 | ili9341_cmd(ctx, ILI9341_RAMWR); 105 | for (uint32_t i = 0; i < ctx->window_pixels; ++i) { 106 | spi_write(ctx->host, hi); 107 | spi_write(ctx->host, lo); 108 | } 109 | ili9341_select(ctx, false); 110 | } 111 | 112 | static void ili9341_select(struct ili9341_ctx * ctx, const bool select) { 113 | enum status_code status = spi_select_slave( 114 | ctx->host, 115 | &ctx->dev, 116 | select 117 | ); 118 | switch(status) { 119 | case STATUS_ERR_UNSUPPORTED_DEV: 120 | error(ERRNO_SPI_SELECT_UNSUPPORTED); 121 | break; 122 | case STATUS_BUSY: 123 | error(ERRNO_SPI_SELECT_BUSY); 124 | break; 125 | default: 126 | break; 127 | } 128 | } 129 | 130 | static void ili9341_reset(struct ili9341_ctx * ctx) { 131 | port_pin_set_output_level(ctx->pin_dc, true); 132 | port_pin_set_output_level(ctx->pin_reset, true); 133 | delay_ms(1); 134 | port_pin_set_output_level(ctx->pin_reset, false); 135 | delay_us(10); 136 | port_pin_set_output_level(ctx->pin_reset, true); 137 | delay_ms(5); 138 | } 139 | 140 | static void ili9341_brightness(struct ili9341_ctx * ctx, uint8_t percent) { 141 | // TODO: PWM 142 | port_pin_set_output_level(ctx->pin_led, percent > 0); 143 | } 144 | 145 | static void set_output(const uint8_t pin) { 146 | struct port_config config_port_pin; 147 | port_get_config_defaults(&config_port_pin); 148 | config_port_pin.direction = PORT_PIN_DIR_OUTPUT; 149 | port_pin_set_config(pin, &config_port_pin); 150 | } 151 | 152 | static void ili9341_cmd(struct ili9341_ctx * ctx, uint8_t cmd) { 153 | port_pin_set_output_level(ctx->pin_dc, false); 154 | retry_ttl = 0xFFFF; 155 | while (!spi_is_ready_to_write(ctx->host) && --retry_ttl); 156 | if (retry_ttl == 0) { 157 | error(ERRNO_SPI_NOT_READY); 158 | } 159 | spi_write(ctx->host, cmd); 160 | retry_ttl = 0xFFFF; 161 | while (!spi_is_write_complete(ctx->host) && --retry_ttl); 162 | if (retry_ttl == 0) { 163 | error(ERRNO_SPI_WRITE_TIMEOUT); 164 | } 165 | port_pin_set_output_level(ctx->pin_dc, true); 166 | } 167 | 168 | static void ili9341_write16(struct ili9341_ctx * ctx, uint16_t data) { 169 | uint16_t buffer = (data << 8) | (data >> 8); 170 | spi_write_buffer_wait(ctx->host, (const uint8_t *)&buffer, 2); 171 | } 172 | -------------------------------------------------------------------------------- /ili9341.h: -------------------------------------------------------------------------------- 1 | #define ILI9341_TFTWIDTH 240 2 | #define ILI9341_TFTHEIGHT 320 3 | 4 | // 16-bit encodings of common colors (RRRRR GGGGGG BBBBB) 5 | #define ILI9341_BLACK 0x0000 // 0, 0, 0 6 | #define ILI9341_NAVY 0x000F // 0, 0, 128 7 | #define ILI9341_DARKGREEN 0x03E0 // 0, 128, 0 8 | #define ILI9341_DARKCYAN 0x03EF // 0, 128, 128 9 | #define ILI9341_MAROON 0x7800 // 128, 0, 0 10 | #define ILI9341_PURPLE 0x780F // 128, 0, 128 11 | #define ILI9341_OLIVE 0x7BE0 // 128, 128, 0 12 | #define ILI9341_LIGHTGREY 0xC618 // 192, 192, 192 13 | #define ILI9341_DARKGREY 0x7BEF // 128, 128, 128 14 | #define ILI9341_BLUE 0x001F // 0, 0, 255 15 | #define ILI9341_GREEN 0x07E0 // 0, 255, 0 16 | #define ILI9341_CYAN 0x07FF // 0, 255, 255 17 | #define ILI9341_RED 0xF800 // 255, 0, 0 18 | #define ILI9341_MAGENTA 0xF81F // 255, 0, 255 19 | #define ILI9341_YELLOW 0xFFE0 // 255, 255, 0 20 | #define ILI9341_WHITE 0xFFFF // 255, 255, 255 21 | #define ILI9341_ORANGE 0xFD20 // 255, 165, 0 22 | #define ILI9341_GREENYELLOW 0xAFE5 // 173, 255, 47 23 | #define ILI9341_PINK 0xF81F 24 | 25 | struct ili9341_ctx { 26 | uint8_t pin_cs; // e.g. PIN_PA15 27 | uint8_t pin_dc; // e.g. PIN_PA14 28 | uint8_t pin_led; // e.g. PIN_PA20 29 | uint8_t pin_reset; // e.g. PIN_PA21 30 | struct spi_module * host; 31 | struct spi_slave_inst dev; 32 | uint16_t window_height; 33 | uint16_t window_width; 34 | uint16_t window_x1; 35 | uint16_t window_x2; 36 | uint16_t window_y1; 37 | uint16_t window_y2; 38 | uint32_t window_pixels; 39 | uint32_t window_bytes; 40 | }; 41 | 42 | void ili9341_init(struct ili9341_ctx *); 43 | 44 | void ili9341_blank(struct ili9341_ctx *, uint16_t color); 45 | 46 | void ili9341_window(struct ili9341_ctx *, uint16_t x, uint16_t y, uint16_t width, uint16_t height); 47 | -------------------------------------------------------------------------------- /ili9341_cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Level 1 Commands 4 | #define ILI9341_SWRESET 0x01 // Software Reset 5 | #define ILI9341_READ_DISPLAY_ID 0x04 // Read display identification information 6 | #define ILI9341_RDDST 0x09 // Read Display Status 7 | #define ILI9341_RDDPM 0x0A // Read Display Power Mode 8 | #define ILI9341_RDDMADCTL 0x0B // Read Display MADCTL 9 | #define ILI9341_RDDCOLMOD 0x0C // Read Display Pixel Format 10 | #define ILI9341_RDDIM 0x0D // Read Display Image Format 11 | #define ILI9341_RDDSM 0x0E // Read Display Signal Mode 12 | #define ILI9341_RDDSDR 0x0F // Read Display Self-Diagnostic Result 13 | #define ILI9341_SPLIN 0x10 // Enter Sleep Mode 14 | #define ILI9341_SLEEP_OUT 0x11 // Sleep out 15 | #define ILI9341_PTLON 0x12 // Partial Mode ON 16 | #define ILI9341_NORMAL_MODE_ON 0x13 // Normal Display Mode ON 17 | #define ILI9341_DINVOFF 0x20 // Display Inversion OFF 18 | #define ILI9341_DINVON 0x21 // Display Inversion ON 19 | #define ILI9341_GAMMA 0x26 // Gamma 20 | #define ILI9341_DISPLAY_OFF 0x28 // Display off 21 | #define ILI9341_DISPLAY_ON 0x29 // Display on 22 | #define ILI9341_COLUMN_ADDR 0x2A // Colomn address 23 | #define ILI9341_PAGE_ADDR 0x2B // Page address 24 | #define ILI9341_RAMWR 0x2C // GRAM 25 | #define ILI9341_RGBSET 0x2D // Color SET 26 | #define ILI9341_RAMRD 0x2E // Memory Read 27 | #define ILI9341_PLTAR 0x30 // Partial Area 28 | #define ILI9341_VSCRDEF 0x33 // Vertical Scrolling Definition 29 | #define ILI9341_TEOFF 0x34 // Tearing Effect Line OFF 30 | #define ILI9341_TEON 0x35 // Tearing Effect Line ON 31 | #define ILI9341_MAC 0x36 // Memory Access Control 32 | #define ILI9341_VSCRSADD 0x37 // Vertical Scrolling Start Address 33 | #define ILI9341_IDMOFF 0x38 // Idle Mode OFF 34 | #define ILI9341_IDMON 0x39 // Idle Mode ON 35 | #define ILI9341_PIXEL_FORMAT 0x3A // Pixel Format 36 | #define ILI9341_WRITE_MEM_CONTINUE 0x3C // Write Memory Continue 37 | #define ILI9341_READ_MEM_CONTINUE 0x3E // Read Memory Continue 38 | #define ILI9341_SET_TEAR_SCANLINE 0x44 // Set Tear Scanline 39 | #define ILI9341_GET_SCANLINE 0x45 // Get Scanline 40 | #define ILI9341_WDB 0x51 // Write Brightness Display 41 | #define ILI9341_RDDISBV 0x52 // Read Display Brightness 42 | #define ILI9341_WCD 0x53 // Write Control Display 43 | #define ILI9341_RDCTRLD 0x54 // Read CTRL Display 44 | #define ILI9341_WRCABC 0x55 // Write Content Adaptive Brightness Control 45 | #define ILI9341_RDCABC 0x56 // Read Content Adaptive Brightness Control 46 | #define ILI9341_WRITE_CABC 0x5E // Write CABC Minimum Brightness 47 | #define ILI9341_READ_CABC 0x5F // Read CABC Minimum Brightness 48 | #define ILI9341_READ_ID1 0xDA // Read ID1 49 | #define ILI9341_READ_ID2 0xDB // Read ID2 50 | #define ILI9341_READ_ID3 0xDC // Read ID3 51 | 52 | // Level 2 Commands 53 | #define ILI9341_RGB_INTERFACE 0xB0 // RGB Interface Signal Control 54 | #define ILI9341_FRMCTR1 0xB1 // Frame Rate Control (In Normal Mode) 55 | #define ILI9341_FRMCTR2 0xB2 // Frame Rate Control (In Idle Mode) 56 | #define ILI9341_FRMCTR3 0xB3 // Frame Rate Control (In Partial Mode) 57 | #define ILI9341_INVTR 0xB4 // Display Inversion Control 58 | #define ILI9341_BPC 0xB5 // Blanking Porch Control 59 | #define ILI9341_DFC 0xB6 // Display Function Control 60 | #define ILI9341_ETMOD 0xB7 // Entry Mode Set 61 | #define ILI9341_BACKLIGHT1 0xB8 // Backlight Control 1 62 | #define ILI9341_BACKLIGHT2 0xB9 // Backlight Control 2 63 | #define ILI9341_BACKLIGHT3 0xBA // Backlight Control 3 64 | #define ILI9341_BACKLIGHT4 0xBB // Backlight Control 4 65 | #define ILI9341_BACKLIGHT5 0xBC // Backlight Control 5 66 | #define ILI9341_BACKLIGHT7 0xBE // Backlight Control 7 67 | #define ILI9341_BACKLIGHT8 0xBF // Backlight Control 8 68 | #define ILI9341_POWER1 0xC0 // Power Control 1 69 | #define ILI9341_POWER2 0xC1 // Power Control 2 70 | #define ILI9341_VCOM1 0xC5 // VCOM Control 1 71 | #define ILI9341_VCOM2 0xC7 // VCOM Control 2 72 | #define ILI9341_NVMWR 0xD0 // NV Memory Write 73 | #define ILI9341_NVMPKEY 0xD1 // NV Memory Protection Key 74 | #define ILI9341_RDNVM 0xD2 // NV Memory Status Read 75 | #define ILI9341_READ_ID4 0xD3 // Read ID4 76 | #define ILI9341_PGAMMA 0xE0 // Positive Gamma Correction 77 | #define ILI9341_NGAMMA 0xE1 // Negative Gamma Correction 78 | #define ILI9341_DGAMCTRL1 0xE2 // Digital Gamma Control 1 79 | #define ILI9341_DGAMCTRL2 0xE3 // Digital Gamma Control 2 80 | #define ILI9341_INTERFACE 0xF6 // Interface control 81 | 82 | // Extend register commands 83 | #define ILI9341_POWERA 0xCB // Power control A 84 | #define ILI9341_POWERB 0xCF // Power control B 85 | #define ILI9341_DTCA 0xE8 // Driver timing control A 86 | #define ILI9341_DTCB 0xEA // Driver timing control B 87 | #define ILI9341_POWER_SEQ 0xED // Power on sequence 88 | #define ILI9341_3GAMMA_EN 0xF2 // 3 Gamma enable 89 | #define ILI9341_PRC 0xF7 // Pump ratio control 90 | 91 | // Undocumented 92 | #define ILI9341_UNDOCUMENTED_EF 0xEF // Undocumented initialization command 93 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "errno.h" 8 | #include "ili9341.h" 9 | 10 | #define LED_0_PIN PIN_PA17 11 | 12 | #define TFT_PIN_CS PIN_PA15 13 | #define TFT_PIN_DC PIN_PA14 14 | #define TFT_PIN_LED PIN_PA20 15 | #define TFT_PIN_RESET PIN_PA21 16 | 17 | 18 | static void set_output(const uint8_t pin); 19 | static void setup(); 20 | static void setup_display(); 21 | static void setup_spi(); 22 | 23 | struct spi_module spi_bus; 24 | struct ili9341_ctx display_ctx; 25 | 26 | int main() { 27 | setup(); 28 | ili9341_window(&display_ctx, 0, 0, ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT); 29 | ili9341_blank(&display_ctx, ILI9341_BLACK); 30 | while (true) { 31 | ili9341_blank(&display_ctx, ILI9341_RED); 32 | ili9341_blank(&display_ctx, ILI9341_GREEN); 33 | ili9341_blank(&display_ctx, ILI9341_BLUE); 34 | } 35 | } 36 | 37 | static void setup() { 38 | system_clock_init(); 39 | delay_init(); 40 | set_output(LED_0_PIN); 41 | setup_spi(); 42 | setup_display(); 43 | } 44 | 45 | static void set_output(const uint8_t pin) { 46 | struct port_config config_port_pin; 47 | port_get_config_defaults(&config_port_pin); 48 | config_port_pin.direction = PORT_PIN_DIR_OUTPUT; 49 | port_pin_set_config(pin, &config_port_pin); 50 | } 51 | 52 | static void setup_display() { 53 | display_ctx.pin_cs = TFT_PIN_CS; 54 | display_ctx.pin_dc = TFT_PIN_DC; 55 | display_ctx.pin_led = TFT_PIN_LED; 56 | display_ctx.pin_reset = TFT_PIN_RESET; 57 | display_ctx.host = &spi_bus; 58 | ili9341_init(&display_ctx); 59 | } 60 | 61 | static void setup_spi() { 62 | struct spi_config host_cfg; 63 | spi_get_config_defaults(&host_cfg); 64 | host_cfg.mux_setting = SPI_SIGNAL_MUX_SETTING_C; 65 | host_cfg.pinmux_pad0 = PINMUX_PA08C_SERCOM0_PAD0; // MOSI 66 | host_cfg.pinmux_pad1 = PINMUX_PA09C_SERCOM0_PAD1; // SCK 67 | host_cfg.pinmux_pad2 = PINMUX_PA10C_SERCOM0_PAD2; // MISO 68 | host_cfg.pinmux_pad3 = PINMUX_UNUSED; 69 | host_cfg.receiver_enable = false; 70 | host_cfg.mode_specific.master.baudrate = 4000000; 71 | enum status_code status = spi_init(&spi_bus, SERCOM0, &host_cfg); 72 | if (status != STATUS_OK) { 73 | error(ERRNO_SPI_INIT); 74 | } 75 | spi_enable(&spi_bus); 76 | } 77 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | # https://github.com/arduino/OpenOCD/blob/arduino/tcl/board/arduino_zero.cfg 2 | 3 | source [find interface/cmsis-dap.cfg] 4 | set CHIPNAME at91samd21g18 5 | set ENDIAN little 6 | set telnet_port 4444 7 | source [find target/at91samdXX.cfg] 8 | -------------------------------------------------------------------------------- /samd21g18a_flash.ld: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * 4 | * \brief Linker script for running in internal FLASH on the SAMD21G18A 5 | * 6 | * Copyright (c) 2014-2015 Atmel Corporation. All rights reserved. 7 | * 8 | * \asf_license_start 9 | * 10 | * \page License 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. The name of Atmel may not be used to endorse or promote products derived 23 | * from this software without specific prior written permission. 24 | * 25 | * 4. This software may only be redistributed and used in connection with an 26 | * Atmel microcontroller product. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED 29 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 30 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE 31 | * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR 32 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 36 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 37 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | * POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | * \asf_license_stop 41 | * 42 | */ 43 | 44 | 45 | OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 46 | OUTPUT_ARCH(arm) 47 | SEARCH_DIR(.) 48 | 49 | /* Memory Spaces Definitions */ 50 | MEMORY 51 | { 52 | /* 0x2000 offset for Arduino Zero bootloader */ 53 | rom (rx) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000-0x2000 54 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 55 | } 56 | 57 | /* The stack size used by the application. NOTE: you need to adjust according to your application. */ 58 | STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000; 59 | 60 | /* Section Definitions */ 61 | SECTIONS 62 | { 63 | .text : 64 | { 65 | . = ALIGN(4); 66 | _sfixed = .; 67 | KEEP(*(.vectors .vectors.*)) 68 | *(.text .text.* .gnu.linkonce.t.*) 69 | *(.glue_7t) *(.glue_7) 70 | *(.rodata .rodata* .gnu.linkonce.r.*) 71 | *(.ARM.extab* .gnu.linkonce.armextab.*) 72 | 73 | /* Support C constructors, and C destructors in both user code 74 | and the C library. This also provides support for C++ code. */ 75 | . = ALIGN(4); 76 | KEEP(*(.init)) 77 | . = ALIGN(4); 78 | __preinit_array_start = .; 79 | KEEP (*(.preinit_array)) 80 | __preinit_array_end = .; 81 | 82 | . = ALIGN(4); 83 | __init_array_start = .; 84 | KEEP (*(SORT(.init_array.*))) 85 | KEEP (*(.init_array)) 86 | __init_array_end = .; 87 | 88 | . = ALIGN(4); 89 | KEEP (*crtbegin.o(.ctors)) 90 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 91 | KEEP (*(SORT(.ctors.*))) 92 | KEEP (*crtend.o(.ctors)) 93 | 94 | . = ALIGN(4); 95 | KEEP(*(.fini)) 96 | 97 | . = ALIGN(4); 98 | __fini_array_start = .; 99 | KEEP (*(.fini_array)) 100 | KEEP (*(SORT(.fini_array.*))) 101 | __fini_array_end = .; 102 | 103 | KEEP (*crtbegin.o(.dtors)) 104 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 105 | KEEP (*(SORT(.dtors.*))) 106 | KEEP (*crtend.o(.dtors)) 107 | 108 | . = ALIGN(4); 109 | _efixed = .; /* End of text section */ 110 | } > rom 111 | 112 | /* .ARM.exidx is sorted, so has to go in its own output section. */ 113 | PROVIDE_HIDDEN (__exidx_start = .); 114 | .ARM.exidx : 115 | { 116 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 117 | } > rom 118 | PROVIDE_HIDDEN (__exidx_end = .); 119 | 120 | . = ALIGN(4); 121 | _etext = .; 122 | 123 | .relocate : AT (_etext) 124 | { 125 | . = ALIGN(4); 126 | _srelocate = .; 127 | *(.ramfunc .ramfunc.*); 128 | *(.data .data.*); 129 | . = ALIGN(4); 130 | _erelocate = .; 131 | } > ram 132 | 133 | /* .bss section which is used for uninitialized data */ 134 | .bss (NOLOAD) : 135 | { 136 | . = ALIGN(4); 137 | _sbss = . ; 138 | _szero = .; 139 | *(.bss .bss.*) 140 | *(COMMON) 141 | . = ALIGN(4); 142 | _ebss = . ; 143 | _ezero = .; 144 | } > ram 145 | 146 | /* stack section */ 147 | .stack (NOLOAD): 148 | { 149 | . = ALIGN(8); 150 | _sstack = .; 151 | . = . + STACK_SIZE; 152 | . = ALIGN(8); 153 | _estack = .; 154 | } > ram 155 | 156 | . = ALIGN(4); 157 | _end = . ; 158 | } 159 | --------------------------------------------------------------------------------