YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
soft_i2c_master.cpp
1// ---------------------------------------------
2// This file is part of
3// _ _ __ _ _ __ __
4// ( \/ ) /__\ ( )_( ) /__\ ( )
5// \ / /(__)\ ) _ ( /(__)\ )(__
6// (__)(__)(__)(_) (_)(__)(__)(____)
7//
8// Yet Another HW Abstraction Library
9// Copyright (C) Andreas Terstegge
10// BSD Licensed (see file LICENSE)
11//
12// ---------------------------------------------
13//
14
15#include "soft_i2c_master.h"
16
17soft_i2c_master::soft_i2c_master(gpio_interface & sda, gpio_interface & scl,
18 void (*delay)(uint32_t us), bool pullup)
19 : _init(false),
20 _sda(sda),
21 _scl(scl),
22 _delay(delay),
23 _pullup(pullup),
24 _us(0)
25{
26}
27
28soft_i2c_master::~soft_i2c_master()
29{
30 if (!_init) return;
31 _sda.gpioMode(GPIO::INPUT);
32 _scl.gpioMode(GPIO::INPUT);
33}
34
35int16_t soft_i2c_master::i2cRead(uint16_t addr, uint8_t *rxbuf, uint16_t len,
36 bool sendStop)
37{
38 if (!_init) init();
39 if (!send_start()) return -1;
40 addr <<= 1;
41 addr |= 1;
42 addr &= 0xff;
43 if (!write_byte(addr)) {
44 if (sendStop) send_stop();
45 return -2;
46 }
47 for (uint16_t i = 0; i < len; ++i) {
48 rxbuf[i] = read_byte(i==(len-1));
49 }
50 if (sendStop) send_stop();
51 return len;
52}
53
54int16_t soft_i2c_master::i2cWrite(uint16_t addr, uint8_t *txbuf, uint16_t len,
55 bool sendStop)
56{
57 if (!_init) init();
58 if (!send_start()) return -1;
59 addr <<= 1;
60 addr &= 0xff;
61 if (!write_byte(addr)) {
62 if (sendStop) send_stop();
63 return -2;
64 }
65 for (uint16_t i = 0; i < len; ++i) {
66 if (!write_byte(txbuf[i])) {
67 if (sendStop) send_stop();
68 return i;
69 }
70 }
71 if (sendStop) send_stop();
72 return len;
73}
74
75void soft_i2c_master::setSpeed(uint32_t hz) {
76 if (!_init) init();
77 _us = 1000000 / hz;
78 // There are 3 delays during sending/receiving
79 // of a single bit (one SCL cycle). So we have
80 // to divide the time by 3.
81 _us /= 3;
82}
83
84void soft_i2c_master::init()
85{
86 uint16_t mode = GPIO::INPUT |
87 GPIO::OUTPUT_OPEN_DRAIN |
88 GPIO::INIT_HIGH;
89 if (_pullup)
90 mode |= GPIO::PULLUP;
91 // Initialize HW pins
92 _sda.gpioMode(mode);
93 _scl.gpioMode(mode);
94 _init = true;
95}
96
97// private methods
98
99uint8_t soft_i2c_master::read_byte(bool nack)
100{
101 uint8_t byte = 0;
102 for (uint8_t bit = 0; bit < 8; ++bit) {
103 byte = (byte << 1);
104 byte |= read_bit();
105 }
106 write_bit(nack);
107 return byte;
108}
109
110bool soft_i2c_master::write_byte(uint8_t byte)
111{
112 for (uint8_t bit = 0; bit < 8; ++bit) {
113 write_bit(byte & 0x80);
114 byte <<= 1;
115 }
116 return !read_bit(); // LOW means ACK
117}
118
119bool soft_i2c_master::read_bit()
120{
121 _scl.gpioWrite(LOW);
122 _delay(_us);
123 _sda.gpioWrite(HIGH);
124 _delay(_us);
125 _scl.gpioWrite(HIGH);
126 // Clock stretching
127 while (_scl.gpioRead() == LOW) _delay(_us);
128 bool bit = _sda.gpioRead();
129 _delay(_us);
130 return bit;
131}
132
133bool soft_i2c_master::write_bit(bool bit)
134{
135 _scl.gpioWrite(LOW);
136 _delay(_us);
137 _sda.gpioWrite(bit);
138 _delay(_us);
139 _scl.gpioWrite(HIGH);
140 // Clock stretching
141 do _delay(_us); while (_scl.gpioRead() == LOW);
142 return true;
143}
144
145bool soft_i2c_master::send_start()
146{
147 if (!check_bus_idle()) return false;
148 _sda.gpioWrite(LOW);
149 _delay(_us);
150 return true;
151}
152
153void soft_i2c_master::send_stop()
154{
155 _scl.gpioWrite(LOW);
156 _delay(_us);
157 _sda.gpioWrite(LOW);
158 _delay(_us);
159 _scl.gpioWrite(HIGH);
160 // Clock stretching
161 do _delay(_us); while (_scl.gpioRead() == LOW);
162 _sda.gpioWrite(HIGH);
163 _delay(_us);
164}
165
166bool soft_i2c_master::check_bus_idle() {
167 // Do not touch the I2C bus
168 _scl.gpioWrite(HIGH);
169 _sda.gpioWrite(HIGH);
170 // Toggle SCL until bus is idle
171 int i=0;
172 while((_sda.gpioRead() == LOW) && (i < 20)) {
173 _scl.gpioWrite(LOW);
174 _delay(_us);
175 _scl.gpioWrite(HIGH);
176 _delay(_us);
177 ++i;
178 }
179 return i != 20;
180}