@@ -382,56 +382,127 @@ int i2c_slave_receive(i2c_t *obj) {
382
382
}
383
383
384
384
int i2c_slave_read (i2c_t * obj , char * data , int length ) {
385
+
385
386
int count = 0 ;
386
- int status ;
387
-
388
- do {
389
- i2c_clear_SI (obj );
390
- i2c_wait_SI (obj );
391
- status = i2c_status (obj );
392
- if ((status == 0x80 ) || (status == 0x90 )) {
393
- data [count ] = I2C_DAT (obj ) & 0xFF ;
387
+
388
+ if (i2c_status (obj ) != 0x60 && i2c_status (obj ) != 0x70 ) {
389
+ return -1 ; // I2C peripheral not in setup-write-to-slave mode
390
+ }
391
+
392
+ if (i2c_status (obj ) == 0x70 ) {
393
+ // When addressed with the general call address, per the manual we can only receive a max of 1 byte.
394
+ if (length > 1 ) {
395
+ length = 1 ;
394
396
}
395
- count ++ ;
396
- } while (((status == 0x80 ) || (status == 0x90 ) ||
397
- (status == 0x060 ) || (status == 0x70 )) && (count < length ));
398
-
399
- // Clear old status and wait for Serial Interrupt.
400
- i2c_clear_SI (obj );
401
- i2c_wait_SI (obj );
402
-
403
- // Obtain new status.
404
- status = i2c_status (obj );
405
-
406
- if (status != 0xA0 ) {
407
- i2c_stop (obj );
408
397
}
409
-
398
+
399
+ i2c_conset (obj , 0 , 0 , 0 , length > 0 ); // Set AA flag to acknowledge write as long as we have buffer space to store a byte in
410
400
i2c_clear_SI (obj );
411
-
412
- return count ;
401
+
402
+ // This is implemented as a state machine according to section 19.10.8 in the reference manual.
403
+ while (true) {
404
+
405
+ // Wait until the I2C peripheral has an event for us
406
+ i2c_wait_SI (obj );
407
+
408
+ #if LPC1768_I2C_DEBUG
409
+ printf ("i2c_slave_read(): in state 0x%hhx\n" , i2c_status (obj ));
410
+ #endif
411
+
412
+ switch (i2c_status (obj )) {
413
+ case 0x68 :
414
+ case 0x78 :
415
+ // Arbitration Lost event. I'm a bit confused about how this can happen as a slave device but the manual says it can...
416
+ i2c_conclr (obj , 1 , 0 , 0 , 1 ); // Clear start and ack
417
+ i2c_clear_SI (obj );
418
+
419
+ // Reset the I2C state machine. This doesn't actually send a STOP condition in slave mode.
420
+ i2c_stop (obj );
421
+ return -2 ; // Arbitration lost
422
+
423
+ case 0x80 :
424
+ case 0x90 :
425
+ // Received another data byte from master. ACK has been returned.
426
+ data [count ++ ] = I2C_DAT (obj ) & 0xFF ;
427
+
428
+ if (count >= length ) {
429
+ // Out of buffer space, NACK the next byte
430
+ i2c_conclr (obj , 0 , 0 , 0 , 1 );
431
+ i2c_clear_SI (obj );
432
+ }
433
+ else {
434
+ // Ack the next byte
435
+ i2c_conset (obj , 0 , 0 , 0 , 1 );
436
+ i2c_clear_SI (obj );
437
+ }
438
+ break ;
439
+
440
+ case 0x88 :
441
+ case 0x98 :
442
+ // Master wrote us a byte and we NACKed it. Slave receive mode has been exited.
443
+ i2c_conset (obj , 0 , 0 , 0 , 1 ); // Set AA flag so that we go back to idle slave state
444
+ i2c_clear_SI (obj );
445
+ return count ;
446
+
447
+ case 0xA0 :
448
+ // Stop condition received. Slave receive mode has been exited.
449
+ i2c_conset (obj , 0 , 0 , 0 , 1 ); // Set AA flag so that we go back to idle slave state
450
+ i2c_clear_SI (obj );
451
+ return count ;
452
+ }
453
+ }
413
454
}
414
455
415
456
int i2c_slave_write (i2c_t * obj , const char * data , int length ) {
416
- int count = 0 ;
417
- int status ;
418
-
419
- if (length <= 0 ) {
420
- return (0 );
421
- }
422
-
423
- do {
424
- status = i2c_do_write (obj , data [count ], 0 );
425
- count ++ ;
426
- } while ((count < length ) && (status == 0xB8 ));
427
-
428
- if ((status != 0xC0 ) && (status != 0xC8 )) {
429
- i2c_stop (obj );
457
+
458
+ int next_byte_idx = 0 ;
459
+
460
+ if (i2c_status (obj ) != 0xA8 ) {
461
+ return -1 ; // I2C peripheral not in setup-read-from-slave mode
430
462
}
431
463
464
+ // Write first data byte. Note that there's no way for the slave to NACK a read-from-slave event, so if we run out of bytes,
465
+ // just send zeroes.
466
+ I2C_DAT (obj ) = (next_byte_idx < length ) ? data [next_byte_idx ] : 0 ;
467
+ ++ next_byte_idx ;
468
+
469
+ i2c_conset (obj , 0 , 0 , 0 , 1 ); // Set AA flag to acknowledge read as long as we have something to transmit
432
470
i2c_clear_SI (obj );
433
-
434
- return (count );
471
+
472
+ // This is implemented as a state machine according to section 19.10.9 in the reference manual.
473
+ while (true) {
474
+
475
+ // Wait until the I2C peripheral has an event for us
476
+ i2c_wait_SI (obj );
477
+
478
+ switch (i2c_status (obj )) {
479
+ case 0xB0 :
480
+ // Arbitration Lost event. I'm a bit confused about how this can happen as a slave device but the manual says it can...
481
+ i2c_conclr (obj , 1 , 0 , 0 , 1 ); // Clear start and ack
482
+ i2c_clear_SI (obj );
483
+
484
+ // Reset the I2C state machine. This doesn't actually send a STOP condition in slave mode.
485
+ i2c_stop (obj );
486
+ return -2 ; // Arbitration lost
487
+
488
+ case 0xB8 :
489
+ // Prev data byte has been transmitted. ACK has been received. Write the next data byte.
490
+ I2C_DAT (obj ) = (next_byte_idx < length ) ? data [next_byte_idx ] : 0 ;
491
+ ++ next_byte_idx ;
492
+
493
+ i2c_conset (obj , 0 , 0 , 0 , 1 ); // Set AA flag to acknowledge read as long as we have something to transmit
494
+ i2c_clear_SI (obj );
495
+ break ;
496
+
497
+ case 0xC0 :
498
+ case 0xC8 :
499
+ // Last data byte transmitted (ended either by us not setting AA, or by the master NACKing)
500
+ i2c_conset (obj , 0 , 0 , 0 , 1 ); // Set AA flag so that we go back to idle slave state
501
+ i2c_clear_SI (obj );
502
+
503
+ return next_byte_idx ;
504
+ }
505
+ }
435
506
}
436
507
437
508
void i2c_slave_address (i2c_t * obj , int idx , uint32_t address , uint32_t mask ) {
0 commit comments