Hi there,
I have a project for which I want the Teensy 4 to take in an external master audio clock generated by a different component on the SPDIF_EXT_CLK pin and transmit audio synchronized to that clock on SPDIF_OUT. I only need that audio for sending to a sound card so that it can synchronize its clock to it, so transmitting zeros on the SPDIF signal is OK. The sampling rate should be 48000, and the master clock is coming in at 48000 * 256 = 12.288 MHz. This project is not using the audio library.
I have read throught the audio section of the IMXRT manual multiple times, ctrl+f'ed for SPDIF, EXT_CLK, and all other related strings I can think of, checked the forums, and dug into the audio library code. I am hitting a wall.
The clock signal is confirmed fine on the scope, and I think my muxing is fine, but there must be something off with my register set up (I think). On the SPDIF_OUT pin I get a constant low signal, rather than a SPDIF signal encoding zero or anything else.
Any help would be appreciated. Code is below (it hasn't gone through it's final prettifying yet since it isn't working--sorry about that), and I'm sure some of the SPDIF stuff that's in there doesn't need to be. There is some GPT stuff in there as well, but that doesn't have to do with this directly.
I have a project for which I want the Teensy 4 to take in an external master audio clock generated by a different component on the SPDIF_EXT_CLK pin and transmit audio synchronized to that clock on SPDIF_OUT. I only need that audio for sending to a sound card so that it can synchronize its clock to it, so transmitting zeros on the SPDIF signal is OK. The sampling rate should be 48000, and the master clock is coming in at 48000 * 256 = 12.288 MHz. This project is not using the audio library.
I have read throught the audio section of the IMXRT manual multiple times, ctrl+f'ed for SPDIF, EXT_CLK, and all other related strings I can think of, checked the forums, and dug into the audio library code. I am hitting a wall.
The clock signal is confirmed fine on the scope, and I think my muxing is fine, but there must be something off with my register set up (I think). On the SPDIF_OUT pin I get a constant low signal, rather than a SPDIF signal encoding zero or anything else.
Any help would be appreciated. Code is below (it hasn't gone through it's final prettifying yet since it isn't working--sorry about that), and I'm sure some of the SPDIF stuff that's in there doesn't need to be. There is some GPT stuff in there as well, but that doesn't have to do with this directly.
C:
#include <Arduino.h>
#define IN_SAMPLE_TEST_MODE 0 // 0 uses the actual incoming clock, 1 uses the PIT to create the clock interrupts
#define CLKSRC 2 // 1 is peripheral clock, 2 is HF clock (which defaults to 24 MHz--figure out how to do 150 MHz)
#define TPS 204000000 // 150000000 or 24000000; or 180000000 (720 MHz) 204000000 (816 MHz) overclocking
#define EPOCH_PIN 19
#define TEST_PIN_16384 2
#define TXB0104_OE_PIN 4
#define RESET_4096_PIN 5
#define FS_PIN 8 // high is 48000, low is 44100
#if defined(__IMXRT1062__)
extern "C" uint32_t set_arm_clock(uint32_t frequency);
#endif
#define SPDIF_DPLL_GAIN24 0
#define SPDIF_DPLL_GAIN16 1
#define SPDIF_DPLL_GAIN12 2
#define SPDIF_DPLL_GAIN8 3
#define SPDIF_DPLL_GAIN6 4
#define SPDIF_DPLL_GAIN4 5
#define SPDIF_DPLL_GAIN3 6
#define SPDIF_DPLL_GAIN1 7
#define SPDIF_DPLL_GAIN SPDIF_DPLL_GAIN8 //Actual Gain
// for some reason there are long gaps when GCF is 2 or 4. They don't seem to happen when it's 64, though tough to say for sure. Should try to found out why they happen
const uint32_t FS_IN = 16384;
const uint32_t GCF = 4; // this MUST be integer factor of FS_IN and fs_out (4 is good for 44100 and 48000)
const uint32_t FS_OPTIONS[] = {44100, 48000};
const uint32_t N_IN = FS_IN / GCF;
volatile uint32_t fs_out; // will be read from a pin
volatile uint32_t n_out; // will be set once fs_out is read
const float IN_GAP_TOL = .025;
const uint32_t IN_GAP_NOM = TPS / GCF;
const uint32_t IN_GAP_MIN = (1. - IN_GAP_TOL) * IN_GAP_NOM;
const uint32_t IN_GAP_MAX = (1. + IN_GAP_TOL) * IN_GAP_NOM;
volatile uint32_t in_times[N_IN] = {0}; // ring buffer of in sample times
volatile uint32_t calc_time; // ring buffer of in sample times
volatile uint32_t in_gap = 0; // the time since N_IN samples ago
volatile uint32_t idx_in = 0; // which input sample did we just receive
volatile bool zeroed = false; // are the epoch onsets aligned?
volatile uint32_t out_step = 0; // the time since N_IN samples ago
void isr_samples();
void setup()
{
// enable the GPT2 clock https://forum.pjrc.com/index.php?threads/teensy-4-1-gpt2-failures.69684/
CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON); // | CCM_CCGR0_GPT2_SERIAL(CCM_CCGR_ON);
if (TPS >= 150000000) {
CCM_CSCMR1 &= ~64; // run clock in fast mode for better precision
set_arm_clock(TPS * 4);
delay(100);
}
// Setup USB serial port so we can print the timer value
Serial.begin(115200);
Serial.println("Initializing...");
// Set up the SPDIF output ======================================================================
// see: https://github.com/PaulStoffregen/Audio/blob/master/output_spdif3.cpp
pinMode(14, OUTPUT); // SPDIF OUT
pinMode(16, INPUT); // SPDIF EXT_CLK
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_02 = 3; // SPDIF_OUT on PIN 14
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_07 = 3; // SPDIF_EXT_CLK on PIN 16
IOMUXC_SPDIF_IN_SELECT_INPUT = 0; // 0 = GPIO_AD_B1_03_ALT3;
int n1 = 7; //0: divide by 1 (do not use with high input frequencies), 1:/2, 2: /3, 7:/8
int n2 = 0; //0: divide by 1, 7: divide by 8
CCM_CCGR5 &= ~CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate off -- transfer clock controlled by this
CCM_CDCDR = (CCM_CDCDR & ~(CCM_CDCDR_SPDIF0_CLK_SEL_MASK | CCM_CDCDR_SPDIF0_CLK_PRED_MASK | CCM_CDCDR_SPDIF0_CLK_PODF_MASK))
| CCM_CDCDR_SPDIF0_CLK_SEL(0) // 0 PLL4, 1 PLL3 PFD2, 2 PLL5, 3 pll3_sw_clk
| CCM_CDCDR_SPDIF0_CLK_PRED(n1)
| CCM_CDCDR_SPDIF0_CLK_PODF(n2);
CCM_CCGR5 |= CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate on
SPDIF_SCR =
~SPDIF_SCR_RXFIFO_CTR | // 0 normal operation, 1 read zeros
~SPDIF_SCR_RXFIFO_OFF_ON | // 0 is on, 1 is off
SPDIF_SCR_RXFIFOFULL_SEL(0) | // Full interrupt if at least 1 sample in Rx left and right FIFOs -- can use this instead of comparing last input FIFO samples?
SPDIF_SCR_RXAUTOSYNC |
SPDIF_SCR_TXAUTOSYNC |
SPDIF_SCR_TXFIFOEMPTY_SEL(2) | // Empty interrupt if at most 8 samples in Tx left and right FIFOs
SPDIF_SCR_TXFIFO_CTRL(0) | // 0:Send zeros 1: normal operation // can eventually poll for SPDIF_SIS_TXEM in SPDIF_SIS to see if a new output sample is needed
SPDIF_SCR_VALCTRL | // Outgoing Validity always clear
SPDIF_SCR_TXSEL(5) | // 0:off and output 0, 1:Feed-though SPDIFIN, 5:Tx Normal operation
SPDIF_SCR_USRC_SEL(3);
SPDIF_SRPC =
SPDIF_SRPC_CLKSRC_SEL(8) | // 8 is ext_clk
SPDIF_SRPC_GAINSEL(SPDIF_DPLL_GAIN);
SPDIF_STC = SPDIF_STC_SYSCLK_DF(0) | // no master clock signal
SPDIF_STC_TXCLK_SOURCE(3) | // 3 is EXT_CLK
SPDIF_STC_TX_ALL_CLK_EN | // enable the transfer clock
SPDIF_STC_TXCLK_DF(3); // clock divider (-1, such that 0 is 1, 1 is 2...).
// Signal on EXT_CLK pin is 48000 * 256 = 12.288 MHz
// I think this should either be 2 (6.144) or 4 (3.072--but transmits on each clock change)? Page 2041.
// The recovered clock from Rx is 128 * fs (so 6.144). page 2049
// SPDIFTxLeft, SPDIFTxRight registers are audio data called SPDIF_STL and SPDIF_SRL
// The Tx left and right FIFOs are also 16-deep and 24-width (equal to the audio data width)
// Set up the timer stuff for the sample syncing ================================================
// output a ~16384 test signal on TEST_PIN_16384
// would be better to do this with the PIT with the 32768 clock
analogWriteFrequency(TEST_PIN_16384, FS_IN);
analogWrite(TEST_PIN_16384, 127);
digitalWriteFast(TXB0104_OE_PIN, LOW);
pinMode(TXB0104_OE_PIN, OUTPUT);
pinMode(RESET_4096_PIN, OUTPUT);
pinMode(FS_PIN, INPUT_PULLUP);
digitalWriteFast(RESET_4096_PIN, HIGH); // HIGH is reset, LOW is enabled
fs_out = FS_OPTIONS[digitalReadFast(FS_PIN)];
n_out = fs_out * 2 / GCF; // times 2 because it needs to both SET and CLEAR
// Set up the GPT
pinMode(15, INPUT_PULLDOWN); // capture pin
pinMode(17, OUTPUT); // output clock
pinMode(EPOCH_PIN, OUTPUT); // Toggle on each n_out samples (GCF Hz toggle)
IOMUXC_GPT2_IPP_IND_CAPIN1_SELECT_INPUT = 1; // 1 = GPIO_AD_B1_03_ALT8
IOMUXC_GPT2_IPP_IND_CAPIN2_SELECT_INPUT = 1; // 1 = GPIO_AD_B1_04_ALT8
/// Set pin functions
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 8; // GPT2 CAPTURE1 on PIN 15
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_06 = 8; // GPT2 COMPARE2 on PIN 17
// Set up the pads
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_03 = 0x13000; // Pulldown & Hyst
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_06 = (7 << 3) + // off-high drive strength (1-7)
(3 << 6) + // low-high speed (0-3)
(1 << 0); // slow-fast slew (0-1)
// Those pin modes are instead of setting IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_03 and 07, but lots of options for configuring there
GPT2_CR = 0; // Disable for configuration
GPT2_PR = 0; // No prescaler
GPT2_SR = 0x3F; // clear all prior status
GPT2_CR |= GPT_CR_CLKSRC(1) // HF clock as clock source
| GPT_CR_FRR // Free-Run, do not reset
| GPT_CR_IM1(1); // capture 1 on rising edge
GPT2_IR = GPT_IR_OF2IE; // enable interrupt for output compare event only, it will also keep track of input samples as they come in
attachInterruptVector(IRQ_GPT2, isr_samples); // declare which routine performs the ISR function
NVIC_SET_PRIORITY(IRQ_GPT2, 48); // set it it to a pretty high priority (may not be needed though, since its not directly driving the pins) 32 is systick, 64 is serial
NVIC_ENABLE_IRQ(IRQ_GPT2);
Serial.println("Running...");
GPT2_CR |= GPT_CR_EN; // Enable timer
GPT2_OCR2 = GPT2_CNT + TPS * 0.25; // first event will be a quarter second from now, then should hopefully be self-sustaining
GPT2_CR &= ~GPT_CR_OM3(7) & ~GPT_CR_OM2(7); // clear the three output mode bits
GPT2_CR |= GPT_CR_OM3(1) | GPT_CR_OM2(1); // output TOGGLE mode (1)
delay(50);
digitalWriteFast(TXB0104_OE_PIN, HIGH); // enable the logic level translater
}
void loop()
{
delay(1);
}