PX4 Firmware
PX4 Autopilot Software http://px4.io
SMBus.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file SMBus.cpp
36  * SMBus v2.0 protocol implementation.
37  *
38  * @author Jacob Dahl <dahl.jakejacob@gmail.com>
39  *
40  * TODO
41  * - Enable SMBus mode at the NuttX level. This may be tricky sharing the bus with i2c.
42  * i.e STM32f4 see platforms/nuttx/Nuttx/nuttx/arch/arm/src/stm32/stm32f40xxx_i2c.c
43  * - Add remaining SMBus protocol messages as needed
44  */
45 
46 #include "SMBus.hpp"
47 
48 SMBus::SMBus(int bus_num, uint16_t address) :
49  I2C("BATT_SMBUS_I2C", nullptr, bus_num, address, 100000)
50 {
51 }
52 
53 int SMBus::read_word(const uint8_t cmd_code, uint16_t &data)
54 {
55  uint8_t buf[6];
56  // 2 data bytes + pec byte
57  int result = transfer(&cmd_code, 1, buf + 3, 3);
58 
59  if (result == PX4_OK) {
60  data = buf[3] | ((uint16_t)buf[4] << 8);
61  // Check PEC.
62  uint8_t addr = get_device_address() << 1;
63  buf[0] = addr | 0x00;
64  buf[1] = cmd_code;
65  buf[2] = addr | 0x01;
66 
67  uint8_t pec = get_pec(buf, sizeof(buf) - 1);
68 
69  if (pec != buf[sizeof(buf) - 1]) {
70  result = -EINVAL;
71  }
72  }
73 
74  return result;
75 }
76 
77 int SMBus::write_word(const uint8_t cmd_code, uint16_t data)
78 {
79  // 2 data bytes + pec byte
80  uint8_t buf[5];
81  buf[0] = (get_device_address() << 1) | 0x10;
82  buf[1] = cmd_code;
83  buf[2] = data & 0xff;
84  buf[3] = (data >> 8) & 0xff;
85 
86  buf[4] = get_pec(buf, 4);
87 
88  int result = transfer(&buf[1], 4, nullptr, 0);
89 
90  return result;
91 }
92 
93 int SMBus::block_read(const uint8_t cmd_code, void *data, const uint8_t length, bool use_pec)
94 {
95  unsigned byte_count = 0;
96  // addr(wr), cmd_code, addr(r), byte_count, data (32 bytes max), pec
97  uint8_t rx_data[32 + 5];
98 
99  int result = transfer(&cmd_code, 1, (uint8_t *)&rx_data[3], length + 2);
100 
101  uint8_t device_address = get_device_address();
102  rx_data[0] = (device_address << 1) | 0x00;
103  rx_data[1] = cmd_code;
104  rx_data[2] = (device_address << 1) | 0x01;
105  byte_count = rx_data[3];
106 
107  memcpy(data, &rx_data[4], byte_count);
108 
109  if (use_pec) {
110  uint8_t pec = get_pec(rx_data, byte_count + 4);
111 
112  if (pec != rx_data[byte_count + 4]) {
113  result = -EINVAL;
114  }
115  }
116 
117  return result;
118 }
119 
120 int SMBus::block_write(const uint8_t cmd_code, void *data, uint8_t byte_count, bool use_pec)
121 {
122  // cmd code[1], byte count[1], data[byte_count] (32max), pec[1] (optional)
123  uint8_t buf[32 + 2];
124 
125  buf[0] = cmd_code;
126  buf[1] = (uint8_t)byte_count;
127  memcpy(&buf[2], data, byte_count);
128 
129  if (use_pec) {
130  uint8_t pec = get_pec(buf, byte_count + 2);
131  buf[byte_count + 2] = pec;
132  byte_count++;
133  }
134 
135  unsigned i = 0;
136  int result = 0;
137 
138  // If block_write fails, try up to 10 times.
139  while (i < 10 && ((result = transfer((uint8_t *)buf, byte_count + 2, nullptr, 0)) != PX4_OK)) {
140  i++;
141  }
142 
143  if (i == 10 || result) {
144  PX4_WARN("Block_write failed %d times", i);
145  result = -EINVAL;
146  }
147 
148  return result;
149 }
150 
151 uint8_t SMBus::get_pec(uint8_t *buff, const uint8_t len)
152 {
153  // Initialise CRC to zero.
154  uint8_t crc = 0;
155  uint8_t shift_register = 0;
156  bool invert_crc;
157 
158  // Calculate crc for each byte in the stream.
159  for (uint8_t i = 0; i < len; i++) {
160  // Load next data byte into the shift register
161  shift_register = buff[i];
162 
163  // Calculate crc for each bit in the current byte.
164  for (uint8_t j = 0; j < 8; j++) {
165  invert_crc = (crc ^ shift_register) & 0x80;
166  crc <<= 1;
167  shift_register <<= 1;
168 
169  if (invert_crc) {
170  crc ^= SMBUS_PEC_POLYNOMIAL;
171  }
172  }
173  }
174 
175  return crc;
176 }
int block_read(const uint8_t cmd_code, void *data, const uint8_t length, bool use_pec)
Sends a block read command.
Definition: SMBus.cpp:93
SMBus(int bus_num, uint16_t address)
Definition: SMBus.cpp:48
int block_write(const uint8_t cmd_code, void *data, uint8_t byte_count, bool use_pec)
Sends a block write command.
Definition: SMBus.cpp:120
int read_word(const uint8_t cmd_code, uint16_t &data)
Sends a read word command.
Definition: SMBus.cpp:53
int write_word(const uint8_t cmd_code, uint16_t data)
Sends a write word command.
Definition: SMBus.cpp:77
SMBus v2.0 protocol implementation.
uint8_t * data
Definition: dataman.cpp:149
uint8_t get_pec(uint8_t *buffer, uint8_t length)
Calculates the PEC from the data.
Definition: SMBus.cpp:151
#define SMBUS_PEC_POLYNOMIAL
Polynomial for calculating PEC.
Definition: SMBus.hpp:45