summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/spi.c
blob: 4b6896f89f6f1f29751e88cf81df197d199db8ac (plain) (blame)
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/* ---------------------------------------------------------------------------- */
/*                  Atmel Microcontroller Software Support                      */
/*                       SAM Software Package License                           */
/* ---------------------------------------------------------------------------- */
/* Copyright (c) 2015, Atmel Corporation                                        */
/*                                                                              */
/* All rights reserved.                                                         */
/*                                                                              */
/* Redistribution and use in source and binary forms, with or without           */
/* modification, are permitted provided that the following condition is met:    */
/*                                                                              */
/* - Redistributions of source code must retain the above copyright notice,     */
/* this list of conditions and the disclaimer below.                            */
/*                                                                              */
/* Atmel's name may not be used to endorse or promote products derived from     */
/* this software without specific prior written permission.                     */
/*                                                                              */
/* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
/* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
/* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
/* ---------------------------------------------------------------------------- */

/** \addtogroup spi_module Working with SPI
 * The SPI driver provides the interface to configure and use the SPI
 * peripheral.
 *
 * The Serial Peripheral Interface (SPI) circuit is a synchronous serial
 * data link that provides communication with external devices in Master
 * or Slave Mode.
 *
 * To use the SPI, the user has to follow these few steps:
 * -# Enable the SPI pins required by the application (see pio.h).
 * -# Configure the SPI using the \ref SPI_Configure(). This enables the
 *    peripheral clock. The mode register is loaded with the given value.
 * -# Configure all the necessary chip selects with \ref SPI_ConfigureNPCS().
 * -# Enable the SPI by calling \ref SPI_Enable().
 * -# Send/receive data using \ref SPI_Write() and \ref SPI_Read(). Note that
 * \ref SPI_Read()
 *    must be called after \ref SPI_Write() to retrieve the last value read.
 * -# Disable the SPI by calling \ref SPI_Disable().
 *
 * For more accurate information, please look at the SPI section of the
 * Datasheet.
 *
 * Related files :\n
 * \ref spi.c\n
 * \ref spi.h.\n
 */
/*@{*/
/*@}*/

/**
 * \file
 *
 * Implementation of Serial Peripheral Interface (SPI) controller.
 *
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"

#include <stdint.h>

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Enables a SPI peripheral.
 *
 * \param spi  Pointer to an SPI instance.
 */
extern void SPI_Enable(Spi *spi)
{
	spi->SPI_CR = SPI_CR_SPIEN;
}

/**
 * \brief Disables a SPI peripheral.
 *
 * \param spi  Pointer to an SPI instance.
 */
extern void SPI_Disable(Spi *spi)
{
	spi->SPI_CR = SPI_CR_SPIDIS;
}

/**
 * \brief Enables one or more interrupt sources of a SPI peripheral.
 *
 * \param spi  Pointer to an SPI instance.
 * \param sources Bitwise OR of selected interrupt sources.
 */
extern void SPI_EnableIt(Spi *spi, uint32_t dwSources)
{
	spi->SPI_IER = dwSources;
}

/**
 * \brief Disables one or more interrupt sources of a SPI peripheral.
 *
 * \param spi  Pointer to an SPI instance.
 * \param sources Bitwise OR of selected interrupt sources.
 */
extern void SPI_DisableIt(Spi *spi, uint32_t dwSources)
{
	spi->SPI_IDR = dwSources;
}

/**
 * \brief Configures a SPI peripheral as specified. The configuration can be
 * computed using several macros (see \ref spi_configuration_macros).
 *
 * \param spi  Pointer to an SPI instance.
 * \param id   Peripheral ID of the SPI.
 * \param configuration  Value of the SPI configuration register.
 */
extern void SPI_Configure(Spi *spi, uint32_t dwId, uint32_t dwConfiguration)
{
	PMC_EnablePeripheral(dwId);

	spi->SPI_CR = SPI_CR_SPIDIS;

	/* Execute a software reset of the SPI twice */
	spi->SPI_CR = SPI_CR_SWRST;
	spi->SPI_CR = SPI_CR_SWRST;
	spi->SPI_MR = dwConfiguration;
}

/**
 * \brief Configures SPI chip select.
 *
 * \param spi  Pointer to an SPI instance.
 * \param cS  Chip select of NPSCx.
 */
extern void SPI_ChipSelect(Spi *spi, uint8_t cS)
{
	spi->SPI_MR |= SPI_MR_PCS_Msk;
	spi->SPI_MR &= ~(SPI_MR_PCS (cS));
}

