forked from FastLED/FastLED
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fastspi_arm_sam.h
160 lines (128 loc) · 5.09 KB
/
fastspi_arm_sam.h
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
#ifndef __INC_FASTSPI_ARM_SAM_H
#define __INC_FASTSPI_ARM_SAM_H
#if defined(__SAM3X8E__)
#define m_SPI ((Spi*)SPI0)
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER>
class SAMHardwareSPIOutput {
Selectable *m_pSelect;
static inline void waitForEmpty() { while ((m_SPI->SPI_SR & SPI_SR_TDRE) == 0); }
void enableConfig() { m_SPI->SPI_WPMR &= ~SPI_WPMR_WPEN; }
void disableConfig() { m_SPI->SPI_WPMR |= SPI_WPMR_WPEN; }
void enableSPI() { m_SPI->SPI_CR = SPI_CR_SPIEN; }
void disableSPI() { m_SPI->SPI_CR = SPI_CR_SPIDIS; }
void resetSPI() { m_SPI->SPI_CR = SPI_CR_SWRST; }
static inline void readyTransferBits(register uint32_t bits) {
bits -= 8;
// don't change the number of transfer bits while data is still being transferred from TDR to the shift register
waitForEmpty();
m_SPI->SPI_CSR[0] = SPI_CSR_NCPHA | SPI_CSR_CSAAT | (bits << SPI_CSR_BITS_Pos) | SPI_CSR_DLYBCT(1) | SPI_CSR_SCBR(_SPI_CLOCK_DIVIDER);
}
template<int BITS> static inline void writeBits(uint16_t w) {
waitForEmpty();
m_SPI->SPI_TDR = (uint32_t)w | SPI_PCS(0);
}
public:
SAMHardwareSPIOutput() { m_pSelect = NULL; }
SAMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
// set the object representing the selectable
void setSelect(Selectable *pSelect) { /* TODO */ }
// initialize the SPI subssytem
void init() {
// m_SPI = SPI0;
// set the output pins master out, master in, clock. Note doing this here because I still don't
// know how I want to expose this type of functionality in FastPin.
PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN>::mask(), PIO_DEFAULT);
PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN-1>::mask(), PIO_DEFAULT);
PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_CLOCK_PIN>::mask(), PIO_DEFAULT);
release();
// Configure the SPI clock, divider between 1-255
// SCBR = _SPI_CLOCK_DIVIDER
pmc_enable_periph_clk(ID_SPI0);
disableSPI();
// reset twice (what the sam code does, not sure why?)
resetSPI();
resetSPI();
// Configure SPI as master, enable
// Bits we want in MR: master, disable mode fault detection, variable peripheral select
m_SPI->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS;
enableSPI();
// Send everything out in 8 bit chunks, other sizes appear to work, poorly...
readyTransferBits(8);
}
// latch the CS select
void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
// release the CS select
void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
// wait until all queued up data has been written
void waitFully() { while((m_SPI->SPI_SR & SPI_SR_TXEMPTY) == 0); }
// write a byte out via SPI (returns immediately on writing register)
static void writeByte(uint8_t b) {
writeBits<8>(b);
}
// write a word out via SPI (returns immediately on writing register)
static void writeWord(uint16_t w) {
writeBits<16>(w);
}
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
static void writeBytesValueRaw(uint8_t value, int len) {
while(len--) { writeByte(value); }
}
// A full cycle of writing a value for len bytes, including select, release, and waiting
void writeBytesValue(uint8_t value, int len) {
select(); writeBytesValueRaw(value, len); release();
}
template <class D> void writeBytes(register uint8_t *data, int len) {
uint8_t *end = data + len;
select();
// could be optimized to write 16bit words out instead of 8bit bytes
while(data != end) {
writeByte(D::adjust(*data++));
}
D::postBlock(len);
waitFully();
release();
}
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
// write a single bit out, which bit from the passed in byte is determined by template parameter
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
template <uint8_t BIT> inline void writeBit(uint8_t b) {
// need to wait for all exisiting data to go out the door, first
waitFully();
disableSPI();
if(b & (1 << BIT)) {
FastPin<_DATA_PIN>::hi();
} else {
FastPin<_DATA_PIN>::lo();
}
FastPin<_CLOCK_PIN>::hi();
FastPin<_CLOCK_PIN>::lo();
enableSPI();
}
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
select();
int len = pixels.mLen;
if(FLAGS & FLAG_START_BIT) {
while(pixels.has(1)) {
writeBits<9>((1<<8) | D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
} else {
while(pixels.has(1)) {
writeByte(D::adjust(pixels.loadAndScale0()));
writeByte(D::adjust(pixels.loadAndScale1()));
writeByte(D::adjust(pixels.loadAndScale2()));
pixels.advanceData();
pixels.stepDithering();
}
}
D::postBlock(len);
release();
}
};
#endif
#endif