-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspi.c
254 lines (242 loc) · 11.5 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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{ }
{ Filename: spi.c }
{ Copyright(c): Leon de Boer(LdB) 2019, 2020 }
{ Version: 1.10 }
{ }
{***************************************************************************}
{ }
{ Defines an API interface for the SPI devices on Linux }
{ }
{++++++++++++++++++++++++[ REVISIONS ]++++++++++++++++++++++++++++++++++++++}
{ 1.00 Initial version }
{ 1.10 Compacted stuct fields }
{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include <stdbool.h> // C standard unit for bool, true, false
#include <stdint.h> // C standard unit for uint32_t etc
#include <fcntl.h> // Needed for SPI port
#include <sys/ioctl.h> // Needed for SPI port
#include <linux/spi/spidev.h> // Needed for SPI port
#include <unistd.h> // Needed for SPI port
#include <stdio.h> // neded for sprintf_s
#include <pthread.h> // Posix thread unit
#include <semaphore.h> // Linux Semaphore unit
#include "spi.h" // This units header
#if SPI_DRIVER_VERSION != 1100
#error "Header does not match this version of file"
#endif
struct spi_device
{
int spi_fd; // File descriptor for the SPI device
uint32_t spi_speed; // SPI speed
sem_t lock; // Semaphore for lock
struct {
uint16_t spi_bitsPerWord: 8; // SPI bits per word
uint16_t spi_num : 4; // SPI device table number
uint16_t _reserved: 2; // reserved
uint16_t uselocks : 1; // Locks to be used for access
uint16_t inuse : 1; // In use flag
};
};
/* Global table of SPI devices. */
static struct spi_device spitab[NSPI] = { {0} };
/*-[ SpiOpenPort ]----------------------------------------------------------}
. Creates a SPI handle which provides access to the SPI device number.
. The SPI device is setup to the bits, speed and mode provided.
. RETURN: valid SPI_HANDLE for success, NULL for any failure
.--------------------------------------------------------------------------*/
SPI_HANDLE SpiOpenPort (uint8_t spi_devicenum, uint8_t spi_port, uint16_t bit_exchange_size, uint32_t speed, uint8_t mode, bool useLock)
{
SPI_HANDLE spi = 0; // Preset null handle
struct spi_device* spi_ptr = &spitab[spi_devicenum]; // SPI device pointer
if (spi_devicenum < NSPI && spi_ptr->inuse == 0 && speed != 0)
{
spi_ptr->spi_fd = 0; // Zero SPI file device
spi_ptr->spi_num = spi_devicenum; // Hold spi device number
spi_ptr->spi_bitsPerWord = bit_exchange_size; // Hold SPI exchange size
spi_ptr->spi_speed = speed; // Hold SPI speed setting
spi_ptr->uselocks = (useLock == true) ? 1 : 0; // Set use lock
if (useLock) // Using locks
{
sem_init(&spi_ptr->lock, 0, 1); // Initialize mutex to 1
}
uint8_t spi_mode = mode;
uint16_t spi_bitsPerWord = bit_exchange_size;
char buf[256] = { 0 };
//sprintf(&buf[0], "/dev/spidev%c.%c", (char)(spi_port), (char)(0x30 + spi_devicenum));
if (spi_port == 0x0)
{
sprintf(&buf[0], "/dev/spidev0.%c", (char)(0x30 + spi_devicenum));
}
if (spi_port == 0x3)
{
sprintf(&buf[0], "/dev/spidev3.%c", (char)(0x30 + spi_devicenum));
}
int fd = open(&buf[0], O_RDWR); // Open the SPI device
if (fd >= 0) // SPI device opened correctly
{
spi_ptr->spi_fd = fd; // Hold the file device to SPI
if (ioctl(spi_ptr->spi_fd, SPI_IOC_WR_MODE, &spi_mode) >= 0 &&
ioctl(spi_ptr->spi_fd, SPI_IOC_RD_MODE, &spi_mode) >= 0 &&
ioctl(spi_ptr->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bitsPerWord) >= 0 &&
ioctl(spi_ptr->spi_fd, SPI_IOC_RD_BITS_PER_WORD, &spi_bitsPerWord) >= 0 &&
ioctl(spi_ptr->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_ptr->spi_speed) >= 0 &&
ioctl(spi_ptr->spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_ptr->spi_speed) >= 0)
{
spi_ptr->inuse = 1; // The handle is now in use
spi = spi_ptr; // Return SPI handle
}
}
}
return(spi); // Return SPI handle result
}
/*-[ SpiClosePort ]---------------------------------------------------------}
. Given a valid SPI handle the access is released and the handle freed.
. RETURN: true for success, false for any failure
.--------------------------------------------------------------------------*/
bool SpiClosePort (SPI_HANDLE spiHandle)
{
if (spiHandle && spiHandle->inuse) // SPI handle valid and SPI handle is in use
{
if (spiHandle->uselocks) // Using locks
{
sem_destroy(&spiHandle->lock); // Destroy lock mutex
}
close(spiHandle->spi_fd); // Close the spi handle
spiHandle->spi_fd = 0; // Zero the SPI handle
spiHandle->spi_num = 0; // Zero SPI handle number
spiHandle->inuse = 0; // The SPI handle is now free
return true; // Return success
}
return false; // Return failure
}
/*-[ SpiWriteAndRead ]------------------------------------------------------}
. Given a valid SPI handle and valid data pointers the call will send and
. receive data to and from the buffer pointers. As the write occurs before
. the read the buffer pointers can be the same buffer space.
. RETURN: >= 0 transfer count for success, < 0 for any error
.--------------------------------------------------------------------------*/
int SpiWriteAndRead (SPI_HANDLE spiHandle, uint8_t* TxData, uint8_t* RxData, uint8_t Length, bool LeaveCsLow)
{
int retVal = -1; // Preset -1
if (spiHandle && spiHandle->inuse) // SPI handle valid and SPI handle is in use
{
if (spiHandle->uselocks) // Using locks
{
sem_wait(&spiHandle->lock); // Take semaphore
}
struct spi_ioc_transfer spi = { 0 };
spi.tx_buf = (unsigned long)TxData; // transmit from "data"
spi.rx_buf = (unsigned long)RxData; // receive into "data"
spi.len = Length; // length of data to tx/rx
spi.delay_usecs = 0; // Delay before sending
spi.speed_hz = spiHandle->spi_speed; // Speed for transfer
spi.bits_per_word = spiHandle->spi_bitsPerWord; // Bits per exchange
spi.cs_change = LeaveCsLow; // 0=Set CS high after a transfer, 1=leave CS set low
retVal = ioctl(spiHandle->spi_fd, SPI_IOC_MESSAGE(1), &spi);// Execute exchange
if (spiHandle->uselocks) // Using locks
{
sem_post(&spiHandle->lock); // Give semaphore
}
}
return retVal; // Return result
}
/*-[ SpiWriteAndRead16 ]------------------------------------------------------}
. Given a valid SPI handle and valid data pointers the call will send and
. receive data to and from the buffer pointers. As the write occurs before
. the read the buffer pointers can be the same buffer space.
. RETURN: >= 0 transfer count for success, < 0 for any error
.--------------------------------------------------------------------------*/
int SpiWriteAndRead16 (SPI_HANDLE spiHandle, uint16_t* TxData, uint16_t* RxData, uint8_t Length, bool LeaveCsLow)
{
int retVal = -1; // Preset -1
if (spiHandle && spiHandle->inuse) // SPI handle valid and SPI handle is in use
{
if (spiHandle->uselocks) // Using locks
{
sem_wait(&spiHandle->lock); // Take semaphore
}
struct spi_ioc_transfer spi = { 0 };
spi.tx_buf = (unsigned long)TxData; // transmit from "data"
spi.rx_buf = (unsigned long)RxData; // receive into "data"
spi.len = Length; // length of data to tx/rx
spi.delay_usecs = 0; // Delay before sending
spi.speed_hz = spiHandle->spi_speed; // Speed for transfer
spi.bits_per_word = spiHandle->spi_bitsPerWord; // Bits per exchange
spi.cs_change = LeaveCsLow; // 0=Set CS high after a transfer, 1=leave CS set low
retVal = ioctl(spiHandle->spi_fd, SPI_IOC_MESSAGE(1), &spi);// Execute exchange
if (spiHandle->uselocks) // Using locks
{
sem_post(&spiHandle->lock); // Give semaphore
}
}
return retVal; // Return result
}
/*-[ SpiWriteAndRead32 ]------------------------------------------------------}
. Given a valid SPI handle and valid data pointers the call will send and
. receive data to and from the buffer pointers. As the write occurs before
. the read the buffer pointers can be the same buffer space.
. RETURN: >= 0 transfer count for success, < 0 for any error
.--------------------------------------------------------------------------*/
int SpiWriteAndRead32 (SPI_HANDLE spiHandle, uint32_t* TxData, uint32_t* RxData, uint8_t Length, bool LeaveCsLow)
{
int retVal = -1; // Preset -1
if (spiHandle && spiHandle->inuse) // SPI handle valid and SPI handle is in use
{
if (spiHandle->uselocks) // Using locks
{
sem_wait(&spiHandle->lock); // Take semaphore
}
struct spi_ioc_transfer spi = { 0 };
spi.tx_buf = (unsigned long)TxData; // transmit from "data"
spi.rx_buf = (unsigned long)RxData; // receive into "data"
spi.len = Length; // length of data to tx/rx
spi.delay_usecs = 0; // Delay before sending
spi.speed_hz = spiHandle->spi_speed; // Speed for transfer
spi.bits_per_word = spiHandle->spi_bitsPerWord; // Bits per exchange
spi.cs_change = LeaveCsLow; // 0=Set CS high after a transfer, 1=leave CS set low
retVal = ioctl(spiHandle->spi_fd, SPI_IOC_MESSAGE(1), &spi);// Execute exchange
if (spiHandle->uselocks) // Using locks
{
sem_post(&spiHandle->lock); // Give semaphore
}
}
return retVal; // Return result
}
/*-[ SpiWriteBlockRepeat ]--------------------------------------------------}
. Given a valid SPI handle and valid data pointers the call will send the
. data block repeat times. It is used to speed up things like writing to SPI
. LCD screen with areas a fixed colour.
. RETURN: >= 0 blocks transfered for success, < 0 for any error
.--------------------------------------------------------------------------*/
int SpiWriteBlockRepeat (SPI_HANDLE spiHandle, uint16_t* TxBlock, uint16_t TxBlockLen, uint32_t Repeats, bool LeaveCsLow)
{
int retVal = -1; // Preset -1
if (spiHandle && TxBlock && spiHandle->inuse) // SPI handle and TxBlock valid and SPI handle is in use
{
if (spiHandle->uselocks) // Using locks
{
sem_wait(&spiHandle->lock); // Take semaphore
}
struct spi_ioc_transfer spi = { 0 };
spi.tx_buf = (unsigned long)TxBlock; // transmit from "data"
spi.rx_buf = (unsigned long)0; // receive into "data"
spi.len = TxBlockLen; // length of data to tx/rx
spi.delay_usecs = 0; // Delay before sending
spi.speed_hz = spiHandle->spi_speed; // Speed for transfer
spi.bits_per_word = spiHandle->spi_bitsPerWord; // Bits per exchange
spi.cs_change = LeaveCsLow; // 0=Set CS high after a transfer, 1=leave CS set low
retVal = 0; // Zero retVal
uint32_t j;
for (j = 0; j < Repeats && retVal == TxBlockLen; j++) // For each block repeat
{
retVal = ioctl(spiHandle->spi_fd, SPI_IOC_MESSAGE(1), &spi);// Execute exchange
}
retVal = j; // Return block count
if (spiHandle->uselocks) // Using locks
{
sem_post(&spiHandle->lock); // Give semaphore
}
}
return retVal; // Return result
}