blob: 0f010f550256eaf870cdd2b01f40fd13f2b27070 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "HAL/I2C.h"
9#include "I2C.h"
10
11#include "HAL/HAL.h"
12#include "WPIErrors.h"
13
14using namespace frc;
15
16/**
17 * Constructor.
18 *
19 * @param port The I2C port to which the device is connected.
20 * @param deviceAddress The address of the device on the I2C bus.
21 */
22I2C::I2C(Port port, int deviceAddress)
23 : m_port(port), m_deviceAddress(deviceAddress) {
24 int32_t status = 0;
25 HAL_InitializeI2C(m_port, &status);
26 // wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
27
28 HAL_Report(HALUsageReporting::kResourceType_I2C, deviceAddress);
29}
30
31/**
32 * Destructor.
33 */
34I2C::~I2C() { HAL_CloseI2C(m_port); }
35
36/**
37 * Generic transaction.
38 *
39 * This is a lower-level interface to the I2C hardware giving you more control
40 * over each transaction.
41 *
42 * @param dataToSend Buffer of data to send as part of the transaction.
43 * @param sendSize Number of bytes to send as part of the transaction.
44 * @param dataReceived Buffer to read data into.
45 * @param receiveSize Number of bytes to read from the device.
46 * @return Transfer Aborted... false for success, true for aborted.
47 */
48bool I2C::Transaction(uint8_t* dataToSend, int sendSize, uint8_t* dataReceived,
49 int receiveSize) {
50 int32_t status = 0;
51 status = HAL_TransactionI2C(m_port, m_deviceAddress, dataToSend, sendSize,
52 dataReceived, receiveSize);
53 // wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
54 return status < receiveSize;
55}
56
57/**
58 * Attempt to address a device on the I2C bus.
59 *
60 * This allows you to figure out if there is a device on the I2C bus that
61 * responds to the address specified in the constructor.
62 *
63 * @return Transfer Aborted... false for success, true for aborted.
64 */
65bool I2C::AddressOnly() { return Transaction(nullptr, 0, nullptr, 0); }
66
67/**
68 * Execute a write transaction with the device.
69 *
70 * Write a single byte to a register on a device and wait until the
71 * transaction is complete.
72 *
73 * @param registerAddress The address of the register on the device to be
74 * written.
75 * @param data The byte to write to the register on the device.
76 * @return Transfer Aborted... false for success, true for aborted.
77 */
78bool I2C::Write(int registerAddress, uint8_t data) {
79 uint8_t buffer[2];
80 buffer[0] = registerAddress;
81 buffer[1] = data;
82 int32_t status = 0;
83 status = HAL_WriteI2C(m_port, m_deviceAddress, buffer, sizeof(buffer));
84 return status < static_cast<int>(sizeof(buffer));
85}
86
87/**
88 * Execute a bulk write transaction with the device.
89 *
90 * Write multiple bytes to a device and wait until the
91 * transaction is complete.
92 *
93 * @param data The data to write to the register on the device.
94 * @param count The number of bytes to be written.
95 * @return Transfer Aborted... false for success, true for aborted.
96 */
97bool I2C::WriteBulk(uint8_t* data, int count) {
98 int32_t status = 0;
99 status = HAL_WriteI2C(m_port, m_deviceAddress, data, count);
100 return status < count;
101}
102
103/**
104 * Execute a read transaction with the device.
105 *
106 * Read bytes from a device.
107 * Most I2C devices will auto-increment the register pointer internally allowing
108 * you to read consecutive registers on a device in a single transaction.
109 *
110 * @param registerAddress The register to read first in the transaction.
111 * @param count The number of bytes to read in the transaction.
112 * @param buffer A pointer to the array of bytes to store the data
113 * read from the device.
114 * @return Transfer Aborted... false for success, true for aborted.
115 */
116bool I2C::Read(int registerAddress, int count, uint8_t* buffer) {
117 if (count < 1) {
118 wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
119 return true;
120 }
121 if (buffer == nullptr) {
122 wpi_setWPIErrorWithContext(NullParameter, "buffer");
123 return true;
124 }
125 return Transaction(reinterpret_cast<uint8_t*>(&registerAddress),
126 sizeof(registerAddress), buffer, count);
127}
128
129/**
130 * Execute a read only transaction with the device.
131 *
132 * Read bytes from a device. This method does not write any data to prompt the
133 * device.
134 *
135 * @param buffer A pointer to the array of bytes to store the data read from
136 * the device.
137 * @param count The number of bytes to read in the transaction.
138 * @return Transfer Aborted... false for success, true for aborted.
139 */
140bool I2C::ReadOnly(int count, uint8_t* buffer) {
141 if (count < 1) {
142 wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
143 return true;
144 }
145 if (buffer == nullptr) {
146 wpi_setWPIErrorWithContext(NullParameter, "buffer");
147 return true;
148 }
149 int32_t status = 0;
150 status = HAL_ReadI2C(m_port, m_deviceAddress, buffer, count);
151 return status < count;
152}
153
154/**
155 * Send a broadcast write to all devices on the I2C bus.
156 *
157 * This is not currently implemented!
158 *
159 * @param registerAddress The register to write on all devices on the bus.
160 * @param data The value to write to the devices.
161 */
162// [[gnu::warning("I2C::Broadcast() is not implemented.")]] void I2C::Broadcast(
163// int registerAddress, uint8_t data) {}
164
165/**
166 * Verify that a device's registers contain expected values.
167 *
168 * Most devices will have a set of registers that contain a known value that
169 * can be used to identify them. This allows an I2C device driver to easily
170 * verify that the device contains the expected value.
171 *
172 * @pre The device must support and be configured to use register
173 * auto-increment.
174 *
175 * @param registerAddress The base register to start reading from the device.
176 * @param count The size of the field to be verified.
177 * @param expected A buffer containing the values expected from the
178 * device.
179 */
180bool I2C::VerifySensor(int registerAddress, int count,
181 const uint8_t* expected) {
182 // TODO: Make use of all 7 read bytes
183 uint8_t deviceData[4];
184 for (int i = 0, curRegisterAddress = registerAddress; i < count;
185 i += 4, curRegisterAddress += 4) {
186 int toRead = count - i < 4 ? count - i : 4;
187 // Read the chunk of data. Return false if the sensor does not respond.
188 if (Read(curRegisterAddress, toRead, deviceData)) return false;
189
190 for (int j = 0; j < toRead; j++) {
191 if (deviceData[j] != expected[i + j]) return false;
192 }
193 }
194 return true;
195}