forked from BenW0/IMC_Closed_Loop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspienc.c
248 lines (218 loc) · 7.38 KB
/
spienc.c
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/********************************************************************************
* SPI Encoder Decode File
* Ben Weiss, University of Washington 2014
* Purpose: Reads an encoder's signal using the SPI interface
*
* Source:
*
*
* License:
* This software is (c) 2014 by Ben Weiss and is released under the following license:
* The MIT License (MIT)
*
* Copyright (c) 2014 Ben Weiss
*
* 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.
********************************************************************************/
#include "common.h"
#include <usb_serial.h>
#include <string.h>
#include <stdio.h>
#include <pin_config.h>
#include <util.h>
#include "spienc.h"
#ifndef USE_QD_ENC
#include "SPIFIFO.h"
// Constants =================================================
const uint32_t ROLLOVER = 4096; // rollover of the internal SPI counter
//const uint32_t TENUS_BEFORE_LOST_TRACK = 600; // If we don't get an update at least this often, we may lose track of the counter rollover (2 mm / 400 mm/sec)
#define DISP_BEFORE_LOST_TRACK 1500 // If the encoder value has changed by more than this, we might have skipped a step!
// Global Variables ==========================================
// Local Variables ===========================================
static volatile long encoder_offset = 0; // added to the actual encoder value when read.
//static char message[100];
static uint32_t last_update_tenus = 0, readval, last_readval = 0;
static int32_t rollovers = 0;
static bool lost_track = true;
static int32_t offset = 0;
// Local Function Defines ====================================
void read_enc(void);
uint8_t read_spi(uint32_t *value);
uint32_t spibitbang_read(void);
// enc_init()
// Initializes the SPI interface and the device as required.
void enc_Init(void)
{
#ifdef ENC_USE_SPIFIFO
spififo_begin(10, SPI_CLOCK_1MHz, SPI_MODE2);
#else
// setup the pins
ENC_SCK_CTRL = STANDARD_OUTPUT;
ENC_SCK_PORT(DDR) |= ENC_SCK_BIT;
SCK_ON();
ENC_DIN_CTRL = MUX_GPIO;
ENC_DIN_PORT(DDR) &= ~ENC_DIN_BIT;
ENC_CS_CTRL = STANDARD_OUTPUT;
ENC_CS_PORT(DDR) |= ENC_CS_BIT;
CS_ON();
#endif // !ENC_USE_SPFIFO
last_update_tenus = get_systick_tenus();
if(read_spi(&readval))
{
last_readval = readval;
lost_track = false;
}
}
// enc_idle()
// idle task for the encoder. We use this to keep track of rollovers
void enc_idle(void)
{
read_enc();
}
// this function reads the encoder and traps rollovers.
void read_enc(void)
{
uint32_t time = get_systick_tenus();
bool rolled = false;
uint32_t val, last_val = last_readval;
if(!read_spi(&val))
{
readval = val;
last_readval = val;
// check for rollover
if(val > ROLLOVER * 3 / 4 && last_val < ROLLOVER / 4)
{
// rolled over negative
rollovers--;
rolled = true;
}
else if(val < ROLLOVER / 4 && last_val > ROLLOVER * 3 / 4)
{
// rolled over positive
rollovers++;
rolled = true;
}
// check for high absolute change --> possibility of skipping a step.
if(labs((rolled ? ROLLOVER : 0) - labs(last_val - val)) > DISP_BEFORE_LOST_TRACK)
{
if(!lost_track)
{
hid_printf("'High Enc Change: %u. readval = %u, last_readval = %u, Time change = %u\n",
(unsigned int)labs((rolled ? -ROLLOVER : 0) + labs(last_val - val)),
(unsigned int)val,
(unsigned int)last_val,
(unsigned int)(time - last_update_tenus));
}
lost_track = true;
}
}
else
{
// something's wrong! Assume we lost track
lost_track = true;
}
last_update_tenus = time;
}
// get_enc_value()
// returns the current encoder tic index
uint8_t get_enc_value(volatile int32_t *value)
{
read_enc();
*value = rollovers * ROLLOVER + (int32_t)readval + offset;
return lost_track ? 1 : 0;
}
// set_enc_value()
// sets the current encoder tic index (by offsetting the current value)
void set_enc_value(int32_t newvalue)
{
offset = newvalue - rollovers * ROLLOVER - (int32_t)readval;
lost_track = false;
}
bool enc_lost_track(void)
{
return lost_track;
}
// reads the encoder value over SPI. Returns 0 if read was successful, 1 otherwise.
uint8_t read_spi(uint32_t *value)
{
uint8_t i;
// write out some uint8_ts...The content is bogus, we just need the clock to fire.
// we need to block the control interrupt from firing while we read the serial port (if it fires half way through
// reading, we'll be in big trouble!)
SET_BASEPRI(2<<4);
#ifdef ENC_USE_SPIFIFO
spififo_write16(0x1234, 1);
spififo_write16(0x1234, 0);
uint32_t inp = spififo_read();
uint32_t inp2 = spififo_read();
inp = (inp << 16) | inp2;
#else
uint32_t inp;
inp = spibitbang_read();
#endif
CLEAR_BASEPRI();
// first bit (msb) is garbage
inp &= 0x7fffffff;
// next 12 bits are the reading
uint32_t rd = (inp >> 19);
// next 5 bits are flags
uint8_t ocf = (inp & (1 << 18)) ? 1 : 0;
uint8_t cof = (inp & (1 << 17)) ? 1 : 0;
uint8_t lin = (inp & (1 << 16)) ? 1 : 0;
uint8_t mag_inc = (inp & (1 << 15)) ? 1 : 0;
uint8_t mag_dec = (inp & (1 << 14)) ? 1 : 0;
uint8_t par = (inp & (1 << 13)) ? 1 : 0;
// check parity bit
uint8_t parcheck = 0;
for(i = 14; i < 31; i++)
if(inp & (1 << i)) parcheck ^= 1;
// valid packet if ocf = 1, cof = 0, lin = 0, mag_inc and mag_dec not both 1, parcheck == par.
if(!ocf || cof || lin || (mag_inc && mag_dec) || parcheck != par)
{
hid_printf("'Lost Track: ocf=%i cof=%i lin=%i mag_inc=%i mag_dec=%i parcheck=%i\n", ocf, cof, lin, mag_inc, mag_dec, (int)(parcheck != par));
//usb_serial_write(message,strlen(message));
return 1;
}
*value = rd;
return 0;
}
// This routine bitbangs pins (see spienc.h for pinnout) to read the encoder. This is NOT a full implementation of
// software SPI, but just enough to do the job for the chip at hand. As a result, message length, etc. is hard coded.
uint32_t spibitbang_read()
{
uint32_t data = 0;
// pull down CS to signal the start of a transfer. SCK should have been left high after the last round.
CS_OFF();
delay_microseconds(1); // >600 ns
for(uint8_t i = 0; i < 18; ++i)
{
SCK_OFF();
// Take the reading
data |= (DIN_READ() << (31 - i));
delay_0125us(4); // 500 ns
SCK_ON();
delay_0125us(4); // 500 ns
}
// read one last bit (parity) without cycling the clock back down
data |= (DIN_READ() << 12); // (31 - 19)
// signal end of read...
CS_ON();
return data;
}
#endif