/**
 * \brief Configures SPI Mode Register.
 *
 * \param spi  Pointer to an SPI instance.
 * \param configuration  Value of the SPI mode register.
 */
extern void SPI_SetMode(Spi *spi,
						 uint32_t dwConfiguration)
{
	spi->SPI_MR = dwConfiguration;
}

/**
 * \brief Configures SPI to release last used CS line.
 *
 * \param spi  Pointer to an SPI instance.
 */
extern void SPI_ReleaseCS(Spi *spi)
{
	spi->SPI_CR = SPI_CR_LASTXFER;
}


/**
 * \brief Configures a chip select of a SPI peripheral. The chip select
 * configuration is computed using several macros
 * (see \ref spi_configuration_macros).
 *
 * \param spi   Pointer to an SPI instance.
 * \param npcs  Chip select to configure (0, 1, 2 or 3).
 * \param configuration  Desired chip select configuration.
 */
void SPI_ConfigureNPCS(Spi *spi, uint32_t dwNpcs, uint32_t dwConfiguration)
{
	spi->SPI_CSR[dwNpcs] = dwConfiguration;
}

/**
 * \brief Configures a chip select active mode of a SPI peripheral.
 *
 * \param spi   Pointer to an SPI instance.
 * \param dwNpcs  Chip select to configure (0, 1, 2 or 3).
 * \param bReleaseOnLast CS controlled by last transfer.
 *                       SPI_ReleaseCS() is used to release CS.
 */
void SPI_ConfigureCSMode(Spi *spi, uint32_t dwNpcs, uint32_t bReleaseOnLast)
{
	if (bReleaseOnLast)
		spi->SPI_CSR[dwNpcs] |=  SPI_CSR_CSAAT;
	else
		spi->SPI_CSR[dwNpcs] &= ~SPI_CSR_CSAAT;
}

/**
 * \brief Get the current status register of the given SPI peripheral.
 * \note This resets the internal value of the status register, so further
 * read may yield different values.
 * \param spi   Pointer to a Spi instance.
 * \return  SPI status register.
 */
extern uint32_t SPI_GetStatus(Spi *spi)
{
	return spi->SPI_SR;
}

/**
 * \brief Reads and returns the last word of data received by a SPI peripheral.
 * This method must be called after a successful SPI_Write call.
 *
 * \param spi  Pointer to an Spi instance.
 *
 * \return read data.
 */
extern uint32_t SPI_Read(Spi *spi)
{
	while ((spi->SPI_SR & SPI_SR_RDRF) == 0);

	return spi->SPI_RDR & 0xFFFF;
}

/**
 * \brief Sends data through a SPI peripheral. If the SPI is configured to use a
 * fixed  peripheral select, the npcs value is meaningless. Otherwise,
 * it identifies the component which shall be addressed.
 *
 * \param spi   Pointer to an SPI instance.
 * \param npcs  Chip select of the component to address (0, 1, 2 or 3).
 * \param data  Word of data to send.
 */
extern void SPI_Write(Spi *spi, uint32_t dwNpcs, uint16_t wData)
{
	/* Send data */
	while ((spi->SPI_SR & SPI_SR_TXEMPTY) == 0);

	spi->SPI_TDR = wData | SPI_PCS(dwNpcs);

	while ((spi->SPI_SR & SPI_SR_TDRE) == 0);
}

/**
 * \brief Sends last data through a SPI peripheral.
 * If the SPI is configured to use a fixed peripheral select, the npcs value is
 * meaningless. Otherwise, it identifies the component which shall be addressed.
 *
 * \param spi   Pointer to an SPI instance.
 * \param npcs  Chip select of the component to address (0, 1, 2 or 3).
 * \param data  Word of data to send.
 */
extern void SPI_WriteLast(Spi *spi, uint32_t dwNpcs, uint16_t wData)
{
	/* Send data */
	while ((spi->SPI_SR & SPI_SR_TXEMPTY) == 0);

	spi->SPI_TDR = wData | SPI_PCS(dwNpcs) | SPI_TDR_LASTXFER;

	while ((spi->SPI_SR & SPI_SR_TDRE) == 0);
}

/**
 * \brief Check if SPI transfer finish.
 *
 * \param spi  Pointer to an SPI instance.
 *
 * \return Returns 1 if there is no pending write operation on the SPI;
 * otherwise returns 0.
 */
extern uint32_t SPI_IsFinished(Spi *spi)
{
	return ((spi->SPI_SR & SPI_SR_TXEMPTY) != 0);
}