forked from Twilight-Logic/AR488
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AR488-mega.ino
3224 lines (2736 loc) · 76.9 KB
/
AR488-mega.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
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <EEPROM.h>
/*
Arduino IEEE-488 implementation by John Chajecki
Inspired by the original work of Emanuele Girlando, licensed under a Creative
Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
Any code in common with the original work is reproduced here with the explicit
permission of Emanuele Girlando, who has kindly reviewed and tested this code.
Ported to the Mega by John Chajecki, 28/07/2019.
Thanks also to Luke Mester for comparison testing against the Prologix interface.
AR488 is Licenced under the GNU Public licence.
Thanks to maxwell3e10 on the EEVblog forum for suggesting additional auto mode
settings and the macro feature.
*/
// Includes
#include <EEPROM.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
// Firmware version
#define FWVER "AR488M GPIB controller, version 0.01.02, 29/07/2019"
// Macro options
// Note: MACROS must be enabled to use the STARTUP macro
//#define MACROS // Enable the user macros feature
//#define STARTUP // Enable the startup macro
// Macro options
// Bluetooth support
/*
#define AR_BT_EN 12 // Bluetooth control enable pin
#define AR_BT_NAME "AR488-BT" // Bluetooth device name
#define AR_BT_BAUD "115200" // Bluetooth module baud rate
#define AR_BT_CODE "488488" // Bluetooth pairing code
*/
// Bluetooth support
// Debug options
//#define DEBUG1 // getCmd
//#define DEBUG2 // setGpibControls
//#define DEBUG3 // gpibSendData
//#define DEBUG4 // spoll_h
//#define DEBUG5 // attnRequired
//#define DEBUG6 // EEPROM
//#define DEBUG7 // gpibReceiveData
//#define DEBUG8 // ppoll_h
/*
Implements most of the CONTROLLER functions;
Substantially compatible with 'standard' Prologix "++" commands
(see +savecfg command in the manual for differences)
Principle of operation:
- Commands received from USB are buffered and whole terminated lines processed
- Interface commands prefixed with "++" are passed to the command handler
- Instrument commands and data not prefixed with '++' are sent directly to the GPIB bus.
- To receive from the instrument, issue a ++read command or put the controller in auto mode (++auto 1)
- Characters received over the GPIB bus are unbuffered and sent directly to USB
NOTES:
- GPIB line in a HIGH state is un-asserted
- GPIB line in a LOW state is asserted
- The ATMega processor control pins have a high impedance when set as inputs
- When set to INPUT_PULLUP, a 10k pull-up (to VCC) resistor is applied to the input
*/
/*
Standard commands
++addr - display/set device address
++auto - automatically request talk and read response
++clr - send Selected Device Clear to current GPIB address
++eoi - enable/disable assertion of EOI signal
++eos - specify GPIB termination character
++eot_enable - enable/disable appending user specified character to USB output on EOI detection
++eot_char - set character to append to USB output when EOT enabled
++ifc - assert IFC signal for 150 miscoseconds - make AR488 controller in charge
++llo - local lockout - disable front panel operation on instrument
++loc - enable front panel operation on instrument
++mode - set the interface mode (0=controller/1=device)
++read - read data from instrument
++read_tmo_ms - read timeout specified between 1 - 3000 milliseconds
++rst - reset the controller
++savecfg - save configration
++spoll - serial poll the addressed host or all instruments
++srq - return status of srq signal (1-srq asserted/0-srq not asserted)
++status - set the status byte to be returned on being polled (bit 6 = RQS, i.e SRQ asserted)
++trg - send trigger to selected devices (up to 15 addresses)
++ver - display firmware version
*/
/*
Proprietry commands:
++aspoll - serial poll all instruments (alias: ++spoll all)
++default - set configuration to controller default settings
++dcl - send unaddressed (all) device clear [power on reset] (is the rst?)
//++ren - assert or unassert the REN signal
++ppoll - conduct a parallel poll
++setvstr - set custom version string (to identify controller, e.g. "GPIB-USB"). Max 47 chars, excess truncated.
++verbose - verbose (human readable) mode
*/
/*
NOT YET IMPLEMENTED
++help - show summary of commands
++debug - debug mode (0=off, 1=basic, 2=verbose) [maybe best left in ifdef statements?]
++lon - put controller in listen-only mode (listen to all traffic)
++myaddr - aset the controller address
*/
/*
Mapping between the Arduino pins and the GPIB connector.
NOTE:
GPIB pins 10 and 18-24 are connected to GND
GPIB pin 12 should be connected to the cable shield (might be n/c)
Pin mapping follows the layout originally used by Emanuelle Girlando, but adds
the SRQ line (GPIB 10) on pin 2 and the REN line (GPIB 17) on pin 13. The program
should therefore be compatible with the original interface design but for full
functionality will need the remaining two pins to be connected.
For further information about the AR488 see:
For information regarding the GPIB firmware by Emanualle Girlando see:
http://egirland.blogspot.com/2014/03/arduino-uno-as-usb-to-gpib-controller.html
*/
/*********************************/
/***** USER DEFINED MACROS *****/
/*********************************/
#ifdef MACROS
/*
STARTUP MACRO
(See the AR488 user manual for details)
*/
const char startup_macro[] PROGMEM = {
/* Insert startup macro here ->*/
"++addr 7\n"
"++auto 1\n"
"*RST\n"
":func 'volt:ac'"
/*<-End of startup macro*/
};
/*
USER MACROS 1 - 9
(See the AR488 user manual for details)
*/
const char macro_1 [] PROGMEM = {
/* Insert macro here ->*/
"++addr 3\n"
"++auto 0\n"
"M3\n"
/*<-End of macro*/
};
const char macro_2 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_3 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_4 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_5 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_6 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_7 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_8 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
const char macro_9 [] PROGMEM = {
/* Insert of macro here ->*/
""
/*<-End of macro*/
};
/***** END OF USER MACROS *****/
/** DO NOT MODIFY BELOW HERE **/
/******************************/
/*
Macro pointer array
*** DO NOT MODIFY ***
*/
const char * const macros[] PROGMEM = {
startup_macro,
macro_1,
macro_2,
macro_3,
macro_4,
macro_5,
macro_6,
macro_7,
macro_8,
macro_9
};
/*** DO NOT MODIFY ***/
#endif
/*****************************/
/****** END OF SCRIPTS *****/
/*****************************/
// NOTE: Pinout last updated 28/07/2019
#define DIO1 A0 /* GPIB 1 : PORTF bit 0 */
#define DIO2 A1 /* GPIB 2 : PORTF bit 1 */
#define DIO3 A2 /* GPIB 3 : PORTF bit 2 */
#define DIO4 A3 /* GPIB 4 : PORTF bit 3 */
#define DIO5 A4 /* GPIB 13 : PORTF bit 4 */
#define DIO6 A5 /* GPIB 14 : PORTF bit 5 */
#define DIO7 A6 /* GPIB 15 : PORTF bit 4 */
#define DIO8 A7 /* GPIB 16 : PORTF bit 5 */
#define IFC 17 /* GPIB 9 : PORTH bit 0 */
#define NDAC 16 /* GPIB 8 : PORTH bit 1 */
#define NRFD 6 /* GPIB 7 : PORTH bit 3 */
#define DAV 7 /* GPIB 6 : PORTH bit 4 */
#define EOI 8 /* GPIB 5 : PORTH bit 5 */
#define REN 9 /* GPIB 17 : PORTD bit 6 */
#define SRQ 10 /* GPIB 10 : PORTB bit 4 */
#define ATN 11 /* GPIB 11 : PORTB bit 5 */
/*
PIN interrupts
*/
#define ATNint 0b00100000
#define SRQint 0b00010000
/*
GPIB BUS commands
*/
// Universal Multiline commands (apply to all devices)
#define GC_LLO 0x11
#define GC_DCL 0x14
#define GC_PPU 0x15
#define GC_SPE 0x18
#define GC_SPD 0x19
#define GC_UNL 0x3F
#define GC_TAD 0x40
#define GC_PPE 0x60
#define GC_PPD 0x70
#define GC_UNT 0x5F
// Address commands
#define GC_LAD 0x20
// Addressed commands
#define GC_GTL 0x01
#define GC_SDC 0x04
#define GC_PPC 0x05
#define GC_GET 0x08
/*
GPIB control states
*/
// Controller mode
#define CINI 0x01 // Controller idle state
#define CIDS 0x02 // Controller idle state
#define CCMS 0x03 // Controller command state
#define CTAS 0x04 // Controller talker active state
#define CLAS 0x05 // Controller listner active state
// Listner/device mode
#define DINI 0x06 // Device initialise state
#define DIDS 0x07 // Device idle state
#define DLAS 0x08 // Device listener active (listening/receiving)
#define DTAS 0x09 // Device talker active (sending) state
/*
Process status values
*/
#define OK 0
#define ERR 1
/*
Control characters
*/
#define ESC 0x1B // the USB escape char
#define CR 0xD // Carriage return
#define LF 0xA // Newline/linefeed
#define PLUS 0x2B // '+' character
/*
PARSE BUFFER
*/
// Serial input parsing buffer
static const uint8_t PBSIZE = 128;
char pBuf[PBSIZE];
uint8_t pbPtr = 0;
/*
Controller configuration
Default values set for controller mode
*/
struct AR488conf {
uint8_t ew; // EEPROM write indicator byte
bool eot_en; // Enable/disable append EOT char to string received from GPIB bus before sending to USB
bool eoi; // Assert EOI on last data char written to GPIB - 0-disable, 1-enable
uint8_t cmode; // Controller mode - 0=unset, 1=device, 2=controller
uint8_t caddr; // Controller address
uint8_t paddr; // Primary device address
uint8_t saddr; // Secondary device address
uint8_t eos; // EOS (end of send - to GPIB) character flag [0=CRLF, 1=CR, 2=LF, 3=None]
uint8_t stat; // Status byte to return in response to a poll
uint8_t amode; // Auto mode setting (0=off; 1=Prologix; 2=onquery; 3=continuous;
int rtmo; // Read timout (read_tmo_ms) in milliseconds - 0-3000 - value depends on instrument
char eot_ch; // EOT character to append to USB output when EOI signal detected
char vstr[48]; // Custom version string
uint16_t tmbus; // Delay to allow the bus control/data lines to settle (1-30,000 microseconds)
};
struct AR488conf AR488;
/*
Global variables with volatile values related to controller state
*/
// Internal LED
const int LED = 13;
// GPIB control state
uint8_t cstate = 0;
// Verbose mode
bool isVerb = false;
// We have a line
//bool crFl = false; // Carriage return flag
uint8_t lnRdy = 0; // Line ready to process
// GPIB data receive flags
bool isReading = false; // Is a GPIB read in progress?
bool aRead = false; // GPIB data read in progress
bool rEoi = false; // read eoi requested
bool rEbt = false; // read with specified terminator character
bool isQuery = false; // Direct instrument command is a query
//uint8_t aMode = 0; // Auto read mode: 0=off; 1=Prologix; 2=on query (CMD?); 3=continuous;
uint8_t tranBrk = 0; // transmission break on 1=++, 2=EOI, 3=ATN 4=UNL
uint8_t eByte = 0; // termination character
// Device mode - send data
bool snd = false;
// Escaped character flag
bool isEsc = false; // Charcter escaped
bool isPle = false; // Plus escaped
// Received serial poll request?
bool isSprq = false;
// Read only mode flag
bool isRO = false;
// GPIB command parser
bool aTt = false;
bool aTl = false;
// Interrupts
volatile uint8_t pinbMem = PINB;
volatile bool isATN = false; // has ATN been asserted?
volatile bool isSRQ = false; // has SRQ been asserted?
// SRQ auto mode
bool isSrqa = false;
volatile bool isBAD = false;
uint8_t runMacro = 0;
/*
***** Arduino SETUP procedure *****
*/
void setup() {
// Turn off internal LED (set OUPTUT/LOW)
DDRB |= 0b10000000;
PORTB &= 0b01111111;
// Turn on interrupts
cli();
PCICR |= 0b00000001; // PORTB
// PCICR |= 0b00000100; // PORTD
sei();
// Initialise parse buffer
flushPbuf();
// Initialise serial comms over USB or Bluetooth
#ifdef AR_BT_EN
// Initialise Bluetooth
btInit();
#else
// Start the serial port
Serial.begin(115200);
#endif
// Initialise
initAR488();
// Read data from non-volatile memory
//(will only read if previous config has already been saved)
epGetCfg();
// Print version string
// if (strlen(AR488.vstr)>0) {
// Serial.println(AR488.vstr);
// }else{
// Serial.println(FWVER);
// }
// Initialize the interface in device mode
if (AR488.cmode == 1) initDevice();
// Initialize the interface in controller mode
if (AR488.cmode == 2) initController();
isATN = false;
isSRQ = false;
// Save state of the PORTB (pcint related) pins
pinbMem = PINB;
#if defined(MACROS) && defined(STARTUP)
// Run startup macro
execMacro(0);
#endif
}
/*
***** MAIN LOOP *****
*/
void loop() {
#ifdef MACROS
// Run user macro if flagged
if (runMacro > 0) {
execMacro(runMacro);
runMacro = 0;
}
#endif
// int bytes = 0;
// NOTE: serialEvent() handles serial interrupt
// Each received char is passed through parser until an un-escaped CR
// is encountered. If we have a command then parse and execute.
// If the line is data (inclding direct instrument commands) then
// send it to the instrument.
// NOTE: parseInput() sets lnRdy in serialEvent or readBreak
// lnRdy=1: process command;
// lnRdy=2: send data to Gpib
// lnRdy=1: received a command so execute it...
if (lnRdy == 1) {
processLine(pBuf, pbPtr, 1);
}
// Controller mode:
if (AR488.cmode == 2) {
// lnRdy=2: received data - send it to the instrument...
if (lnRdy == 2) {
processLine(pBuf, pbPtr, 2);
// Auto-read data from GPIB bus following any command
if (AR488.amode == 1) {
// delay(10);
gpibReceiveData();
}
// Auto-receive data from GPIB bus following a query command
if (AR488.amode == 2 && isQuery) {
// delay(10);
gpibReceiveData();
isQuery = false;
}
}
// Check status of SRQ and SPOLL if asserted
if (isSRQ) {
spoll_h(NULL);
isSRQ = false;
}
// Continuous auto-receive data from GPIB bus
if (AR488.amode == 3 && aRead) gpibReceiveData();
}
// Device mode:
// Check whether ATN asserted, get command then send or receive
if (AR488.cmode == 1) {
if (isATN) { // ATN asserted
attnRequired();
} else {
// Not allowed to send data in listen only (++lon) mode so clear the buffer and ATT flag
if (isRO && lnRdy == 2) {
flushPbuf();
aTt = false;
}
// Addressed to talk
if (aTt) {
if (isSprq) {
gpibSendStatus();
} else {
if (lnRdy == 2) {
processLine(pBuf, pbPtr, 2);
}
}
aTt = false; // Clear talk flag
}
if (aTl) { // Addressed to listen
gpibReceiveData();
aTl = false; // Clear listen flag
}
}
}
delayMicroseconds(5);
}
/***** END MAIN LOOP *****/
/*
Initialise the interface
*/
void initAR488() {
// Set default values ({'\0'} sets version string array to null)
AR488 = {0xCC, false, false, 2, 0, 1, 0, 0, 0, 0, 1200, 0, {'\0'}, 0};
}
/*
Initialise device mode
*/
void initDevice() {
// Set GPIB control bus to device idle mode
setGpibControls(DINI);
// Disable SRQ and enable ATN interrupt
cli();
// PCMSK2 &= SRQint; // SRQ interrupt now controlled by ++status command
PCMSK2 |= ATNint;
sei();
// Initialise GPIB data lines (sets to INPUT_PULLUP)
readGpibDbus();
}
/*
Initialise controller mode
*/
void initController() {
// Set GPIB control bus to controller idle mode
setGpibControls(CINI); // Controller initialise state
// Disable ATN and enable SRQ interrupt
cli();
PCMSK2 &= ATNint;
// PCMSK2 |= SRQint; // SRQ interrupt now controlled by ++status command
sei();
// Initialise GPIB data lines (sets to INPUT_PULLUP)
readGpibDbus();
// Assert IFC to signal controller in charge (CIC)
ifc_h();
}
/*
Serial event interrupt handler
*/
void serialEvent() {
lnRdy = parseInput(Serial.read());
}
/*
Interrupt data transfer when escape pressed
*/
void readBreak() {
// Check whether EOI is asserted
if (digitalRead(EOI) == LOW) {
tranBrk = 5;
return;
}
// Check serial input to see if we need to break on ++ character
if (Serial.available()) { // Only need to parse if a character is available
lnRdy = parseInput(Serial.read());
if (lnRdy == 1) tranBrk = 7;
}
}
/******************************/
/***** Interrupt handlers *****/
/******************************/
// Catches mis-spelled ISR vectors
#pragma GCC diagnostic error "-Wmisspelled-isr"
// Interrupt for ATN pin
ISR(PCINT0_vect) {
// Has PCINT5 fired (ATN asserted)?
if (AR488.cmode == 1) { // Only in device mode
if ((PINB ^ pinbMem) & ATNint) {
isATN = (PINB & ATNint) == 0;
}
}
// Has PCINT4 fired (SRQ asserted)?
if (AR488.cmode == 2) { // Only in controller mode
if ((PINB ^ pinbMem) & SRQint) {
isSRQ = (PINB & SRQint) == 0;
}
}
// Save current state of PORTD register
pinbMem = PINB;
}
// Catchall interrupt vector
/*
ISR(BADISR_vect) {
// ISR to catch ISR firing without handler
isBAD = true;
}
*/
/*************************************/
/***** Device operation routines *****/
/*************************************/
/*
Unrecognized command
*/
void errBadCmd() {
Serial.println(F("Unrecognized command"));
}
/*
Read configuration from EEPROM
*/
void epGetCfg() {
// int ew = 0x00;
int epaddr = 0;
int val;
val = EEPROM.read(0);
if (val == 0xCC) {
EEPROM.get(epaddr, AR488);
}
}
/*
Save configuraton to EEPROM
4 x 128byte 'pages'
*/
//uint8_t epSaveCfg(uint8_t page){
uint8_t epSaveCfg() {
int epaddr = 0;
// if (page<0 || page>4) return ERR;
#ifdef debug5
long int sz;
sz = sizeof(AR488);
Serial.print(F("Size of structure: "));
Serial.println(sz);
#endif
// epaddr = 128 * (page-1);
EEPROM.put(epaddr, AR488);
if (isVerb) Serial.print(F("Settings saved."));
// if (isVerb) { Serial.print(F("Settings saved to configuration profile ")); Serial.println(page); };
return OK;
}
/*
Add character to the buffer and parse
*/
uint8_t parseInput(char c) {
uint8_t r = 0;
// Read until buffer full (buffer-size - 2 characters)
if (pbPtr < PBSIZE - 2) {
// Actions on specific characters
switch (c) {
// Carriage return or newline? Then process the line
case CR:
case LF:
// Character must not be escaped
if (isEsc) {
addPbuf(c);
isEsc = false;
} else {
// Carriage return on blank line?
if (pbPtr == 0) {
flushPbuf();
if (isVerb) {
Serial.println();
Serial.print("> ");
}
return 0;
} else {
#ifdef DEBUG1
Serial.print(F("parseInput: Received ")); Serial.println(pBuf);
#endif
// Buffer starts with ++ and contains at least 3 characters - command?
if (pbPtr > 2 && isCmd(pBuf) && !isPle) {
// if (isReading) tranBrk = 7;
r = 1;
// Buffer has at least 1 character
} else if (pbPtr > 0) { // Its other data (or instrument commands from user) so pass characters to GPIB bus
r = 2;
}
isPle = false;
return r;
}
}
break;
case ESC:
// Handle the escape character
if (isEsc) {
// Add character to buffer and cancel escape
addPbuf(c);
isEsc = false;
} else {
// Set escape flag
isEsc = true; // Set escape flag
}
break;
case PLUS:
if (isEsc) {
isEsc = false;
if (pbPtr < 2) isPle = true;
}
addPbuf(c);
if (isVerb) Serial.print(c);
// Break on '++'?
// if (pbPtr==2 && isCmd(pBuf)) {
// if (isReading){
// tranBrk = 1;
// isAuto = false;
// }
// }
break;
// Something else?
default: // any char other than defined above
if (isVerb) Serial.print(c); // Humans like to see what they are typing...
// Buffer contains '++' (start of command). Stop sending data to serial port by halting GPIB receive.
addPbuf(c);
isEsc = false;
}
} else {
// Buffer full - cannot be a command so treat as data and pass to GPIB bus
r = 2;
}
return r;
}
/*
Is this a command?
*/
bool isCmd(char *buffr) {
if (buffr[0] == PLUS && buffr[1] == PLUS) {
#ifdef DEBUG1
Serial.println(F("isCmd: Command detected."));
#endif
return true;
}
return false;
}
/*
++read command detected?
*/
bool isRead(char *buffr) {
char cmd[4];
// Copy 2nd to 5th character
for (int i = 2; i < 6; i++) {
cmd[i - 2] = buffr[i];
}
// Compare with 'read'
if (strncmp(cmd, "read", 4) == 0) return true;
return false;
}
/*
Add character to the buffer
*/
void addPbuf(char c) {
pBuf[pbPtr] = c;
pbPtr++;
}
/*
Clear the parse buffer
*/
void flushPbuf() {
memset(pBuf, '\0', PBSIZE);
pbPtr = 0;
}
/*
Command index record structure
Record: command-token, command-handler, modes-available
NOTE: modes: 1=device, 2=controller, 3=both
*/
struct cmdIdx {
const char* token;
uint8_t ctype;
uint8_t cidx;
uint8_t opmode;
};
/*
Command handler record structure
Record: command-token, command-handler, modes-available
NOTE: modes: 1=device, 2=controller, 3=both
*/
struct cmdVRec {
void (*handler)();
};
struct cmdPRec {
void (*handler)(char*);
};
/*
Array containing index of accepted ++ commands
Parameters:
command - command name
type - 1-without parameters; 2-with parameters
idnum - unique sequence number
mode - 1-device; 2-controller; 3-both
*/
static const cmdIdx plusCmdIdx [] = {
{ "allspoll", 1, 0x00, 2 },
{ "clr", 1, 0x01, 2 },
{ "dcl", 1, 0x02, 2 },
{ "default", 1, 0x03, 3 },
{ "ifc", 1, 0x04, 2 },
{ "ppoll", 1, 0x05, 2 },
{ "rst", 1, 0x06, 3 },
{ "srq", 1, 0x07, 2 },
{ "savecfg", 1, 0x08, 3 },
{ "verbose" , 1, 0x09, 3 },
{ "addr", 2, 0x00, 3 },
{ "auto", 2, 0x01, 2 },
{ "eoi", 2, 0x02, 3 },
{ "eos", 2, 0x03, 3 },
{ "eot_char", 2, 0x04, 3 },
{ "eot_enable", 2, 0x05, 3 },
{ "llo", 2, 0x06, 2 },
{ "loc", 2, 0x07, 2 },
// { "lon", 2, 0x08, 1 },
{ "macro", 2, 0x09, 2 },
{ "mode" , 2, 0x0A, 3 },
{ "read", 2, 0x0B, 2 },
{ "read_tmo_ms", 2, 0x0C, 2 },
{ "ren", 2, 0x0D, 2 },
{ "repeat", 2, 0x0E, 2 },
{ "trg", 2, 0x0F, 2 },
{ "setvstr", 2, 0x10, 3 },
{ "spoll", 2, 0X11, 2 },
{ "srqauto", 2, 0x12, 2 },
{ "status", 2, 0x13, 1 },
{ "ver", 2, 0x14, 3 },
{ "tmbus", 2, 0x15, 3 },
{ "xdiag", 2, 0x16, 3 }
};
/*
Array containing index of accepted ++ commands (type 1) without parameters
*/
static const cmdVRec cmdVHlist [] = {
aspoll_h,
clr_h,
dcl_h,
default_h,
ifc_h,
ppoll_h,
rst_h,
srq_h,
save_h,
verb_h
};
/*
Array containing index of accepted ++ commands (type 2) with parameters
*/
static const cmdPRec cmdPHlist [] = {
addr_h,
amode_h,
eoi_h,
eos_h,
eot_char_h,
eot_en_h,
llo_h,
loc_h,
lon_h,
macro_h,
cmode_h,
read_h,
rtmo_h,
ren_h,
repeat_h,
trg_h,
setvstr_h,
spoll_h,
srqa_h,
stat_h,
ver_h,
tmbus_h,
xdiag_h
};
/*
Execute a ++command or send characters to instrument
mode: 1=command; 2=data;
*/
void processLine(char *buffr, uint8_t dsize, uint8_t mode) {
char line[PBSIZE];
// Copy collected chars to line buffer
memcpy(line, buffr, dsize);