Skip to content

Instantly share code, notes, and snippets.

@LightningStalker
Last active July 17, 2022 04:29
Show Gist options
  • Save LightningStalker/ecaf6b26f6c48334a6190d6a4ce945c1 to your computer and use it in GitHub Desktop.
Save LightningStalker/ecaf6b26f6c48334a6190d6a4ce945c1 to your computer and use it in GitHub Desktop.
Digispark MPX Stereo Modulator
/* Digispark MPX Stereo Modulator
Based on a design found at
"http://cappels.org/dproj/FM_MPX_STEREO/
SIMPLE FM STEREO MULTIPLEX ENOCDER CIRCUIT.html"
Be sure to use adequate low paths pants filtering and stay FCC
part 15 compliant.
ATTinyCore board library v1.5.2
7/9/2022 ©The Lightning Stalker
*/
#define MUXL_PIN 1 // The left speaker
#define MUXR_PIN 2 // The right speaker
#define PILOT_TONE_PIN 0 // 19KHz pilot tone
// initialize state machine at binary 011 (decimal 3)
volatile int DDRSHADOW = 3, PORTSHADOW = 0, othertimes = 0;
ISR(TIMER0_COMPA_vect) // Interrupt driven 2 * 38KHz
{
cli();
DDRSHADOW ^= 6; // Use of shadow registers for indirect ->
if (othertimes == 0) // half time for 19KHz
{
PORTSHADOW ^= 1; // XOR bit toggling
PORTB = PORTSHADOW;
DDRB = DDRSHADOW; // -> port manipulation
}
else
{
PORTSHADOW ^= 0; // Let's try making these two things the same.
PORTB = PORTSHADOW;
DDRB = DDRSHADOW; // to even out the timing a little
}
othertimes ^= 1; // since the pilot tone is half or 19KHz
sei(); // for some reason booleans don't work here.
}
void setup() {
// put your setup code here, to run once:
pinMode(MUXL_PIN, OUTPUT); // Why reinvent the wheel when you can
pinMode(MUXR_PIN, OUTPUT); // grab the wheel, stick it on your
pinMode(PILOT_TONE_PIN, OUTPUT); // cart and make it your bitch
// set up counter interrupt
TCCR0A = 0x00; // Normal port operation
TCCR0B = 0x00;
TCCR0A |= (1 << WGM01); // set compare match CTC mode
TCCR0B |= (1 << CS00); // no prescaling
OCR0A = 217; // 38KHz * 2 at the 16.5MHz Digispeak default
TIMSK |= (1 << OCIE0A); // enabling timer0 compare match interrupt
}
/* The Digispeak uses the internal oscillator of the Attiny85 which
is very unstable. It's best to use an external crystal and
change the above OCR1C compare match value accordingly. Most
easily done by just ditching the Digispock entirely and using a
bare Attiny85 to get all of the USB components out of the way
You will lose the USB functionality anyway.
*/
void loop() {
// put your main code here, to run repeatedly:
// You can put something else here if you need to.
}
@LightningStalker
Copy link
Author

LightningStalker commented Jul 14, 2022

I had to change this to use timer0 because timer1 is used by millis() and other things on the Digispark.

@LightningStalker
Copy link
Author

It's advisable to use a crystal because there is some instability in the timing that is most likely an aspect of the internal RC oscillator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment