spi.c
3.15 KB
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
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* 2016 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_lpc2387
* @ingroup drivers_periph_spi
* @{
*
* @file
* @brief Low-level SPI driver implementation
*
* This implementation is very basic and only supports a single SPI device with
* limited configuration options.
*
* @todo This implementation needs a major rework
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include "cpu.h"
#include "mutex.h"
#include "assert.h"
#include "periph/spi.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define SPI_TX_EMPTY (SSP0SR & SSPSR_TFE)
#define SPI_BUSY (SSP0SR & SSPSR_BSY)
#define SPI_RX_AVAIL (SSP0SR & SSPSR_RNE)
/**
* @brief Array holding one pre-initialized mutex for each SPI device
*/
static mutex_t lock = MUTEX_INIT;
void spi_init(spi_t bus)
{
assert(bus == SPI_DEV(0));
/* interface setup */
SSP0CR0 = 7;
/* configure pins */
spi_init_pins(bus);
/* power off the bus (default is on) */
PCONP &= ~(PCSSP0);
}
void spi_init_pins(spi_t bus)
{
PINSEL3 |= (BIT8 | BIT9); /* SCLK */
PINSEL3 |= (BIT14 | BIT15); /* MISO */
PINSEL3 |= (BIT16 | BIT17); /* MOSI */
}
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
{
uint32_t pclksel;
uint32_t cpsr;
/* only support for mode 0 at the moment */
if (mode != SPI_MODE_0) {
return SPI_NOMODE;
}
/* lock bus */
mutex_lock(&lock);
/* power on */
PCONP |= (PCSSP0);
/* configure bus clock */
lpc2387_pclk_scale(CLOCK_CORECLOCK / 1000, (uint32_t)clk, &pclksel, &cpsr);
PCLKSEL1 &= ~(BIT10 | BIT11); /* CCLK to PCLK divider*/
PCLKSEL1 |= pclksel << 10;
SSP0CPSR = cpsr;
/* enable the bus */
SSP0CR1 |= BIT1;
/* clear RxFIFO */
int dummy;
while (SPI_RX_AVAIL) { /* while RNE (Receive FIFO Not Empty)...*/
dummy = SSP0DR; /* read data*/
}
(void) dummy; /* to suppress unused-but-set-variable */
return SPI_OK;
}
void spi_release(spi_t bus)
{
/* disable, power off, and release the bus */
SSP0CR1 &= ~(BIT1);
PCONP &= ~(PCSSP0);
mutex_unlock(&lock);
}
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
const uint8_t *out_buf = out;
uint8_t *in_buf = in;
assert(out_buf || in_buf);
if (cs != SPI_CS_UNDEF) {
gpio_clear((gpio_t)cs);
}
for (size_t i = 0; i < len; i++) {
uint8_t tmp = (out_buf) ? out_buf[i] : 0;
while (!SPI_TX_EMPTY) {}
SSP0DR = tmp;
while (SPI_BUSY) {}
while (!SPI_RX_AVAIL) {}
tmp = (uint8_t)SSP0DR;
if (in_buf) {
in_buf[i] = tmp;
}
}
if ((!cont) && cs != SPI_CS_UNDEF) {
gpio_set((gpio_t)cs);
}
}