Skip to content

Commit a5edb33

Browse files
committed
Add D11 bootloader
1 parent 28e4bf4 commit a5edb33

File tree

4 files changed

+308
-0
lines changed

4 files changed

+308
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include <Wire.h>
2+
#include "reset.h"
3+
#include "Sodaq_wdt.h"
4+
5+
#define I2C_ADDRESS 0x09
6+
#define LED_BUILTIN 0
7+
8+
extern uint16_t PAGE_SIZE;
9+
extern uint32_t dest_addr;
10+
extern volatile uint32_t app_start_address;
11+
12+
extern uint32_t *flash_ptr;
13+
volatile uint8_t buffer[64];
14+
volatile int buf_available = 0;
15+
16+
inline void setup_ptrs() __attribute__((always_inline));
17+
inline void erase_all() __attribute__((always_inline));
18+
inline void nvm_write_buffer(uint32_t destination_address, const uint8_t *buffer, uint16_t length) __attribute__((always_inline));
19+
inline void nvm_erase_row(const uint32_t row_address, uint32_t PAGE_SIZE) __attribute__((always_inline));
20+
21+
volatile bool stay_in_bootloader = false;
22+
23+
void requestEvent() {
24+
Wire.write(dest_addr);
25+
}
26+
27+
static volatile uint8_t command;
28+
static volatile uint8_t last_crc;
29+
30+
void receiveEvent(int howMany) {
31+
32+
command = Wire.read();
33+
if (command == 'r') {
34+
stay_in_bootloader = true;
35+
while (Wire.available()) {
36+
Wire.read();
37+
}
38+
return;
39+
}
40+
41+
if (command == 'x') {
42+
digitalWrite(LED_BUILTIN, HIGH);
43+
stay_in_bootloader = false;
44+
}
45+
46+
if (command == 'w') {
47+
last_crc = (uint8_t)Wire.read();
48+
while (Wire.available() && buf_available < 64) {
49+
buffer[buf_available++] = (uint8_t)Wire.read();
50+
}
51+
return;
52+
}
53+
54+
// empty the buffer in case of spurious data
55+
while (Wire.available()) {
56+
Wire.read();
57+
}
58+
}
59+
60+
void setup() {
61+
// put your setup code here, to run once:
62+
Wire.begin(I2C_ADDRESS);
63+
Wire.onRequest(requestEvent);
64+
Wire.onReceive(receiveEvent);
65+
pinMode(LED_BUILTIN, OUTPUT);
66+
pinMode(3, INPUT_PULLUP);
67+
68+
setup_ptrs();
69+
sodaq_wdt_enable(WDT_PERIOD_2X);
70+
71+
int start = millis();
72+
73+
if (system_get_reset_cause() == SYSTEM_RESET_CAUSE_WDT) {
74+
stay_in_bootloader = true;
75+
}
76+
77+
while (millis() - start < 100) {
78+
if (stay_in_bootloader) {
79+
erase_all();
80+
break;
81+
}
82+
}
83+
84+
if (*flash_ptr != 0xFFFFFFFF && !stay_in_bootloader) {
85+
digitalWrite(LED_BUILTIN, HIGH);
86+
boot_app();
87+
}
88+
}
89+
90+
int status = HIGH;
91+
int next = 0;
92+
93+
void loop() {
94+
95+
if ((millis() % 1000 > 500) && ((millis() / 1000) % 2 != status)) {
96+
digitalWrite(LED_BUILTIN, status);
97+
status = (millis() / 1000) % 2;
98+
}
99+
100+
if (sodaq_wdt_flag) {
101+
sodaq_wdt_flag = false;
102+
sodaq_wdt_reset();
103+
}
104+
105+
if (stay_in_bootloader == false) {
106+
NVIC_SystemReset();
107+
}
108+
109+
if (buf_available == 64) {
110+
111+
uint8_t crc = 0;
112+
113+
for (int i = 0; i < 64; i++) {
114+
crc ^= buffer[i];
115+
}
116+
117+
if (crc != last_crc) {
118+
buf_available = 0;
119+
return;
120+
}
121+
122+
noInterrupts();
123+
nvm_write_buffer(dest_addr, (const uint8_t*)buffer, PAGE_SIZE);
124+
dest_addr += PAGE_SIZE;
125+
buf_available = 0;
126+
interrupts();
127+
}
128+
}

