-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspi.c
240 lines (202 loc) · 7.97 KB
/
spi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
////////////////////////////////////////////////////////////////////////////////
/// @file
/// @brief Serial Peripheral Interface (SPI) driver.
////////////////////////////////////////////////////////////////////////////////
// *****************************************************************************
// ************************** System Include Files *****************************
// *****************************************************************************
#include <sys/attribs.h>
// *****************************************************************************
// ************************** User Include Files *******************************
// *****************************************************************************
#include "spi.h"
// *****************************************************************************
// ************************** Macros *******************************************
// *****************************************************************************
// *****************************************************************************
// ************************** Defines ******************************************
// *****************************************************************************
// *****************************************************************************
// ************************** Definitions **************************************
// *****************************************************************************
static volatile SPI_XFER *spi2Xfer = NULL; // Null if idle, else pointer to data.
static volatile unsigned int rxCount = 0; // Count of SPI bytes received.
static volatile unsigned int txCount = 0; // Count of SPI bytes transmitted.
// *****************************************************************************
// ************************** Function Prototypes ******************************
// *****************************************************************************
// *****************************************************************************
// ************************** Global Functions *********************************
// *****************************************************************************
void SPITask( void )
{
uint8_t data;
static enum {
SM_IDLE,
SM_START,
SM_PROCESS,
} spiTaskState = SM_IDLE;
switch (spiTaskState)
{
case SM_IDLE:
{
if ((SPI2STATbits.SPIBUSY == 1) || (spi2Xfer == 0))
{
break;
}
spiTaskState = SM_START;
// No break.
}
case SM_START:
{
// Disable SPI2 Interrupts.
IEC1CLR = _IEC1_SPI2EIE_MASK | _IEC1_SPI2RXIE_MASK |
_IEC1_SPI2TXIE_MASK;
while (SPI2STATbits.SPIRBE == 0)
{
SPI2BUF; // Flush RX buffer.
}
mSPI2_SS_SET(); // Assert slave select.
rxCount = 0; // Clear RX byte counter.
txCount = 0; // Clear TX byte counter.
// Clear SPI2 Interrupt flags.
IFS1CLR = _IFS1_SPI2EIF_MASK | _IFS1_SPI2RXIF_MASK |
_IFS1_SPI2TXIF_MASK;
// Enable SPI2 Interrupts.
IEC1SET = _IEC1_SPI2EIE_MASK | _IEC1_SPI2RXIE_MASK |
_IEC1_SPI2TXIE_MASK;
spiTaskState = SM_PROCESS;
break;
}
case SM_PROCESS:
{
// Disable SPI2 Interrupts.
IEC1CLR = _IEC1_SPI2EIE_MASK | _IEC1_SPI2RXIE_MASK |
_IEC1_SPI2TXIE_MASK;
// Unload RX buffer. --------------------------
while (SPI2STATbits.SPIRBE == 0)
{
data = SPI2BUF;
if (spi2Xfer->rxBuf != 0)
{
spi2Xfer->rxBuf[rxCount] = data;
}
rxCount++;
}
// Load TX buffer. ----------------------------
// (See SPI2 TX interrupt routine for transmit logic.)
// Check if transfer is complete. -------------
if (rxCount == spi2Xfer->length)
{
mSPI2_SS_CLR(); // Deassert slave select.
spi2Xfer->xferDone = 1; // Set transfer done flag.
spi2Xfer = 0; // Clear data pointer.
spiTaskState = SM_IDLE;
}
else
{
// Enable SPI2 Interrupts.
IEC1SET = _IEC1_SPI2EIE_MASK | _IEC1_SPI2RXIE_MASK |
_IEC1_SPI2TXIE_MASK;
}
break;
}
}
}
//==============================================================================
int SPIXfer(SPI_XFER *xfer)
{
enum {
SUCCESS = 0,
FAILURE = 1,
} spiXferStatus = FAILURE;
// Non-NULL data supplied ?
if (xfer != 0)
{
// Process data based on SPI port.
switch (xfer->port)
{
case SPI_PORT_SPI2:
{
// SPI2 is currently not transferring data ?
if (spi2Xfer == 0)
{
// Set up buffer for transfer.
spi2Xfer = xfer; // Copy data pointer.
xfer->xferDone = 0; // Clear transfer done flag.
spiXferStatus = SUCCESS; // Success.
}
break;
}
default:
{
// Unknown SPI port.
break;
}
}
}
return spiXferStatus;
}
// *****************************************************************************
// ************************** Static Functions *********************************
// *****************************************************************************
////////////////////////////////////////////////////////////////////////////////
/// @brief Periodic SPI interrupt.
///
/// This function manages the SPI hardware to sending and receiving data
/// when hardware buffers are available.
////////////////////////////////////////////////////////////////////////////////
void __ISR (_SPI_2_VECTOR, IPL7SRS) SPI2ISR(void)
{
uint8_t data;
// SPI2 Error Interrupt -------------------------------
if (IFS1bits.SPI2EIF == 1)
{
// TODO: Error handling. See FRMERREN, SPIROVEN, and SPITUREN bits
// in the SPIxCON2 register.
IFS1CLR = _IFS1_SPI2EIF_MASK; // Clear error interrupt flag.
}
// SPI2 RX Interrupt ----------------------------------
if (IFS1bits.SPI2RXIF == 1)
{
// RX buffer is one-half or more full.
while (SPI2STATbits.SPIRBE == 0)
{
// Unload RX buffer.
data = SPI2BUF;
if (spi2Xfer->rxBuf != 0)
{
spi2Xfer->rxBuf[rxCount] = data;
}
rxCount++;
}
IFS1CLR = _IFS1_SPI2RXIF_MASK; // Clear RX interrupt flag.
}
// SPI2 TX Interrupt ----------------------------------
if (IFS1bits.SPI2TXIF == 1)
{
// TX buffer is one-half or more empty.
while ((SPI2STATbits.SPITBF == 0) && // TX buffer not full, and
(txCount < spi2Xfer->length) && // more TX bytes left, and
((txCount - rxCount) < 16)) // TX count not exceeding
// 16 byte RX buffer.
{
// Load the TX buffer.
if (spi2Xfer->txBuf == 0)
{
SPI2BUF = 0x00;
}
else
{
SPI2BUF = spi2Xfer->txBuf[txCount];
}
txCount++;
}
if (txCount == spi2Xfer->length)
{
// TX data is fully loaded. Disable the SPI2 TX interrupt.
IEC1CLR = _IEC1_SPI2TXIE_MASK;
}
IFS1CLR = _IFS1_SPI2TXIF_MASK; // Clear TX interrupt flag.
}
}