Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
BitBangI2C.hxx
Go to the documentation of this file.
1
34#ifndef _FREERTOS_DRIVERS_COMMON_BITBANG_I2C_HXX_
35#define _FREERTOS_DRIVERS_COMMON_BITBANG_I2C_HXX_
36
37#include "I2C.hxx"
38#include "os/Gpio.hxx"
39#include "os/OS.hxx"
40
44{
45protected:
48 {
49 }
50
52 enum class State : uint8_t
53 {
54 START,
55 ADDRESS,
56 DATA_TX,
57 DATA_RX,
58 RECOVERY,
59 STOP,
60 };
61
63 enum class StateStart : uint8_t
64 {
65 SDA_SET,
66 SCL_SET,
67 SDA_CLR,
68 FIRST = SDA_SET,
69 LAST = SDA_CLR,
70 };
71
73 enum class StateStop : uint8_t
74 {
75 SDA_CLR,
76 SCL_SET,
77 SDA_SET,
78 FIRST = SDA_CLR,
79 LAST = SDA_SET,
80 };
81
107
132
158
161
163 friend StateStop &operator++(StateStop &);
164
166 friend StateTx &operator++(StateTx &);
167
169 friend StateRx &operator++(StateRx &);
170
173};
174
235template <class HW> class BitBangI2C : protected BitBangI2CStates
236 , public I2C
237 , private Atomic
238{
239public:
242 BitBangI2C(const char *name)
243 : I2C(name)
244 , msg_(nullptr)
245 , sem_()
246 , count_(0)
247 , state_(State::STOP) // start-up with a stop sequence
249 , stop_(true)
250 , clockStretchActive_(false)
251 {
252 HW::SDA_Pin::set(1);
253 HW::SCL_Pin::set(0);
254 }
255
258 {
259 }
260
262 inline void tick_interrupt();
263
264private:
268 inline bool state_start();
269
273 inline bool state_stop();
274
280 inline bool state_tx(uint8_t data);
281
287 inline bool state_rx(uint8_t *data, bool nack);
288
292 inline bool state_recovery();
293
294 void enable() override {}
295 void disable() override {}
301 inline int transfer(struct i2c_msg *msg, bool stop) override;
302
303 struct i2c_msg *msg_;
305 int count_;
307 union
308 {
314 };
315 bool stop_;
317};
318
323{
325 x < BitBangI2CStates::StateStart::LAST)
326 {
327 x = static_cast<BitBangI2CStates::StateStart>(static_cast<int>(x) + 1);
328 }
329 return x;
330}
331
336{
339 {
340 x = static_cast<BitBangI2CStates::StateStop>(static_cast<int>(x) + 1);
341 }
342 return x;
343}
344
349{
352 {
353 x = static_cast<BitBangI2CStates::StateTx>(static_cast<int>(x) + 1);
354 }
355 return x;
356}
357
362{
365 {
366 x = static_cast<BitBangI2CStates::StateRx>(static_cast<int>(x) + 1);
367 }
368 return x;
369}
370
376{
379 {
380 x = static_cast<BitBangI2CStates::StateRecovery>(
381 static_cast<int>(x) + 1);
382 }
383 return x;
384}
385
386//
387// BitBangI2C::tick_interrupt()
388//
389template <class HW> __attribute__((optimize("-O3")))
390inline void BitBangI2C<HW>::tick_interrupt()
391{
392 bool exit = false;
393 switch (state_)
394 {
395 // start sequence
396 case State::START:
397 if (state_start())
398 {
399 // start done, send the address
400 state_ = State::ADDRESS;
401 stateTx_ = StateTx::FIRST;
402 }
403 break;
404 case State::ADDRESS:
405 {
407 uint8_t address = msg_->addr << 1;
408 if (msg_->flags & I2C_M_RD)
409 {
410 address |= 0x1;
411 }
412 if (state_tx(address))
413 {
414 if (count_ < 0)
415 {
416 // Some error occured, likely an unexpected NACK. Send a
417 // stop in order to shutdown gracefully.
418 state_ = State::STOP;
419 stateStop_ = StateStop::FIRST;
420 }
421 else
422 {
423 if (msg_->flags & I2C_M_RD)
424 {
425 state_ = State::DATA_RX;
426 stateRx_ = StateRx::FIRST;
427 }
428 else
429 {
430 state_ = State::DATA_TX;
431 stateTx_ = StateTx::FIRST;
432 }
433 }
434 }
435 break;
436 }
437 case State::DATA_TX:
438 if (state_tx(msg_->buf[count_]))
439 {
440 if (count_ < 0)
441 {
442 // Some error occured, likely an unexpected NACK. Send a
443 // stop in order to shutdown gracefully.
444 state_ = State::STOP;
445 stateStop_ = StateStop::FIRST;
446 }
447 else if (++count_ >= msg_->len)
448 {
449 // All of the data has been transmitted.
450 if (stop_)
451 {
452 state_ = State::STOP;
453 stateStop_ = StateStop::FIRST;
454 }
455 else
456 {
457 exit = true;
458 }
459 }
460 else
461 {
462 // Setup the next byte TX
463 stateTx_ = StateTx::FIRST;
464 }
465 }
466 break;
467 case State::DATA_RX:
468 if (state_rx(msg_->buf + count_, (count_ + 1 >= msg_->len)))
469 {
470 if (count_ < 0)
471 {
472 // Some error occured, likely an unexpected NACK. Send a
473 // stop in order to shutdown gracefully.
474 state_ = State::STOP;
475 stateStop_ = StateStop::FIRST;
476 }
477 else if (++count_ >= msg_->len)
478 {
479 // All of the data has been received.
480 if (stop_)
481 {
482 state_ = State::STOP;
483 stateStop_ = StateStop::FIRST;
484 }
485 else
486 {
487 exit = true;
488 }
489 }
490 else
491 {
492 // Setup the next byte RX
493 stateRx_ = StateRx::FIRST;
494 }
495 }
496 break;
497 case State::RECOVERY:
498 if (state_recovery())
499 {
500 // Recovery always concludes with a stop condition
501 state_ = State::STOP;
502 stateStop_ = StateStop::FIRST;
503 }
504 break;
505 case State::STOP:
506 exit = state_stop();
507 break;
508 }
509
510 if (exit)
511 {
512 HW::tick_disable();
513 int woken = 0;
514 sem_.post_from_isr(&woken);
515 os_isr_exit_yield_test(woken);
516 }
517}
518
519//
520// BitBangI2C::state_start()
521//
522template <class HW> __attribute__((optimize("-O3")))
523inline bool BitBangI2C<HW>::state_start()
524{
525 switch (stateStart_)
526 {
527 // start sequence
528 case StateStart::SDA_SET:
529 HW::SDA_Pin::set(1);
530 ++stateStart_;
531 break;
532 case StateStart::SCL_SET:
533 HW::SCL_Pin::set(1);
534 ++stateStart_;
535 break;
536 case StateStart::SDA_CLR:
537 HW::SDA_Pin::set(0);
538 ++stateStart_;
539 return true;
540 }
541 return false;
542}
543
544//
545// BitBangI2C::state_stop()
546//
547template <class HW> __attribute__((optimize("-O3")))
548inline bool BitBangI2C<HW>::state_stop()
549{
550 switch (stateStop_)
551 {
552 case StateStop::SDA_CLR:
553 HW::SDA_Pin::set(0);
554 ++stateStop_;
555 break;
556 case StateStop::SCL_SET:
557 HW::SCL_Pin::set(1);
558 ++stateStop_;
559 break;
560 case StateStop::SDA_SET:
561 HW::SDA_Pin::set(1);
562 stop_ = false;
563 return true; // exit
564 }
565 return false;
566}
567
568//
569// BitBangI2C::state_tx()
570//
571template <class HW> __attribute__((optimize("-O3")))
572inline bool BitBangI2C<HW>::state_tx(uint8_t data)
573{
574 // I2C is specified such that the data on the SDA line must be stable
575 // during the high period of the clock, and the data line can only change
576 // when SCL is Low.
577 //
578 // This means that the sequence always has to be
579 // 1a. SCL := low
580 // 1b. set/clear SDA
581 // 2. wait a cycle for lines to settle
582 // 3a. SCL := high
583 // 3b. this edge is when the receiver samples
584 // 4. wait a cycle, lines are stable
585 switch(stateTx_)
586 {
587 case StateTx::DATA_7_SCL_CLR:
588 case StateTx::DATA_6_SCL_CLR:
589 case StateTx::DATA_5_SCL_CLR:
590 case StateTx::DATA_4_SCL_CLR:
591 case StateTx::DATA_3_SCL_CLR:
592 case StateTx::DATA_2_SCL_CLR:
593 case StateTx::DATA_1_SCL_CLR:
594 case StateTx::DATA_0_SCL_CLR:
595 {
596 // We can only change the data when SCL is low.
597 HW::SCL_Pin::set(0);
598 // The divide by 2 factor (shift right by 1) is because the enum
599 // states alternate between *_SCL_SET and *_SCL_CLR states in
600 // increasing value.
601 uint8_t mask = 0x80 >>
602 ((static_cast<int>(stateTx_) -
603 static_cast<int>(StateTx::DATA_7_SCL_CLR)) >> 1);
604 if (data & mask)
605 {
606 HW::SDA_Pin::set(1);
607 }
608 else
609 {
610 HW::SDA_Pin::set(0);
611 }
612 ++stateTx_;
613 break;
614 }
615 case StateTx::DATA_7_SCL_SET:
616 case StateTx::DATA_6_SCL_SET:
617 case StateTx::DATA_5_SCL_SET:
618 case StateTx::DATA_4_SCL_SET:
619 case StateTx::DATA_3_SCL_SET:
620 case StateTx::DATA_2_SCL_SET:
621 case StateTx::DATA_1_SCL_SET:
622 case StateTx::DATA_0_SCL_SET:
623 // Data is sampled by the slave following this state transition.
624 HW::SCL_Pin::set(1);
625 ++stateTx_;
626 break;
627 case StateTx::ACK_SDA_SET_SCL_CLR:
628 HW::SCL_Pin::set(0);
629 HW::SDA_Pin::set(1);
630 ++stateTx_;
631 break;
632 case StateTx::ACK_SCL_SET:
633 HW::SCL_Pin::set(1);
634 ++stateTx_;
635 break;
636 case StateTx::ACK_SCL_CLR:
637 if (HW::SCL_Pin::get() == false)
638 {
639 // Clock stretching, do the same state again.
640 clockStretchActive_ = true;
641 break;
642 }
643 if (clockStretchActive_)
644 {
645 // I2C spec requires minimum 4 microseconds after the SCL line
646 // goes high following a clock stretch for the SCL line to be
647 // pulled low again by the master. Do the same state one more
648 // time to ensure this.
649 clockStretchActive_ = false;
650 break;
651 }
652 bool ack = (HW::SDA_Pin::get() == false);
653 HW::SCL_Pin::set(0);
654 if (!ack)
655 {
656 count_ = -EIO;
657 }
658 return true; // done
659 }
660 return false;
661}
662
663//
664// BitBangI2C::state_rx()
665//
666template <class HW> __attribute__((optimize("-O3")))
667inline bool BitBangI2C<HW>::state_rx(uint8_t *data, bool nack)
668{
669 switch(stateRx_)
670 {
671 case StateRx::DATA_7_SCL_SET:
672 HW::SDA_Pin::set(1); // Always start with SDA high.
673 // fall through
674 case StateRx::DATA_6_SCL_SET:
675 case StateRx::DATA_5_SCL_SET:
676 case StateRx::DATA_4_SCL_SET:
677 case StateRx::DATA_3_SCL_SET:
678 case StateRx::DATA_2_SCL_SET:
679 case StateRx::DATA_1_SCL_SET:
680 case StateRx::DATA_0_SCL_SET:
681 HW::SCL_Pin::set(1);
682 ++stateRx_;
683 break;
684 case StateRx::DATA_7_SCL_CLR:
685 case StateRx::DATA_6_SCL_CLR:
686 case StateRx::DATA_5_SCL_CLR:
687 case StateRx::DATA_4_SCL_CLR:
688 case StateRx::DATA_3_SCL_CLR:
689 case StateRx::DATA_2_SCL_CLR:
690 case StateRx::DATA_1_SCL_CLR:
691 case StateRx::DATA_0_SCL_CLR:
692 if (HW::SCL_Pin::get() == false)
693 {
694 // Clock stretching, do the same state again.
695 clockStretchActive_ = true;
696 break;
697 }
698 if (clockStretchActive_)
699 {
700 // I2C spec requires minimum 4 microseconds after the SCL line
701 // goes high following a clock stretch for the SCL line to be
702 // pulled low again by the master or for the master to sample
703 // the SDA data. Do the same state one more time to ensure this.
704 clockStretchActive_ = false;
705 break;
706 }
707 *data <<= 1;
708 if (HW::SDA_Pin::get() == true)
709 {
710 *data |= 0x01;
711 }
712 HW::SCL_Pin::set(0);
713 if (stateRx_ == StateRx::DATA_0_SCL_CLR && !nack)
714 {
715 // Send the ACK. If a NACK, SDA is already set.
716 HW::SDA_Pin::set(0);
717 }
718 ++stateRx_;
719 break;
720 case StateRx::ACK_SDA_SCL_SET:
721 HW::SCL_Pin::set(1);
722 ++stateRx_;
723 break;
724 case StateRx::ACK_SCL_CLR:
725 if (HW::SCL_Pin::get() == false)
726 {
727 // Clock stretching, do the same state again.
728 clockStretchActive_ = true;
729 break;
730 }
731 if (clockStretchActive_)
732 {
733 // I2C spec requires minimum 4 microseconds after the SCL line
734 // goes high following a clock stretch for the SCL line to be
735 // pulled low again by the master. Do the same state one more
736 // time to ensure this.
737 clockStretchActive_ = false;
738 break;
739 }
740 HW::SCL_Pin::set(0);
741 HW::SDA_Pin::set(1);
742 return true;
743 }
744 return false;
745}
746
747//
748// BitBangI2C::state_rx()
749//
750template <class HW> __attribute__((optimize("-O3")))
751inline bool BitBangI2C<HW>::state_recovery()
752{
753 switch(stateRecovery_)
754 {
755 case StateRecovery::SDA_SET:
756 HW::SDA_Pin::set(1); // Always start with SDA high.
757 break;
758 case StateRecovery::DATA_7_SCL_SET:
759 case StateRecovery::DATA_6_SCL_SET:
760 case StateRecovery::DATA_5_SCL_SET:
761 case StateRecovery::DATA_4_SCL_SET:
762 case StateRecovery::DATA_3_SCL_SET:
763 case StateRecovery::DATA_2_SCL_SET:
764 case StateRecovery::DATA_1_SCL_SET:
765 case StateRecovery::DATA_0_SCL_SET:
766 case StateRecovery::ACK_SCL_SET:
767 HW::SCL_Pin::set(1);
768 break;
769 case StateRecovery::DATA_7_SCL_CLR:
770 case StateRecovery::DATA_6_SCL_CLR:
771 case StateRecovery::DATA_5_SCL_CLR:
772 case StateRecovery::DATA_4_SCL_CLR:
773 case StateRecovery::DATA_3_SCL_CLR:
774 case StateRecovery::DATA_2_SCL_CLR:
775 case StateRecovery::DATA_1_SCL_CLR:
776 case StateRecovery::DATA_0_SCL_CLR:
777 case StateRecovery::ACK_SCL_CLR:
778 HW::SCL_Pin::set(0);
779 break;
780 }
781 if (stateRecovery_ == StateRecovery::LAST)
782 {
783 return true;
784 }
785
786 ++stateRecovery_;
787 return false;
788}
789
790//
791// BitBangI2C::transfer()
792//
793template <class HW>
794inline int BitBangI2C<HW>::transfer(struct i2c_msg *msg, bool stop)
795{
796 while (stop_)
797 {
798 // No need for a timed wait here because the stop state machine is not
799 // gated by what happens on the bus (e.g. zero stretching).
800 sem_.wait();
801 }
802 if (msg->len == 0)
803 {
804 // Message must have length of at least 1.
805 return -EINVAL;
806 }
807
808 // Flush/reset semaphore.
809 while (sem_.timedwait(0) == 0)
810 {
811 }
812
813 {
814 AtomicHolder h(this);
815 // Reset state for a start/restart.
816 msg_ = msg;
817 count_ = 0;
818 stop_ = stop;
819 state_ = State::START;
820 stateStart_ = StateStart::FIRST;
821
822 // Enable tick timer.
823 HW::tick_enable();
824 }
825
826 // We wait a minimum of 10 msec to account for any rounding in the "tick"
827 // rate conversion. msg_->len is at least 1. We assume that worst ~50kHz
828 // is the slowest that anyone will try to run the I2C bus, which will
829 // result in just under 200 microseconds per byte. This leaves some room
830 // for clock stretching.
831 long long wait = std::max(MSEC_TO_NSEC(msg_->len), MSEC_TO_NSEC(10));
832 if (sem_.timedwait(wait) == 0)
833 {
834 return count_;
835 }
836 else
837 {
838 // Flush/reset semaphore.
839 while (sem_.timedwait(0) == 0)
840 {
841 }
842
843 // Some kind of problem occured. Try a recovery operation.
844 {
845 AtomicHolder h(this);
846 state_ = State::RECOVERY;
847 stateRecovery_ = StateRecovery::FIRST;
848 }
849
850 // We don't care about the sempahore return value because there is
851 // isn't much more we can do anyways. Just ensure stop_ is false and
852 // ticks are disabled. There is actually no reason to expect the
853 // semaphore to timeout, so this is really just defensive programming.
854 sem_.timedwait(wait);
855
856 // stop_ must be false for the next call of this method. It should only
857 // be true on first entry at start to "reset" the bus to a known state.
858 // On a timeout, it may not have been reset back to false.
859 {
860 AtomicHolder h(this);
861 stop_ = false;
862 HW::tick_disable();
863 }
864
865 return -ETIMEDOUT;
866 }
867}
868
869#endif // _FREERTOS_DRIVERS_COMMON_BITBANG_I2C_HXX_
BitBangI2CStates::StateStart & operator++(BitBangI2CStates::StateStart &x)
Pre-increment operator overload.
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
Consolidate all the state machine enumerations for easy operator overloading.
StateRx
Low level I2C data RX states.
@ FIRST
first data RX sequence state
@ ACK_SDA_SCL_SET
data RX sequence
@ LAST
last data RX sequence state
StateStart
Low level I2C start states.
@ FIRST
first start sequence state
friend StateStart & operator++(StateStart &)
Allow pre-increment definition.
StateRecovery
Low level I2C data RX states.
@ FIRST
first recovery sequence state
@ LAST
last recovery sequence state
StateStop
Low level I2C stop states.
@ FIRST
first stop sequence state
@ LAST
last stop sequence state
BitBangI2CStates()
Default constructor.
State
High level I2C States.
@ ADDRESS
address state
@ DATA_RX
data RX state
@ RECOVERY
recovery state
@ DATA_TX
data TX state
StateTx
Low level I2C data TX states.
@ ACK_SCL_CLR
data TX sequence
@ DATA_4_SCL_CLR
data TX sequence
@ DATA_2_SCL_CLR
data TX sequence
@ DATA_6_SCL_CLR
data TX sequence
@ DATA_7_SCL_CLR
data TX sequence
@ FIRST
first data TX sequence state
@ DATA_5_SCL_SET
data TX sequence
@ DATA_3_SCL_SET
data TX sequence
@ DATA_4_SCL_SET
data TX sequence
@ DATA_3_SCL_CLR
data TX sequence
@ ACK_SDA_SET_SCL_CLR
data TX sequence
@ DATA_5_SCL_CLR
data TX sequence
@ DATA_1_SCL_SET
data TX sequence
@ DATA_1_SCL_CLR
data TX sequence
@ DATA_0_SCL_CLR
data TX sequence
@ DATA_6_SCL_SET
data TX sequence
@ DATA_0_SCL_SET
data TX sequence
@ DATA_2_SCL_SET
data TX sequence
@ LAST
last data TX sequence state
@ ACK_SCL_SET
data TX sequence
@ DATA_7_SCL_SET
data TX sequence
Implement a bit-banged I2C master.
~BitBangI2C()
Destructor.
StateTx stateTx_
I2C data TX state machine state.
bool state_start()
Execute state machine for sending start condition.
StateStart stateStart_
I2C start state machine state.
StateStop stateStop_
I2C stop state machine state.
void disable() override
function to disable device
OSSem sem_
semaphore to wakeup task level from ISR
StateRx stateRx_
I2C data RX state machine state.
bool state_recovery()
Execute recovery state machine.
void enable() override
function to enable device
bool clockStretchActive_
true if the slave is clock stretching.
bool state_tx(uint8_t data)
Execute data TX state machine.
bool state_stop()
Execute state machine for sending the stop condition.
bool stop_
if true, issue stop condition at the end of the message
int count_
the count of data bytes transferred, error if < 0
void tick_interrupt()
Called at a periodic tick, when enabled.
State state_
state machine state
bool state_rx(uint8_t *data, bool nack)
Execute data RX state machine.
struct i2c_msg * msg_
I2C message to presently act upon
StateRecovery stateRecovery_
I2C recovery state machine state.
BitBangI2C(const char *name)
Constructor.
int transfer(struct i2c_msg *msg, bool stop) override
Method to transmit/receive the data.
const char * name
device name
Definition Devtab.hxx:266
Private data for an I2C device.
Definition I2C.hxx:51
This class provides a counting semaphore API.
Definition OS.hxx:243
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268