This repository has been archived by the owner on Jul 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpineblaster.pde
314 lines (287 loc) · 10.3 KB
/
pineblaster.pde
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
#######################################################################
# #
# pineblaster.pde #
# #
# Copyright 2013, Christopher Billington #
# #
# This file is used to flash a Digilent ChipKIT Max32 microcontroller #
# prototyping board to create a PineBlaster (see readme.txt and #
# http://hardware.labscriptsuite.org). #
# This file is licensed under the Simplified BSD License. #
# See the license.txt file for the full license. #
# #
#######################################################################
*/
#include <plib.h>
//#include <SPI.h>
//#include <Ethernet.h>
//byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
//byte ip[] = { 192,168,1, 177 };
//Server server(8000);
const unsigned int max_instructions = 15000;
int autostart;
volatile int reset_on_serial = 0;
unsigned int instructions[2*max_instructions + 2];
volatile unsigned int * resume_address = instructions;
char readstring[256] = "";
void __attribute__((naked, at_vector(3), nomips16)) ExtInt0Handler(void){
// This interrupt is called when a hardware trigger goes high to start the run.
// set Status to acknowledge that we're starting the interrupt handler:
asm volatile ("mtc0 $k0, $12\n\t");
// set IPC0 so as to disable this interrupt from occuring again:
asm volatile ("sw $zero, 0($t6)\n\t");
// Write to IFSO to indicate that the interrupt has been handled:
asm volatile ("sw $v1, 0($v0)\n\t");
// set Status to indicate the end of the interrupt handler:
asm volatile ("mtc0 $k1, $12\n\t");
// return:
asm volatile ("eret\n\t");
}
void __attribute__((naked, at_vector(24), nomips16)) IntSer0Handler(void){
// This interrupt is called whenever serial communication arrives. We
// intercept it and decide whether to treat it as ordinary serial communication,
// or as an abort signal (for when the sequence is running). In the case of an abort signal,
// we can reset the CPU.
// Load in the address of reset_on_serial:
asm volatile ("la $k0, reset_on_serial\n\t");
// load in the value of reset_on_serial:
asm volatile ("lw $k0, 0($k0)\n\t");
// if it's zero, do the usual serial handler:
asm volatile ("beq $k0, $zero, IntSer0Handler\n\t");
// Otherwise, do a reset! (jump to the below function)
asm volatile ("j reset\n\t");
}
void __attribute__((naked, nomips16)) Reset(void){
// does a software reset of the CPU:
// We have to do some kind of 'unlocking' sequence before we're allowed to reset:
asm volatile ("reset: di\n\t");
asm volatile ("la $k0, SYSKEY\n\t");
asm volatile ("li $v0, 0xAA996655\n\t");
asm volatile ("li $v1, 0x556699AA\n\t");
// Have to write these two keys to SYSKEY in two back to back instructions to 'unlock' the system:
asm volatile ("sw $v0, 0($k0)\n\t");
asm volatile ("sw $v1, 0($k0)\n\t");
// ok, now we can reset.
asm volatile ("la $k0, RSWRST\n\t");
// 'arm' the reset by writing a 1 to this register:
asm volatile ("li $v0, 1\n\t");
asm volatile ("sw $v0, 0($k0)\n\t");
// execute the reset by reading the register back in (this is a funny bunch of hoops we're having to jump through):
asm volatile ("lw $v0, 0($k0)");
// Aight, now we wait for the end to come:
asm volatile ("seeya: j seeya");
}
void start(){
Serial.println("ok");
// Any serial communication will now reset the CPU:
reset_on_serial = 1;
int incomplete = 1;
while (incomplete==1){
run();
autostart = 0;
incomplete = (uint)resume_address != (uint)instructions;
}
// no longer reset on serial communication:
reset_on_serial = 0;
// say that we're done!
Serial.println("done");
}
void run(){
// Enable our hardware trigger, if we're doing a hardware triggered start:
if (autostart==0){
attachInterrupt(0,0,RISING);
}
// 32 bit mode, no prescaler:
T2CON = 0x0008;
OC2CON = 0x0000;
OC2CON = 0x0023;
OC2R = 0;
PR2 = 0;
TMR2 = 0;
TMR3 = 0;
asm volatile ("nop\n\t");
T2CONSET = 0x8000;
OC2CONSET = 0x8000;
// don't fill our branch delay slots with nops, thank you very much:
asm volatile (".set noreorder\n\t":::"t0","t1","t2","t3","t4", "t5", "t6", "t7", "t8", "k0", "k1", "v0", "v1");
// load the ram address of PR2 into register $t0:
asm volatile ("la $t0, PR2\n\t");
// load the ram address of OC2R into register $t1:
asm volatile ("la $t1, OC2R\n\t");
// load the address of the instruction array into register $t2:
asm volatile ("la $t2, resume_address\n\t");
asm volatile ("lw $t2, 0($t2)\n\t");
// load the half-period time into register $t3:
asm volatile ("lw $t3, 0($t2)\n\t");
// load the delay time into register $t4:
asm volatile ("lw $t4, 4($t2)\n\t");
// load the the autostart flag into register $t5:
asm volatile ("la $t5, autostart\n\t");
asm volatile ("lw $t5, 0($t5)\n\t");
// load the address of IPC0 into register $t6:
asm volatile ("la $t6, IPC0\n\t");
// load the address of OC2CON into register $t7:
asm volatile ("la $t7, OC2CON\n\t");
// a small constant used for getting to the right part of the waveform:
asm volatile ("addi $t8, $zero, 2\n\t");
// Prepare registers for hardware triggering (makes the interrupt code faster):
asm volatile ("li $k0, 0x101001\n\t"); // the status we need to write to acknowledge that we're servicing the interrupt
asm volatile ("li $k1, 0x100003\n\t"); // The status we need to write to say we've finished the interrupt
asm volatile ("la $v0, IFS0\n\t"); // load the address of IFS0
asm volatile ("li $v1, 0x10088880\n\t"); // the value of IFSO we need to indicate we've serviced the interrupt
// if we're set to autostart, jump right in:
asm volatile ("bne $t5, $zero, start\n\t");
asm volatile ("nop\n\t");
// otherwise, wait for it...
asm volatile ("wait\n\t");
// We need to get up to the right part of the waveform, we don't want the initial digital low.
// We want to start with a digital high. So let's set the period to some small constant, and wait
// until it is about to go high, switching the period to our first instruction at just the right moment:
asm volatile ("start: sw $t8, 0($t0)\n\t");
asm volatile ("sw $t8, 0($t1)\n\t");
asm volatile ("nop\n\t");
// update the period of the output:
asm volatile ("top: sw $t3, 0($t0)\n\t");
asm volatile ("sw $t3, 0($t1)\n\t");
// wiat for the delay time:
asm volatile ("wait_loop: bne $t4, $zero, wait_loop\n\t");
asm volatile ("addi $t4, -1\n\t");
// load the next half-period in:
asm volatile ("lw $t3, 8($t2)\n\t");
// increment our instruction pointer:
asm volatile ("addi $t2, 8\n\t");
// go to the top of the loop if it's not a stop instruction:
asm volatile ("bne $t3, $zero, top\n\t");
//load the the next delay time in:
asm volatile ("lw $t4, 4($t2)\n\t");
// We got a stop instruction (indicated by half_period==0). Disable output:
asm volatile ("sw $zero, 0($t7)\n\t");
// We might be stopping for good, or we might be resuming after a hardware trigger.
// In case of the latter (indicated by delay_time != 0),
// save the next instruction pointer so we can resume from it:
asm volatile ("la $t8, resume_address");
asm volatile ("la $t7, instructions");
asm volatile ("beq $t4, $zero, end\n\t");
asm volatile ("sw $t7, 0($t8)\n\t");
// increment our instruction pointer:
asm volatile ("addi $t2, 8\n\t");
asm volatile ("sw $t2, 0($t8)\n\t");
asm volatile ("end:\n\t");
}
void readline(){
byte i = 0;
char c;
byte crfound = 0;
while (true){
if (Serial.available() > 0){
char c = Serial.read();
if (c == '\r'){
crfound = 1;
}
else if (c == '\n'){
if (crfound == 1){
readstring[i] = '\0';
return;
}
else{
readstring[i] = '\n';
i++;
}
}
else if (crfound){
crfound = 0;
readstring[i] = '\r';
i++;
readstring[i] = c;
i++;
}
else{
readstring[i] = c;
i++;
}
}
}
}
void setup(){
// start the Ethernet connection and the server:
//Ethernet.begin(mac, ip);
//server.begin();
Serial.begin(115200);
int i = 0;
for (i=0;i<86;i++){
pinMode(i, OUTPUT);
digitalWrite(i,LOW);
}
// Disable our hardware trigger until it is needed:
IPC0 = 0;
}
void loop(){
readline();
if (strcmp(readstring, "hello") == 0){
Serial.println("hello");
}
else if (strcmp(readstring, "hwstart") == 0){
autostart = 0;
start();
}
else if ((strcmp(readstring, "start") == 0) || (strcmp(readstring, "") == 0)){
autostart = 1;
start();
}
else if (strncmp(readstring, "set ", 4) == 0){
unsigned int addr;
unsigned int half_period;
unsigned int reps;
int parsed = sscanf(readstring,"%*s %u %u %u",&addr, &half_period, &reps);
if (parsed < 3){
Serial.println("invalid request");
}
else if (addr >= max_instructions){
Serial.println("invalid address");
}
else if (half_period == 0){
// This indicates either a stop or a wait instruction
instructions[2*addr] = 0;
if (reps == 0){
// It's a stop instruction
instructions[2*addr+1] = 0;
Serial.println("ok");
}
else if (reps == 1){
// It's a wait instruction:
instructions[2*addr+1] = 1;
Serial.println("ok");
}
else{
Serial.println("invalid request");
}
}
else if (half_period < 4){
Serial.println("half-period too short");
}
else if (reps < 1){
Serial.println("reps must be at least one");
}
else{
instructions[2*addr] = half_period - 1;
instructions[2*addr+1] = half_period*reps - 4;
Serial.println("ok");
}
}
else if (strcmp(readstring, "go high") == 0){
digitalWrite(5,HIGH);
Serial.println("ok");
}
else if (strcmp(readstring, "go low") == 0){
digitalWrite(5,LOW);
Serial.println("ok");
}
else if (strcmp(readstring, "reset") == 0){
Serial.println("ok");
asm volatile ("j reset\n\t");
}
else{
Serial.println("invalid request");
}
}