Skip to content

Instantly share code, notes, and snippets.

@xobs
Created August 16, 2017 07:45
Show Gist options
  • Save xobs/74f19632820a340f7af5c34c12da79b2 to your computer and use it in GitHub Desktop.
Save xobs/74f19632820a340f7af5c34c12da79b2 to your computer and use it in GitHub Desktop.
#include "Arduino.h"
#include "ChibiOS.h"
#include "kl02.h"
#include "memio.h"
// Enable interpolation to make the output smoother.
// Set to 0 to disable interpolation.
// Note that some instruments don't support interpolation.
#define INTERPOLATION_ENABLED 1
// Sets the maximum value of the phase accumulator, which is
// used to skip through the sample array.
#define PHASEACC_MAX 131072L
// The system is running off of a 32.768 kHz crystal going through
// a 1464x FLL multiplier, giving a system frequency of
// 47.972352 MHz.
// We set the PWM counter to 258, which gives a PWM period of 185.939 kHz.
// Happily, 185.939 is evenly divisible by 13, giving us an actual sample
// rate of 14303 kHz, assuming we delay 13 times.
#define PWM_DELAY_LOOPS 3
// The number of ticks that the sound system has gone through.
// Overflows after about three days, at 14 kHz.
volatile uint32_t global_tick_counter;
// The next sample to be played, nominally between -128 and 127
static int32_t next_sample;
// Nonzero if a sample has been queued, zero if the sample buffer is empty.
static volatile uint8_t sample_queued;
static int pwm0_stable_timer(void)
{
static int loops = 0;
/* Reset the timer IRQ, to allow us to fire again next time */
writel(TPM0_STATUS_CH1F | TPM0_STATUS_CH0F | TPM0_STATUS_TOF, TPM0_STATUS);
if (loops++ > PWM_DELAY_LOOPS) {
int32_t scaled_sample = next_sample + 130;
if (scaled_sample > 255)
scaled_sample = 255;
if (scaled_sample <= 0)
scaled_sample = 1;
writel(scaled_sample, TPM0_C1V);
writel(scaled_sample, TPM0_C0V);
loops = 0;
sample_queued = 0;
global_tick_counter++;
}
return 0;
}
static void prepare_pwm()
{
// Write dummy values out, to configure PWM mux
pinMode(0, OUTPUT);
analogWrite(0, 63);
pinMode(1, OUTPUT);
analogWrite(1, 63);
// Disable TPM0, allowing us to configure it
writel(0, TPM0_SC);
// Also disable both channels, which are running from the
// calls to analogWrite() above
writel(0, TPM0_C0SC);
writel(0, TPM0_C1SC);
// Configure the TPM to use the MCGFLLCLK (~32 MHz?)
writel(readl(SIM_SOPT2) | (1 << 24), SIM_SOPT2);
// We've picked pin 0, which is on TPM0_CH1
writel(256, TPM0_MOD);
writel(0, TPM0_CNT);
writel(TPM0_C0SC_MSB | TPM0_C0SC_ELSB, TPM0_C0SC);
writel(TPM0_C1SC_MSB | TPM0_C1SC_ELSA, TPM0_C1SC);
writel(100, TPM0_C1V);
writel(100, TPM0_C0V);
writel(TPM0_SC_TOF | TPM0_SC_TOIE | TPM0_SC_CMOD(1) | TPM0_SC_PS(0), TPM0_SC); // Enable TPM0
/* Enable the IRQ in the system-wide interrupt table */
attachFastInterrupt(PWM0_IRQ, pwm0_stable_timer);
}
void setup(void)
{
prepare_pwm();
enableInterrupt(PWM0_IRQ);
}
void loop(void)
{
// If a sample is still in the buffer, don't do anything.
if (sample_queued)
return;
uint32_t t = global_tick_counter;
uint32_t y = 0;
uint32_t x = 0;
next_sample = (((int)(3e3/(y=t&16383))&1)*35) +
(x=t*("6689"[t>>16&3]&15)/24&127)*y/4e4 +
(((t>>8^t>>10)|t>>14|x)&63);
sample_queued = 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment