Last active
November 15, 2022 12:50
-
-
Save jfrey-xx/37c645b0fa87d86375f8a356104951d9 to your computer and use it in GitHub Desktop.
MCLK for I2S arduino-pico using PIO -- see https://github.com/earlephilhower/arduino-pico/issues/647
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include "MCLK.h" | |
#include "mclk.pio.h" | |
MCLK::MCLK() { | |
_running = false; | |
_pin = 29; | |
_freq = 48000; | |
} | |
MCLK::~MCLK() { | |
} | |
bool MCLK::setPin(pin_size_t pin) { | |
if (_running) { | |
return false; | |
} | |
_pin = pin; | |
return true; | |
} | |
bool MCLK::setFrequency(unsigned int newFreq, unsigned int newMultiplier) { | |
_freq = newFreq; | |
_multiplier = newMultiplier; | |
if (_running) { | |
float bitClk = _freq * _multiplier * 2.0 /* edges per clock */; | |
pio_sm_set_clkdiv(_pio, _sm, (float)clock_get_hz(clk_sys) / bitClk); | |
} | |
return true; | |
} | |
bool MCLK::begin() { | |
_running = true; | |
int off = 0; | |
_mclk = new PIOProgram(&mclk_program); | |
_mclk->prepare(&_pio, &_sm, &off); | |
mclk_program_init(_pio, _sm, off, _pin); | |
setFrequency(_freq, _multiplier); | |
pio_sm_set_enabled(_pio, _sm, true); | |
return true; | |
} | |
void MCLK::end() { | |
_running = false; | |
delete _mclk; | |
_mclk = nullptr; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
class MCLK { | |
public: | |
MCLK(); | |
virtual ~MCLK(); | |
bool setPin(pin_size_t pin); | |
// set MCLK clock, based on audio sampling rate (e.g. 48000hz) and multiplier (typically 256, check specs) | |
bool setFrequency(unsigned int newFreq, unsigned int newMultiplier); | |
bool begin(unsigned int sampleRate, unsigned int multiplier ) { | |
setFrequency(sampleRate, multiplier); | |
return begin(); | |
} | |
bool begin(); | |
void end(); | |
private: | |
pin_size_t _pin; | |
unsigned int _freq = 48000; | |
unsigned int _multiplier = 256; | |
bool _running; | |
PIOProgram *_mclk; | |
PIO _pio; | |
int _sm; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.program mclk | |
; Generate mclk clock | |
.wrap_target | |
set pins, 1 ; Turn on | |
set pins, 0 ; Turn off, most complicated ever | |
.wrap | |
% c-sdk { | |
// Helper function (for use in C program) to initialize this PIO program | |
void mclk_program_init(PIO pio, uint sm, uint offset, uint pin) { | |
// Sets up state machine and wrap target. This function is automatically | |
// generated in mclk.pio.h. | |
pio_sm_config c = mclk_program_get_default_config(offset); | |
// Allow PIO to control GPIO pin (as output) | |
pio_gpio_init(pio, pin); | |
// Connect pin to SET pin (control with 'set' instruction) | |
sm_config_set_set_pins(&c, pin, 1); | |
// Set the pin direction to output (in PIO) | |
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); | |
// Load configuration and jump to start of the program | |
pio_sm_init(pio, sm, offset, &c); | |
} | |
%} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -------------------------------------------------- // | |
// This file is autogenerated by pioasm; do not edit! // | |
// -------------------------------------------------- // | |
#pragma once | |
#if !PICO_NO_HARDWARE | |
#include "hardware/pio.h" | |
#endif | |
// ---- // | |
// mclk // | |
// ---- // | |
#define mclk_wrap_target 0 | |
#define mclk_wrap 1 | |
static const uint16_t mclk_program_instructions[] = { | |
// .wrap_target | |
0xe001, // 0: set pins, 1 | |
0xe000, // 1: set pins, 0 | |
// .wrap | |
}; | |
#if !PICO_NO_HARDWARE | |
static const struct pio_program mclk_program = { | |
.instructions = mclk_program_instructions, | |
.length = 2, | |
.origin = -1, | |
}; | |
static inline pio_sm_config mclk_program_get_default_config(uint offset) { | |
pio_sm_config c = pio_get_default_sm_config(); | |
sm_config_set_wrap(&c, offset + mclk_wrap_target, offset + mclk_wrap); | |
return c; | |
} | |
// Helper function (for use in C program) to initialize this PIO program | |
void mclk_program_init(PIO pio, uint sm, uint offset, uint pin) { | |
// Sets up state machine and wrap target. This function is automatically | |
// generated in mclk.pio.h. | |
pio_sm_config c = mclk_program_get_default_config(offset); | |
// Allow PIO to control GPIO pin (as output) | |
pio_gpio_init(pio, pin); | |
// Connect pin to SET pin (control with 'set' instruction) | |
sm_config_set_set_pins(&c, pin, 1); | |
// Set the pin direction to output (in PIO) | |
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); | |
// Load configuration and jump to start of the program | |
pio_sm_init(pio, sm, offset, &c); | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// minimal example to setup MCLK clock with PIO, used that in combination with I2S library | |
#include "MCLK.h" | |
// changing clock | |
#include <pico/stdlib.h> | |
MCLK mclk; | |
#define pMCLK 18 // pin 24 | |
// chose arbitrary samplerate so we can have a cpu clock which is a divider of the MCLK | |
const int sampleRate = 40000; | |
// typical for many DAC | |
const int mclkMultiplier = 256; | |
void setup() { | |
// set CPU speed for a good ratio with 256*fs -- flag to bock if requested speed cannot be achieved | |
set_sys_clock_khz(102400, true); | |
Serial.begin(115200); | |
mclk.setPin(pMCLK); | |
if (!mclk.begin(sampleRate, mclkMultiplier)) { | |
while (1) { | |
Serial.println("Failed to initialize MCLK!"); | |
delay(1000); | |
} | |
} | |
} | |
void loop() { | |
// do your things | |
delay(1000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment