#include "settings.h" // <<------- SETTINGS
//const float codeVersion = 1.33; // Software revision
//
// =======================================================================================================
// LIRBARIES & TABS
// =======================================================================================================
//
#include "curves.h" // load nonlinear throttle curve arrays
//
// =======================================================================================================
// PIN ASSIGNMENTS & GLOBAL VARIABLES (Do not play around here)
// =======================================================================================================
//
#define SPEAKER 3 // Amplifier PAM8403 connected to pin 3 (via 10kOhm potentiometer)
#define FREQ 16000000L // 16MHz clock frequency
// Define global variables
volatile uint16_t currentSmpleRate = BASE_RATE; // Current playback rate, this is adjusted depending on engine RPM
volatile uint16_t fixedSmpleRate = FREQ / BASE_RATE; // Current playback rate, this is adjusted depending on engine RPM
volatile boolean engineOn = false; // Signal for engine on / off
volatile uint16_t curEngineSample; // Index of currently loaded engine sample
uint16_t currentThrottle = 0; // 0 - 500, a top value of 1023 is acceptable
const int analogInPin = A0; // Analog input pin that the potentiometer is attached to
int sensorValue = 0; // value read from the pot
//
// =======================================================================================================
// MAIN ARDUINO SETUP (1x during startup)
// =======================================================================================================
//
void setup() {
// wait for RC receiver to initialize
delay(1000);
setupPcm();
}
//
// =======================================================================================================
// PCM setup
// =======================================================================================================
//
void setupPcm() {
Serial.begin(9600);
pinMode(SPEAKER, OUTPUT);
pinMode(analogInPin,INPUT);
// Set up Timer 2 to do pulse width modulation on the speaker pin.
ASSR &= ~(_BV(EXCLK) | _BV(AS2)); // Use internal clock (datasheet p.160)
TCCR2A |= _BV(WGM21) | _BV(WGM20); // Set fast PWM mode (p.157)
TCCR2B &= ~_BV(WGM22);
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0); // Do non-inverting PWM on pin OC2B (p.155)
TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0)); // On the Arduino this is pin 3.
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10); // No prescaler (p.158)
OCR2B = pgm_read_byte(&idle_data[0]); // Set initial pulse width to the first sample.
// Set up Timer 1 to send a sample every interrupt.
cli();
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12); // Set CTC mode (Clear Timer on Compare Match) (p.133)
TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10)); // Have to set OCR1A *after*, otherwise it gets reset to 0!
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10); // No prescaler (p.134)
OCR1A = FREQ / BASE_RATE; // Set the compare register (OCR1A).
// OCR1A is a 16-bit register, so we have to do this with
// interrupts disabled to be safe.
TIMSK1 |= _BV(OCIE1A); // Enable interrupt when TCNT1 == OCR1A (p.136)
curEngineSample = 0;
sei();
}
void engineMassSimulation() {
static int16_t mappedThrottle = 0;
static int16_t currentRpm = 0;
static unsigned long throtMillis;
if (millis() - throtMillis > 5) { // Every 5ms
throtMillis = millis();
// compute unlinear throttle curve
mappedThrottle = reMap(curveShifting, currentThrottle);
// Accelerate engine
if (mappedThrottle + acc > currentRpm) {
currentRpm += acc;
if (currentRpm > maxRpm) currentRpm = maxRpm;
}
// Decelerate engine
else if (mappedThrottle - dec < currentRpm) {
currentRpm -= dec;
if (currentRpm < minRpm) currentRpm = minRpm;
}
// Speed (sample rate) output
currentSmpleRate = FREQ / (BASE_RATE + long(currentRpm * TOP_SPEED_MULTIPLIER) );
}
}
void loop() {
sensorValue = analogRead(analogInPin);
Serial.println(sensorValue);
// map it to the range of the analog out:
currentThrottle = map(sensorValue, 200, 900, 0, 500);
// Simulate engine mass, generate RPM signal
engineMassSimulation();
}
// This is the main sound playback interrupt, keep this nice and tight!! ------------------------------
ISR(TIMER1_COMPA_vect) {
static float attenuator; // Float required for finer granularity!
// running ----
if (curEngineSample >= idle_length) { // Loop the sample
curEngineSample = 0;
attenuator = 1;
}
OCR1A = currentSmpleRate; // variable sample rate (RPM)!
OCR2B = pgm_read_byte(&idle_data[curEngineSample]);
curEngineSample++;
}