Linduino  1.3.0
Linear Technology Arduino-Compatible Demonstration Board
LT_SMBusBase.cpp
Go to the documentation of this file.
1 /*!
2 LTC SMBus Support: API for a shared SMBus layer
3 
4 @verbatim
5 
6 This API is shared with Linduino and RTOS code. End users should code to this
7 API to enable use of the PMBus code without modifications.
8 
9 @endverbatim
10 
11 
12 Copyright 2018(c) Analog Devices, Inc.
13 
14 All rights reserved.
15 
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions are met:
18  - Redistributions of source code must retain the above copyright
19  notice, this list of conditions and the following disclaimer.
20  - Redistributions in binary form must reproduce the above copyright
21  notice, this list of conditions and the following disclaimer in
22  the documentation and/or other materials provided with the
23  distribution.
24  - Neither the name of Analog Devices, Inc. nor the names of its
25  contributors may be used to endorse or promote products derived
26  from this software without specific prior written permission.
27  - The use of this software may or may not infringe the patent rights
28  of one or more patent holders. This license does not release you
29  from the requirement that you obtain separate licenses from these
30  patent holders to use this software.
31  - Use of the software either in source or binary form, must be run
32  on or directly connected to an Analog Devices Inc. component.
33 
34 THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
35 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
36 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
37 IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
38 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
40 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
43 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 */
45 
46 //! @ingroup PMBus_SMBus
47 //! @{
48 //! @defgroup LT_SMBusBase LT_SMBusBase: Implementation of SMBus
49 //! @}
50 
51 /*! @file
52  @ingroup LT_SMBusBase
53  Library Header File for LT_SMBusBase
54 */
55 
56 #include "LT_SMBusBase.h"
57 
58 #define USE_BLOCK_TRANSACTION 0
59 #define FOUND_SIZE 0x79
60 
61 bool LT_SMBusBase::open_ = false;
63 
65 {
66  i2cbus_ = new LT_I2CBus();
67 
68  if (open_ == false)
69  {
70  i2cbus_->quikevalI2CInit(); //! Initializes Linduino I2C port.
71  i2cbus_->quikevalI2CConnect(); //! Connects I2C port to the QuikEval connector
72 
73  open_ = true;
74  }
75 }
76 
78 {
79  i2cbus_ = new LT_I2CBus(speed);
80 
81  if (open_ == false)
82  {
83  i2cbus_->quikevalI2CInit(); //! Initializes Linduino I2C port.
84  i2cbus_->quikevalI2CConnect(); //! Connects I2C port to the QuikEval connector
85 
86  open_ = true;
87  }
88 }
89 
91 {
92  delete i2cbus_;
93 }
94 
96 {
97  uint8_t address;
98 
99  if (i2cbus_->readByte(0x0C, &address))
100  Serial.print(F("Read Alert: fail.\n"));
101 
102  return (address >> 1);
103 }
104 
105 uint8_t LT_SMBusBase::waitForAck(uint8_t address, uint8_t command)
106 {
107  uint8_t data;
108  // A real application should timeout at 4.1 seconds.
109  uint16_t timeout = 8192;
110  while (timeout-- > 0)
111  {
112  if (0 == i2cbus_->readByteData(address, command, &data))
113  return SUCCESS;
114  }
115  return FAILURE;
116 }
117 
118 
119 uint8_t *LT_SMBusBase::probe(uint8_t command)
120 {
121  uint8_t address;
122  uint8_t found = 0;
123 
124  for (address = 0x10; address < 0x7F; address++)
125  {
126  if (address == 0x0C)
127  continue;
128 
129  if (0==i2cbus_->writeByte(address, command))
130  {
131 
132  if (found < FOUND_SIZE)
133  found_address_[found++] = address;
134 
135  }
136 
137 
138  }
139 
140  found_address_[found] = 0;
141 
142  return found_address_;
143 }
144 
145 uint8_t *LT_SMBusBase::probeUnique(uint8_t command)
146 {
147  uint8_t address;
148  uint8_t found = 0;
149 
150  for (address = 0x10; address < 0x7F; address++)
151  {
152  if (address == 0x0C)
153  continue;
154  if (address == 0x5B)
155  continue;
156  if (address == 0x5A)
157  continue;
158  if (address == 0x7C)
159  continue;
160 
161  if (0==i2cbus_->writeByte(address, command))
162  {
163 
164  if (found < FOUND_SIZE)
165  found_address_[found++] = address;
166 
167  }
168 
169 
170  }
171 
172  found_address_[found] = 0;
173 
174  return found_address_;
175 }
176 
177 void LT_SMBusBase::writeByte(uint8_t address, uint8_t command, uint8_t data)
178 {
179  if (pec_enabled_)
180  {
181  uint8_t buffer[2];
182  buffer[0] = data;
183 
184  pecClear();
185  pecAdd(address << 1);
186  pecAdd(command);
187  pecAdd(data);
188  buffer[1] = pecGet();
189  if (i2cbus_->writeBlockData(address, command, 2, buffer))
190  Serial.print(F("Write Byte With Pec: fail.\n"));
191  }
192  else
193  {
194  if (i2cbus_->writeByteData(address, command, data))
195  Serial.print(F("Write Byte: fail.\n"));
196  }
197 }
198 
199 void LT_SMBusBase::writeBytes(uint8_t *addresses, uint8_t *commands,
200  uint8_t *data, uint8_t no_addresses)
201 {
202  if (pec_enabled_)
203  {
204  uint8_t buffer[2];
205  uint16_t index = 0;
206 
207  while (index < no_addresses)
208  {
209  buffer[0] = data[index];
210  pecClear();
211  pecAdd(addresses[index] << 1);
212  pecAdd(commands[index]);
213  pecAdd(data[index]);
214  buffer[1] = pecGet();
215 
216  if (i2cbus_->writeBlockData(addresses[index], commands[index], 2, buffer))
217  Serial.print(F("Write Bytes With Pec: fail.\n"));
218  index++;
219  }
220  }
221  else
222  {
223  uint16_t index = 0;
224 
225  while (index < no_addresses)
226  {
227  if (i2cbus_->writeBlockData(addresses[index], commands[index], 1, &data[index]))
228  Serial.print(F("Write Bytes: fail.\n"));
229  index++;
230  }
231  }
232 }
233 
234 uint8_t LT_SMBusBase::readByte(uint8_t address, uint8_t command)
235 {
236  if (pec_enabled_)
237  {
238  uint8_t input[2];
239  input[0] = 0x00;
240  input[1] = 0x00;
241 
242  pecClear();
243  pecAdd(address << 1);
244  pecAdd(command);
245  pecAdd((address << 1) | 0x01);
246  if (i2cbus_->readBlockData(address, command, 2, input))
247  Serial.print(F("Read Byte With Pec: fail.\n"));
248 
249  pecAdd(input[0]);
250  if (pecGet() != input[1])
251  Serial.print(F("Read Byte With Pec: fail pec\n"));
252 
253  return input[0];
254  }
255  else
256  {
257  uint8_t result;
258 
259  if (i2cbus_->readByteData(address, command, &result))
260  Serial.print(F("Read Byte: fail.\n"));
261  return result;
262  }
263 }
264 
265 void LT_SMBusBase::writeWord(uint8_t address, uint8_t command, uint16_t data)
266 {
267  if (pec_enabled_)
268  {
269  uint8_t buffer[3];
270  buffer[0] = (uint8_t) (data & 0xff);
271  buffer[1] = (uint8_t) (data >> 8);
272 
273  pecClear();
274  pecAdd(address << 1);
275  pecAdd(command);
276  pecAdd(data & 0xff);
277  pecAdd(data >> 8);
278  buffer[2] = pecGet();
279  if (i2cbus_->writeBlockData(address, command, 3, buffer))
280  Serial.print(F("Write Word With Pec: fail.\n"));
281  }
282  else
283  {
284 
285 #if USE_BLOCK_TRANSACTION
286  uint8_t buffer[2];
287  buffer[0] = (uint8_t) (data & 0xff);
288  buffer[1] = (uint8_t) (data >> 8);
289 
290  if (i2cbus_->writeBlockData(address, command, 2, buffer))
291  Serial.print(F("Write Word: fail.\n"));
292 #else
293  uint16_t rdata;
294  rdata = (data << 8) | (data >> 8);
295  if (i2cbus_->writeWordData(address, command, rdata))
296  Serial.print(F("Write Word: fail.\n"));
297 #endif
298  }
299 }
300 
301 uint16_t LT_SMBusBase::readWord(uint8_t address, uint8_t command)
302 {
303  if (pec_enabled_)
304  {
305  uint8_t input[3];
306  input[0] = 0x00;
307  input[1] = 0x00;
308  input[2] = 0x00;
309 
310  pecClear();
311  pecAdd(address << 1);
312  pecAdd(command);
313  pecAdd((address << 1) | 0x01);
314 
315  if (i2cbus_->readBlockData(address, command, 3, input))
316  Serial.print(F("Read Word With Pec: fail.\n"));
317 
318  pecAdd(input[0]);
319  pecAdd(input[1]);
320  if (pecGet() != input[2])
321  Serial.print(F("Read Word With Pec: fail pec\n"));
322 
323  return input[1] << 8 | input[0];
324  }
325  else
326  {
327 
328 #if USE_BLOCK_TRANSACTION
329  uint8_t input[2];
330  input[0] = 0x00;
331  input[1] = 0x00;
332 
333  if (i2cbus_->readBlockData(address, command, 2, input))
334  Serial.print(F("Read Word: fail.\n"));
335  return input[1] << 8 | input[0];
336 #else
337  uint16_t rdata;
338  if (i2cbus_->readWordData(address, command, &rdata))
339  Serial.print(F("Read Word: fail.\n"));
340  return (rdata << 8) | (rdata >> 8);
341 #endif
342  }
343 }
344 
345 void LT_SMBusBase::writeBlock(uint8_t address, uint8_t command,
346  uint8_t *block, uint16_t block_size)
347 {
348  if (pec_enabled_)
349  {
350  uint16_t pos = 0;
351 
352  pecClear();
353  pecAdd(address << 1);
354  pecAdd(command);
355  pecAdd(block_size);
356 
357  while (pos < block_size)
358  pecAdd(block[pos++]);
359  uint8_t pec = pecGet();
360 
361  uint8_t *data_with_pec = (uint8_t *) malloc(block_size + 2);
362  data_with_pec[0] = block_size;
363  memcpy(data_with_pec + 1, block, block_size);
364  data_with_pec[block_size + 1] = pec;
365 
366  if (i2cbus_->writeBlockData(address, command, block_size + 2, data_with_pec))
367  Serial.print(F("Write Block With Pec: fail.\n"));
368  free(data_with_pec);
369  }
370  else
371  {
372  uint8_t *buffer = (uint8_t *)malloc(block_size + 1);
373  buffer[0] = block_size;
374  memcpy(buffer + 1, block, block_size);
375  if (i2cbus_->writeBlockData(address, command, block_size + 1, buffer))
376  Serial.print(F("Write Block: fail.\n"));
377  free(buffer);
378  }
379 }
380 
381 uint8_t LT_SMBusBase::writeReadBlock(uint8_t address, uint8_t command,
382  uint8_t *block_out, uint16_t block_out_size, uint8_t *block_in, uint16_t block_in_size)
383 {
384  if (pec_enabled_)
385  {
386  uint16_t pos = 0;
387  uint8_t actual_block_size;
388 
389  pecClear();
390  pecAdd(address << 1);
391  pecAdd(command);
392  pecAdd(block_out_size);
393  while (pos < block_out_size)
394  pecAdd(block_out[pos++]);
395 
396 
397  uint8_t *buffer = (uint8_t *)malloc(block_out_size + 1);
398  buffer[0] = block_out_size;
399  memcpy(buffer + 1, block_out, block_out_size);
400 
402  if (i2cbus_->writeBlockData(address, command, block_out_size + 1, buffer))
403  Serial.print(F("Write/Read Block w/PEC: write fail\n"));
404  free(buffer);
405 
406 
407 
408  pecAdd((address << 1) | 0x01);
409 
411  buffer = (uint8_t *)malloc(block_in_size + 2);
412  if (i2cbus_->readBlockData(address, block_in_size + 2, buffer))
413  Serial.print(F("Write/Read Block w/PEC: read fail.\n"));
414  if (buffer[0] > block_in_size)
415  {
416  Serial.print(F("Write/Read Block w/PEC: fail read size too big.\n"));
417  }
418  memcpy(block_in, buffer + 1, block_in_size);
419 
420  for (pos = 0; pos<buffer[0] + 1u; pos++)
421  pecAdd(buffer[pos]);
422  if (pecGet() != buffer[buffer[0]+1])
423  Serial.print(F("Write/Read Block w/Pec: fail pec\n"));
424 
425  actual_block_size = buffer[0];
426  free(buffer);
427  return actual_block_size;
428  }
429  else
430  {
431  uint8_t *buffer = (uint8_t *)malloc(block_out_size + 1);
432  uint8_t actual_block_size;
433 
434  buffer[0] = block_out_size;
435  memcpy(buffer + 1, block_out, block_out_size);
436 
438  if (i2cbus_->writeBlockData(address, command, block_out_size + 1, buffer))
439  Serial.print(F("Write/Read Block write fail\n"));
440  free(buffer);
441 
443  buffer = (uint8_t *)malloc(block_in_size + 1);
444  if (i2cbus_->readBlockData(address, block_in_size + 1, buffer))
445  Serial.print(F("Write/Read Block: read fail.\n"));
446  if (buffer[0] > block_in_size)
447  {
448  Serial.print(F("Write/Read Block: fail size too big.\n"));
449  }
450  memcpy(block_in, buffer + 1, block_in_size);
451 
452  actual_block_size = buffer[0];
453  free(buffer);
454  return actual_block_size;
455  }
456 
457 }
458 
459 uint8_t LT_SMBusBase::readBlock(uint8_t address, uint8_t command,
460  uint8_t *block, uint16_t block_size)
461 {
462  if (pec_enabled_)
463  {
464  uint16_t pos;
465  uint8_t *buffer = (uint8_t *)malloc(block_size + 2);
466  uint8_t actual_block_size;
467 
468  pecClear();
469  pecAdd(address << 1);
470  pecAdd(command);
471  pecAdd((address << 1) | 0x01);
472 
473  if (i2cbus_->readBlockData(address, command, block_size + 2, buffer))
474 
475  if (buffer[0] > block_size)
476  Serial.print(F("Read Block with PEC: fail size too big.\n"));
477 
478  memcpy(block, buffer + 1, block_size);
479 
480  for (pos = 0; pos<buffer[0] + 1u; pos++)
481  pecAdd(buffer[pos]);
482  if (pecGet() != buffer[buffer[0]+1])
483  Serial.print(F("Read Block With Pec: fail pec\n"));
484 
485  actual_block_size = buffer[0];
486  free(buffer);
487  return actual_block_size;
488  }
489  else
490  {
491  uint8_t *buffer = (uint8_t *)malloc(block_size + 1);
492  uint8_t actual_block_size;
493 
494  if (i2cbus_->readBlockData(address, command, block_size + 1, buffer))
495  Serial.print(F("Read Block: fail.\n"));
496  if (buffer[0] > block_size)
497  {
498  Serial.print(F("Read Block: fail size too big.\n"));
499  }
500  memcpy(block, buffer + 1, block_size);
501 
502  actual_block_size = buffer[0];
503  free(buffer);
504  return actual_block_size;
505  }
506 }
507 
508 void LT_SMBusBase::sendByte(uint8_t address, uint8_t command)
509 {
510  if (pec_enabled_)
511  {
512  uint8_t pec;
513 
514  pecClear();
515  pecAdd(address << 1);
516  pecAdd(command);
517  pec = pecGet();
518 
519  if (i2cbus_->writeBlockData(address, command, 1, &pec))
520  Serial.print(F("Send Byte With Pec: fail.\n"));
521  }
522  else
523  {
524  if (i2cbus_->writeByte(address, command))
525  Serial.print(F("Send Byte: fail.\n"));
526  }
527 }
void sendByte(uint8_t address, uint8_t command)
SMBus send byte command.
void endGroupProtocol(void)
ends group protocol so I2CBus knows to send STOPs again.
Definition: LT_I2CBus.cpp:244
void quikevalI2CInit(void)
Initializes Linduino I2C port.
Definition: LT_I2CBus.cpp:225
int8_t writeWordData(uint8_t address, uint8_t command, uint16_t value)
Write a 16-bit word of data to register specified by "command".
Definition: LT_I2CBus.cpp:148
static uint8_t found_address_[]
Definition: LT_SMBusBase.h:64
int8_t readByteData(uint8_t address, uint8_t command, uint8_t *value)
Read a byte of data at register specified by "command", store in "value".
Definition: LT_I2CBus.cpp:106
void writeWord(uint8_t address, uint8_t command, uint16_t data)
SMBus write word command.
uint8_t * probe(uint8_t command)
SMBus bus probe.
int8_t readBlockData(uint8_t address, uint8_t command, uint16_t length, uint8_t *values)
Read a block of data, starting at register specified by "command" and ending at (command + length - 1...
Definition: LT_I2CBus.cpp:161
int8_t writeBlockData(uint8_t address, uint8_t command, uint16_t length, uint8_t *values)
Write a block of data, starting at register specified by "command" and ending at (command + length - ...
Definition: LT_I2CBus.cpp:187
virtual ~LT_SMBusBase()
#define FAILURE
Definition: LT_PMBus.h:63
LT_I2CBus * i2cbus_
Definition: LT_SMBusBase.h:65
void writeByte(uint8_t address, uint8_t command, uint8_t data)
SMBus write byte command.
#define FOUND_SIZE
void startGroupProtocol(void)
starts group protocol so I2CBus knows to repeat START instead of STOP.
Definition: LT_I2CBus.cpp:240
bool pec_enabled_
Definition: LT_SMBus.h:64
uint8_t * probeUnique(uint8_t command)
SMBus bus probe.
uint8_t readAlert(void)
Perform ARA.
static uint8_t address
Definition: DC2091A.ino:83
union LT_union_int32_4bytes data
Definition: DC2094A.ino:138
static int16_t pos
uint8_t pecGet(void)
Get the current pec result.
Definition: LT_SMBus.cpp:192
static bool open_
Used to ensure initialisation of i2c once.
Definition: LT_SMBusBase.h:63
void writeBytes(uint8_t *addresses, uint8_t *commands, uint8_t *data, uint8_t no_addresses)
SMBus write byte command for a list of addresses.
int8_t readWordData(uint8_t address, uint8_t command, uint16_t *value)
Read a 16-bit word of data from register specified by "command".
Definition: LT_I2CBus.cpp:131
uint8_t readByte(uint8_t address, uint8_t command)
SMBus read byte command.
#define input(pin)
Return the state of pin "pin".
Definition: Linduino.h:79
static bool pec
Definition: program.ino:88
void quikevalI2CConnect(void)
Switch MUX to connect I2C pins to QuikEval connector.
Definition: LT_I2CBus.cpp:232
uint8_t readBlock(uint8_t address, uint8_t command, uint8_t *block, uint16_t block_size)
SMBus read block command.
int8_t writeByte(uint8_t address, uint8_t value)
Write "value" byte to device at "address".
Definition: LT_I2CBus.cpp:95
long timeout
static int index
void pecClear(void)
Clear the pec value so it can start a new calculation.
Definition: LT_SMBus.cpp:162
void writeBlock(uint8_t address, uint8_t command, uint8_t *block, uint16_t block_size)
SMBus write block command.
#define SUCCESS
Definition: LT_PMBus.h:62
int8_t writeByteData(uint8_t address, uint8_t command, uint8_t value)
Write a byte of data to register specified by "command".
Definition: LT_I2CBus.cpp:119
void pecAdd(uint8_t byte_value)
Add a byte to the pec calculation.
Definition: LT_SMBus.cpp:170
int8_t readByte(uint8_t address, uint8_t *value)
Read a byte, store in "value".
Definition: LT_I2CBus.cpp:84
LTC SMBus Support: Implementation for a shared SMBus layer.
uint16_t readWord(uint8_t address, uint8_t command)
SMBus read word command.
uint8_t waitForAck(uint8_t address, uint8_t command)
Read with the address and command in loop until ack, then issue stop.
uint8_t writeReadBlock(uint8_t address, uint8_t command, uint8_t *block_out, uint16_t block_out_size, uint8_t *block_in, uint16_t block_in_size)
SMBus write then read block command.