Merge pull request #111 from dhalbert/spurious-writes
fix SAMD51 spurious writes; allow gcc9 compilation; improve delay() calibration
This commit is contained in:
commit
b3e13dfbe0
12 changed files with 209 additions and 90 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
build/
|
||||
.vs/
|
||||
uf2-bootloader.elf
|
||||
tmp
|
||||
*.sw?
|
||||
Makefile.user
|
||||
node_modules
|
||||
scripts/clean-ifaces/*.plist
|
||||
.vscode/c_cpp_properties.json
|
||||
TAGS
|
||||
|
|
@ -15,12 +15,12 @@ node_js:
|
|||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/gcc-arm-none-eabi-6-2017-q1-update
|
||||
- $HOME/gcc-arm-none-eabi-9-2019-q4-major
|
||||
|
||||
install:
|
||||
- export GCC_DIR=$HOME/gcc-arm-none-eabi-6-2017-q1-update
|
||||
- export GCC_ARCHIVE=$HOME/gcc-arm-none-eabi-6-2017-q1-update-linux.tar.bz2
|
||||
- export GCC_URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/6_1-2017q1/gcc-arm-none-eabi-6-2017-q1-update-linux.tar.bz2?product=GNU%20ARM%20Embedded%20Toolchain,64-bit,,Linux,6-2017-q1-update
|
||||
- export GCC_DIR=$HOME/gcc-arm-none-eabi-9-2019-q4-major
|
||||
- export GCC_ARCHIVE=$HOME/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
|
||||
- export GCC_URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
|
||||
- if [ ! -e $GCC_DIR/bin/arm-none-eabi-g++ ]; then wget $GCC_URL -O $GCC_ARCHIVE; tar xfj $GCC_ARCHIVE -C $HOME; fi
|
||||
- export PATH=$PATH:$GCC_DIR/bin
|
||||
|
||||
|
|
|
|||
3
Makefile
3
Makefile
|
|
@ -207,8 +207,11 @@ drop-board: all
|
|||
mkdir -p build/drop/$(BOARD)
|
||||
cp $(SELF_EXECUTABLE) build/drop/$(BOARD)/
|
||||
cp $(EXECUTABLE) build/drop/$(BOARD)/
|
||||
# .ino works only for SAMD21 right now; suppress for SAMD51
|
||||
ifeq ($(CHIP_FAMILY),samd21)
|
||||
cp $(SELF_EXECUTABLE_INO) build/drop/$(BOARD)/
|
||||
cp boards/$(BOARD)/board_config.h build/drop/$(BOARD)/
|
||||
endif
|
||||
|
||||
drop-pkg:
|
||||
mv build/drop build/uf2-samd21-$(UF2_VERSION_BASE)
|
||||
|
|
|
|||
|
|
@ -260,6 +260,8 @@ void padded_memcpy(char *dst, const char *src, int len);
|
|||
|
||||
void resetIntoApp(void);
|
||||
void resetIntoBootloader(void);
|
||||
extern uint32_t current_cpu_frequency_MHz;
|
||||
extern volatile bool led_tick_on;
|
||||
void system_init(void);
|
||||
|
||||
#define LED_TICK led_tick
|
||||
|
|
@ -288,6 +290,12 @@ void delay(uint32_t ms);
|
|||
void hidHandoverLoop(int ep);
|
||||
void handoverPrep(void);
|
||||
|
||||
// Useful for debugging.
|
||||
#ifdef BLINK_DEBUG
|
||||
void blink_n(uint32_t pin, uint32_t n, uint32_t interval);
|
||||
void blink_n_forever(uint32_t pin, uint32_t n, uint32_t interval);
|
||||
#endif
|
||||
|
||||
#define CONCAT_1(a, b) a##b
|
||||
#define CONCAT_0(a, b) CONCAT_1(a, b)
|
||||
#define STATIC_ASSERT(e) enum { CONCAT_0(_static_assert_, __LINE__) = 1 / ((e) ? 1 : 0) }
|
||||
|
|
|
|||
|
|
@ -453,7 +453,8 @@ __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void)
|
|||
*/
|
||||
__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack)
|
||||
{
|
||||
__ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp");
|
||||
// : "sp" removed from clobber list to avoid gcc 9 warning.
|
||||
__ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ selfdata_c_path = os.path.join(os.path.dirname(bin_name), "selfdata.c")
|
|||
with open(selfdata_c_path, "w") as output:
|
||||
output.write("#include <stdint.h>\n")
|
||||
output.write("const uint8_t bootloader[{}] ".format(bootloader_size) +
|
||||
"__attribute__ ((aligned (4))) = {")
|
||||
"__attribute__ ((aligned (4))) = {\n")
|
||||
crcs = []
|
||||
crc = 0
|
||||
for row in range(bootloader_size / 16):
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
volatile bool g_interrupt_enabled = true;
|
||||
|
||||
// SAMD21 starts at 1MHz by default.
|
||||
uint32_t current_cpu_frequency_MHz = 1;
|
||||
|
||||
static void gclk_sync(void) {
|
||||
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
|
||||
;
|
||||
|
|
@ -125,6 +128,9 @@ void system_init(void) {
|
|||
// PORT->Group[0].PINCFG[30].bit.PMUXEN = 1;
|
||||
// Set the port mux mask for odd processor pin numbers, PA30 = 30 is even number, PMUXE = PMUX Even
|
||||
// PORT->Group[0].PMUX[30 / 2].reg |= PORT_PMUX_PMUXE_H;
|
||||
|
||||
current_cpu_frequency_MHz = 48;
|
||||
|
||||
}
|
||||
|
||||
void SysTick_Handler(void) { LED_TICK(); }
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
#include "uf2.h"
|
||||
|
||||
// SAMD51 starts at 48MHz by default.
|
||||
uint32_t current_cpu_frequency_MHz = 48;
|
||||
|
||||
void system_init(void) {
|
||||
/* Set 1 Flash Wait State for 48MHz */
|
||||
NVMCTRL->CTRLA.reg |= NVMCTRL_CTRLA_RWS(0);
|
||||
// Automatic wait states.
|
||||
NVMCTRL->CTRLA.bit.AUTOWS = 1;
|
||||
|
||||
// Output GCLK0 to Metro M4 D5. This way we can see if/when we mess it up.
|
||||
//PORT->Group[1].PINCFG[14].bit.PMUXEN = true;
|
||||
//PORT->Group[1].PMUX[7].bit.PMUXE = 12;
|
||||
// PORT->Group[1].PINCFG[14].bit.PMUXEN = true;
|
||||
// PORT->Group[1].PMUX[7].bit.PMUXE = 12;
|
||||
|
||||
/* Software reset the module to ensure it is re-initialized correctly */
|
||||
/* Note: Due to synchronization, there is a delay from writing CTRL.SWRST until the reset is complete.
|
||||
|
|
@ -79,6 +82,8 @@ void system_init(void) {
|
|||
MCLK->CPUDIV.reg = MCLK_CPUDIV_DIV_DIV1;
|
||||
|
||||
SysTick_Config(1000);
|
||||
// No change from initial frequency.
|
||||
// current_cpu_frequency_MHz = 48;
|
||||
}
|
||||
|
||||
void SysTick_Handler(void) { LED_TICK(); }
|
||||
|
|
|
|||
46
src/main.c
46
src/main.c
|
|
@ -29,11 +29,11 @@
|
|||
|
||||
/**
|
||||
* --------------------
|
||||
* SAM-BA Implementation on SAMD21
|
||||
* SAM-BA Implementation on SAMD21 and SAMD51
|
||||
* --------------------
|
||||
* Requirements to use SAM-BA :
|
||||
*
|
||||
* Supported communication interfaces :
|
||||
* Supported communication interfaces (SAMD21):
|
||||
* --------------------
|
||||
*
|
||||
* SERCOM5 : RX:PB23 TX:PB22
|
||||
|
|
@ -68,8 +68,6 @@
|
|||
*
|
||||
* Applications compiled to be executed along with the bootloader will start at
|
||||
* 0x2000 (samd21) or 0x4000 (samd51)
|
||||
* The bootloader doesn't changes the VTOR register, application code is
|
||||
* taking care of this.
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
@ -110,7 +108,7 @@ static void check_start_application(void) {
|
|||
if (RESET_CONTROLLER->RCAUSE.bit.POR || *DBL_TAP_PTR != DBL_TAP_MAGIC_QUICK_BOOT) {
|
||||
// the second tap on reset will go into app
|
||||
*DBL_TAP_PTR = DBL_TAP_MAGIC_QUICK_BOOT;
|
||||
// this will be cleared after succesful USB enumeration
|
||||
// this will be cleared after successful USB enumeration
|
||||
// this is around 1.5s
|
||||
resetHorizon = timerHigh + 50;
|
||||
return;
|
||||
|
|
@ -150,7 +148,7 @@ extern char _etext;
|
|||
extern char _end;
|
||||
|
||||
/**
|
||||
* \brief SAMD21 SAM-BA Main loop.
|
||||
* \brief SAM-BA Main loop.
|
||||
* \return Unused (ANSI-C compatibility).
|
||||
*/
|
||||
int main(void) {
|
||||
|
|
@ -166,6 +164,42 @@ int main(void) {
|
|||
#elif defined(SAMD51)
|
||||
WDT->CTRLA.reg = 0;
|
||||
while(WDT->SYNCBUSY.reg) {}
|
||||
|
||||
// Enable 2.7V brownout detection. The default fuse value is 1.7
|
||||
// Set brownout detection to ~2.7V. Default from factory is 1.7V,
|
||||
// which is too low for proper operation of external SPI flash chips (they are 2.7-3.6V).
|
||||
// Also without this higher level, the SAMD51 will write zeros to flash intermittently.
|
||||
// Disable while changing level.
|
||||
|
||||
SUPC->BOD33.bit.ENABLE = 0;
|
||||
while (!SUPC->STATUS.bit.B33SRDY) {} // Wait for BOD33 to synchronize.
|
||||
SUPC->BOD33.bit.LEVEL = 200; // 2.7V: 1.5V + LEVEL * 6mV.
|
||||
// Don't reset right now.
|
||||
SUPC->BOD33.bit.ACTION = SUPC_BOD33_ACTION_NONE_Val;
|
||||
SUPC->BOD33.bit.ENABLE = 1; // enable brown-out detection
|
||||
|
||||
// Wait for BOD33 peripheral to be ready.
|
||||
while (!SUPC->STATUS.bit.BOD33RDY) {}
|
||||
|
||||
// Wait for voltage to rise above BOD33 value.
|
||||
while (SUPC->STATUS.bit.BOD33DET) {}
|
||||
|
||||
// If we are starting from a power-on or a brownout,
|
||||
// wait for the voltage to stabilize. Don't do this on an
|
||||
// external reset because it interferes with the timing of double-click.
|
||||
// "BODVDD" means BOD33.
|
||||
if (RSTC->RCAUSE.bit.POR || RSTC->RCAUSE.bit.BODVDD) {
|
||||
do {
|
||||
// Check again in 100ms.
|
||||
delay(100);
|
||||
} while (SUPC->STATUS.bit.BOD33DET);
|
||||
}
|
||||
|
||||
// Now enable reset if voltage falls below minimum.
|
||||
SUPC->BOD33.bit.ENABLE = 0;
|
||||
while (!SUPC->STATUS.bit.B33SRDY) {} // Wait for BOD33 to synchronize.
|
||||
SUPC->BOD33.bit.ACTION = SUPC_BOD33_ACTION_RESET_Val;
|
||||
SUPC->BOD33.bit.ENABLE = 1;
|
||||
#endif
|
||||
|
||||
#if USB_VID == 0x239a && USB_PID == 0x0013 // Adafruit Metro M0
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ uint8_t pageBuf[FLASH_ROW_SIZE];
|
|||
|
||||
void setBootProt(int v) {
|
||||
#if defined(SAMD21)
|
||||
uint32_t fuses[2],
|
||||
uint32_t fuses[2],
|
||||
newfuses[2];
|
||||
while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)) {
|
||||
}
|
||||
|
|
|
|||
154
src/sketch.cpp
154
src/sketch.cpp
|
|
@ -1,5 +1,16 @@
|
|||
#if defined(__SAMD51__)
|
||||
#error "update_bootloader*.ino is not available for SAMD51 boards"
|
||||
#endif
|
||||
|
||||
#define BOOTLOADER_K 8
|
||||
|
||||
// Error indications:
|
||||
// 2 quick flashes repeated forever: Flash page size wrong
|
||||
// 3: checksum error
|
||||
// 4: write verify failed
|
||||
//
|
||||
// Success: 5 slower flashes, then switch to BOOT drive
|
||||
|
||||
static uint16_t crcCache[256];
|
||||
|
||||
#define CRC16POLY 0x1021
|
||||
|
|
@ -23,8 +34,6 @@ uint16_t add_crc(uint8_t ch, unsigned short crc0) {
|
|||
return ((crc0 << 8) ^ crcCache[((crc0 >> 8) ^ ch) & 0xff]) & 0xffff;
|
||||
}
|
||||
|
||||
uint8_t pageBuf[FLASH_ROW_SIZE];
|
||||
|
||||
#define NVM_USER_MEMORY ((volatile uint16_t *)NVMCTRL_USER)
|
||||
|
||||
static inline void wait_ready(void) {
|
||||
|
|
@ -32,37 +41,35 @@ static inline void wait_ready(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void flash_erase_row(uint32_t *dst) {
|
||||
wait_ready();
|
||||
NVMCTRL->STATUS.reg = NVMCTRL_STATUS_MASK;
|
||||
|
||||
// Execute "ER" Erase Row
|
||||
NVMCTRL->ADDR.reg = (uint32_t)dst / 2;
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
||||
void exec_cmd(uint32_t cmd, const uint32_t *addr) {
|
||||
NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;
|
||||
NVMCTRL->ADDR.reg = (uint32_t)addr / 2;
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd;
|
||||
wait_ready();
|
||||
}
|
||||
|
||||
void flash_write_words(uint32_t *dst, uint32_t *src, uint32_t n_words) {
|
||||
// Set automatic page write
|
||||
NVMCTRL->CTRLB.bit.MANW = 0;
|
||||
void flash_erase_row(uint32_t *dst) {
|
||||
wait_ready();
|
||||
// Execute "ER" Erase Row
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_ER, dst);
|
||||
}
|
||||
|
||||
void flash_write_words(uint32_t *dst, uint32_t *src, uint32_t n_words) {
|
||||
while (n_words > 0) {
|
||||
uint32_t len = min(FLASH_PAGE_SIZE >> 2, n_words);
|
||||
n_words -= len;
|
||||
|
||||
// Execute "PBC" Page Buffer Clear
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
|
||||
wait_ready();
|
||||
const uint32_t* dst_start = dst;
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_PBC, dst);
|
||||
|
||||
// make sure there are no other memory writes here
|
||||
// otherwise we get lock-ups
|
||||
|
||||
while (len--)
|
||||
// Write data to page buffer.
|
||||
while (len--) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
// Execute "WP" Write Page
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
|
||||
wait_ready();
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_WP, dst_start);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,100 +78,115 @@ void flash_write_row(uint32_t *dst, uint32_t *src) {
|
|||
flash_write_words(dst, src, FLASH_ROW_SIZE / 4);
|
||||
}
|
||||
|
||||
#define exec_cmd(cmd) \
|
||||
do { \
|
||||
NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; \
|
||||
NVMCTRL->ADDR.reg = (uint32_t)NVMCTRL_USER / 2; \
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd; \
|
||||
while (NVMCTRL->INTFLAG.bit.READY == 0) \
|
||||
; \
|
||||
} while (0)
|
||||
|
||||
void setBootProt(int v) {
|
||||
uint32_t *NVM_FUSES = (uint32_t *)NVMCTRL_AUX0_ADDRESS;
|
||||
|
||||
uint32_t fuses[2];
|
||||
|
||||
while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY))
|
||||
;
|
||||
|
||||
fuses[0] = *((uint32_t *)NVMCTRL_AUX0_ADDRESS);
|
||||
fuses[1] = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1);
|
||||
fuses[0] = NVM_FUSES[0];
|
||||
fuses[1] = NVM_FUSES[1];
|
||||
|
||||
uint32_t bootprot = (fuses[0] & NVMCTRL_FUSES_BOOTPROT_Msk) >> NVMCTRL_FUSES_BOOTPROT_Pos;
|
||||
|
||||
if (bootprot == v)
|
||||
if (bootprot == v) {
|
||||
return;
|
||||
}
|
||||
|
||||
fuses[0] = (fuses[0] & ~NVMCTRL_FUSES_BOOTPROT_Msk) | (v << NVMCTRL_FUSES_BOOTPROT_Pos);
|
||||
|
||||
NVMCTRL->CTRLB.reg = NVMCTRL->CTRLB.reg | NVMCTRL_CTRLB_CACHEDIS | NVMCTRL_CTRLB_MANW;
|
||||
wait_ready();
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_EAR, (uint32_t *)NVMCTRL_USER);
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_PBC, (uint32_t *)NVMCTRL_USER);
|
||||
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_EAR);
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_PBC);
|
||||
NVM_FUSES[0] = fuses[0];
|
||||
NVM_FUSES[1] = fuses[1];
|
||||
|
||||
*((uint32_t *)NVMCTRL_AUX0_ADDRESS) = fuses[0];
|
||||
*(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = fuses[1];
|
||||
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_WAP);
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_WAP, (uint32_t *)NVMCTRL_USER);
|
||||
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void mydelay(int ms) {
|
||||
// We can't use regular loop_delay() because it uses interrupts, which we turn off.
|
||||
void loop_delay(int ms) {
|
||||
ms <<= 13;
|
||||
while (ms--) {
|
||||
asm("nop");
|
||||
}
|
||||
}
|
||||
|
||||
void blink_n(int n, int interval) {
|
||||
// Start out off.
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
loop_delay(interval);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
loop_delay(interval);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
loop_delay(interval);
|
||||
}
|
||||
}
|
||||
|
||||
void blink_n_forever(int n, int interval) {
|
||||
while(1) {
|
||||
blink_n(n, interval);
|
||||
loop_delay(interval*5);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
|
||||
if (8 << NVMCTRL->PARAM.bit.PSZ != FLASH_PAGE_SIZE)
|
||||
while (1) {
|
||||
}
|
||||
if (8 << NVMCTRL->PARAM.bit.PSZ != FLASH_PAGE_SIZE) {
|
||||
blink_n_forever(2, 200);
|
||||
}
|
||||
|
||||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
NVMCTRL->CTRLB.bit.CACHEDIS = 1;
|
||||
|
||||
__disable_irq();
|
||||
|
||||
setBootProt(7); // 0k
|
||||
// This will cause a reset and re-enter the program if a change was necessary.
|
||||
// If no change was necessary we'll fall through.
|
||||
setBootProt(7); // 0kB; disable BOOTPROT while writing.
|
||||
|
||||
const uint8_t *ptr = bootloader;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BOOTLOADER_K; ++i) {
|
||||
for (int i = 0; i < BOOTLOADER_K; ++i) {
|
||||
int crc = 0;
|
||||
for (int j = 0; j < 1024; ++j) {
|
||||
crc = add_crc(*ptr++, crc);
|
||||
}
|
||||
if (bootloader_crcs[i] != crc) {
|
||||
while (1) {
|
||||
}
|
||||
blink_n_forever(3, 200);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < BOOTLOADER_K * 1024; i += FLASH_ROW_SIZE) {
|
||||
memcpy(pageBuf, &bootloader[i], FLASH_ROW_SIZE);
|
||||
flash_write_row((uint32_t *)(void *)i, (uint32_t *)(void *)pageBuf);
|
||||
// Writing starts at 0x0, so flash_addr can be used as an index into bootloader[].
|
||||
for (int flash_addr = 0; flash_addr < BOOTLOADER_K * 1024; flash_addr += FLASH_ROW_SIZE) {
|
||||
flash_write_row((uint32_t *)flash_addr, (uint32_t *)&bootloader[flash_addr]);
|
||||
if (memcmp((const void *)flash_addr, &bootloader[flash_addr], FLASH_ROW_SIZE) != 0) {
|
||||
// Write verify failed.
|
||||
blink_n_forever(4, 200);
|
||||
}
|
||||
}
|
||||
|
||||
// re-base int vector back to bootloader, so that the flash erase below doesn't write over the
|
||||
// vectors
|
||||
SCB->VTOR = 0;
|
||||
|
||||
// erase first row of this updater app, so the bootloader doesn't start us again
|
||||
flash_erase_row((uint32_t *)(void *)(BOOTLOADER_K * 1024));
|
||||
blink_n(5, 750);
|
||||
|
||||
for (i = 0; i < 5; ++i) {
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
mydelay(100);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
mydelay(200);
|
||||
}
|
||||
// Write zeros to the stack location and reset handler location so the
|
||||
// bootloader doesn't run us a second time. We don't need to erase to write
|
||||
// zeros. The remainder of the write unit will be set to 1s which should
|
||||
// preserve the existing values but it's not critical.
|
||||
uint32_t zeros[2] = {0, 0};
|
||||
flash_write_words((uint32_t *)(BOOTLOADER_K * 1024), zeros, 2);
|
||||
|
||||
setBootProt(2); // 8k
|
||||
setBootProt(2); // 8kB
|
||||
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
void loop() {
|
||||
}
|
||||
|
|
|
|||
44
src/utils.c
44
src/utils.c
|
|
@ -4,20 +4,48 @@
|
|||
static uint32_t timerLow;
|
||||
uint32_t timerHigh, resetHorizon;
|
||||
|
||||
void delay(uint32_t ms) {
|
||||
// SAMD21 starts up at 1mhz by default.
|
||||
void __attribute__ ((noinline)) delay(uint32_t ms) {
|
||||
// These multipliers were determined empirically and are only approximate.
|
||||
// After the pulsing LED is enabled (led_tick_on), the multipliers need to change
|
||||
// due to the interrupt overhead of the pulsing.
|
||||
// SAMD21 starts up at 1MHz by default.
|
||||
#ifdef SAMD21
|
||||
ms <<= 8;
|
||||
uint32_t count = ms * (current_cpu_frequency_MHz) * (led_tick_on ? 149: 167);
|
||||
#endif
|
||||
// SAMD51 starts up at 48mhz by default.
|
||||
#ifdef SAMD51
|
||||
ms <<= 12;
|
||||
// SAMD51 starts up at 48MHz by default, and we set the clock to
|
||||
// 48MHz, so we don't need to adjust for current_cpu_frequency_MHz.
|
||||
uint32_t count = ms * (led_tick_on ? 6353 : 6826);
|
||||
#endif
|
||||
for (int i = 1; i < ms; ++i) {
|
||||
asm("nop");
|
||||
for (uint32_t i = 1; i < count; ++i) {
|
||||
asm volatile("");
|
||||
}
|
||||
}
|
||||
|
||||
// Useful for debugging.
|
||||
// PIN_PA19 is D12 on Metro M0, D11 on Metro M4
|
||||
#ifdef BLINK_DEBUG
|
||||
void blink_n(uint32_t pin, uint32_t n, uint32_t interval) {
|
||||
// Start out off.
|
||||
PINOP(pin, DIRSET);
|
||||
PINOP(pin, OUTCLR);
|
||||
delay(interval);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
PINOP(pin, OUTSET);
|
||||
delay(interval);
|
||||
PINOP(pin, OUTCLR);
|
||||
delay(interval);
|
||||
}
|
||||
}
|
||||
|
||||
void blink_n_forever(uint32_t pin, uint32_t n, uint32_t interval) {
|
||||
while(1) {
|
||||
blink_n(pin, n, interval);
|
||||
delay(interval*5);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void timerTick(void) {
|
||||
if (timerLow-- == 0) {
|
||||
timerLow = TIMER_STEP;
|
||||
|
|
@ -108,9 +136,11 @@ void logval(const char *lbl, uint32_t v) {
|
|||
static uint32_t now;
|
||||
static uint32_t signal_end;
|
||||
int8_t led_tick_step = 1;
|
||||
volatile bool led_tick_on = false;
|
||||
static uint8_t limit = 200;
|
||||
|
||||
void led_tick() {
|
||||
led_tick_on = true;
|
||||
now++;
|
||||
if (signal_end) {
|
||||
if (now == signal_end - 1000) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue