- Notifications
You must be signed in to change notification settings - Fork 7.6k
/
Copy pathesp32-hal-i2c.c
1764 lines (1571 loc) · 66.5 KB
/
esp32-hal-i2c.c
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
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include"esp32-hal-i2c.h"
#include"esp32-hal.h"
#include"freertos/FreeRTOS.h"
#include"freertos/task.h"
#include"freertos/semphr.h"
#include"freertos/event_groups.h"
#include"rom/ets_sys.h"
#include"driver/periph_ctrl.h"
#include"soc/i2c_reg.h"
#include"soc/i2c_struct.h"
#include"soc/dport_reg.h"
#include"esp_attr.h"
#include"esp32-hal-cpu.h"// cpu clock change support 31DEC2018
//#define I2C_DEV(i) (volatile i2c_dev_t *)((i)?DR_REG_I2C1_EXT_BASE:DR_REG_I2C_EXT_BASE)
//#define I2C_DEV(i) ((i2c_dev_t *)(REG_I2C_BASE(i)))
#defineI2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0))
#defineI2C_SDA_IDX(p) ((p==0)?I2CEXT0_SDA_OUT_IDX:((p==1)?I2CEXT1_SDA_OUT_IDX:0))
#defineDR_REG_I2C_EXT_BASE_FIXED 0x60013000
#defineDR_REG_I2C1_EXT_BASE_FIXED 0x60027000
/* Stickbreaker ISR mode debug support
ENABLE_I2C_DEBUG_BUFFER
Enable debug interrupt history buffer, fifoTx history buffer.
Setting this define will result in 2570 bytes of RAM being used whenever CORE_DEBUG_LEVEL
is higher than WARNING. Unless you are debugging a problem in the I2C subsystem,
I would recommend you leave it commented out.
*/
//#define ENABLE_I2C_DEBUG_BUFFER
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
#defineINTBUFFMAX 64
#defineFIFOMAX 512
staticuint32_tintBuff[INTBUFFMAX][3][2];
staticuint32_tintPos[2]= {0,0};
staticuint16_tfifoBuffer[FIFOMAX];
staticuint16_tfifoPos=0;
#endif
// start from tools/sdk/include/soc/soc/i2c_struct.h
typedefunion {
struct {
uint32_tbyte_num: 8; /*Byte_num represent the number of data need to be send or data need to be received.*/
uint32_tack_en: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/
uint32_tack_exp: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/
uint32_tack_val: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/
uint32_top_code: 3; /*op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END.*/
uint32_treserved14: 17;
uint32_tdone: 1; /*When command0 is done in I2C Master mode this bit changes to high level.*/
};
uint32_tval;
} I2C_COMMAND_t;
typedefunion {
struct {
uint32_trx_fifo_full_thrhd: 5;
uint32_ttx_fifo_empty_thrhd:5; //Config tx_fifo empty threhd value when using apb fifo access * /
uint32_tnonfifo_en: 1; //Set this bit to enble apb nonfifo access. * /
uint32_tfifo_addr_cfg_en: 1; //When this bit is set to 1 then the byte after address represent the offset address of I2C Slave's ram. * /
uint32_trx_fifo_rst: 1; //Set this bit to reset rx fifo when using apb fifo access. * /
// chuck while this bit is 1, the RX fifo is held in REST, Toggle it * /
uint32_ttx_fifo_rst: 1; //Set this bit to reset tx fifo when using apb fifo access. * /
// chuck while this bit is 1, the TX fifo is held in REST, Toggle it * /
uint32_tnonfifo_rx_thres: 6; //when I2C receives more than nonfifo_rx_thres data it will produce rx_send_full_int_raw interrupt and update the current offset address of the receiving data.* /
uint32_tnonfifo_tx_thres: 6; //when I2C sends more than nonfifo_tx_thres data it will produce tx_send_empty_int_raw interrupt and update the current offset address of the sending data. * /
uint32_treserved26: 6;
};
uint32_tval;
} I2C_FIFO_CONF_t;
typedefunion {
struct {
uint32_trx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/
uint32_trx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/
uint32_ttx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/
uint32_ttx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/
uint32_treserved20: 12;
};
uint32_tval;
} I2C_FIFO_ST_t;
// end from tools/sdk/include/soc/soc/i2c_struct.h
// sync between dispatch(i2cProcQueue) and worker(i2c_isr_handler_default)
typedefenum {
//I2C_NONE=0,
I2C_STARTUP=1,
I2C_RUNNING,
I2C_DONE
} I2C_STAGE_t;
typedefenum {
I2C_NONE=0,
I2C_MASTER,
I2C_SLAVE,
I2C_MASTERSLAVE
} I2C_MODE_t;
// internal Error condition
typedefenum {
// I2C_NONE=0,
I2C_OK=1,
I2C_ERROR,
I2C_ADDR_NAK,
I2C_DATA_NAK,
I2C_ARBITRATION,
I2C_TIMEOUT
} I2C_ERROR_t;
// i2c_event bits for EVENTGROUP bits
// needed to minimize change events, FreeRTOS Daemon overload, so ISR will only set values
// on Exit. Dispatcher will set bits for each dq before/after ISR completion
#defineEVENT_ERROR_NAK (BIT(0))
#defineEVENT_ERROR (BIT(1))
#defineEVENT_ERROR_BUS_BUSY (BIT(2))
#defineEVENT_RUNNING (BIT(3))
#defineEVENT_DONE (BIT(4))
#defineEVENT_IN_END (BIT(5))
#defineEVENT_ERROR_PREV (BIT(6))
#defineEVENT_ERROR_TIMEOUT (BIT(7))
#defineEVENT_ERROR_ARBITRATION (BIT(8))
#defineEVENT_ERROR_DATA_NAK (BIT(9))
#defineEVENT_MASK 0x3F
// control record for each dq entry
typedefunion {
struct {
uint32_taddr: 16; // I2C address, if 10bit must have 0x7800 mask applied, else 8bit
uint32_tmode: 1; // transaction direction 0 write, 1 read
uint32_tstop: 1; // sendStop 0 no, 1 yes
uint32_tstartCmdSent: 1; // START cmd has been added to command[]
uint32_taddrCmdSent: 1; // addr WRITE cmd has been added to command[]
uint32_tdataCmdSent: 1; // all necessary DATA(READ/WRITE) cmds added to command[]
uint32_tstopCmdSent: 1; // completed all necessary commands
uint32_taddrReq: 2; // number of addr bytes need to send address
uint32_taddrSent: 2; // number of addr bytes added to FIFO
uint32_treserved_31: 6;
};
uint32_tval;
} I2C_DATA_CTRL_t;
// individual dq element
typedefstruct {
uint8_t*data; // data pointer for read/write buffer
uint16_tlength; // size of data buffer
uint16_tposition; // current position for next char in buffer (<length)
uint16_tcmdBytesNeeded; // used to control number of I2C_COMMAND_t blocks added to queue
I2C_DATA_CTRL_tctrl;
EventGroupHandle_tqueueEvent; // optional user supplied for Async feedback EventBits
} I2C_DATA_QUEUE_t;
structi2c_struct_t {
i2c_dev_t*dev;
#if !CONFIG_DISABLE_HAL_LOCKS
xSemaphoreHandlelock;
#endif
uint8_tnum;
int8_tsda;
int8_tscl;
I2C_MODE_tmode;
I2C_STAGE_tstage;
I2C_ERROR_terror;
EventGroupHandle_ti2c_event; // a way to monitor ISR process
// maybe use it to trigger callback for OnRequest()
intr_handle_tintr_handle; /*!< I2C interrupt handle*/
I2C_DATA_QUEUE_t*dq;
uint16_tqueueCount; // number of dq entries in queue.
uint16_tqueuePos; // current queue that still has or needs data (out/in)
int16_terrorByteCnt; // byte pos where error happened, -1 devId, 0..(length-1) data
uint16_terrorQueue; // errorByteCnt is in this queue,(for error locus)
uint32_texitCode;
uint32_tdebugFlags;
};
enum {
I2C_CMD_RSTART,
I2C_CMD_WRITE,
I2C_CMD_READ,
I2C_CMD_STOP,
I2C_CMD_END
};
#ifCONFIG_DISABLE_HAL_LOCKS
#defineI2C_MUTEX_LOCK()
#defineI2C_MUTEX_UNLOCK()
statici2c_t_i2c_bus_array[2] = {
{(volatilei2c_dev_t*)(DR_REG_I2C_EXT_BASE_FIXED), 0, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0},
{(volatilei2c_dev_t*)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
};
#else
#defineI2C_MUTEX_LOCK() do {} while (xSemaphoreTakeRecursive(i2c->lock, portMAX_DELAY) != pdPASS)
#defineI2C_MUTEX_UNLOCK() xSemaphoreGiveRecursive(i2c->lock)
statici2c_t_i2c_bus_array[2] = {
{(volatilei2c_dev_t*)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0},
{(volatilei2c_dev_t*)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0}
};
#endif
/*
* index - command index (0 to 15)
* op_code - is the command
* byte_num - This register is to store the amounts of data that is read and written. byte_num in RSTART, STOP, END is null.
* ack_val - Each data byte is terminated by an ACK bit used to set the bit level.
* ack_exp - This bit is to set an expected ACK value for the transmitter.
* ack_check - This bit is to decide whether the transmitter checks ACK bit. 1 means yes and 0 means no.
* */
/* Stickbreaker ISR mode debug support
*/
staticvoidIRAM_ATTRi2cDumpCmdQueue(i2c_t*i2c)
{
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR)&&(defined ENABLE_I2C_DEBUG_BUFFER)
staticconstchar*constcmdName[] ={"RSTART","WRITE","READ","STOP","END"};
uint8_ti=0;
while(i<16) {
I2C_COMMAND_tc;
c.val=i2c->dev->command[i].val;
log_e("[%2d]\t%c\t%s\tval[%d]\texp[%d]\ten[%d]\tbytes[%d]",i,(c.done?'Y':'N'),
cmdName[c.op_code],
c.ack_val,
c.ack_exp,
c.ack_en,
c.byte_num);
i++;
}
#endif
}
/* Stickbreaker ISR mode debug support
*/
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
staticvoidi2cDumpDqData(i2c_t*i2c)
{
#if defined (ENABLE_I2C_DEBUG_BUFFER)
uint16_ta=0;
charbuff[140];
I2C_DATA_QUEUE_t*tdq;
intdigits=0,lenDigits=0;
a=i2c->queueCount;
while(a>0) {
digits++;
a /= 10;
}
while(a<i2c->queueCount) { // find maximum number of len decimal digits for formatting
if (i2c->dq[a].length>lenDigits ) lenDigits=i2c->dq[a].length;
a++;
}
a=0;
while(lenDigits>0){
a++;
lenDigits /= 10;
}
lenDigits=a;
a=0;
while(a<i2c->queueCount) {
tdq=&i2c->dq[a];
charbuf1[10],buf2[10];
sprintf(buf1,"%0*d",lenDigits,tdq->length);
sprintf(buf2,"%0*d",lenDigits,tdq->position);
log_i("[%0*d] %sbit %x %c %s buf@=%p, len=%s, pos=%s, ctrl=%d%d%d%d%d",digits,a,
(tdq->ctrl.addr>0x100)?"10":"7",
(tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1),
(tdq->ctrl.mode)?'R':'W',
(tdq->ctrl.stop)?"STOP":"",
tdq->data,
buf1,buf2,
tdq->ctrl.startCmdSent,tdq->ctrl.addrCmdSent,tdq->ctrl.dataCmdSent,(tdq->ctrl.stop)?tdq->ctrl.stopCmdSent:0,tdq->ctrl.addrSent
);
uint16_toffset=0;
while(offset<tdq->length) {
memset(buff,' ',140);
buff[139]='\0';
uint16_ti=0,j;
j=sprintf(buff,"0x%04x: ",offset);
while((i<32)&&(offset<tdq->length)) {
charch=tdq->data[offset];
sprintf((char*)&buff[(i*3)+41],"%02x ",ch);
if((ch<32)||(ch>126)) {
ch='.';
}
j+=sprintf((char*)&buff[j],"%c",ch);
buff[j]=' ';
i++;
offset++;
}
log_i("%s",buff);
}
a++;
}
#else
log_i("Debug Buffer not Enabled");
#endif
}
#endif
#ifARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
staticvoidi2cDumpI2c(i2c_t*i2c)
{
log_e("i2c=%p",i2c);
log_i("dev=%p date=%p",i2c->dev,i2c->dev->date);
#if !CONFIG_DISABLE_HAL_LOCKS
log_i("lock=%p",i2c->lock);
#endif
log_i("num=%d",i2c->num);
log_i("mode=%d",i2c->mode);
log_i("stage=%d",i2c->stage);
log_i("error=%d",i2c->error);
log_i("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0);
log_i("intr_handle=%p",i2c->intr_handle);
log_i("dq=%p",i2c->dq);
log_i("queueCount=%d",i2c->queueCount);
log_i("queuePos=%d",i2c->queuePos);
log_i("errorByteCnt=%d",i2c->errorByteCnt);
log_i("errorQueue=%d",i2c->errorQueue);
log_i("debugFlags=0x%08X",i2c->debugFlags);
if(i2c->dq) {
i2cDumpDqData(i2c);
}
}
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
staticvoidi2cDumpInts(uint8_tnum)
{
#if defined (ENABLE_I2C_DEBUG_BUFFER)
uint32_tb;
log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num);
for(uint32_ta=1; a<=INTBUFFMAX; a++) {
b=(a+intPos[num])%INTBUFFMAX;
if(intBuff[b][0][num]!=0) {
log_i("[%02d]\t0x%04x\t0x%04x\t0x%04x\t0x%04x\t0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]);
}
}
#else
log_i("Debug Buffer not Enabled");
#endif
}
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
staticvoidIRAM_ATTRi2cDumpStatus(i2c_t*i2c){
typedefunion {
struct {
uint32_tack_rec: 1; /*This register stores the value of ACK bit.*/
uint32_tslave_rw: 1; /*when in slave mode 1:master read slave 0: master write slave.*/
uint32_ttime_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/
uint32_tarb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/
uint32_tbus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/
uint32_tslave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/
uint32_tbyte_trans: 1; /*This register changes to high level when one byte is transferred.*/
uint32_treserved7: 1;
uint32_trx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/
uint32_treserved14: 4;
uint32_ttx_fifo_cnt: 6; /*This register stores the amount of received data in ram.*/
uint32_tscl_main_state_last: 3; /*This register stores the value of state machine for i2c module. 3'h0: SCL_MAIN_IDLE 3'h1: SCL_ADDRESS_SHIFT 3'h2: SCL_ACK_ADDRESS 3'h3: SCL_RX_DATA 3'h4 SCL_TX_DATA 3'h5:SCL_SEND_ACK 3'h6:SCL_WAIT_ACK*/
uint32_treserved27: 1;
uint32_tscl_state_last: 3; /*This register stores the value of state machine to produce SCL. 3'h0: SCL_IDLE 3'h1:SCL_START 3'h2:SCL_LOW_EDGE 3'h3: SCL_LOW 3'h4:SCL_HIGH_EDGE 3'h5:SCL_HIGH 3'h6:SCL_STOP*/
uint32_treserved31: 1;
};
uint32_tval;
} status_reg;
status_regsr;
sr.val=i2c->dev->status_reg.val;
log_i("ack(%d) sl_rw(%d) to(%d) arb(%d) busy(%d) sl(%d) trans(%d) rx(%d) tx(%d) sclMain(%d) scl(%d)",sr.ack_rec,sr.slave_rw,sr.time_out,sr.arb_lost,sr.bus_busy,sr.slave_addressed,sr.byte_trans, sr.rx_fifo_cnt, sr.tx_fifo_cnt,sr.scl_main_state_last, sr.scl_state_last);
}
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
staticvoidi2cDumpFifo(i2c_t*i2c){
charbuf[64];
uint16_tk=0;
uint16_ti=fifoPos+1;
i %=FIFOMAX;
while((fifoBuffer[i]==0)&&(i!=fifoPos)){
i++;
i %=FIFOMAX;
}
if(i!=fifoPos){// actual data
do{
if(fifoBuffer[i] &0x8000){ // address byte
if(fifoBuffer[i] &0x100) { // read
if(fifoBuffer[i] &0x1) { // valid read dev id
k+=sprintf(&buf[k],"READ 0x%02X",(fifoBuffer[i]&0xff)>>1);
} else { // invalid read dev id
k+=sprintf(&buf[k],"Bad READ 0x%02X",(fifoBuffer[i]&0xff));
}
} else { // write
if(fifoBuffer[i] &0x1) { // bad write dev id
k+=sprintf(&buf[k],"bad WRITE 0x%02X",(fifoBuffer[i]&0xff));
} else { // good Write
k+=sprintf(&buf[k],"WRITE 0x%02X",(fifoBuffer[i]&0xff)>>1);
}
}
} elsek+=sprintf(&buf[k],"% 4X ",fifoBuffer[i]);
i++;
i %= FIFOMAX;
booloutBuffer=false;
if( fifoBuffer[i] &0x8000){
outBuffer=true;
k=0;
}
if((outBuffer)||(k>50)||(i==fifoPos)) log_i("%s",buf);
outBuffer= false;
if(k>50) {
k=sprintf(buf,"-> ");
}
}while( i!=fifoPos);
}
}
#endif
staticvoidIRAM_ATTRi2cTriggerDumps(i2c_t*i2c, uint8_ttrigger, constcharlocus[]){
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
if( trigger ){
log_i("%s",locus);
if(trigger&1) i2cDumpI2c(i2c);
if(trigger&2) i2cDumpInts(i2c->num);
if(trigger&4) i2cDumpCmdQueue(i2c);
if(trigger&8) i2cDumpStatus(i2c);
if(trigger&16) i2cDumpFifo(i2c);
}
#endif
}
// end of debug support routines
/* Start of CPU Clock change Support
*/
staticvoidi2cApbChangeCallback(void*arg, apb_change_ev_tev_type, uint32_told_apb, uint32_tnew_apb){
i2c_t*i2c= (i2c_t*) arg; // recover data
if(i2c==NULL) { // point to peripheral control block does not exits
return false;
}
uint32_toldFreq=0;
switch(ev_type){
caseAPB_BEFORE_CHANGE :
if(new_apb<3000000) {// too slow
log_e("apb speed %d too slow",new_apb);
break;
}
I2C_MUTEX_LOCK(); // lock will spin until current transaction is completed
break;
caseAPB_AFTER_CHANGE :
oldFreq= (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); //read old apbCycles
if(oldFreq>0) { // was configured with value
oldFreq=old_apb / oldFreq;
i2cSetFrequency(i2c,oldFreq);
}
I2C_MUTEX_UNLOCK();
break;
default :
log_e("unk ev %u",ev_type);
I2C_MUTEX_UNLOCK();
}
return;
}
/* End of CPU Clock change Support
*/
staticvoidIRAM_ATTRi2cSetCmd(i2c_t*i2c, uint8_tindex, uint8_top_code, uint8_tbyte_num, boolack_val, boolack_exp, boolack_check)
{
I2C_COMMAND_tcmd;
cmd.val=0;
cmd.ack_en=ack_check;
cmd.ack_exp=ack_exp;
cmd.ack_val=ack_val;
cmd.byte_num=byte_num;
cmd.op_code=op_code;
i2c->dev->command[index].val=cmd.val;
}
staticvoidIRAM_ATTRfillCmdQueue(i2c_t*i2c, boolINTS)
{
/* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs
*/
uint16_tcmdIdx=0;
uint16_tqp=i2c->queuePos; // all queues before queuePos have been completely processed,
// so look start by checking the 'current queue' so see if it needs commands added to
// hardware peripheral command list. walk through each queue entry until all queues have been
// checked
booldone;
boolneedMoreCmds= false;
boolena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ
boolena_tx=false; // if we add a Write op, better enable TX_Fifo IRQ
while(!needMoreCmds&&(qp<i2c->queueCount)) { // check if more possible cmds
if(i2c->dq[qp].ctrl.stopCmdSent) {// marks that all required cmds[] have been added to peripheral
qp++;
} else {
needMoreCmds=true;
}
}
//log_e("needMoreCmds=%d",needMoreCmds);
done=(!needMoreCmds)||(cmdIdx>14);
while(!done) { // fill the command[] until either 0..14 filled or out of cmds and last cmd is STOP
//CMD START
I2C_DATA_QUEUE_t*tdq=&i2c->dq[qp]; // simpler coding
if((!tdq->ctrl.startCmdSent) && (cmdIdx<14)) { // has this dq element's START command been added?
// (cmdIdx<14) because a START op cannot directly precede an END op, else a time out cascade occurs
i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false);
tdq->ctrl.startCmdSent=1;
}
//CMD WRITE ADDRESS
if((!done)&&(tdq->ctrl.startCmdSent)) { // have to leave room for continue(END), and START must have been sent!
if(!tdq->ctrl.addrCmdSent) {
i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, tdq->ctrl.addrReq, false, false, true); //load address in cmdlist, validate (low) ack
tdq->ctrl.addrCmdSent=1;
done=(cmdIdx>14);
ena_tx=true; // tx Data necessary
}
}
/* Can I have another Sir?
ALL CMD queues must be terminated with either END or STOP.
If END is used, when refilling the cmd[] next time, no entries from END to [15] can be used.
AND the cmd[] must be filled starting at [0] with commands. Either fill all 15 [0]..[14] and leave the
END in [15] or include a STOP in one of the positions [0]..[14]. Any entries after a STOP are IGNORED by the StateMachine.
The END operation does not complete until ctr->trans_start=1 has been issued.
So, only refill from [0]..[14], leave [15] for a continuation(END) if necessary.
As a corollary, once END exists in [15], you do not need to overwrite it for the
next continuation. It is never modified. But, I update it every time because it might
actually be the first time!
23NOV17 START cannot proceed END. if START is in[14], END cannot be in [15].
So, if END is moved to [14], [14] and [15] can no longer be used for anything other than END.
If a START is found in [14] then a prior READ or WRITE must be expanded so that there is no START element in [14].
*/
if((!done)&&(tdq->ctrl.addrCmdSent)) { //room in command[] for at least One data (read/Write) cmd
uint8_tblkSize=0; // max is 255
while(( tdq->cmdBytesNeeded>tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END
blkSize= (tdq->cmdBytesNeeded>255)?255:(tdq->cmdBytesNeeded-tdq->ctrl.mode); // Last read cmd needs different ACK setting, so leave 1 byte remainder on reads
tdq->cmdBytesNeeded-=blkSize;
if(tdq->ctrl.mode==1) { //read mode
i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read.
ena_rx=true; // need to enable rxFifo IRQ
} else { // write
i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, blkSize, false, false, true); // check for Nak
ena_tx=true; // need to enable txFifo IRQ
}
done=cmdIdx>14; //have to leave room for END
}
if(!done) { // buffer is not filled completely
if((tdq->ctrl.mode==1)&&(tdq->cmdBytesNeeded==1)) { //special last read byte NAK
i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false);
// send NAK to mark end of read
tdq->cmdBytesNeeded=0;
done=cmdIdx>14;
ena_rx=true;
}
}
tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data
if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop
if(!tdq->ctrl.stopCmdSent){
if(tdq->ctrl.stop) { //send a stop
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false);
done=cmdIdx>14;
}
tdq->ctrl.stopCmdSent=1;
}
}
}
if((cmdIdx==14)&&(!tdq->ctrl.startCmdSent)) {
// START would have preceded END, causes SM TIMEOUT
// need to stretch out a prior WRITE or READ to two Command[] elements
done= false; // reuse it
uint16_ti=13; // start working back until a READ/WRITE has >1 numBytes
cmdIdx=15;
while(!done) {
i2c->dev->command[i+1].val=i2c->dev->command[i].val; // push it down
if (((i2c->dev->command[i].op_code==1)||(i2c->dev->command[i].op_code==2))) {
/* add a dummy read/write cmd[] with num_bytes set to zero,just a place holder in the cmd[]list
*/
i2c->dev->command[i].byte_num=0;
done= true;
} else {
if(i>0) {
i--;
} else { // unable to stretch, fatal
log_e("invalid CMD[] layout Stretch Failed");
i2cDumpCmdQueue(i2c);
done= true;
}
}
}
}
if(cmdIdx==15) { //need continuation, even if STOP is in 14, it will not matter
// cmd buffer is almost full, Add END as a continuation feature
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false);
i2c->dev->int_ena.end_detect=1;
i2c->dev->int_clr.end_detect=1;
done= true;
}
if(!done) {
if(tdq->ctrl.stopCmdSent) { // this queue element has been completely added to command[] buffer
qp++;
if(qp<i2c->queueCount) {
tdq=&i2c->dq[qp];
} else {
done= true;
}
}
}
}// while(!done)
if(INTS) { // don't want to prematurely enable fifo ints until ISR is ready to handle them.
if(ena_rx) {
i2c->dev->int_ena.rx_fifo_full=1;
}
if(ena_tx) {
i2c->dev->int_ena.tx_fifo_empty=1;
}
}
}
staticvoidIRAM_ATTRfillTxFifo(i2c_t*i2c)
{
/*
12/01/2017 The Fifo's are independent, 32 bytes of tx and 32 bytes of Rx.
overlap is not an issue, just keep them full/empty the status_reg.xx_fifo_cnt
tells the truth. And the INT's fire correctly
*/
uint16_ta=i2c->queuePos; // currently executing dq,
boolfull=!(i2c->dev->status_reg.tx_fifo_cnt<31);
uint8_tcnt;
boolrxQueueEncountered= false;
while((a<i2c->queueCount) && !full) {
I2C_DATA_QUEUE_t*tdq=&i2c->dq[a];
cnt=0;
// add to address to fifo ctrl.addr already has R/W bit positioned correctly
if(tdq->ctrl.addrSent<tdq->ctrl.addrReq) { // need to send address bytes
if(tdq->ctrl.addrReq==2) { //10bit
if(tdq->ctrl.addrSent==0) { //10bit highbyte needs sent
if(!full) { // room in fifo
i2c->dev->fifo_data.val= ((tdq->ctrl.addr>>8)&0xFF);
cnt++;
tdq->ctrl.addrSent=1; //10bit highbyte sent
}
}
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
if(tdq->ctrl.addrSent==1) { //10bit Lowbyte needs sent
if(!full) { // room in fifo
i2c->dev->fifo_data.val=tdq->ctrl.addr&0xFF;
cnt++;
tdq->ctrl.addrSent=2; //10bit lowbyte sent
}
}
} else { // 7bit}
if(tdq->ctrl.addrSent==0) { // 7bit Lowbyte needs sent
if(!full) { // room in fifo
i2c->dev->fifo_data.val=tdq->ctrl.addr&0xFF;
cnt++;
tdq->ctrl.addrSent=1; // 7bit lowbyte sent
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
uint16_ta=0x8000 | tdq->ctrl.addr | (tdq->ctrl.mode<<8);
fifoBuffer[fifoPos++] =a;
fifoPos %= FIFOMAX;
#endif
}
}
}
}
// add write data to fifo
if(tdq->ctrl.mode==0) { // write
if(tdq->ctrl.addrSent==tdq->ctrl.addrReq) { //address has been sent, is Write Mode!
uint32_tmoveCnt=32-i2c->dev->status_reg.tx_fifo_cnt; // how much room in txFifo?
while(( moveCnt>0)&&(tdq->position<tdq->length)) {
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
fifoBuffer[fifoPos++] =tdq->data[tdq->position];
fifoPos %= FIFOMAX;
#endif
i2c->dev->fifo_data.val=tdq->data[tdq->position++];
cnt++;
moveCnt--;
}
}
} else { // read Queue Encountered, can't update QueuePos past this point, emptyRxfifo will do it
if( ! rxQueueEncountered ) {
rxQueueEncountered= true;
if(a>i2c->queuePos){
i2c->queuePos=a;
}
}
}
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
// update debug buffer tx counts
cnt+=intBuff[intPos[i2c->num]][1][i2c->num]>>16;
intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16);
#endif
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
if(!full) {
a++; // check next buffer for address tx, or write data
}
}
if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued
i2c->dev->int_ena.tx_fifo_empty=0;
}
i2c->dev->int_clr.tx_fifo_empty=1;
}
staticvoidIRAM_ATTRemptyRxFifo(i2c_t*i2c)
{
uint32_td, cnt=0, moveCnt;
moveCnt=i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read
while((moveCnt>0)&&(i2c->queuePos<i2c->queueCount)){ // data to move
I2C_DATA_QUEUE_t*tdq=&i2c->dq[i2c->queuePos]; //short cut
while((tdq->position >= tdq->length)&&( i2c->queuePos<i2c->queueCount)){ // find were to store
i2c->queuePos++;
tdq=&i2c->dq[i2c->queuePos];
}
if(i2c->queuePos >= i2c->queueCount){ // bad stuff, rx data but no place to put it!
log_e("no Storage location for %d",moveCnt);
// discard
while(moveCnt>0){
d=i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
}
break;
}
if(tdq->ctrl.mode==1){ // read command
if(moveCnt> (tdq->length-tdq->position)) { //make sure they go in this dq
// part of these reads go into the next dq
moveCnt= (tdq->length-tdq->position);
}
} else {// error
log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos);
// discard
while(moveCnt>0){
d=i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
}
break;
}
while(moveCnt>0) { // store data
d=i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
tdq->data[tdq->position++] = (d&0xFF);
}
moveCnt=i2c->dev->status_reg.rx_fifo_cnt; //any more out there?
}
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER)
// update Debug rxCount
cnt+= (intBuff[intPos[i2c->num]][1][i2c->num])&0xffFF;
intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt;
#endif
}
staticvoidIRAM_ATTRi2cIsrExit(i2c_t*i2c,constuint32_teventCode,boolFatal)
{
switch(eventCode) {
caseEVENT_DONE:
i2c->error=I2C_OK;
break;
caseEVENT_ERROR_NAK :
i2c->error=I2C_ADDR_NAK;
break;
caseEVENT_ERROR_DATA_NAK :
i2c->error=I2C_DATA_NAK;
break;
caseEVENT_ERROR_TIMEOUT :
i2c->error=I2C_TIMEOUT;
break;
caseEVENT_ERROR_ARBITRATION:
i2c->error=I2C_ARBITRATION;
break;
default :
i2c->error=I2C_ERROR;
}
uint32_texitCode=EVENT_DONE | eventCode |(Fatal?EVENT_ERROR:0);
if((i2c->queuePos<i2c->queueCount) && (i2c->dq[i2c->queuePos].ctrl.mode==1)) {
emptyRxFifo(i2c); // grab last few characters
}
i2c->dev->int_ena.val=0; // shut down interrupts
i2c->dev->int_clr.val=0x1FFF;
i2c->stage=I2C_DONE;
i2c->exitCode=exitCode; //true eventcode
portBASE_TYPEHPTaskAwoken=pdFALSE,xResult;
// try to notify Dispatch we are done,
// else the 50ms time out will recover the APP, just a little slower
HPTaskAwoken=pdFALSE;
xResult=xEventGroupSetBitsFromISR(i2c->i2c_event, exitCode, &HPTaskAwoken);
if(xResult==pdPASS) {
if(HPTaskAwoken==pdTRUE) {
portYIELD_FROM_ISR();
// log_e("Yield to Higher");
}
}
}
staticvoidIRAM_ATTRi2c_update_error_byte_cnt(i2c_t*i2c)
{
/* i2c_update_error_byte_cnt 07/18/2018
Only called after an error has occurred, so, most of the time this function is never used.
This function obliterates the need to interrupt monitor each byte transferred, at high bitrates
the byte interrupts were overwhelming the OS. Spurious Interrupts were being generated.
it updates errorByteCnt, errorQueue.
*/
uint16_ta=0; // start at top of DQ, count how many bytes added to tx fifo, and received from rx_fifo.
int16_tbc=0;
I2C_DATA_QUEUE_t*tdq;
i2c->errorByteCnt=0;
while( a<i2c->queueCount){ // add up all bytes loaded into fifo's
tdq=&i2c->dq[a];
i2c->errorByteCnt+=tdq->ctrl.addrSent;
i2c->errorByteCnt+=tdq->position;
a++;
}
// now errorByteCnt contains total bytes moved into and out of FIFO's
// but, there may still be bytes waiting in Fifo's
i2c->errorByteCnt-=i2c->dev->status_reg.tx_fifo_cnt; // waiting to go out;
i2c->errorByteCnt+=i2c->dev->status_reg.rx_fifo_cnt; // already received
// now walk thru DQ again, find which byte is 'current'
bc=i2c->errorByteCnt;
i2c->errorQueue=0;
while( i2c->errorQueue<i2c->queueCount ){
tdq=&i2c->dq[i2c->errorQueue];
if(bc>0){ // not found yet
if( tdq->ctrl.addrSent >= bc){ // in address
bc=-1; // in address
break;
} else {
bc-=tdq->ctrl.addrSent;
if( tdq->length>bc) { // data nak
break;
} else { // count down
bc-=tdq->length;
}
}
} elsebreak;
i2c->errorQueue++;
}
i2c->errorByteCnt=bc;
}
staticvoidIRAM_ATTRi2c_isr_handler_default(void*arg)
{
i2c_t*p_i2c= (i2c_t*) arg; // recover data
uint32_tactiveInt=p_i2c->dev->int_status.val&0x7FF;
if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured
p_i2c->dev->int_ena.val=0;
p_i2c->dev->int_clr.val=0x1FFF;
#ifARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
uint32_traw=p_i2c->dev->int_raw.val;
log_w("eject raw=%p, int=%p",raw,activeInt);
#endif
return;
}
while (activeInt!=0) { // Ordering of 'if(activeInt)' statements is important, don't change
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
if(activeInt==(intBuff[intPos[p_i2c->num]][0][p_i2c->num]&0x1fff)) {
intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (((intBuff[intPos[p_i2c->num]][0][p_i2c->num]>>16)+1)<<16)|activeInt;
} else {
intPos[p_i2c->num]++;
intPos[p_i2c->num] %= INTBUFFMAX;
intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (1<<16) | activeInt;
intBuff[intPos[p_i2c->num]][1][p_i2c->num] =0;
}
intBuff[intPos[p_i2c->num]][2][p_i2c->num] =xTaskGetTickCountFromISR(); // when IRQ fired
#endif
if (activeInt&I2C_TRANS_START_INT_ST_M) {
if(p_i2c->stage==I2C_STARTUP) {
p_i2c->stage=I2C_RUNNING;
}
activeInt &=~I2C_TRANS_START_INT_ST_M;
p_i2c->dev->int_ena.trans_start=1; // already enabled? why Again?
p_i2c->dev->int_clr.trans_start=1; // so that will trigger after next 'END'
}
if (activeInt&I2C_TXFIFO_EMPTY_INT_ST) {//should this be before Trans_start?
fillTxFifo(p_i2c); //fillTxFifo will enable/disable/clear interrupt
activeInt&=~I2C_TXFIFO_EMPTY_INT_ST;
}
if(activeInt&I2C_RXFIFO_FULL_INT_ST) {
emptyRxFifo(p_i2c);
p_i2c->dev->int_clr.rx_fifo_full=1;
p_i2c->dev->int_ena.rx_fifo_full=1; //why?
activeInt &=~I2C_RXFIFO_FULL_INT_ST;
}
if (activeInt&I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service
if (p_i2c->mode==I2C_MASTER) {
i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data
if(p_i2c->errorByteCnt<0 ) { // address
i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true);
} else {
i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data
}
}
return;
}
if (activeInt&I2C_TIME_OUT_INT_ST_M) {
// let Gross timeout occur, Slave may release SCL before the configured timeout expires
// the Statemachine only has a 13.1ms max timout, some Devices >500ms
p_i2c->dev->int_clr.time_out=1;
activeInt &=~I2C_TIME_OUT_INT_ST;
// since a timeout occurred, capture the rxFifo data
emptyRxFifo(p_i2c);
p_i2c->dev->int_clr.rx_fifo_full=1;
p_i2c->dev->int_ena.rx_fifo_full=1; //why?
}
if (activeInt&I2C_TRANS_COMPLETE_INT_ST_M) {
p_i2c->dev->int_clr.trans_complete=1;
i2cIsrExit(p_i2c,EVENT_DONE,false);
return; // no more work to do
/*
// how does slave mode act?
if (p_i2c->mode == I2C_SLAVE) { // STOP detected
// empty fifo
// dispatch callback
*/
}
if (activeInt&I2C_ARBITRATION_LOST_INT_ST_M) { //fatal
i2cIsrExit(p_i2c,EVENT_ERROR_ARBITRATION,true);
return; // no more work to do
}
if (activeInt&I2C_SLAVE_TRAN_COMP_INT_ST_M) {
p_i2c->dev->int_clr.slave_tran_comp=1;
// need to complete this !
}
if (activeInt&I2C_END_DETECT_INT_ST_M) {
p_i2c->dev->int_ena.end_detect=0;
p_i2c->dev->int_clr.end_detect=1;
p_i2c->dev->ctr.trans_start=0;
fillCmdQueue(p_i2c,true); // enable interrupts
p_i2c->dev->ctr.trans_start=1; // go for it
activeInt&=~I2C_END_DETECT_INT_ST_M;