-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSPI_UART.cpp
149 lines (135 loc) · 3.78 KB
/
SPI_UART.cpp
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
/*
This file is free software; you can redistribute it and/or modify
it under the terms of either the GNU General Public License version 2
or the GNU Lesser General Public License version 2.1, both as
published by the Free Software Foundation.
2020/08 [email protected]
*/
#include "SPI_UART.h"
SPIUARTClass SPI_UART;
uint8_t SPIUARTClass::initialized = 0;
uint8_t SPIUARTClass::interruptSave;
// SPI divider to USART divider mapping (/2 /4 /8 /16 /32 /64 /64 /128)
// and take SPI2X inverted bit into consideration
uint8_t SPIUARTClass::clockDivTranslate[8] = { 1, 0, 7, 3, 31, 15, 63, 31 };
#ifdef __LGT8F__
static boolean SPIUARTClass::useAlternatePins;
#endif
void SPIUARTClass::begin()
{
uint8_t sreg = SREG;
noInterrupts(); // Protect from a scheduler and prevent transactionBegin
if (!initialized) {
#ifdef __LGT8F__
if (useAlternatePins) {
fastioMode(D5, INPUT_PULLUP);
fastioMode(D6, OUTPUT);
PMX0 |= 1 << WCE;
PMX0 |= _BV(TXD6) | _BV(RXD5);
} else
{
fastioMode(D0, INPUT_PULLUP);
fastioMode(D1, OUTPUT);
PMX0 |= 1 << WCE;
PMX0 &= ~(_BV(TXD6) | _BV(RXD5) );
#else
{
pinMode(0, INPUT_PULLUP);
pinMode(1, OUTPUT);
#endif
}
#ifdef __LGT8F__
fastioMode(D4, OUTPUT); // XCK
#else
pinMode(4, OUTPUT); // XCK
#endif
UBRR0 = 0;
UCSR0C |= _BV(UMSEL01) | _BV(UMSEL00);
UCSR0B = _BV(RXEN0) | _BV(TXEN0);
// default SPI speed: 4MHz (as far as possible, or f_cpu/2 if not)
#if F_CPU>=16000000
UBRR0 = ((F_CPU / 4000000 / 2) - 1);
#endif
}
initialized++; // reference count
SREG = sreg;
}
void SPIUARTClass::end() {
uint8_t sreg = SREG;
noInterrupts(); // Protect from a scheduler and prevent transactionBegin
// Decrease the reference counter
if (initialized)
initialized--;
// If there are no more references disable SPI
if (!initialized) {
UCSR0B &= ~(_BV(RXEN0) | _BV(TXEN0) );
UCSR0C &= ~(_BV(UMSEL01) | _BV(UMSEL00) );
#ifdef __LGT8F__
if (useAlternatePins) {
PMX0 |= 1 << WCE;
PMX0 &= ~(_BV(TXD6) | _BV(RXD5) );
}
#endif
}
SREG = sreg;
}
/*
This here has a damn tight timing (especially at 1/2 sysclk SPI), and takes the 1 byte buffer into consideration.
we'll push in data twice (one in serializer, one in buffer), and must be fast enough to catch the already
available buffered result before it will be overwritten (in 7 SPI cycles: 32MHz CPU/16MHz SPI -> in 14 cpu cycles).
due to this, we can manage a constant 16mbit/s clock
*/
const static void SPIUARTClass::transfer(void * buf, void * retbuf, size_t count) {
if (count == 0) return;
uint8_t sreg = SREG;
noInterrupts(); // Protect from interruption
size_t writecount = count;
uint8_t *p = (uint8_t *)buf;
uint8_t *pret = (uint8_t *)retbuf;
UDR0 = p ? *p++ : 0;
writecount--;
if (buf && !retbuf) {
// optimized version: we only need to SEND
while (writecount-- > 0) {
if (UCSR0A & _BV(RXC0)) {
uint8_t in = UDR0;
count--;
}
while (!(UCSR0A & _BV(UDRE0)));
UDR0 = *p++;
}
while (count-- > 0) {
while ( !(UCSR0A & _BV(RXC0) ));
uint8_t in = UDR0;
}
}
else if (!buf && retbuf) {
// optimized version: we only need to RECEIVE
while (writecount-- > 0) {
if (UCSR0A & _BV(RXC0)) {
*pret++ = UDR0;
count--;
}
while (!(UCSR0A & _BV(UDRE0)));
UDR0 = 0;
}
while (count-- > 0) {
while ( !(UCSR0A & _BV(RXC0) ));
*pret++ = UDR0;
}
}
else {
// this last part does write+read at the same time
while (--count > 0) {
while ( !(UCSR0A & _BV(RXC0) )) {
if ((UCSR0A & _BV(UDRE0))) {
UDR0 = *p++;
}
}
*pret++ = UDR0;
}
while ( !(UCSR0A & _BV(RXC0) ));
*pret = UDR0;
}
SREG = sreg;
}