extra/D11-Bootloader/nvm.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#define APP_START 0x00001000 //This gives 1536 bytes of bootloader space.
2+
//#define FLASH_SIZE 0x00004000 //This gives 1536 bytes of bootloader space.
3+
4+
/* Target application size can be 15kB */
5+
/* APP_SIZE is the application section size in kB */
6+
/* Change as per APP_START */
7+
#define APP_SIZE 5 //This is how much flash memory is left for the application.
8+
9+
/* Memory pointer for flash memory */
10+
#define NVM_MEMORY ((volatile uint16_t *)FLASH_ADDR)

extra/D11-Bootloader/nvm.ino

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/* Application starts from 1kB memory - Bootloader size is 1kB
2+
Change the address if higher boot size is needed
3+
good site for quick conversions.
4+
http://www.binaryhexconverter.com/hex-to-decimal-converter */
5+
6+
#include "nvm.h"
7+
8+
/** If \c false, a page write command will be issued automatically when the
9+
page buffer is full. */
10+
bool manual_page_write;
11+
12+
uint8_t data_8 = 1;
13+
uint32_t file_size, i, dest_addr;
14+
uint32_t volatile app_start_address;
15+
uint8_t volatile data_from_flash;
16+
uint32_t *flash_ptr;
17+
uint8_t *flash_byte_ptr;
18+
19+
/* Flash page size is 64 bytes */
20+
uint16_t PAGE_SIZE = (8 << NVMCTRL->PARAM.bit.PSZ); //used to read and write to flash.
21+
22+
void erase_all() {
23+
/* erase all */
24+
for (int i = APP_START; i < FLASH_SIZE; i = i + PAGE_SIZE)
25+
{
26+
nvm_erase_row(i, PAGE_SIZE);
27+
}
28+
}
29+
30+
#define SKETCH_START (uint32_t*)(APP_START)
31+
32+
void boot_app() {
33+
// jump to the sketch
34+
__set_MSP(*SKETCH_START);
35+
36+
//Reset vector table address
37+
SCB->VTOR = ((uint32_t)(SKETCH_START) & SCB_VTOR_TBLOFF_Msk);
38+
39+
// address of Reset_Handler is written by the linker at the beginning of the .text section (see linker script)
40+
uint32_t resetHandlerAddress = (uint32_t) * (SKETCH_START + 1);
41+
// jump to reset handler
42+
asm("bx %0"::"r"(resetHandlerAddress));
43+
}
44+
45+
void setup_ptrs()
46+
{
47+
//set values, for flash pointers.
48+
dest_addr = APP_START;
49+
flash_ptr = (uint32_t*)APP_START;
50+
app_start_address = *flash_ptr;
51+
flash_byte_ptr = (uint8_t*)APP_START;
52+
}
53+
54+
void nvm_erase_row(const uint32_t row_address, uint32_t PAGE_SIZE)
55+
{
56+
#if 0
57+
/* Check if the address to erase is not aligned to the start of a row */
58+
if (row_address > ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages))
59+
{
60+
return 0;
61+
}
62+
63+
/* Get a pointer to the module hardware instance */
64+
if (row_address & ((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1))
65+
{
66+
return 0;
67+
}
68+
#endif
69+
70+
/* Check if the module is busy */
71+
while (!NVMCTRL->INTFLAG.bit.READY);
72+
73+
/* Clear error flags */
74+
NVMCTRL->STATUS.reg &= ~NVMCTRL_STATUS_MASK;
75+
76+
while (!(NVMCTRL->INTFLAG.bit.READY));
77+
78+
/* Set address and command */
79+
NVMCTRL->ADDR.reg = (uintptr_t)&NVM_MEMORY[row_address / 4];
80+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
81+
while (!(NVMCTRL->INTFLAG.bit.READY));
82+
83+
}
84+
85+
void nvm_write_buffer(uint32_t destination_address, const uint8_t *buffer, uint16_t length)
86+
{
87+
#if 0
88+
89+
/* Check if the destination address is valid */
90+
if (destination_address >
91+
((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
92+
return 0;
93+
}
94+
95+
/* Check if the write address not aligned to the start of a page */
96+
if (destination_address & (_nvm_dev.page_size - 1)) {
97+
return 0;
98+
}
99+
100+
/* Check if the write length is longer than a NVM page */
101+
if (length > _nvm_dev.page_size) {
102+
return 0;
103+
}
104+
#endif
105+
106+
/* Check if the module is busy */
107+
while (!NVMCTRL->INTFLAG.bit.READY);
108+
109+
//set auto page writes
110+
NVMCTRL->CTRLB.bit.MANW = 0;
111+
112+
/* Erase the page buffer before buffering new data */
113+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_PBC | NVMCTRL_CTRLA_CMDEX_KEY;
114+
115+
/* Check if the module is busy */
116+
while (!NVMCTRL->INTFLAG.bit.READY);
117+
118+
/* Clear error flags */
119+
NVMCTRL->STATUS.reg &= ~NVMCTRL_STATUS_MASK;
120+
121+
uint32_t nvm_address = destination_address / 2;
122+
123+
/* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
124+
to ensure alignment */
125+
for (uint16_t k = 0; k < length; k += 2)
126+
{
127+
uint16_t data;
128+
129+
/* Copy first byte of the 16-bit chunk to the temporary buffer */
130+
data = buffer[k];
131+
132+
/* If we are not at the end of a write request with an odd byte count,
133+
store the next byte of data as well */
134+
if (k < (length - 1)) {
135+
data |= (buffer[k + 1] << 8);
136+
}
137+
/* Store next 16-bit chunk to the NVM memory space */
138+
NVM_MEMORY[nvm_address++] = data;
139+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
140+
141+
}
142+
143+
/* If automatic page write mode is enable, then perform a manual NVM
144+
write when the length of data to be programmed is less than page size
145+
*/
146+
if ((manual_page_write == 0) && (length < NVMCTRL_PAGE_SIZE)) {
147+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
148+
}
149+
150+
while (!NVMCTRL->INTFLAG.bit.READY);
151+
}

extra/D11-Bootloader/reset.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enum system_reset_cause {
2+
/** The system was last reset by a software reset. */
3+
SYSTEM_RESET_CAUSE_SOFTWARE = PM_RCAUSE_SYST,
4+
/** The system was last reset by the watchdog timer. */
5+
SYSTEM_RESET_CAUSE_WDT = PM_RCAUSE_WDT,
6+
/** The system was last reset because the external reset line was pulled low. */
7+
SYSTEM_RESET_CAUSE_EXTERNAL_RESET = PM_RCAUSE_EXT,
8+
/** The system was last reset by the BOD33. */
9+
SYSTEM_RESET_CAUSE_BOD33 = PM_RCAUSE_BOD33,
10+
/** The system was last reset by the BOD12. */
11+
SYSTEM_RESET_CAUSE_BOD12 = PM_RCAUSE_BOD12,
12+
/** The system was last reset by the POR (Power on reset). */
13+
SYSTEM_RESET_CAUSE_POR = PM_RCAUSE_POR,
14+
};
15+
16+
static inline enum system_reset_cause system_get_reset_cause(void)
17+
{
18+
return (enum system_reset_cause)PM->RCAUSE.reg;
19+
}

0 commit comments

Comments
 (0)