aboutsummaryrefslogtreecommitdiffstats
path: root/core/comm/HardwareSPI.cpp
blob: 51093cb4031eeff732ea3e28b5bad5cb0c9ed7ff (plain)
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
/* *****************************************************************************
 * The MIT License
 *
 * Copyright (c) 2010 Perry Hung.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * ****************************************************************************/

/**
 * @brief HardwareSPI "wiring-like" api for SPI
 */

/* NOTES:
 *
 * Speeds:
 * -----------------------------------
 * Interface num:     SPI1        SPI2
 * Bus                APB2        APB1
 * -----------------------------------
 * Prescaler         Frequencies
 * -----------------------------------
 * 2:                  N/A  18 000 000
 * 4:           18 000 000   9 000 000
 * 8:            9 000 000   4 500 000
 * 16:           4 500 000   2 250 000
 * 32:           2 250 000   1 125 000
 * 64:           1 125 000     562 500
 * 128:            562 500     281 250
 * 256:            281 250     140 625
 *
 * TODO: Do the complementary PWM outputs mess up SPI2?
 * */

#include "wiring.h"
#include "spi.h"
#include "HardwareSPI.h"

static const uint32 prescaleFactors[MAX_SPI_FREQS] = {
   SPI_PRESCALE_2,               // SPI_18MHZ
   SPI_PRESCALE_4,               // SPI_9MHZ
   SPI_PRESCALE_8,               // SPI_4_5MHZ
   SPI_PRESCALE_16,              // SPI_2_25MHZ
   SPI_PRESCALE_32,              // SPI_1_125MHZ
   SPI_PRESCALE_64,              // SPI_562_500KHZ
   SPI_PRESCALE_128,             // SPI_281_250KHZ
   SPI_PRESCALE_256,             // SPI_140_625KHZ
};

/**
 * @brief Initialize a SPI peripheral
 * @param freq frequency to run at, must one of the following values:
 *           - SPI_18MHZ
 *           - SPI_9MHZ
 *           - SPI_4_5MHZ
 *           - SPI_2_25MHZ
 *           - SPI_1_125MHZ
 *           - SPI_562_500KHZ
 *           - SPI_281_250KHZ
 *           - SPI_140_625KHZ
 * @param endianness endianness of the data frame, must be either LSBFIRST
 *           or MSBFIRST
 * @param mode SPI standard CPOL and CPHA levels
 */
void HardwareSPI::begin(SPIFrequency freq, uint32 endianness, uint32 mode) {
   uint32 spi_num    = this->spi_num;
   uint32 high_speed = 0;
   uint32 index;
   uint32 prescale;

   if ((freq >= MAX_SPI_FREQS)     ||
       !((endianness == LSBFIRST)  ||
         (endianness == MSBFIRST)) ||
       (mode >= 4)) {
      return;
   }

   if (spi_num == 1) {
      /* SPI1 is too fast for 140625  */
      if (freq == SPI_140_625KHZ) {
         return;
      }

      /* Turn off PWM on shared pins */
      timers_disable_channel(3, 2);
      timers_disable_channel(3, 1);
   }

   endianness = (endianness == LSBFIRST) ? SPI_LSBFIRST : SPI_MSBFIRST;
   prescale = (spi_num == 1) ? prescaleFactors[freq + 1] : prescaleFactors[freq];

   spi_init(spi_num, prescale, endianness, 0);
}

/**
 * @brief Initialize a SPI peripheral with a default speed of 1.125 MHZ, MSBFIRST,
 *      mode 0
 * @param mode SPI standard CPOL and CPHA levels
 */
void HardwareSPI::begin(void) {
   begin(SPI_1_125MHZ, MSBFIRST, 0);
}

/**
 * @brief send a byte out the spi peripheral
 * @param data byte to send
 */
void HardwareSPI::send(uint8 data) {
   spi_tx(this->spi_num, data);
}


/**
 * @brief read a byte from the spi peripheral
 * @return byte in the buffer
 */
uint8 HardwareSPI::recv(void) {
   return spi_rx(this->spi_num);
}

HardwareSPI::HardwareSPI(uint32 spi_num) {
   this->spi_num = spi_num;
}