File: operacake_sctimer.c

package info (click to toggle)
hackrf 2026.01.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 38,216 kB
  • sloc: ansic: 60,696; python: 6,072; xml: 3,424; perl: 2,730; makefile: 601; asm: 514; vhdl: 319; sh: 179; awk: 20
file content (242 lines) | stat: -rw-r--r-- 7,204 bytes parent folder | download
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
/*
 * Copyright 2017-2022 Great Scott Gadgets <info@greatscottgadgets.com>
 * Copyright 2018 Schuyler St. Leger
 *
 * This file is part of HackRF.
 *
 * 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 2, 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#include "operacake_sctimer.h"

#include <hackrf_core.h>

#include <libopencm3/lpc43xx/sgpio.h>
#include <libopencm3/lpc43xx/rgu.h>
#include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/gima.h>
#include "sct.h"

#define U1CTRL_SET  SCT_OUT14_SET
#define U1CTRL_CLR  SCT_OUT14_CLR
#define U2CTRL0_SET SCT_OUT13_SET
#define U2CTRL0_CLR SCT_OUT13_CLR
#define U2CTRL1_SET SCT_OUT12_SET
#define U2CTRL1_CLR SCT_OUT12_CLR
#define U3CTRL0_SET SCT_OUT11_SET
#define U3CTRL0_CLR SCT_OUT11_CLR
#define U3CTRL1_SET SCT_OUT8_SET
#define U3CTRL1_CLR SCT_OUT8_CLR

static uint32_t default_output = 0;

/**
 * Configure the SCTimer to rotate the antennas with the Operacake in phase with
 * the sample clock. This will configure the SCTimer to output to the pins for
 * GPIO control of the Operacake, however the Operacake must be configured for
 * GPIO control. The timing is not set in this function.
 *
 * To trigger the antenna switching synchronously with the sample clock, the
 * SGPIO is configured to output its clock (f=2 * sample clock) to the SCTimer.
 *
 * On Praline, instead, MS0/CLK1 (SCT_CLK) is configured to output its
 * clock (f=2 * sample clock) to the SCTimer.
 */
