forked from balrog-kun/lights
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathoptiboot-nrf24l01-flasher.ino
259 lines (206 loc) · 5.1 KB
/
optiboot-nrf24l01-flasher.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#define FLASH_TOOL_MODE
#define MAX_PKT_SIZE 32
#define SEQN
static void flasher_setup(void);
static void flasher_rx_handle(void);
static void flasher_tx_handle(void);
/*
* Passthrough between UART and an nRF24.
*
* Licensed under AGPLv3.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <avr/wdt.h>
#include "timer1.h"
#include "uart.h"
/* Extra pins configuration for nRF24L01. Here :
* - CE = PIN9 (PORTB1)
* - CSN = PIN10 (PORTB2)
*/
#define CE_DDR DDRB
#define CE_PORT PORTB
#define CSN_DDR DDRB
#define CSN_PORT PORTB
#define CE_PIN (1 << 1)
#define CSN_PIN (1 << 2)
#include "spi.h"
#include "nrf24.h"
#ifndef MAX_PKT_SIZE
# define MAX_PKT_SIZE 32
#endif
#define FIFO_MASK 255
static struct ring_buffer_s {
uint8_t data[FIFO_MASK + 1];
uint8_t start, len;
} tx_fifo;
static void handle_input(char ch) {
tx_fifo.data[(tx_fifo.start + tx_fifo.len ++) & FIFO_MASK] = ch;
}
#define min(a, b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
static uint8_t eeprom_read(uint16_t addr) {
while (EECR & (1 << EEPE));
EEAR = addr;
EECR |= 1 << EERE; /* Start eeprom read by writing EERE */
return EEDR;
}
void setup(void) {
uint8_t s = SREG;
uint8_t m = MCUCR;
uint8_t i, addrs[6];
wdt_disable();
serial_init();
timer_init();
spi_init();
nrf24_init();
sei();
/*
* Set our radio address and the remote end's radio address, read
* the addresses from EEPROM where they need to be saved first.
*/
for (i = 0; i < 6; i ++)
addrs[i] = eeprom_read(i);
nrf24_set_rx_addr(addrs + 0);
nrf24_set_tx_addr(addrs + 3);
/* Write something to say hello */
serial_write_str("SREG:");
serial_write_hex16(s);
serial_write_str(", MCUCR:");
serial_write_hex16(m);
serial_write_str(", our addr: ");
serial_write1(addrs[0]);
serial_write1(addrs[1]);
serial_write1(addrs[2]);
serial_write_eol();
nrf24_rx_mode();
serial_set_handler(handle_input);
}
void loop(void) {
static uint8_t tx_cnt; /* Consecutive Tx packets counter */
/*
* Note: all nrf24 calls are serialised in this function so as
* to avoid any concurrency issues.
*/
if (nrf24_rx_fifo_data()) {
uint8_t pkt_len, pkt_buf[32], i;
#ifdef SEQN
static uint8_t seqn = 0xff;
#endif
#ifdef FLASH_TOOL_MODE
flasher_rx_handle();
#endif
nrf24_rx_read(pkt_buf, &pkt_len);
#ifdef SEQN
if (pkt_buf[0] != seqn) {
seqn = pkt_buf[0];
for (i = 1; i < pkt_len; i ++)
serial_write1(pkt_buf[i]);
}
#else
for (i = 0; i < pkt_len; i ++)
serial_write1(pkt_buf[i]);
#endif
tx_cnt = 0;
}
if (tx_fifo.len) { /* .len access should be atomic */
uint8_t pkt_len, pkt_buf[MAX_PKT_SIZE], split;
#ifdef SEQN
static uint8_t seqn = 0x00;
uint8_t count = 128;
#define MAX_PLD_SIZE (MAX_PKT_SIZE - 1)
pkt_buf[0] = seqn ++;
#else
uint8_t count = 2;
#define MAX_PLD_SIZE MAX_PKT_SIZE
#endif
#ifdef FLASH_TOOL_MODE
flasher_tx_handle();
#endif
tx_cnt ++;
cli();
pkt_len = min(tx_fifo.len, MAX_PLD_SIZE);
sei();
/* HACK */
if (tx_cnt == 2 && MAX_PLD_SIZE > 2 && pkt_len == MAX_PLD_SIZE)
pkt_len = MAX_PLD_SIZE - 2;
else if (MAX_PLD_SIZE > 2 && tx_cnt == 3)
pkt_len = 1;
split = min(pkt_len,
(uint16_t) (~tx_fifo.start & FIFO_MASK) + 1);
#define START (MAX_PKT_SIZE - MAX_PLD_SIZE)
memcpy(pkt_buf + START, tx_fifo.data +
(tx_fifo.start & FIFO_MASK), split);
memcpy(pkt_buf + START + split, tx_fifo.data, pkt_len - split);
/*
* Or we could just do pkt_buf = tx_fifo.data + ...;
* pkt_len = split;
*/
cli();
tx_fifo.len -= pkt_len;
tx_fifo.start += pkt_len;
sei();
while (-- count) {
/* Don't flood the remote end with the comms */
my_delay(4);
nrf24_tx(pkt_buf, pkt_len + START);
if (!nrf24_tx_result_wait())
break;
}
}
}
int main(void) {
setup();
#ifdef FLASH_TOOL_MODE
flasher_setup();
#endif
for (;;)
loop();
return 0;
}
static void flasher_setup(void) {
}
static uint32_t prev_txrx_ts = 0;
static void flasher_rx_handle(void) {
prev_txrx_ts = timer_read();
}
static void flasher_tx_handle(void) {
static uint8_t first_tx = 1;
/*
* If more than a second has passed since previous communication
* the bootloader will have left the flash mode by now so this is
* probably a new boot and a new flashing attempt.
*/
if ((uint32_t) (timer_read() - prev_txrx_ts) > F_CPU)
first_tx = 1;
/*
* Before any actual STK500v2 communication begins we need to
* attempt to reset the board to start the bootloader, and send it
* our radio address to return the ACK packets to.
*/
if (first_tx) {
uint8_t ch, pkt[4];
/*
* Our protocol requires any program running on the board
* to reset it if it receives a 0xff byte.
*/
ch = 0xff;
nrf24_tx(&ch, 1);
nrf24_tx_result_wait();
/* Give the board time to reboot and enter the bootloader */
my_delay(100);
/* Finally send our address + pkt size as a separate packet */
pkt[0] = eeprom_read(0);
pkt[1] = eeprom_read(1);
pkt[2] = eeprom_read(2);
pkt[3] = MAX_PKT_SIZE;
/* TODO: set the no-ACK bit, the remote board can't ACK yet */
nrf24_tx(pkt, 4);
nrf24_tx_result_wait();
first_tx = 0;
}
prev_txrx_ts = timer_read();
}