From 2ece2daf9077b6515bec028c077002887152513b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 4 Dec 2018 13:45:55 -0800 Subject: [PATCH 01/23] Rework LED flashing and add Particle's boards --- Makefile | 24 +- .../libraries/bootloader_dfu/bootloader.c | 9 +- .../bootloader_dfu/dfu_transport_ble.c | 7 +- .../bootloader_dfu/dfu_transport_serial.c | 15 +- src/boards.c | 224 ++++++++++++------ src/boards.h | 58 +++-- src/boards/feather_nrf52832.h | 6 +- src/boards/feather_nrf52840_express.h | 9 +- src/boards/particle_argon.h | 77 ++++++ src/boards/particle_boron.h | 81 +++++++ src/boards/particle_xenon.h | 77 ++++++ src/boards/pca10056.h | 4 +- src/boards/pca10059.h | 10 +- src/main.c | 30 +-- src/usb/uf2/ghostfat.c | 7 +- src/usb/uf2/uf2cfg.h | 18 +- src/usb/usb.c | 11 +- 17 files changed, 501 insertions(+), 166 deletions(-) create mode 100644 src/boards/particle_argon.h create mode 100644 src/boards/particle_boron.h create mode 100644 src/boards/particle_xenon.h diff --git a/Makefile b/Makefile index ed6a386..dc07ba0 100644 --- a/Makefile +++ b/Makefile @@ -53,9 +53,7 @@ endif ifeq ($(OS),Windows_NT) PROGFILES = C:/Program Files (x86) -GNU_INSTALL_ROOT = $(PROGFILES)/GNU Tools ARM Embedded/7 2018-q2-update -else -GNU_INSTALL_ROOT = /usr +GNU_INSTALL_ROOT = $(PROGFILES)/GNU Tools ARM Embedded/7 2018-q2-update/bin/ endif MK := mkdir @@ -70,14 +68,14 @@ endif GNU_PREFIX = arm-none-eabi # Toolchain commands -CC := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-gcc' -AS := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-as' -AR := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-ar' -r -LD := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-ld' -NM := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-nm' -OBJDUMP := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-objdump' -OBJCOPY := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-objcopy' -SIZE := '$(GNU_INSTALL_ROOT)/bin/$(GNU_PREFIX)-size' +CC := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-gcc' +AS := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-as' +AR := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-ar' -r +LD := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-ld' +NM := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-nm' +OBJDUMP := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-objdump' +OBJCOPY := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-objcopy' +SIZE := '$(GNU_INSTALL_ROOT)$(GNU_PREFIX)-size' #function for removing duplicates in a list remduplicates = $(strip $(if $1,$(firstword $1) $(call remduplicates,$(filter-out $(firstword $1),$1)))) @@ -87,7 +85,7 @@ remduplicates = $(strip $(if $1,$(firstword $1) $(call remduplicates,$(filter-ou #********************************* BOARD_LIST = $(sort $(subst .h,,$(subst src/boards/,,$(wildcard src/boards/*.h)))) -NRF52840_BOARDLIST = pca10056 pca10059 feather_nrf52840_express +NRF52840_BOARDLIST = pca10056 pca10059 feather_nrf52840_express particle_argon particle_boron particle_xenon IS_NRF52840 = $(filter $(BOARD),$(NRF52840_BOARDLIST)) ifeq ($(filter $(MAKECMDGOALS),all-board all-release help),) @@ -370,7 +368,7 @@ all-board: all-release: $(call _make_all_board,clean all release) - + help: @echo To flash (with jlink) a pre-built binary with a specific version to a board @echo $$ make BOARD=feather_nrf52840_express VERSION=6.1.1r0 flash diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index f80f83a..a61739c 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -136,6 +136,8 @@ static void wait_for_events(void) } #endif + led_tick(); + if ((m_update_status == BOOTLOADER_COMPLETE) || (m_update_status == BOOTLOADER_TIMEOUT) || (m_update_status == BOOTLOADER_RESET) ) @@ -221,8 +223,8 @@ void bootloader_dfu_update_process(dfu_update_status_t update_status) else if (update_status.status_code == DFU_UPDATE_SD_COMPLETE) { settings.bank_0_crc = update_status.app_crc; - settings.bank_0_size = update_status.sd_size + - update_status.bl_size + + settings.bank_0_size = update_status.sd_size + + update_status.bl_size + update_status.app_size; settings.bank_0 = BANK_VALID_SD; settings.bank_1 = BANK_INVALID_APP; @@ -326,7 +328,7 @@ uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms) uint32_t err_code; // Clear swap if banked update is used. - err_code = dfu_init(); + err_code = dfu_init(); VERIFY_SUCCESS(err_code); if ( ota ) @@ -467,4 +469,3 @@ void bootloader_settings_get(bootloader_settings_t * const p_settings) p_settings->app_image_size = p_bootloader_settings->app_image_size; p_settings->sd_image_start = p_bootloader_settings->sd_image_start; } - diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index ac33c51..c88a24c 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -589,7 +589,7 @@ static void on_dfu_evt(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) break; case BLE_DFU_PACKET_WRITE: - led_red_blink_fast(true); + led_state(STATE_WRITING_STARTED); on_dfu_pkt_write(p_dfu, p_evt); break; @@ -741,6 +741,7 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) case BLE_GAP_EVT_CONNECTED: m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; m_is_advertising = false; + led_state(STATE_BLE_CONNECTED); break; case BLE_GAP_EVT_DISCONNECTED: @@ -750,9 +751,9 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) m_direct_adv_cnt = APP_DIRECTED_ADV_TIMEOUT; - led_red_blink_fast(false); + led_state(STATE_BLE_DISCONNECTED); - err_code = sd_ble_gatts_sys_attr_get(m_conn_handle, + err_code = sd_ble_gatts_sys_attr_get(m_conn_handle, sys_attr, &sys_attr_len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS); diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c index bef9da5..e3e8dc2 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c @@ -106,7 +106,7 @@ static uint32_t data_queue_element_free(uint8_t element_index) uint32_t retval; retval = NRF_ERROR_INVALID_PARAM; - + if (MAX_BUFFERS > element_index) { p_data = (uint8_t *)DATA_QUEUE_ELEMENT_GET_PDATA(element_index); @@ -137,7 +137,7 @@ static uint32_t data_queue_element_alloc(uint8_t * p_element_index, uint8_t pack uint32_t index; retval = NRF_ERROR_NO_MEM; - + if (INVALID_PACKET == packet_type) { retval = NRF_ERROR_INVALID_PARAM; @@ -216,7 +216,7 @@ static void process_dfu_packet(void * p_event_data, uint16_t event_size) break; case START_PACKET: - packet->params.start_packet = + packet->params.start_packet = (dfu_start_packet_t*)packet->params.data_packet.p_data_packet; retval = dfu_start_pkt_handle(packet); APP_ERROR_CHECK(retval); @@ -227,14 +227,14 @@ static void process_dfu_packet(void * p_event_data, uint16_t event_size) retval = dfu_init_pkt_complete(); APP_ERROR_CHECK(retval); - led_red_blink_fast(true); + led_state(STATE_WRITING_STARTED); break; case STOP_DATA_PACKET: (void)dfu_image_validate(); (void)dfu_image_activate(); - led_red_blink_fast(false); + led_state(STATE_WRITING_FINISHED); // Break the loop by returning. return; @@ -273,7 +273,7 @@ void rpc_transport_event_handler(hci_transport_evt_t event) retval = app_sched_event_put(NULL, 0, process_dfu_packet); } } - + if (p_rpc_cmd_buffer != NULL && NRF_SUCCESS != retval) { // Free the packet that could not be processed. @@ -308,7 +308,6 @@ uint32_t dfu_transport_serial_close(void) { // Remove all buffered packets. data_queue_flush(); - + return hci_transport_close(); } - diff --git a/src/boards.c b/src/boards.c index e7e1dfe..13d34b1 100644 --- a/src/boards.c +++ b/src/boards.c @@ -45,20 +45,11 @@ #define SCHED_MAX_EVENT_DATA_SIZE sizeof(app_timer_event_t) /**< Maximum size of scheduler events. */ #define SCHED_QUEUE_SIZE 30 /**< Maximum number of events in the scheduler queue. */ -/* use PWM for blinky to prevent inconsistency due to MCU blocking in flash operation - * clock = 125khz --> resolution = 8us - * top value = 25000 -> period = 200 ms - * Mode up -> toggle every 100 ms = fast blink - * Mode up and down = 400 ms = slow blink - */ -#define PWM_MAXCOUNT 25000 -#define PWM_CHANNEL_NUM 4 - - -uint16_t _pwm_red_seq [PWM_CHANNEL_NUM] = { PWM_MAXCOUNT/2, 0, 0 , 0 }; -uint16_t _pwm_blue_seq[PWM_CHANNEL_NUM] = { PWM_MAXCOUNT/2, 0, 0 , 0 }; - //------------- IMPLEMENTATION -------------// +#ifdef OUTPUT_500HZ_PIN +void init_clock_pwm(uint32_t pin); +void clock_pwm_teardown(void); +#endif void board_init(void) { @@ -73,22 +64,16 @@ void board_init(void) button_init(BUTTON_FRESET); NRFX_DELAY_US(100); // wait for the pin state is stable - // LED init - nrf_gpio_cfg_output(LED_RED); - nrf_gpio_cfg_output(LED_BLUE); - led_off(LED_RED); - led_off(LED_BLUE); - // use PMW0 for LED RED - led_pwm_init(LED_RED); + led_pwm_init(LED_PRIMARY, LED_PRIMARY_PIN); + #if LEDS_NUMBER > 1 + led_pwm_init(LED_SECONDARY, LED_SECONDARY_PIN); + #endif // use neopixel for use enumeration -#ifdef LED_NEOPIXEL +#if defined(LED_NEOPIXEL) || defined(LED_RGB_RED_PIN) extern void neopixel_init(void); neopixel_init(); - - uint8_t grb[3] = { 0, 32, 0 }; - neopixel_write(grb); #endif // Init scheduler @@ -100,17 +85,13 @@ void board_init(void) void board_teardown(void) { - // Disable and reset PWM for LED - led_pwm_teardown(LED_RED); + // Disable and reset PWM for LEDs + led_pwm_teardown(); -#ifdef LED_NEOPIXEL +#if defined(LED_NEOPIXEL) || defined(LED_RGB_RED_PIN) extern void neopixel_teardown(void); neopixel_teardown(); #endif - - led_off(LED_BLUE); - led_off(LED_RED); - // Button // Stop RTC1 used by app_timer @@ -146,58 +127,129 @@ void pwm_teardown(NRF_PWM_Type* pwm ) pwm->SEQ[0].CNT = 0; } -void led_pwm_init(uint32_t led_pin) -{ - NRF_PWM_Type* pwm = (led_pin == LED_RED) ? NRF_PWM0 : NRF_PWM1; +static uint16_t led_duty_cycles[PWM0_CH_NUM]; - pwm->MODE = PWM_MODE_UPDOWN_UpAndDown; - pwm->COUNTERTOP = PWM_MAXCOUNT; - pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_128; +#if LEDS_NUMBER > PWM0_CH_NUM +#error "Only " PWM0_CH_NUM " concurrent status LEDs are supported." +#endif + +void led_pwm_init(uint32_t led_index, uint32_t led_pin) +{ + NRF_PWM_Type* pwm = NRF_PWM0; + + nrf_gpio_cfg_output(led_pin); + pwm->PSEL.OUT[led_index] = led_pin; + + pwm->ENABLE = 1; + pwm->MODE = PWM_MODE_UPDOWN_Up; + pwm->COUNTERTOP = 0xff; + pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_16; pwm->DECODER = PWM_DECODER_LOAD_Individual; pwm->LOOP = 0; - pwm->SEQ[0].PTR = (uint32_t) (led_pin == LED_RED ? _pwm_red_seq : _pwm_blue_seq); - pwm->SEQ[0].CNT = PWM_CHANNEL_NUM; // default mode is Individual --> count must be 4 + pwm->SEQ[0].PTR = (uint32_t) (led_duty_cycles); + pwm->SEQ[0].CNT = 4; // default mode is Individual --> count must be 4 pwm->SEQ[0].REFRESH = 0; pwm->SEQ[0].ENDDELAY = 0; - - pwm->PSEL.OUT[0] = led_pin; - - pwm->ENABLE = 1; + pwm->LOOP = 0; pwm->TASKS_SEQSTART[0] = 1; } -void led_pwm_teardown(uint32_t led_pin) +void led_pwm_teardown(void) { - pwm_teardown ((led_pin == LED_RED) ? NRF_PWM0 : NRF_PWM1); + pwm_teardown(NRF_PWM0); } -void led_pwm_disable(uint32_t led_pin) +void led_pwm_duty_cycle(uint32_t led_index, uint16_t duty_cycle) { - NRF_PWM_Type* pwm = (led_pin == LED_RED) ? NRF_PWM0 : NRF_PWM1; - - pwm->TASKS_SEQSTART[0] = 0; - pwm->ENABLE = 0; + led_duty_cycles[led_index] = duty_cycle; + nrf_pwm_event_clear(NRF_PWM0, NRF_PWM_EVENT_SEQEND0); + nrf_pwm_task_trigger(NRF_PWM0, NRF_PWM_TASK_SEQSTART0); } -void led_pwm_enable(uint32_t led_pin) -{ - NRF_PWM_Type* pwm = (led_pin == LED_RED) ? NRF_PWM0 : NRF_PWM1; +static uint32_t primary_cycle_length; +#ifdef LED_SECONDARY_PIN +static uint32_t secondary_cycle_length; +#endif +void led_tick() { + uint32_t millis = tusb_hal_millis(); - pwm->ENABLE = 1; - pwm->TASKS_SEQSTART[0] = 1; + uint32_t cycle = millis % primary_cycle_length; + uint32_t half_cycle = primary_cycle_length / 2; + if (cycle > half_cycle) { + cycle = primary_cycle_length - cycle; + } + uint16_t duty_cycle = 0x4f * cycle / half_cycle; + #if LED_STATE_ON == 1 + duty_cycle = 0xff - duty_cycle; + #endif + led_pwm_duty_cycle(LED_PRIMARY, duty_cycle); + + #ifdef LED_SECONDARY_PIN + cycle = millis % secondary_cycle_length; + half_cycle = secondary_cycle_length / 2; + if (cycle > half_cycle) { + cycle = secondary_cycle_length - cycle; + } + duty_cycle = 0x8f * cycle / half_cycle; + #if LED_STATE_ON == 1 + duty_cycle = 0xff - duty_cycle; + #endif + led_pwm_duty_cycle(LED_SECONDARY, duty_cycle); + #endif } - -void led_red_blink_fast(bool enable) +static uint32_t rgb_color; +static bool temp_color_active = false; +void led_state(uint32_t state) { - if ( enable ) - { - NRF_PWM0->MODE = PWM_MODE_UPDOWN_Up; - }else - { - NRF_PWM0->MODE = PWM_MODE_UPDOWN_UpAndDown; - } + uint32_t new_rgb_color = rgb_color; + uint32_t temp_color = 0; + switch (state) { + case STATE_USB_MOUNTED: + new_rgb_color = 0x00ff00; + primary_cycle_length = 4000; + break; + case STATE_BOOTLOADER_STARTED: + case STATE_USB_UNMOUNTED: + new_rgb_color = 0xff0000; + primary_cycle_length = 300; + break; + case STATE_WRITING_STARTED: + temp_color = 0xff0000; + break; + case STATE_WRITING_FINISHED: + // Empty means to unset any temp colors. + break; + case STATE_BLE_CONNECTED: + new_rgb_color = 0x0000ff; + #ifdef LED_SECONDARY_PIN + secondary_cycle_length = 500; + #else + primary_cycle_length = 500; + #endif + break; + case STATE_BLE_DISCONNECTED: + new_rgb_color = 0xff00ff; + #ifdef LED_SECONDARY_PIN + secondary_cycle_length = 300; + #else + primary_cycle_length = 300; + #endif + break; + default: + break; + } + new_rgb_color &= BOARD_RGB_BRIGHTNESS; + if (temp_color != 0){ + neopixel_write((uint8_t*)&temp_color); + temp_color_active = true; + } else if (new_rgb_color != rgb_color) { + neopixel_write((uint8_t*)&new_rgb_color); + rgb_color = new_rgb_color; + } else if (temp_color_active) { + neopixel_write((uint8_t*)&rgb_color); + } } #if LED_NEOPIXEL @@ -267,10 +319,11 @@ void neopixel_teardown(void) // write 3 bytes color to a built-in neopixel void neopixel_write (uint8_t *pixels) { + uint8_t grb[NEO_NUMBYTE] = {pixels[1], pixels[2], pixels[0]}; uint16_t pos = 0; // bit position for ( uint16_t n = 0; n < NEO_NUMBYTE; n++ ) { - uint8_t pix = pixels[n]; + uint8_t pix = grb[n]; for ( uint8_t mask = 0x80; mask > 0; mask >>= 1 ) { @@ -280,8 +333,8 @@ void neopixel_write (uint8_t *pixels) } // Zero padding to indicate the end of sequence - pixels_pattern[++pos] = 0 | (0x8000); // Seq end - pixels_pattern[++pos] = 0 | (0x8000); // Seq end + pixels_pattern[pos++] = 0 | (0x8000); // Seq end + pixels_pattern[pos++] = 0 | (0x8000); // Seq end NRF_PWM_Type* pwm = NRF_PWM2; @@ -293,3 +346,40 @@ void neopixel_write (uint8_t *pixels) } #endif +#if defined(LED_RGB_RED_PIN) && defined(LED_RGB_GREEN_PIN) && defined(LED_RGB_BLUE_PIN) + +#ifdef LED_SECONDARY_PIN +#error "Cannot use secondary LED at the same time as an RGB status LED." +#endif + +#define LED_RGB_RED 1 +#define LED_RGB_BLUE 2 +#define LED_RGB_GREEN 3 + +void neopixel_init(void) +{ + led_pwm_init(LED_RGB_RED, LED_RGB_RED_PIN); + led_pwm_init(LED_RGB_GREEN, LED_RGB_GREEN_PIN); + led_pwm_init(LED_RGB_BLUE, LED_RGB_BLUE_PIN); +} + +void neopixel_teardown(void) +{ + uint8_t grb[3] = { 0, 0, 0 }; + neopixel_write(grb); +} + +// write 3 bytes color to a built-in neopixel +void neopixel_write (uint8_t *pixels) +{ + led_pwm_duty_cycle(LED_RGB_RED, pixels[2]); + led_pwm_duty_cycle(LED_RGB_GREEN, pixels[1]); + led_pwm_duty_cycle(LED_RGB_BLUE, pixels[0]); +} +#endif + +#if !LED_NEOPIXEL && !defined(LED_RGB_RED) +void neopixel_write(uint8_t* pixels) { + (void) pixels; +} +#endif diff --git a/src/boards.h b/src/boards.h index b81bf6e..5266ac5 100644 --- a/src/boards.h +++ b/src/boards.h @@ -25,15 +25,32 @@ #include "boards/pca10056.h" #elif defined BOARD_PCA10059 #include "boards/pca10059.h" +#elif defined BOARD_PARTICLE_ARGON +#include "boards/particle_argon.h" +#elif defined BOARD_PARTICLE_BORON +#include "boards/particle_boron.h" +#elif defined BOARD_PARTICLE_XENON +#include "boards/particle_xenon.h" #else #error No boards defined #endif +#ifndef BUTTON_DFU #define BUTTON_DFU BUTTON_1 +#endif +#ifndef BUTTON_FRESET #define BUTTON_FRESET BUTTON_2 +#endif -#define LED_RED LED_1 -#define LED_BLUE LED_2 +// The primary LED is usually Red but not in all cases. +#define LED_PRIMARY 0 +// The secondary LED, when available, is usually blue. +#define LED_SECONDARY 1 + +// The internal +#ifndef BOARD_RGB_BRIGHTNESS +#define BOARD_RGB_BRIGHTNESS 0x101010 +#endif // Helper function #define memclr(buffer, size) memset(buffer, 0, size) @@ -50,29 +67,24 @@ void board_teardown(void); #define bit(b) (1UL << (b)) -static inline void led_control(uint32_t pin, bool state) -{ - nrf_gpio_pin_write(pin, state ? LED_STATE_ON : (1-LED_STATE_ON)); -} +#define STATE_BOOTLOADER_STARTED 0 +#define STATE_USB_MOUNTED 1 +#define STATE_USB_UNMOUNTED 2 +#define STATE_FACTORY_RESET_STARTED 3 +#define STATE_FACTORY_RESET_FINISHED 4 +#define STATE_WRITING_STARTED 5 +#define STATE_WRITING_FINISHED 6 +#define STATE_BLE_CONNECTED 7 +#define STATE_BLE_DISCONNECTED 8 -static inline void led_on(uint32_t pin) -{ - led_control(pin, true); -} +void led_pwm_init(uint32_t led_index, uint32_t led_pin); +void led_pwm_teardown(void); +void led_pwm_disable(uint32_t led_index); +void led_pwm_enable(uint32_t led_index); +void led_state(uint32_t state); +void led_tick(void); -static inline void led_off(uint32_t pin) -{ - led_control(pin, false); -} - -void led_pwm_init(uint32_t led_pin); -void led_pwm_teardown(uint32_t led_pin); -void led_pwm_disable(uint32_t led_pin); -void led_pwm_enable(uint32_t led_pin); - -void led_red_blink_fast(bool enable); - -#ifdef LED_NEOPIXEL +#if defined(LED_NEOPIXEL) || defined(LED_RGB_RED_PIN) void neopixel_write(uint8_t *pixels); #endif diff --git a/src/boards/feather_nrf52832.h b/src/boards/feather_nrf52832.h index 415c086..05d8798 100644 --- a/src/boards/feather_nrf52832.h +++ b/src/boards/feather_nrf52832.h @@ -41,8 +41,8 @@ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 2 -#define LED_1 17 -#define LED_2 19 +#define LED_PRIMARY_PIN 17 // Red +#define LED_SECODARY_PIN 19 // Blue #define LED_STATE_ON 1 /*------------------------------------------------------------------*/ @@ -66,4 +66,6 @@ #define DIS_MANUFACTURER "Adafruit Industries" #define DIS_MODEL "Bluefruit Feather nRF52832" +#define PRODUCT_NAME "Adafruit Bluefruit Feather nRF52832" + #endif // _FEATHER52832_H diff --git a/src/boards/feather_nrf52840_express.h b/src/boards/feather_nrf52840_express.h index ddc83d0..7859f0d 100644 --- a/src/boards/feather_nrf52840_express.h +++ b/src/boards/feather_nrf52840_express.h @@ -43,9 +43,10 @@ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 2 -#define LED_1 _PINNUM(1, 15) -#define LED_2 _PINNUM(1, 10) +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) #define LED_NEOPIXEL 16 +#define BOARD_RGB_BRIGHTNESS 0x040404 #define LED_STATE_ON 1 /*------------------------------------------------------------------*/ @@ -65,8 +66,12 @@ #define RTS_PIN_NUMBER 5 #define HWFC false +#define OUTPUT_500HZ_PIN _PINNUM(0, 05) // A1 + // Used as model string in OTA mode #define DIS_MANUFACTURER "Adafruit Industries" #define DIS_MODEL "Bluefruit Feather nRF52840 Express" +#define PRODUCT_NAME "Adafruit Feather nRF52840 Express" + #endif // _FEATHER52840_H diff --git a/src/boards/particle_argon.h b/src/boards/particle_argon.h new file mode 100644 index 0000000..cc432d6 --- /dev/null +++ b/src/boards/particle_argon.h @@ -0,0 +1,77 @@ +/**************************************************************************/ +/*! + @file particle_boron.h + @author Scott Shawcroft + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Scott Shawcroft for Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _PARTICLE_ARGON_H +#define _PARTICLE_ARGON_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_RED _PINNUM(0, 13) +#define LED_BLUE _PINNUM(0, 15) +#define LED_STATE_ON 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTONS_NUMBER 2 +#define BUTTON_DFU _PINNUM(0, 11) +#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false + +// Used as model string in OTA mode +#define DIS_MANUFACTURER "Particle Industries" +#define DIS_MODEL "Argon" + +#define VOLUME_LABEL "ARGONBOOT " + +#define BOARD_ID "Particle-Argon-v1" + +#define INDEX_URL "https://www.particle.io/mesh/" + +#endif // _PARTICLE_ARGON_H diff --git a/src/boards/particle_boron.h b/src/boards/particle_boron.h new file mode 100644 index 0000000..aa5e545 --- /dev/null +++ b/src/boards/particle_boron.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/*! + @file particle_boron.h + @author Scott Shawcroft + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Scott Shawcroft for Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _PARTICLE_BORON_H +#define _PARTICLE_BORON_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(1, 12) +#define LED_STATE_ON 0 + +#define LED_RGB_RED_PIN _PINNUM(0, 13) +#define LED_RGB_GREEN_PIN _PINNUM(0, 14) +#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTONS_NUMBER 2 +#define BUTTON_DFU _PINNUM(0, 11) +#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false + +#define OUTPUT_500HZ_PIN _PINNUM(0, 04) // A1 + +// Used as model string in OTA mode +#define DIS_MANUFACTURER "Particle Industries" +#define DIS_MODEL "Boron" + +#define VOLUME_LABEL "BORONBOOT " + +#define BOARD_ID "Particle-Boron-v1" + +#define INDEX_URL "https://www.particle.io/mesh/" + +#endif // _PARTICLE_BORON_H diff --git a/src/boards/particle_xenon.h b/src/boards/particle_xenon.h new file mode 100644 index 0000000..8510dc1 --- /dev/null +++ b/src/boards/particle_xenon.h @@ -0,0 +1,77 @@ +/**************************************************************************/ +/*! + @file particle_boron.h + @author Scott Shawcroft + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Scott Shawcroft for Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _PARTICLE_XENON_H +#define _PARTICLE_XENON_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_RED _PINNUM(0, 13) +#define LED_BLUE _PINNUM(0, 15) +#define LED_STATE_ON 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTONS_NUMBER 2 +#define BUTTON_DFU _PINNUM(0, 11) +#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false + +// Used as model string in OTA mode +#define DIS_MANUFACTURER "Particle Industries" +#define DIS_MODEL "Xenon" + +#define VOLUME_LABEL "XENONBOOT " + +#define BOARD_ID "Particle-Xenon-v1" + +#define INDEX_URL "https://www.particle.io/mesh/" + +#endif // _PARTICLE_XENON_H diff --git a/src/boards/pca10056.h b/src/boards/pca10056.h index 9c05134..c49ba36 100644 --- a/src/boards/pca10056.h +++ b/src/boards/pca10056.h @@ -41,8 +41,8 @@ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 2 -#define LED_1 13 -#define LED_2 14 +#define LED_PRIMARY_PIN 13 +#define LED_SECONDARY_PIN 14 #define LED_STATE_ON 0 /*------------------------------------------------------------------*/ diff --git a/src/boards/pca10059.h b/src/boards/pca10059.h index 97b775c..cfa3cb3 100644 --- a/src/boards/pca10059.h +++ b/src/boards/pca10059.h @@ -40,12 +40,10 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -// LED_RED -#define LED_1 6 -// LED_BLUE -#define LED_2 12 -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 6 // Red +#define LED_SECONDARY_PIN 12 // Blue +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON diff --git a/src/main.c b/src/main.c index 02be46c..d9c9bf5 100644 --- a/src/main.c +++ b/src/main.c @@ -170,12 +170,12 @@ int main(void) // When updating SoftDevice, bootloader will reset before swapping SD if (bootloader_dfu_sd_in_progress()) { - led_red_blink_fast(true); + led_state(STATE_WRITING_STARTED); APP_ERROR_CHECK( bootloader_dfu_sd_update_continue() ); APP_ERROR_CHECK( bootloader_dfu_sd_update_finalize() ); - led_red_blink_fast(false); + led_state(STATE_WRITING_FINISHED); } /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ @@ -209,18 +209,19 @@ int main(void) (*dbl_reset_mem) = 0; - if ( dfu_start || !valid_app ) + led_state(STATE_BOOTLOADER_STARTED); + + if ( dfu_start || !valid_app || true) { if ( _ota_dfu ) { - // Enable BLE if in OTA - led_pwm_init(LED_BLUE); - + led_state(STATE_BLE_DISCONNECTED); softdev_init(!sd_inited); sd_inited = true; } else { + led_state(STATE_USB_UNMOUNTED); // otherwise USB for Serial & UF2 usb_init(serial_only_dfu); } @@ -230,9 +231,6 @@ int main(void) if ( _ota_dfu ) { - led_pwm_teardown(LED_BLUE); - led_off(LED_BLUE); - sd_softdevice_disable(); }else { @@ -267,9 +265,7 @@ int main(void) // Perform factory reset to erase Application + Data void adafruit_factory_reset(void) { - // Blink fast RED and turn on BLUE when erasing - led_red_blink_fast(true); - led_on(LED_BLUE); + led_state(STATE_FACTORY_RESET_STARTED); // clear all App Data if any if ( DFU_APP_DATA_RESERVED ) @@ -281,8 +277,7 @@ void adafruit_factory_reset(void) nrf_nvmc_page_erase(DFU_BANK_0_REGION_START); // back to normal - led_red_blink_fast(false); - led_off(LED_BLUE); + led_state(STATE_FACTORY_RESET_FINISHED); } /** @@ -394,13 +389,12 @@ uint32_t proc_ble(void) { case BLE_GAP_EVT_CONNECTED: _ota_connected = true; - led_pwm_disable(LED_BLUE); - led_on(LED_BLUE); + led_state(STATE_BLE_CONNECTED); break; case BLE_GAP_EVT_DISCONNECTED: _ota_connected = false; - led_pwm_enable(LED_BLUE); // LED Blink + led_state(STATE_BLE_DISCONNECTED); break; default: break; @@ -455,5 +449,3 @@ void SD_EVT_IRQHandler(void) // Use App Scheduler to defer handling code in non-isr context app_sched_event_put(NULL, 0, ada_sd_task); } - - diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 329c6e2..bea65b6 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -235,7 +235,7 @@ void read_block(uint32_t block_no, uint8_t *data) { /** uf2 upgrade complete -> inform bootloader to update setting and reset */ static void uf2_write_complete(uint32_t numBlocks) { - led_red_blink_fast(false); + led_state(STATE_WRITING_FINISHED); dfu_update_status_t update_status; @@ -285,9 +285,9 @@ int write_block(uint32_t block_no, uint8_t *data, bool quiet/*, WriteState *stat static bool first_write = true; if ( first_write ) { first_write = false; - led_red_blink_fast(true); + led_state(STATE_WRITING_STARTED); } - + flash_nrf5x_write(bl->targetAddr, bl->data, bl->payloadSize, true); } @@ -318,4 +318,3 @@ int write_block(uint32_t block_no, uint8_t *data, bool quiet/*, WriteState *stat return 512; } - diff --git a/src/usb/uf2/uf2cfg.h b/src/usb/uf2/uf2cfg.h index d9ef358..548ebf4 100644 --- a/src/usb/uf2/uf2cfg.h +++ b/src/usb/uf2/uf2cfg.h @@ -1,17 +1,27 @@ +#include "boards.h" + #define UF2_VERSION "1.00" -#ifdef BOARD_PCA10056 +#ifndef PRODUCT_NAME #define PRODUCT_NAME DIS_MODEL -#else - #define PRODUCT_NAME "Adafruit " DIS_MODEL #endif +#ifndef BOARD_ID #define BOARD_ID "NRF52-Bluefruit-v0" -#define INDEX_URL "https://www.adafruit.com/product/0000" +#endif + +#ifndef INDEX_URL +#define INDEX_URL "https://www.adafruit.com/" +#endif + #define BOOTLOADER_ID MK_DIS_FIRMWARE #define UF2_NUM_BLOCKS 8000 // at least 4,1 MB for FAT16 + +#ifndef VOLUME_LABEL #define VOLUME_LABEL "NRF52BOOT " +#endif + #define FLASH_SIZE (USER_FLASH_END-USER_FLASH_START) // Max flash size // Only allow to write application TODO dynamic depending on SD size diff --git a/src/usb/usb.c b/src/usb/usb.c index fe27f57..f49e6c0 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -157,17 +157,10 @@ void usb_teardown(void) //--------------------------------------------------------------------+ void tud_mount_cb(void) { -#ifdef LED_NEOPIXEL - uint8_t grb[3] = { 32, 0, 0 }; - neopixel_write(grb); -#endif + led_state(STATE_USB_MOUNTED); } void tud_umount_cb(void) { -#ifdef LED_NEOPIXEL - uint8_t grb[3] = { 0, 32, 0 }; - neopixel_write(grb); -#endif + led_state(STATE_USB_UNMOUNTED); } - From 1b35dbf6431fa230cc010a95565b9c1953de53ec Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 4 Dec 2018 15:18:49 -0800 Subject: [PATCH 02/23] Add build script and hook it into Travis --- .gitignore | 1 + .travis.yml | 28 + Makefile | 7 +- README.md | 24 +- ...feather_nrf52832_bootloader_s132_2.0.1.zip | Bin 131786 -> 0 bytes ...feather_nrf52832_bootloader_s132_5.0.0.zip | Bin 159962 -> 0 bytes ...ather_nrf52832_bootloader_s132_6.1.0r0.hex | 10826 --------------- ...ather_nrf52832_bootloader_s132_6.1.0r0.zip | Bin 171529 -> 0 bytes ...ather_nrf52832_bootloader_s132_6.1.1r0.hex | 10819 --------------- ...ather_nrf52832_bootloader_s132_6.1.1r0.zip | Bin 171022 -> 0 bytes ...f52840_express_bootloader_s140_6.1.0r0.hex | 11394 --------------- ...f52840_express_bootloader_s140_6.1.0r0.zip | Bin 180454 -> 0 bytes ...f52840_express_bootloader_s140_6.1.1r0.hex | 11416 ---------------- ...f52840_express_bootloader_s140_6.1.1r0.zip | Bin 180578 -> 0 bytes .../pca10056_bootloader_s140_6.1.0r0.hex | 11389 --------------- .../pca10056_bootloader_s140_6.1.0r0.zip | Bin 180362 -> 0 bytes .../pca10056_bootloader_s140_6.1.1r0.hex | 11396 --------------- .../pca10056_bootloader_s140_6.1.1r0.zip | Bin 180246 -> 0 bytes .../pca10059_bootloader_s140_6.1.0r0.hex | 11390 --------------- .../pca10059_bootloader_s140_6.1.0r0.zip | Bin 180386 -> 0 bytes .../pca10059_bootloader_s140_6.1.1r0.hex | 11397 --------------- .../pca10059_bootloader_s140_6.1.1r0.zip | Bin 180269 -> 0 bytes src/boards.c | 28 +- src/boards.h | 6 +- src/boards/feather_nrf52832.h | 2 +- src/boards/feather_nrf52840_express.h | 3 +- src/boards/particle_argon.h | 11 +- src/boards/particle_boron.h | 2 - src/boards/particle_xenon.h | 11 +- src/main.c | 2 +- src/usb/uf2/uf2cfg.h | 2 - tools/build_all.py | 55 + 32 files changed, 128 insertions(+), 90081 deletions(-) create mode 100644 .travis.yml delete mode 100644 bin/feather_nrf52832/2.0.1/feather_nrf52832_bootloader_s132_2.0.1.zip delete mode 100644 bin/feather_nrf52832/5.0.0/feather_nrf52832_bootloader_s132_5.0.0.zip delete mode 100644 bin/feather_nrf52832/6.1.0r0/feather_nrf52832_bootloader_s132_6.1.0r0.hex delete mode 100644 bin/feather_nrf52832/6.1.0r0/feather_nrf52832_bootloader_s132_6.1.0r0.zip delete mode 100644 bin/feather_nrf52832/6.1.1r0/feather_nrf52832_bootloader_s132_6.1.1r0.hex delete mode 100644 bin/feather_nrf52832/6.1.1r0/feather_nrf52832_bootloader_s132_6.1.1r0.zip delete mode 100644 bin/feather_nrf52840_express/6.1.0r0/feather_nrf52840_express_bootloader_s140_6.1.0r0.hex delete mode 100644 bin/feather_nrf52840_express/6.1.0r0/feather_nrf52840_express_bootloader_s140_6.1.0r0.zip delete mode 100644 bin/feather_nrf52840_express/6.1.1r0/feather_nrf52840_express_bootloader_s140_6.1.1r0.hex delete mode 100644 bin/feather_nrf52840_express/6.1.1r0/feather_nrf52840_express_bootloader_s140_6.1.1r0.zip delete mode 100644 bin/pca10056/6.1.0r0/pca10056_bootloader_s140_6.1.0r0.hex delete mode 100644 bin/pca10056/6.1.0r0/pca10056_bootloader_s140_6.1.0r0.zip delete mode 100644 bin/pca10056/6.1.1r0/pca10056_bootloader_s140_6.1.1r0.hex delete mode 100644 bin/pca10056/6.1.1r0/pca10056_bootloader_s140_6.1.1r0.zip delete mode 100644 bin/pca10059/6.1.0r0/pca10059_bootloader_s140_6.1.0r0.hex delete mode 100644 bin/pca10059/6.1.0r0/pca10059_bootloader_s140_6.1.0r0.zip delete mode 100644 bin/pca10059/6.1.1r0/pca10059_bootloader_s140_6.1.1r0.hex delete mode 100644 bin/pca10059/6.1.1r0/pca10059_bootloader_s140_6.1.1r0.zip create mode 100644 tools/build_all.py diff --git a/.gitignore b/.gitignore index d528193..32521d3 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ dkms.conf src/segger/Output _build-*/ +bin/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c644e4c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +sudo: required +dist: xenial +language: c +compiler: + - gcc + +deploy: + provider: releases + api_key: + secure: "HGfSIgVPX8O+Wfs3E1l/qI8tJZ6wwEgFg3EO6E1+6eGZolr+LW+ANpMUTqcKy18VzbnSx+AH5Ktu8bw4XuPQRPaAvEIMDEZSbj//WE/nqo+p+SccxUy+mKK6x/gb4aiWUw8gYebeLNvWkU+JVupRqMkUdFOJJRZiIvp1usX+jNU8vTt07INNi90LZ8I/KvDEkvHJ/2nOBKW519rlp5XM0a5j8Bv+9gt5wgfl/hFW42JA4Gmj/nacZtro/3pYJWAZ4ZNLOsupMDPZGFX/Ndb+TqseBAkCLl2bvBZ5gFg4NvtaSRFZW532EXDmdCthUBLUGjw7V3VWq8shgHSi6Iq/5lL9t2jawkJPfp5JdiTPTLkUIZ8BfwwrbHp2qiTOGQHbN8kkfjtcQopjVdaPvavr4oaUN+ju6JNsTNc47FIcKivMi1TV6IUT4byjIWZ7ioV0X7KEdoD5WspjIYVhi1vwOBOLT0CEDMtPwIisZ3QuQGJ5clDMJ82ZU9LBfffzvBkeQNmMCM5L412FN7LP2lVNL5eafuJvFZS9Bpbp9uUhzHsDbbpLnz8TFa5rIMzKQXf1KbLnKZ7LHsieB2NleUE6OUxltNm1PwmWWxZKEZeRYxZSMgzbAOrl/Jl2F7zCU7RWCFRnvC73vVJAXaFJ3/DYBF21ew8NyOgMrRLOeH0s5rg=" + file: + - $(ls -d1 bin/*/* | tr "\n" ":") + skip_cleanup: true + draft: true + on: + tags: true + +before_script: + - (wget https://s3.amazonaws.com/adafruit-circuit-python/gcc-arm-embedded_7-2018q2-1~xenial1_amd64.deb && sudo dpkg -i gcc-arm-embedded*_amd64.deb) + - pip3 install --user adafruit-nrfutil + + # report some good version numbers to the build + - gcc --version + - arm-none-eabi-gcc --version + - python3 --version + +script: + - python3 tools/build_all.py diff --git a/Makefile b/Makefile index dc07ba0..4e13ce2 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,6 @@ SD_API_PATH = $(SD_PATH)/$(SD_FILENAME)_API SD_HEX = $(SD_PATH)/$(SD_FILENAME)_softdevice.hex LD_FILE = $(SRC_PATH)/linker/$(SD_NAME)_v$(SD_VER1).ld -OUTPUT_FILENAME = $(BOARD)_bootloader MERGED_FNAME = $(OUTPUT_FILENAME)_$(SD_NAME)_$(SD_VERSION_FULL) RELEASE_DIR = bin/$(BOARD)/$(SD_VERSION_FULL) @@ -39,6 +38,10 @@ RELEASE_DIR = bin/$(BOARD)/$(SD_VERSION_FULL) MK_DIS_FIRMWARE = "$(SD_NAME) $(SD_VERSION) r$(SD_VER4)" +GIT_VERSION = $(shell git describe --dirty --always --tags) +GIT_SUBMODULE_VERSIONS = $(shell git submodule status | cut -d' ' -f3,4 | paste -s -d" " -) + +OUTPUT_FILENAME = $(BOARD)_bootloader-$(GIT_VERSION) #****************************************************************************** # Tool configure #****************************************************************************** @@ -265,6 +268,8 @@ CFLAGS += -DFLOAT_ABI_HARD CFLAGS += -DMK_DIS_FIRMWARE='$(MK_DIS_FIRMWARE)' CFLAGS += -DDFU_APP_DATA_RESERVED=7*4096 +CFLAGS += -DUF2_VERSION='"$(GIT_VERSION) $(GIT_SUBMODULE_VERSIONS) $(SD_NAME) $(SD_VERSION) r$(SD_VER4)"' + CFLAGS += -DBOARD_$(shell echo $(BOARD) | tr '[:lower:]' '[:upper:]') ifneq ($(IS_NRF52840),) diff --git a/README.md b/README.md index 49c4ebe..f088c32 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # Adafruit Bluefruit nRF52 Bootloader -This is a CDC/DFU/UF2 bootloader for Adafruit nRF52 boards and Nordic development boards. +This is a CDC/DFU/UF2 bootloader for nRF52 boards. - [Adafruit Feather nRF52832](https://www.adafruit.com/product/3406) - Adafruit Feather nRF52840 Express - Nordic nRF52840DK PCA10056 - Nordic nRF52840DK PCA10059 ("Dongle") +- Particle Argon +- Particle Boron +- Particle Xenon UF2 is an easy-to-use bootloader that appears as a flash drive. You can just copy `.uf2`-format application images to the flash drive to load new firmware. @@ -84,24 +87,7 @@ To upgrade with dfu serial make BOARD=feather_nrf52840_express VERSION=6.1.1r0 dfu-flash ``` -Pre-builtin binaries are in the `bin` directory: - -``` -bin/feather_nrf52832/ - 2.0.1 - 5.0.0 - 6.1.0r0 - -bin/feather_nrf52840_express - 6.1.0r0 - -bin/pca10056: - 6.1.0r0 - -bin/pca10059: - 6.1.0r0 -``` - +Pre-builtin binaries are available on GitHub releases. Note: The bootloader can be downgraded. Since the binary release is a merged version of of both bootloader and the Nordic SoftDevice, you can freely upgrade/downgrade to any version you like. diff --git a/bin/feather_nrf52832/2.0.1/feather_nrf52832_bootloader_s132_2.0.1.zip b/bin/feather_nrf52832/2.0.1/feather_nrf52832_bootloader_s132_2.0.1.zip deleted file mode 100644 index 42a83bb9076380ea8dfa15683c40562367ddae9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131786 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