void operacake_sctimer_init()
{
	// We start by resetting the SCTimer
	RESET_CTRL1 = RESET_CTRL1_SCT_RST;

	// Delay to allow reset sigal to be processed
	// The reset generator uses a 12MHz clock (IRC)
	// The difference between this and the core clock (CCLK)
	// determines this value (CCLK/IRC).
	// The value used here is a bit shorter than would be required, since
	// there are additional instructions that fill the time. If the duration of
	// the actions from here to the first access to the SCTimer is changed, then
	// this delay may need to be increased.
	delay(8);

	// Pin definitions for the HackRF
	// U2CTRL0
	scu_pinmux(
		P7_4,
		SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
	// U2CTRL1
	scu_pinmux(
		P7_5,
		SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
	// U3CTRL0
	scu_pinmux(
		P7_6,
		SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
	// U3CTRL1
	scu_pinmux(
		P7_7,
		SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
	// U1CTRL
	scu_pinmux(
		P7_0,
		SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);

#ifndef PRALINE
	// Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12
	SGPIO_OUT_MUX_CFG12 = SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode
		SGPIO_OUT_MUX_CFG_P_OE_CFG(0);                    // gpio_oe
	SGPIO_GPIO_OENREG |= BIT12;

	// Use the GIMA to connect the SGPIO clock to the SCTimer
	GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1

	uint8_t sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1;
#else
	// Configure pin P6_4 as SCT_IN_6
	scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1);

	// Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer
	GIMA_CTIN_6_IN = 0x0 << 4;

	uint8_t sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6;
#endif

	// We configure this register first, because the user manual says to
	SCT_CONFIG |= SCT_CONFIG_UNIFY_32_BIT | SCT_CONFIG_CLKMODE_PRESCALED_BUS_CLOCK |
		sct_clock_input;

	// Halt the SCTimer to enable it to be configured
	SCT_CTRL = SCT_CTRL_HALT_L(1);

	// Prescaler - run at half the SGPIO clock (ie: at the sample clock)
	SCT_CTRL |= SCT_CTRL_PRE_L(1);

	// Default to state 0, events disabled
	SCT_STATE = 0;

	// Enable the SCTimer
	SCT_CTRL &= ~SCT_CTRL_HALT_L(1);
}

static uint32_t operacake_sctimer_port_to_output(uint8_t port)
{
	int bit0 = (port >> 0) & 1;
	int bit1 = (port >> 1) & 1;
	int bit2 = (port >> 2) & 1;

	return (bit0 << 11) | (bit0 << 13) | (bit1 << 8) | (bit1 << 12) |
		(((~bit2) & 1) << 14);
}

void operacake_sctimer_enable(bool enable)
{
	SCT_CTRL = SCT_CTRL_HALT_L(1);
	SCT_STATE = enable;
	SCT_CTRL &= ~SCT_CTRL_HALT_L(1);
}

void operacake_sctimer_set_dwell_times(struct operacake_dwell_times* times, int n)
{
	SCT_CTRL = SCT_CTRL_HALT_L(1);

	uint32_t counter = 0;
	uint32_t bit0_set = 0, bit0_clr = 0, bit1_set = 0, bit1_clr = 0, bit2_set = 0,
		 bit2_clr = 0;
	for (int i = 0; i < n; i++) {
		// Enable event i in state 1, set to match on match register i
		SCT_EVn_STATE(i) = SCT_EVn_STATE_STATEMSK1(1);
		SCT_EVn_CTRL(i) = SCT_EVn_CTRL_COMBMODE_MATCH | SCT_EVn_CTRL_MATCHSEL(i);

		// Calculate the counter value to match on
		counter += times[i].dwell;

		// Wrapping from SCT_LIMIT seems to add an extra cycle,
		// so we reduce the counter value for the first event.
		if (i == 0) {
			counter -= 1;
		}

		SCT_MATCHn(i) = counter;
		SCT_MATCHRELn(i) = counter;

		// The match event selects the *next* port, so retreive that here.
		int port;
		if (i == n - 1) {
			port = times[0].port;
		} else {
			port = times[i + 1].port;
		}

		int bit0 = (port >> 0) & 1;
		int bit1 = (port >> 1) & 1;
		int bit2 = (port >> 2) & 1;

		// Find bits to set/clear on event i
		bit0_set |= SCT_OUTn_SETm(bit0, i);
		bit0_clr |= SCT_OUTn_CLRm(~bit0, i);

		bit1_set |= SCT_OUTn_SETm(bit1, i);
		bit1_clr |= SCT_OUTn_CLRm(~bit1, i);

		// (U1CTRL is inverted)
		bit2_set |= SCT_OUTn_SETm(~bit2, i);
		bit2_clr |= SCT_OUTn_CLRm(bit2, i);
	}

	// Apply event set/clear mappings
	U2CTRL0_SET = bit0_set;
	U2CTRL0_CLR = bit0_clr;
	U3CTRL0_SET = bit0_set;
	U3CTRL0_CLR = bit0_clr;
	U2CTRL1_SET = bit1_set;
	U2CTRL1_CLR = bit1_clr;
	U3CTRL1_SET = bit1_set;
	U3CTRL1_CLR = bit1_clr;
	U1CTRL_SET = bit2_set;
	U1CTRL_CLR = bit2_clr;

	// Set output pins to select the first port in the list
	default_output = operacake_sctimer_port_to_output(times[0].port);
	SCT_OUTPUT = default_output;

	// Reset counter on final event
	SCT_LIMIT = (1 << (n - 1));

	SCT_CTRL &= ~SCT_CTRL_HALT_L(1);
}

void operacake_sctimer_stop()
{
	// Halt timer
	SCT_CTRL |= SCT_CTRL_HALT_L(1);
}

/**
 * This will reset the state of the SCTimer, but retains its configuration.
 * This reset will return the selected antenna to 1 and samesided. This is
 * called by set_transceiver_mode so the HackRF starts capturing with the
 * same antenna selected each time.
 */
void operacake_sctimer_reset_state()
{
	SCT_CTRL |= SCT_CTRL_HALT_L(1);

	// Clear the counter value
	SCT_CTRL |= SCT_CTRL_CLRCTR_L(1);

	// Reset to select the first port
	SCT_OUTPUT = default_output;

	SCT_CTRL &= ~SCT_CTRL_HALT_L(1);
}