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
|
//
// Copyright 2013-2014 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "spi_core_3000.hpp"
#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
#include <boost/thread/thread.hpp> //sleep
#define SPI_DIV _base + 0
#define SPI_CTRL _base + 4
#define SPI_DATA _base + 8
#define SPI_SHUTDOWN _base + 12
using namespace uhd;
spi_core_3000::~spi_core_3000(void){
/* NOP */
}
class spi_core_3000_impl : public spi_core_3000
{
public:
spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback):
_iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0), _divider_cache(0)
{
this->set_divider(30);
}
uint32_t transact_spi(
int which_slave,
const spi_config_t &config,
uint32_t data,
size_t num_bits,
bool readback
){
boost::lock_guard<boost::mutex> lock(_mutex);
//load SPI divider
size_t spi_divider = _div;
if (config.use_custom_divider) {
//The resulting SPI frequency will be f_system/(2*(divider+1))
//This math ensures the frequency will be equal to or less than the target
spi_divider = (config.divider-1)/2;
}
//conditionally send SPI divider
if (spi_divider != _divider_cache) {
_iface->poke32(SPI_DIV, spi_divider);
_divider_cache = spi_divider;
}
//load control word
uint32_t ctrl_word = 0;
ctrl_word |= ((which_slave & 0xffffff) << 0);
ctrl_word |= ((num_bits & 0x3f) << 24);
if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31);
if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30);
//conditionally send control word
if (_ctrl_word_cache != ctrl_word)
{
_iface->poke32(SPI_CTRL, ctrl_word);
_ctrl_word_cache = ctrl_word;
}
//load data word (must be in upper bits)
const uint32_t data_out = data << (32 - num_bits);
//send data word
_iface->poke32(SPI_DATA, data_out);
//conditional readback
if (readback)
{
return _iface->peek32(_readback);
}
return 0;
}
void set_shutdown(const bool shutdown)
{
_shutdown_cache = shutdown;
_iface->poke32(SPI_SHUTDOWN, _shutdown_cache);
}
bool get_shutdown()
{
return(_shutdown_cache);
}
void set_divider(const double div)
{
_div = size_t((div/2) - 0.5);
}
private:
wb_iface::sptr _iface;
const size_t _base;
const size_t _readback;
uint32_t _ctrl_word_cache;
bool _shutdown_cache;
boost::mutex _mutex;
size_t _div;
size_t _divider_cache;
};
spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback)
{
return sptr(new spi_core_3000_impl(iface, base, readback));
}
|