Pico-Arduino
PicoHardwareI2C.h
1 #pragma once
2 #include "HardwareI2C.h"
3 #include "hardware/i2c.h"
4 #include "hardware/gpio.h"
5 #include "PicoLogger.h"
6 #include "pins_arduino.h"
7 #include <algorithm> // std::max
8 
20 class PicoHardwareI2C : public HardwareI2C {
21  public:
23  PicoHardwareI2C(i2c_inst_t *i2c, int maxBufferSize, int sdaPin, int sclPin){
24  this->i2c = i2c;
25  this->maxBufferSize = maxBufferSize;
26  sda_pin = sdaPin;
27  scl_pin = sclPin;
28  }
29 
32  end();
33  }
34 
36  virtual void begin() {
37  begin(-1, -1);
38  }
39 
41  virtual void begin(uint8_t address) {
42  begin(address, -1, -1);
43  }
44 
46  virtual void begin(int sdaPin, int sclPin) {
47  Logger.info("begin");
48  setupPins(sdaPin, sclPin);
49  i2c_init(i2c, 100000);
50  i2c_set_slave_mode(i2c, false, 0);
51  is_slave_mode = false;
52  setupWriteBuffer();
53  }
54 
56  virtual void begin(uint8_t address, int sdaPin, int sclPin) {
57  if (Logger.isLogging(PicoLogger::Info)) Logger.info("begin",Logger.toStr(address));
58  setupPins(sdaPin,sclPin);
59  transmission_address = address;
60  i2c_init(i2c, 100000);
61  i2c_set_slave_mode(i2c, true, address);
62  is_slave_mode = true;
63  setupWriteBuffer();
64  }
65 
67  virtual void end() {
68  Logger.info("end");
69  i2c_deinit(i2c);
70  if (read_buffer !=nullptr){
71  delete[]read_buffer;
72  read_buffer = nullptr;
73  }
74  if (write_buffer !=nullptr){
75  delete[]write_buffer;
76  write_buffer = nullptr;
77  }
78  }
79 
81  virtual void setClock(uint32_t baudrate) {
82  Logger.info("setClock");
83  i2c_set_baudrate(i2c, baudrate);
84  }
85 
87  virtual void beginTransmission(uint8_t address) {
88  if (Logger.isLogging(PicoLogger::Info)) Logger.info("beginTransmission",Logger.toStr(address));
89  transmission_address=address;
90  }
91 
93  virtual uint8_t endTransmission(bool stopBit) {
94  Logger.info("endTransmission", stopBit ? "stop" : "no-stop" );
95  uint8_t result = flush(stopBit);
96  transmission_address = -1;
97  return result;
98  }
99 
101  virtual uint8_t endTransmission(void) {
102  return endTransmission(true);
103  }
104 
106  virtual size_t write(uint8_t c) {
107  Logger.debug("write");
108  size_t result = 0;
109  if (write_pos>=maxBufferSize){
110  flush(false);
111  }
112  // we just write to a buffer
113  if (write_pos<maxBufferSize){
114  result = 1;
115  write_buffer[write_pos]=c;
116  write_pos++;
117  }
118  return result;
119  }
120 
122  size_t write(const char *buffer, size_t size) {
123  Logger.debug("write[]");
124  // if we have any data in the buffer flush this first
125  flush();
126  // and now write out the requested data
127  return i2c_write_blocking(i2c, transmission_address,(const uint8_t * ) buffer, size, true);
128  }
129 
131  virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) {
132  if (Logger.isLogging(PicoLogger::Info)) {
133  char msg[80];
134  sprintf(msg,"(%d, %ld, %s)", address,len, stopBit?"stop":"no-stop");
135  Logger.info("requestFrom",msg);
136  }
137  flush();
138  setupReadBuffer(len);
139  read_address = address;
140 
141  // call requestHandler
142  if (this->requestHandler!=nullptr){
143  Logger.info("requestHandler");
144  (*this->requestHandler)();
145  }
146 
147  // read the data into the read_buffer
148  read_len = i2c_read_blocking(i2c, read_address, read_buffer, len, !stopBit);
149  read_pos = 0;
150  if (read_len==PICO_ERROR_GENERIC){
151  Logger.warning("requestFrom->","PICO_ERROR_GENERIC");
152  read_len = 0;
153  }
154  if (Logger.isLogging(PicoLogger::Info)) Logger.info("requestFrom ->",Logger.toStr(read_len));
155 
156  // call recieveHandler
157  if (read_len>0 && this->recieveHandler!=nullptr){
158  Logger.info("recieveHandler");
159  (*this->recieveHandler)(read_len);
160  }
161 
162  return read_len;
163  }
164 
166  virtual size_t requestFrom(uint8_t address, size_t len) {
167  return requestFrom(address, len, false);
168  }
169 
171  virtual int read() {
172  // trigger flush of write buffer
173  int result = (read_pos<read_len) ? read_buffer[read_pos++] : -1;
174  Logger.debug("read", Logger.toStr(result));
175  return result;
176  }
177 
179  virtual int peek() {
180  Logger.debug("peek");
181  return (read_pos<read_len) ? read_buffer[read_pos] : -1;
182  }
183 
185  virtual int available() {
186  int buffer_available = read_len - read_pos;
187  if (buffer_available < 0){
188  buffer_available = 0;
189  Logger.error("buffer_available is negative - corrected to 0");
190  }
191  if (Logger.isLogging(PicoLogger::Info)) {
192  Logger.debug("available",Logger.toStr(buffer_available));
193  }
194  return buffer_available;
195  }
196 
198  virtual void onReceive(void(*recieveHandler)(int)) {
199  this->recieveHandler = recieveHandler;
200  Logger.info("onReceive");
201  }
202 
204  virtual void onRequest(void(*requestHandler)(void)) {
205  this->requestHandler = requestHandler;
206  Logger.info("onRequest");
207  };
208 
209  protected:
210  bool is_slave_mode;
211  i2c_inst_t *i2c;
212  int maxBufferSize;
213  // write
214  uint8_t transmission_address;
215  int write_pos;
216  uint8_t *write_buffer;
217 
218  // read
219  size_t read_len;
220  size_t read_pos;
221  bool read_stop_bit;
222  uint8_t read_address;
223  uint8_t *read_buffer;
224 
225  // handler
226  void(*recieveHandler)(int);
227  void(*requestHandler)(void);
228 
229  // pins
230  int sda_pin;
231  int scl_pin;
232  bool pin_is_setup = false;
233 
234  void setupPins(int sda, int scl){
235  if (!pin_is_setup) {
236  if (sda>=0){
237  sda_pin = sda;
238  }
239  if (scl>=0){
240  scl_pin = scl;
241  }
242  gpio_set_function(sda_pin, GPIO_FUNC_I2C);
243  gpio_set_function(scl_pin, GPIO_FUNC_I2C);
244  gpio_pull_up(sda_pin);
245  gpio_pull_up(scl_pin);
246 
247  }
248  }
249 
250 
251  int flush(bool stop=false) {
252  bool result = 0; // 4:other error
253  if (write_pos>0) {
254  if (Logger.isLogging(PicoLogger::Debug)) {
255  char msg[80];
256  sprintf(msg, "address:%d, len:%d, end:%s",transmission_address, write_pos, stop ? "stop": "no-stop" );
257  Logger.debug("flush", msg);
258  }
259  int result = i2c_write_blocking(i2c, transmission_address, write_buffer, write_pos, !stop);
260  if (result == write_pos){
261  result = 0; // OK
262  } else if (result < write_pos){
263  result = 1; // 1:data too long to fit in transmit buffer
264  } else {
265  result = 4; // 4:other error
266  }
267  write_pos = 0;
268  if (Logger.isLogging(PicoLogger::Debug)) {
269  Logger.debug("flush->", Logger.toStr(result));
270  }
271  }
272 
273  return result;
274  }
275 
276  void setupWriteBuffer(){
277  // setup buffer only if needed
278  if (write_buffer==nullptr){
279  if (Logger.isLogging(PicoLogger::Info)) Logger.info("setupWriteBuffer");
280  write_buffer = new uint8_t(maxBufferSize);
281  }
282  }
283 
284  void setupReadBuffer(int len){
285  if (read_buffer==nullptr){
286  // setup buffer only if needed
287  maxBufferSize = std::max(len, maxBufferSize);
288  if (Logger.isLogging(PicoLogger::Info)) {
289  Logger.info("setupReadBuffer: ",Logger.toStr(maxBufferSize));
290  }
291  read_buffer = new uint8_t(maxBufferSize);
292  } else {
293  if (len>maxBufferSize){
294  // grow the read buffer to the requested size
295  maxBufferSize = len;
296  if (Logger.isLogging(PicoLogger::Info)) {
297  Logger.info("setupReadBuffer: ",Logger.toStr(maxBufferSize));
298  }
299  delete[] read_buffer;
300  read_buffer = new uint8_t(maxBufferSize);
301  }
302  }
303  }
304 
305 };
306 
307 
Arduino I2C implementation using the Pico functionality. In Arduino we can read and write individal c...
Definition: PicoHardwareI2C.h:20
virtual void beginTransmission(uint8_t address)
Begin a transmission to the I2C slave device with the given address. Subsequently,...
Definition: PicoHardwareI2C.h:87
virtual void begin(int sdaPin, int sclPin)
Initiate the Wire library and join the I2C bus as a master. This should normally be called only once.
Definition: PicoHardwareI2C.h:46
PicoHardwareI2C(i2c_inst_t *i2c, int maxBufferSize, int sdaPin, int sclPin)
Standard Constructor for PicoHardwareI2C class.
Definition: PicoHardwareI2C.h:23
virtual size_t write(uint8_t c)
Writes data from a slave device in response to a request from a master, or queues bytes for transmiss...
Definition: PicoHardwareI2C.h:106
virtual void end()
Closes the Wire Library.
Definition: PicoHardwareI2C.h:67
size_t write(const char *buffer, size_t size)
Writes data from a slave device in response to a request from a master, or queues bytes for transmiss...
Definition: PicoHardwareI2C.h:122
virtual int read()
Reads a byte that was transmitted from a slave device to a master after a call to requestFrom() or wa...
Definition: PicoHardwareI2C.h:171
virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit)
Used by the master to request bytes from a slave device. The bytes may then be retrieved with the ava...
Definition: PicoHardwareI2C.h:131
virtual void begin(uint8_t address)
Initiate the Wire library and join the I2C bus as a slave. This should normally be called only once.
Definition: PicoHardwareI2C.h:41
virtual uint8_t endTransmission(void)
Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes t...
Definition: PicoHardwareI2C.h:101
virtual void setClock(uint32_t baudrate)
This function modifies the clock frequency for I2C communication. I2C slave devices have no minimum w...
Definition: PicoHardwareI2C.h:81
virtual void onRequest(void(*requestHandler)(void))
Register a function to be called when a master requests data from this slave device.
Definition: PicoHardwareI2C.h:204
~PicoHardwareI2C()
Destructor for PicoHardwareI2C class.
Definition: PicoHardwareI2C.h:31
virtual int peek()
Peeks current unread byte that was transmitted from a slave device to a master after a call to reques...
Definition: PicoHardwareI2C.h:179
virtual void begin()
Initiate the Wire library and join the I2C bus as a master. This should normally be called only once.
Definition: PicoHardwareI2C.h:36
virtual int available()
Returns the number of bytes available for retrieval with read(). This should be called on a master de...
Definition: PicoHardwareI2C.h:185
virtual void onReceive(void(*recieveHandler)(int))
Registers a function to be called when a slave device receives a transmission from a master.
Definition: PicoHardwareI2C.h:198
virtual void begin(uint8_t address, int sdaPin, int sclPin)
Initiate the Wire library and join the I2C bus as a slave. This should normally be called only once.
Definition: PicoHardwareI2C.h:56
virtual uint8_t endTransmission(bool stopBit)
Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes t...
Definition: PicoHardwareI2C.h:93
virtual size_t requestFrom(uint8_t address, size_t len)
Used by the master to request bytes from a slave device. The bytes may then be retrieved with the ava...
Definition: PicoHardwareI2C.h:166
Definition: HardwareI2C.h:27