blob: 5e18fc1853550e363c0df5c22d7c867c540c47de [file] [log] [blame]
Parker Schuhd3b7a8872018-02-19 16:42:27 -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/SPI.h"
9#include "frc971/wpilib/ahal/SPI.h"
10
11#include <cstring>
12
13#include "HAL/HAL.h"
14#include "llvm/SmallVector.h"
15
16using namespace frc;
17
18#define HAL_FATAL_WITH_STATUS(status)
19
20/**
21 * Constructor
22 *
23 * @param SPIport the physical SPI port
24 */
25SPI::SPI(Port SPIport) {
26#ifdef WPILIB2017
27 m_port = SPIport;
28#else
29 m_port = static_cast<HAL_SPIPort>(SPIport);
30#endif
31 int32_t status = 0;
32 HAL_InitializeSPI(m_port, &status);
33 HAL_FATAL_WITH_STATUS(status);
34
35 static int instances = 0;
36 instances++;
37 HAL_Report(HALUsageReporting::kResourceType_SPI, instances);
38}
39
40/**
41 * Destructor.
42 */
43SPI::~SPI() { HAL_CloseSPI(m_port); }
44
45/**
46 * Configure the rate of the generated clock signal.
47 *
48 * The default value is 500,000Hz.
49 * The maximum value is 4,000,000Hz.
50 *
51 * @param hz The clock rate in Hertz.
52 */
53void SPI::SetClockRate(double hz) { HAL_SetSPISpeed(m_port, hz); }
54
55/**
56 * Configure the order that bits are sent and received on the wire
57 * to be most significant bit first.
58 */
59void SPI::SetMSBFirst() {
60 m_msbFirst = true;
61 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
62}
63
64/**
65 * Configure the order that bits are sent and received on the wire
66 * to be least significant bit first.
67 */
68void SPI::SetLSBFirst() {
69 m_msbFirst = false;
70 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
71}
72
73/**
74 * Configure that the data is stable on the falling edge and the data
75 * changes on the rising edge.
76 */
77void SPI::SetSampleDataOnFalling() {
78 m_sampleOnTrailing = true;
79 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
80}
81
82/**
83 * Configure that the data is stable on the rising edge and the data
84 * changes on the falling edge.
85 */
86void SPI::SetSampleDataOnRising() {
87 m_sampleOnTrailing = false;
88 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
89}
90
91/**
92 * Configure the clock output line to be active low.
93 * This is sometimes called clock polarity high or clock idle high.
94 */
95void SPI::SetClockActiveLow() {
96 m_clk_idle_high = true;
97 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
98}
99
100/**
101 * Configure the clock output line to be active high.
102 * This is sometimes called clock polarity low or clock idle low.
103 */
104void SPI::SetClockActiveHigh() {
105 m_clk_idle_high = false;
106 HAL_SetSPIOpts(m_port, m_msbFirst, m_sampleOnTrailing, m_clk_idle_high);
107}
108
109/**
110 * Configure the chip select line to be active high.
111 */
112void SPI::SetChipSelectActiveHigh() {
113 int32_t status = 0;
114 HAL_SetSPIChipSelectActiveHigh(m_port, &status);
115 HAL_FATAL_WITH_STATUS(status);
116}
117
118/**
119 * Configure the chip select line to be active low.
120 */
121void SPI::SetChipSelectActiveLow() {
122 int32_t status = 0;
123 HAL_SetSPIChipSelectActiveLow(m_port, &status);
124 HAL_FATAL_WITH_STATUS(status);
125}
126
127/**
128 * Write data to the slave device. Blocks until there is space in the
129 * output FIFO.
130 *
131 * If not running in output only mode, also saves the data received
132 * on the MISO input during the transfer into the receive FIFO.
133 */
134int SPI::Write(uint8_t *data, int size) {
135 int retVal = 0;
136 retVal = HAL_WriteSPI(m_port, data, size);
137 return retVal;
138}
139
140/**
141 * Read a word from the receive FIFO.
142 *
143 * Waits for the current transfer to complete if the receive FIFO is empty.
144 *
145 * If the receive FIFO is empty, there is no active transfer, and initiate
146 * is false, errors.
147 *
148 * @param initiate If true, this function pushes "0" into the transmit buffer
149 * and initiates a transfer. If false, this function assumes
150 * that data is already in the receive FIFO from a previous
151 * write.
152 */
153int SPI::Read(bool initiate, uint8_t *dataReceived, int size) {
154 int retVal = 0;
155 if (initiate) {
156 llvm::SmallVector<uint8_t, 32> dataToSend;
157 dataToSend.resize(size);
158 retVal = HAL_TransactionSPI(m_port, dataToSend.data(), dataReceived, size);
159 } else {
160 retVal = HAL_ReadSPI(m_port, dataReceived, size);
161 }
162 return retVal;
163}
164
165/**
166 * Perform a simultaneous read/write transaction with the device
167 *
168 * @param dataToSend The data to be written out to the device
169 * @param dataReceived Buffer to receive data from the device
170 * @param size The length of the transaction, in bytes
171 */
172int SPI::Transaction(uint8_t *dataToSend, uint8_t *dataReceived, int size) {
173 int retVal = 0;
174 retVal = HAL_TransactionSPI(m_port, dataToSend, dataReceived, size);
175 return retVal;
176}