Spieler splodiert.
[hackover2013-badge-firmware.git] / core / ssp / ssp.c
1 /**************************************************************************/
2 /*!
3 @file ssp.c
4 @author K. Townsend (microBuilder.eu)
5
6 @section DESCRIPTION
7
8 Generic code for SSP/SPI communications. By default, the SSP block
9 is initialised in SPI master mode.
10
11 @section Example
12
13 @code
14 #include "core/cpu/cpu.h"
15 #include "core/ssp/ssp.h"
16 ...
17 cpuInit();
18 sspInit(0, sspClockPolarity_High, sspClockPhase_RisingEdge);
19 ...
20 uint8_t request[SSP_FIFOSIZE];
21 uint8_t response[SSP_FIFOSIZE];
22
23 // Send 0x9C to the slave device and wait for a response
24 request[0] = 0x80 | 0x1C;
25 // Toggle the select pin
26 ssp0Select();
27 // Send 1 byte from the request buffer
28 sspSend(0, (uint8_t *)&request, 1);
29 // Receive 1 byte into the response buffer
30 sspReceive(0, (uint8_t *)&response, 1);
31 // Reset the select pin
32 ssp0Deselect();
33 // Print the results
34 debug_printf("Ox%x ", response[0]);
35 @endcode
36
37 @section LICENSE
38
39 Software License Agreement (BSD License)
40
41 Copyright (c) 2012, K. Townsend
42 All rights reserved.
43
44 Redistribution and use in source and binary forms, with or without
45 modification, are permitted provided that the following conditions are met:
46 1. Redistributions of source code must retain the above copyright
47 notice, this list of conditions and the following disclaimer.
48 2. Redistributions in binary form must reproduce the above copyright
49 notice, this list of conditions and the following disclaimer in the
50 documentation and/or other materials provided with the distribution.
51 3. Neither the name of the copyright holders nor the
52 names of its contributors may be used to endorse or promote products
53 derived from this software without specific prior written permission.
54
55 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
56 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
57 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
58 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
59 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
60 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
62 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66 /**************************************************************************/
67 #include "ssp.h"
68 #include "core/gpio/gpio.h"
69
70 /* Statistics for all interrupts */
71 volatile uint32_t interruptRxStat = 0;
72 volatile uint32_t interruptOverRunStat = 0;
73 volatile uint32_t interruptRxTimeoutStat = 0;
74
75 /**************************************************************************/
76 /*!
77 @brief SSP0 interrupt handler for SPI communication
78
79 The algorithm is, if RXFIFO is at least half full,
80 start receive until it's empty; if TXFIFO is at least
81 half empty, start transmit until it's full.
82 This will maximize the use of both FIFOs and performance.
83 */
84 /**************************************************************************/
85 void SSP_IRQHandler (void)
86 {
87 uint32_t regValue;
88
89 regValue = SSP_SSP0MIS;
90
91 /* Check for overrun interrupt */
92 if ( regValue & SSP_SSP0MIS_RORMIS_FRMRCVD )
93 {
94 interruptOverRunStat++;
95 SSP_SSP0ICR = SSP_SSP0ICR_RORIC_CLEAR; // Clear interrupt
96 }
97
98 /* Check for timeout interrupt */
99 if ( regValue & SSP_SSP0MIS_RTMIS_NOTEMPTY )
100 {
101 interruptRxTimeoutStat++;
102 SSP_SSP0ICR = SSP_SSP0ICR_RTIC_CLEAR; // Clear interrupt
103 }
104
105 /* Check if Rx buffer is at least half-full */
106 if ( regValue & SSP_SSP0MIS_RXMIS_HALFFULL )
107 {
108 // ToDo: Receive until it's empty
109 interruptRxStat++;
110 }
111 return;
112 }
113
114 /**************************************************************************/
115 /*!
116 @brief Initialises the SSP0 port
117
118 By default, SSP0 is set to SPI frame-format with 8-bit data. Pin 2.11
119 is routed to serve as serial clock (SCK), and SSEL (0.2) is set to
120 GPIO to allow manual control of when the SPI port is enabled or
121 disabled. Overrun and timeout interrupts are both enabled.
122
123 @param[in] portNum
124 The SPI port to use (0..1)
125 @param[in] polarity
126 Indicates whether the clock should be held high
127 (sspClockPolarity_High) or low (sspClockPolarity_Low)
128 when inactive.
129 @param[in] phase
130 Indicates whether new bits start on the leading
131 (sspClockPhase_RisingEdge) or falling
132 (sspClockPhase_FallingEdge) edge of clock transitions.
133
134 @note sspSelect() and sspDeselect() macros have been defined in
135 ssp.h to control the SSEL line without having to know the
136 specific pin being used.
137 */
138 /**************************************************************************/
139 void sspInit (uint8_t portNum, sspClockPolarity_t polarity, sspClockPhase_t phase)
140 {
141 gpioInit();
142
143 if (portNum == 0)
144 {
145 /* Reset SSP */
146 SCB_PRESETCTRL &= ~SCB_PRESETCTRL_SSP0_MASK;
147 SCB_PRESETCTRL |= SCB_PRESETCTRL_SSP0_RESETDISABLED;
148
149 /* Enable AHB clock to the SSP domain. */
150 SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_SSP0);
151
152 /* Divide by 1 (SSPCLKDIV also enables to SSP CLK) */
153 SCB_SSP0CLKDIV = SCB_SSP0CLKDIV_DIV1;
154
155 /* Set P0.8 to SSP MISO */
156 IOCON_PIO0_8 &= ~IOCON_PIO0_8_FUNC_MASK;
157 IOCON_PIO0_8 |= IOCON_PIO0_8_FUNC_MISO0;
158
159 /* Set P0.9 to SSP MOSI */
160 IOCON_PIO0_9 &= ~IOCON_PIO0_9_FUNC_MASK;
161 IOCON_PIO0_9 |= IOCON_PIO0_9_FUNC_MOSI0;
162
163 /* Set 2.11 to SSP SCK (0.6 and 0.10 can also be used) */
164 #ifdef CFG_SSP0_SCKPIN_2_11
165 IOCON_SCKLOC = IOCON_SCKLOC_SCKPIN_PIO2_11;
166 IOCON_PIO2_11 = IOCON_PIO2_11_FUNC_SCK0;
167 #endif
168
169 /* Set 0.6 to SSP SCK (2.11 and 0.10 can also be used) */
170 #ifdef CFG_SSP0_SCKPIN_0_6
171 IOCON_SCKLOC = IOCON_SCKLOC_SCKPIN_PIO0_6;
172 IOCON_PIO0_6 = IOCON_PIO0_6_FUNC_SCK;
173 #endif
174
175 /* Set P0.2/SSEL to GPIO output and high */
176 IOCON_PIO0_2 &= ~IOCON_PIO0_2_FUNC_MASK;
177 IOCON_PIO0_2 |= IOCON_PIO0_2_FUNC_GPIO;
178 gpioSetDir(SSP0_CSPORT, SSP0_CSPIN, 1);
179 gpioSetValue(SSP0_CSPORT, SSP0_CSPIN, 1);
180 gpioSetPullup(&IOCON_PIO0_2, gpioPullupMode_Inactive); // Board has external pull-up
181
182 /* If SSP0CLKDIV = DIV1 -- (PCLK / (CPSDVSR * [SCR+1])) = (72,000,000 / (2 x [8 + 1])) = 4.0 MHz */
183 uint32_t configReg = ( SSP_SSP0CR0_DSS_8BIT // Data size = 8-bit
184 | SSP_SSP0CR0_FRF_SPI // Frame format = SPI
185 | SSP_SSP0CR0_SCR_8); // Serial clock rate = 8
186
187 // Set clock polarity
188 if (polarity == sspClockPolarity_High)
189 configReg |= SSP_SSP0CR0_CPOL_HIGH; // Clock polarity = High between frames
190 else
191 configReg &= ~SSP_SSP0CR0_CPOL_MASK; // Clock polarity = Low between frames
192
193 // Set edge transition
194 if (phase == sspClockPhase_FallingEdge)
195 configReg |= SSP_SSP0CR0_CPHA_SECOND; // Clock out phase = Trailing edge clock transition
196 else
197 configReg &= ~SSP_SSP0CR0_CPHA_MASK; // Clock out phase = Leading edge clock transition
198
199 // Assign config values to SSP0CR0
200 SSP_SSP0CR0 = configReg;
201
202 /* Clock prescale register must be even and at least 2 in master mode */
203 SSP_SSP0CPSR = SSP_SSP0CPSR_CPSDVSR_DIV2;
204
205 /* Clear the Rx FIFO */
206 uint8_t i, Dummy=Dummy;
207 for ( i = 0; i < SSP_FIFOSIZE; i++ )
208 {
209 Dummy = SSP_SSP0DR;
210 }
211
212 /* Enable the SSP Interrupt */
213 NVIC_EnableIRQ(SSP_IRQn);
214
215 /* Set SSPINMS registers to enable interrupts
216 * enable all error related interrupts */
217 SSP_SSP0IMSC = ( SSP_SSP0IMSC_RORIM_ENBL // Enable overrun interrupt
218 | SSP_SSP0IMSC_RTIM_ENBL); // Enable timeout interrupt
219
220 /* Enable device and set it to master mode, no loopback */
221 SSP_SSP0CR1 = SSP_SSP0CR1_SSE_ENABLED | SSP_SSP0CR1_MS_MASTER | SSP_SSP0CR1_LBM_NORMAL;
222 }
223
224 return;
225 }
226
227 /**************************************************************************/
228 /*!
229 @brief Sends a block of data to the SSP0 port
230
231 @param[in] portNum
232 The SPI port to use (0..1)
233 @param[in] buf
234 Pointer to the data buffer
235 @param[in] length
236 Block length of the data buffer
237 */
238 /**************************************************************************/
239 void sspSend (uint8_t portNum, uint8_t const *buf, uint32_t length)
240 {
241 uint32_t i;
242 uint8_t Dummy = Dummy;
243
244 if (portNum == 0)
245 {
246 for (i = 0; i < length; i++)
247 {
248 /* Move on only if NOT busy and TX FIFO not full. */
249 while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_NOTFULL | SSP_SSP0SR_BSY_BUSY)) != SSP_SSP0SR_TNF_NOTFULL);
250 SSP_SSP0DR = *buf;
251 buf++;
252
253 while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY );
254 /* Whenever a byte is written, MISO FIFO counter increments, Clear FIFO
255 on MISO. Otherwise, when SSP0Receive() is called, previous data byte
256 is left in the FIFO. */
257 Dummy = SSP_SSP0DR;
258 }
259 }
260
261 return;
262 }
263
264 /**************************************************************************/
265 /*!
266 @brief Receives a block of data from the SSP0 port
267
268 @param[in] portNum
269 The SPI port to use (0..1)
270 @param[in] buf
271 Pointer to the data buffer
272 @param[in] length
273 Block length of the data buffer
274 */
275 /**************************************************************************/
276 void sspReceive(uint8_t portNum, uint8_t *buf, uint32_t length)
277 {
278 uint32_t i;
279
280 if (portNum == 0)
281 {
282 for ( i = 0; i < length; i++ )
283 {
284 /* As long as the receive FIFO is not empty, data can be received. */
285 SSP_SSP0DR = 0xFF;
286
287 /* Wait until the Busy bit is cleared */
288 while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY );
289
290 *buf = SSP_SSP0DR;
291 buf++;
292 }
293 }
294
295 return;
296 }
297
298 void sspSendReceive(uint8_t portNum, uint8_t *buf, uint32_t length)
299 {
300 uint32_t i;
301 uint8_t Dummy = Dummy;
302
303 if (portNum == 0)
304 {
305 for (i = 0; i < length; i++)
306 {
307 /* Move on only if NOT busy and TX FIFO not full. */
308 while ((SSP_SSP0SR & (SSP_SSP0SR_TNF_NOTFULL | SSP_SSP0SR_BSY_BUSY)) != SSP_SSP0SR_TNF_NOTFULL);
309 SSP_SSP0DR = *buf;
310
311 while ( (SSP_SSP0SR & (SSP_SSP0SR_BSY_BUSY|SSP_SSP0SR_RNE_NOTEMPTY)) != SSP_SSP0SR_RNE_NOTEMPTY );
312 /* Whenever a byte is written, MISO FIFO counter increments, Clear FIFO
313 on MISO. Otherwise, when SSP0Receive() is called, previous data byte
314 is left in the FIFO. */
315 *buf = SSP_SSP0DR;
316 buf++;
317 }
318 }
319
320 return;
321 }
This page took 0.068057 seconds and 5 git commands to generate.