-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmaple_in.pio
197 lines (162 loc) · 6.36 KB
/
maple_in.pio
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
; MIT License
;
; Copyright (c) 2022-2025 James Smith of OrangeFox86
; https://github.com/OrangeFox86/DreamcastControllerUsbPico
;
; 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.
.program maple_in
; Wait for start sequence
wait_start:
mov y, ~NULL ; This sets y to 0xFFFFFFFF
b_toggle_loop:
jmp pin check_toggle_count ; Once A goes HIGH, check how much B toggled
; `mov x, pins` wouldn't work because I just want the first 2 pins
mov osr, pins ; copy pin values into output shift register
out x 2 ; put A and B into register x (should either be 0b01 or 0b00)
jmp x-- b_toggle_loop ; keep waiting if B is high
; else: fall through
wait 1 pin 1 ; wait for B to go back HIGH
jmp pin wait_start ; if A went HIGH before B, try again
jmp y-- b_toggle_loop ; toggle detected; decrement do another wait
; The above should normally jump unless the count overloaded
check_toggle_count:
mov y, ~y ; This converts the negative count to a positive count
set x, 4 ; We are looking for 4 B toggles
jmp x!=y wait_start ; If 4 B toggles haven't been detected, continue waiting
; else: fall through
data_start:
; The first IRQ tells application that start sequence detected
irq set 0 rel
; Wait for A to go low then fall through to first end detect phase (which should cause sample of B)
wait 0 pin 0
.wrap_target
data_loop:
; wait for B to toggle HIGH then LOW or for A go LOW
set y, 1
end_detect_b_toggle:
mov osr, pins ; copy pin values into output shift register
out x 1
jmp !x sample_b_from_osr ; if A went low before B went high, then sample B now
out x 1
jmp !y end_detect_b_to_low ; if y is 0 then we are waiting for B to be low
end_detect_b_to_high:
jmp !x end_detect_b_toggle ; if B is still LOW, try again
jmp y-- end_detect_b_toggle ; B just went HIGH, so decrement y and sample again
end_detect_b_to_low:
jmp x-- end_detect_b_toggle ; if B is still HIGH, try again
; otherwise, fall through to next end detect phase
; Warning: the full end sequence is not sampled for simplicity
; Either end is actually there or there was a glitch (rely on len and CRC of packet data in that case)
end_detected:
in null 24 ; shift out the crc byte
; Wait for IRQ to be handled before continuing
irq wait 0 rel
; Application should kill the state machine before reaching this point
sample_b_from_osr:
in osr 1 ; shift in B value
sample_a:
; wait for B to be high
wait 1 pin 1
; wait for B to be low
wait 0 pin 1
; shift in A value
in pins 1
; wait for A to be high
wait 1 pin 0
.wrap
% c-sdk {
#include "hardware/gpio.h"
#include "hardware/pio.h"
#include "PioProgram.hpp"
#define MAPLE_IN_PIO pio1
class MapleInStateMachine
{
public:
inline MapleInStateMachine(uint pin_a) :
mProgram(getMapleInProgram()),
mPinA(pin_a),
mPinB(pin_a + 1),
mMaskAB(3 << pin_a),
mSmIdx(pio_claim_unused_sm(mProgram.mPio, true)),
mPrestarted(false)
{
// Initialize the two pins as inputs with pullups
gpio_set_dir_in_masked(mMaskAB);
gpio_set_pulls(mPinA, true, false);
gpio_set_pulls(mPinB, true, false);
pio_sm_config c = maple_in_program_get_default_config(mProgram.mProgramOffset);
sm_config_set_in_pins(&c, mPinA);
// jmp pin checks pin A
sm_config_set_jmp_pin(&c, mPinA);
// Shift to right, autopull disabled, 32 bits at a time
sm_config_set_out_shift(&c, true, false, 32);
// Shift to left, autopush enabled, 32 bits at a time
sm_config_set_in_shift(&c, false, true, 32);
// Sample as fast as possible
sm_config_set_clkdiv(&c, 1);
// Load our configuration, and jump to the start of the program
pio_sm_init(mProgram.mPio, mSmIdx, mProgram.mProgramOffset, &c);
}
inline void prestart()
{
// Reset pointers
pio_sm_clear_fifos(mProgram.mPio, mSmIdx);
pio_sm_restart(mProgram.mPio, mSmIdx);
pio_sm_clkdiv_restart(mProgram.mPio, mSmIdx);
pio_sm_exec(mProgram.mPio, mSmIdx, pio_encode_jmp(mProgram.mProgramOffset));
// Pin direction starts as input
pio_sm_set_consecutive_pindirs(mProgram.mPio, mSmIdx, mPinA, 2, false);
mPrestarted = true;
}
inline void start()
{
if (!mPrestarted)
{
prestart();
}
mPrestarted = false;
// Set this pin's GPIO function (connect PIO to the pad)
pio_gpio_init(mProgram.mPio, mPinA);
pio_gpio_init(mProgram.mPio, mPinB);
// Set the state machine running
pio_sm_set_enabled(mProgram.mPio, mSmIdx, true);
}
inline void stop() const
{
pio_sm_set_enabled(mProgram.mPio, mSmIdx, false);
// Reset the pins' function to standard I/O with pullups
gpio_set_dir_in_masked(mMaskAB);
gpio_set_function(mPinA, GPIO_FUNC_SIO);
gpio_set_function(mPinB, GPIO_FUNC_SIO);
}
private:
inline static const PioProgram& getMapleInProgram()
{
static const PioProgram program(MAPLE_IN_PIO, &maple_in_program);
return program;
}
public:
const PioProgram& mProgram;
const uint mPinA;
const uint mPinB;
const uint mMaskAB;
const uint mSmIdx;
bool mPrestarted;
};
%}