Skip to content

Instantly share code, notes, and snippets.

@kargeor
Created February 26, 2021 06:23
Show Gist options
  • Save kargeor/b4200fc859a8e6c2234701368c82acd2 to your computer and use it in GitHub Desktop.
Save kargeor/b4200fc859a8e6c2234701368c82acd2 to your computer and use it in GitHub Desktop.
Using the APLL clock source to output DMA parallel output from I2S1 at greater than 20Mhz bitrate
// Source: https://www.esp32.com/viewtopic.php?t=14185
/* Project ESP32 Logic Analyzer Testes com I2S Bit Clock
ESP32 Dev Kit 38 pins - Arduino IDE 1.8.12 - ESP32 Arduino V1.0.4
https://github.com/Gustavomurta/ESP32-Logic-analyzer
Gustavo Murta and Rui Vianna - 14/jun/2020
I2S1 MCLK pin = GPIO0 (derived from APLL Clock)
using PLL_D2 clock - master clock up to 80 MHz
using APLL clock - master clock up to 70 MHz
I2S1.clkm_conf.clka_en >>> Set this bit to enable clk_apll or reset to enable PLL Clock!
References:
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/driver/driver/i2s.h
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-cpu.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/rtc.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/rtc_cntl_reg.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/dport_reg.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/rtc_cntl_struct.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/syscon_reg.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/syscon_struct.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/periph_defs.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/i2s_struct.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/i2s_reg.h
https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/soc/soc/io_mux_reg.h
*/
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "soc/syscon_reg.h"
#include "soc/rtc_cntl_struct.h"
//#include "esp32-hal-cpu.h"
#include "driver/i2s.h"
float APL_CLK;
int sdm0, sdm1, sdm2, o_div;
float fclk;
float div_num, div_b, div_a;
void setup()
{
Serial.begin(115200); // IDE Console 115200 bps
socDetails (); // system on a chip details
apllClock (); // Read RTC Control Clock configuration
enableAudioPLLClock (); // power up audio PLL clock
configAPLclock (); // enable APLL clock 80 Mhz
mclkI2S1config (); // configure I2S1 MCLK clock
bckI2S1config (); // configure I2S1 BCK clock
mclkClock (); // configure GPIO0 to CLock OUT 1 - I2S MCLK
}
void socDetails ()
{
Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
Serial.println();
Serial.print("ESP32 - Chip Revision: ");
Serial.println(ESP.getChipRevision()); // print ESP32 chip revision
Serial.print("ESP32 - CPU frequency: ");
Serial.println(ESP.getCpuFreqMHz()); // print ESP32 cpu frequency
Serial.print("ESP32 - RTC Crystal Clock frequency = ");
Serial.println(rtc_clk_xtal_freq_get()); // print ESP32 RTC Crystal Clock frequency soc/rtc.h 40 MHz
Serial.print("ESP32 - RTC APB frequency: ");
Serial.println(rtc_clk_apb_freq_get()); // print ESP32 RTC APB frequency soc/rtc.h 80 MHz
Serial.println();
}
void apllClock ()
{
Serial.print("RTC Control Clock configuration (0x");
Serial.print(RTC_CNTL_CLK_CONF_REG, HEX); // Read RTC Control Clock configuration (0x3FF48070) = 0x29580010
Serial.print(") = 0x"); Serial.println (REG_READ(RTC_CNTL_CLK_CONF_REG), HEX);
Serial.print("APLL Tick configuration (0x");
Serial.print(SYSCON_APLL_TICK_CONF_REG, HEX); // Read APLL Tick configuration (0x3FF6603C) = 0x63
Serial.print(") = 0x"); Serial.println (REG_READ(SYSCON_APLL_TICK_CONF_REG), HEX);
Serial.print("RTC OptonS1 control configuration (0x");
Serial.print(RTC_CNTL_OPTIONS0_REG, HEX); // Read RTC Optons 0 control configuration (0x3FF48000) = 0x1C124000
Serial.print(") = 0x"); Serial.println (REG_READ(RTC_CNTL_OPTIONS0_REG), HEX);
Serial.println();
}
void rtcCntlAnaConfReg ()
{
Serial.print("RTC power up/down configuration (0x");
Serial.print(RTC_CNTL_ANA_CONF_REG, HEX); // Read RTC power up/down configuration (0x3FF48030)= 0x800000
Serial.print(") = 0x"); Serial.println (REG_READ(RTC_CNTL_ANA_CONF_REG), HEX);
}
void enableAudioPLLClock ()
{
rtcCntlAnaConfReg (); // Read RTC power up/down configuration
Serial.println("RTC PLLA power down"); // Disable audio PLL clock
RTCCNTL.ana_conf.plla_force_pd = 0; // RTC APLL power down
rtcCntlAnaConfReg (); // Read RTC power up/down configuration
Serial.println("RTC PLLA power up"); // Enable audio PLL clock
RTCCNTL.ana_conf.plla_force_pu = 1; // RTC APLL power up
rtcCntlAnaConfReg (); // Read RTC power up/down configuration
}
void configAPLclock ()
{
// @param sdm0 frequency adjustment parameter, 0..255
// @param sdm1 frequency adjustment parameter, 0..255
// @param sdm2 frequency adjustment parameter, 0..63 max = 10
// @param o_div frequency divider, 0..31
// The dividend in this expression should be in the range of 240 and 560 MHz - tested
// apll_freq = xtal_freq * (4 + sdm0/65536 + sdm1/256 + sdm2)/((o_div + 2) * 2)
// rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1,uint32_t sdm2, uint32_t o_div);
// apll_freq = 40MHz * (4+0+0+6)/(0+2)*2
// apll_freq = 40 * 10 / 4 = 100 MHz // enable APLL clock 100 Mhz
sdm0 = 0; sdm1 = 0; sdm2 = 4; o_div = 0;
rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, o_div); // enable APLL clock
APL_CLK = 40 * (4 + (sdm0 / 65536) + (sdm1 / 256) + sdm2) / (2 * (o_div + 2));
Serial.println();
Serial.print ("APL_CLK = "); Serial.print (APL_CLK, 3); Serial.println (" MHz");
}
void mclkClock ()
{
// Enable I2S1 MCLK clock output at GPIO0 pin - ESP32 Tech Reference pag 70 and 71
Serial.println();
Serial.print("IO_MUX_PIN_CTRL ("); Serial.print(PIN_CTRL, HEX); // Clock output configuration register (0x3FF49000)
Serial.print(") = 0x"); Serial.println (REG_READ(PIN_CTRL), HEX); // print register value = 0x3FF
// PIN_FUNC_SELECT(PIN_CTRL, CLK_OUT1); // wrong function?
REG_WRITE(PIN_CTRL, 0xFF0); // it works - Clock output 1 to I2S1
Serial.print("IO_MUX_PIN_CTRL ("); Serial.print(PIN_CTRL, HEX); // Clock output configuration register (0x3FF49000)
Serial.print(") = 0x"); Serial.print (REG_READ(PIN_CTRL), HEX); // print register value = 0xFF0
Serial.println(" after configuration");
Serial.println();
Serial.print("PERIPHS_IO_MUX_GPIO0_U ("); Serial.print(PERIPHS_IO_MUX_GPIO0_U, HEX); // Configuration register for pad GPIO 0 (0x3FF49044)
Serial.print(") = 0x"); Serial.println (REG_READ(PERIPHS_IO_MUX_GPIO0_U), HEX); // print register value = 0xB00
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); // Configuration register for pad GPIO0 => set function 2
//PIN_INPUT_DISABLE(PERIPHS_IO_MUX_GPIO0_U); // disable input at GPIO 0
Serial.print("PERIPHS_IO_MUX_GPIO0_U ("); Serial.print(PERIPHS_IO_MUX_GPIO0_U, HEX); // Configuration register for pad GPIO 0 (0x3FF49044)
Serial.print(") = 0x"); Serial.print (REG_READ(PERIPHS_IO_MUX_GPIO0_U), HEX); // print register value = 0x1B00
Serial.println(" after configuration");
}
void mclkI2S1config ()
{
periph_module_enable(PERIPH_I2S1_MODULE); // enable peripheral I2S1 module (essential)
Serial.println();
Serial.print("I2S1 Bit clock configuration (0x"); // print register name and address (0x3FF4F0AC)
Serial.print(I2S_CLKM_CONF_REG(0), HEX); // Register I2S1 Bit clock configuration
Serial.print(") = 0x"); Serial.println (REG_READ(I2S_CLKM_CONF_REG(0)), HEX); // print register value = 0x4 (default)
div_num = 2; div_b = 0; div_a = 1;
I2S1.clkm_conf.clkm_div_num = div_num; // I2S clock divider’s integral value >= 2
I2S1.clkm_conf.clkm_div_b = div_b; // Fractional clock divider’s numerator value
I2S1.clkm_conf.clkm_div_a = div_a; // Fractional clock divider’s denominator value
I2S1.clkm_conf.clk_en = 1; // I2S clock enable
I2S1.clkm_conf.clka_en = 1; // Set this bit to enable clk_apll
// Configure Bit Clock configuration - ESP32 Tech Reference page 308 and 337
// fclk = fapll / (N + b/a)
// fclk = 100 MHz / (2 +(0/1) = 50 MHz (using APLL clock)
fclk = APL_CLK / ( div_num + ( div_b / div_a));
Serial.print("I2S1 master clock = "); Serial.print(fclk, 3); Serial.println (" MHz");
Serial.println();
Serial.print("I2S1 Bit clock configuration (0x"); // print register name and address (0x3FF4F0AC)
Serial.print(I2S_CLKM_CONF_REG(0), HEX); // Register I2S1 Bit clock configuration
Serial.print(") = 0x"); Serial.print (REG_READ(I2S_CLKM_CONF_REG(0)), HEX); // print register value = 0x104008
Serial.println(" after configuration");
}
void bckI2S1config ()
{
Serial.println(); // ESP32 Tech Reference page 308
Serial.print("I2S1 sample rate configuration (0x"); // print register name and address (0x3FF4F0B0)
Serial.print(I2S_SAMPLE_RATE_CONF_REG(0), HEX); // Register I2S1 sample rate configuration
Serial.print(") = 0x"); Serial.println (REG_READ(I2S_SAMPLE_RATE_CONF_REG(0)), HEX); // print register value = 0x410186 (default)
I2S1.sample_rate_conf.tx_bck_div_num = 4; // TX BCK clock = MCLK / num
I2S1.sample_rate_conf.rx_bck_div_num = 4; // RX BCK clock = MCLK / num
Serial.print("I2S1 sample rate configuration (0x"); // print register name and address (0x3FF4F0B0)
Serial.print(I2S_SAMPLE_RATE_CONF_REG(0), HEX); // Register I2S1 sample rate configuration
Serial.print(") = 0x"); Serial.print (REG_READ(I2S_SAMPLE_RATE_CONF_REG(0)), HEX); // print register value = 0x410104
Serial.println(" after configuration");
}
void loop()
{
// put your main code here, to run repeatedly:
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment