Hello everyone!
I'm writing because i have problem with reading another pwm signal. I found that code on youtube and im trying to do some sound simulator. Everything works ok until i wanna put third channel as auxilary "start engine" button. I'm thinking that something is wrong with interruptions but i can't find what is wrong.
#include "idle.h"
boolean managedThrottle = true;
boolean pwmThrottle = true; // Takes a standard servo signal on pin 2 (UNO)
boolean engineOn = true;
// Stuff not to play with!
#define BASE_RATE 16000 // The base sample rate of the audio
#define DEFAULT_VOLUME 127 // Volume when in non managed mode
#define VOL_MIN 20 // Min volume in managed mode 0 - 127
#define VOL_MAX 127 // Max volume in managed mode 0 - 127
#define TOP_SPEED_MULTIPLIER 15 // RPM multiplier in managed mode, bigger the number the larger the rev range, 10 - 15 is a good place to start
#define SPEAKER 3 // This is kept as 3, original code had 11 as option, but this conflicts with SPI
volatile uint16_t currentSmpleRate = BASE_RATE; // Current playback rate, this is adjusted depending on engine RPM
boolean audioRunning = false; // Audio state, used so we can toggle the sound system
uint16_t curVolume = 0; // Current digi pot volume, used for fade in/out
volatile uint16_t curEngineSample; // Index of current loaded sample
uint8_t lastSample; // Last loaded sample
int16_t currentThrottle = 0; // 0 - 1000, a top value of 1023 is acceptable
uint8_t throttleByte = 0; // Raw throttle position in SPI mode, gets mapped to currentThrottle
uint8_t spiReturnByte = 0; // The current RPM mapped to a byte for SPI return
volatile int16_t pulseWidth = 0; // Current pulse width when in PWM mode
volatile int16_t pulseWidth2 = 0;
void setup()
{
Serial.begin(9600);
if(managedThrottle) writePot(0);
else writePot(DEFAULT_VOLUME);
// pwm in setup, for a standard servo pulse
pinMode(2, INPUT); // We don't want INPUT_PULLUP as the 5v may damage some receivers!
if(pwmThrottle){ // And we don't want the interrupt firing when not in pwm mode
attachInterrupt(0, getPulsewidth, CHANGE);
}
pinMode(4, INPUT);
attachInterrupt(1, getPulsewidth2, CHANGE);
startPlayback();
}
void loop()
{
doPwmThrottle();
manageSpeed();
Serial.println(pulseWidth2);
}
void doPwmThrottle(){
if(pulseWidth > 800 && pulseWidth < 2200){ // check if the pulsewidth looks like a servo pulse
if(pulseWidth < 1000) pulseWidth = 1000; // Constrain the value
if(pulseWidth > 2000) pulseWidth = 2000;
if(pulseWidth > 1520) currentThrottle = (pulseWidth - 1500) *3; // make a throttle value from the pulsewidth 0 - 1000
else if(pulseWidth < 1470) currentThrottle = abs( (pulseWidth - 1500) *3);
else currentThrottle = 0;
}
}
void doengine(){
if(pulseWidth2 > 1955) engineOn = true;
else if(pulseWidth2 < 1200) engineOn = false;
else engineOn = false;
}
void manageSpeed(){
static int16_t prevThrottle = 0xFFFF;
static int16_t currentRpm = 0;
const int16_t maxRpm = 18184;
const int16_t minRpm = 0;
static unsigned long throtMillis;
static unsigned long volMillis;
// Engine RPM
if(millis() - throtMillis > 5) {
throtMillis = millis();
if(currentThrottle +12 > currentRpm){
currentRpm += 6;
if(currentRpm > maxRpm) currentRpm = maxRpm;
prevThrottle = currentThrottle;
}
else if(currentThrottle -15 < currentRpm){
currentRpm -= 12;
if(currentRpm < minRpm) currentRpm = minRpm;
prevThrottle = currentThrottle;
}
if(currentRpm >> 2 < 255) spiReturnByte = currentRpm >> 2;
else spiReturnByte = 255;
if(currentRpm >> 2 < 0) spiReturnByte = 0;
currentSmpleRate = F_CPU / (BASE_RATE + long(currentRpm * TOP_SPEED_MULTIPLIER) );
}
// Engine Volume
if(millis() - volMillis > 50) {
volMillis = millis();
int vol = map(currentThrottle, 0, 1023, VOL_MIN, VOL_MAX);
if(vol > curVolume) curVolume = vol;
else {
curVolume -= (curVolume/10);
if(curVolume < VOL_MIN) curVolume = VOL_MIN;
}
int lastVolume = 0xFFFF;
if(curVolume != lastVolume){
lastVolume = curVolume;
writePot(curVolume);
}
}
}
void writePot(uint8_t data){
// This function should get a value from 0 - 127
// It would be trivial to convert this to work with
// an I2C device.
if(data > VOL_MAX) data = VOL_MAX; // cap it just in case
}
void startPlayback()
{
pinMode(SPEAKER, OUTPUT);
audioRunning = true;
// 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 = F_CPU / 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)
lastSample = pgm_read_byte(&idle_data[idle_len-1]);
curEngineSample = 0;
sei();
uint8_t target = map(currentThrottle, 0, 1023, VOL_MIN, VOL_MAX); // Fadein the volume pot
for(uint8_t i = 0; i < target; i ++){
curVolume = i;
writePot(curVolume);
delay(1);
}
}
void stopPlayback()
{
// Fadeout the volume pot
for(uint8_t i = curVolume; i > 0; i--){
curVolume = i;
writePot(i);
delay(1);
}
audioRunning = false;
TIMSK1 &= ~_BV(OCIE1A); // Disable playback per-sample interrupt.
TCCR1B &= ~_BV(CS10); // Disable the per-sample timer completely.
TCCR2B &= ~_BV(CS10); // Disable the PWM timer.
digitalWrite(SPEAKER, LOW);
}
// Uses a pin change interrupt and micros() to get the pulsewidth at pin 2
void getPulsewidth(){
unsigned long currentMicros = micros();
boolean currentState = digitalRead(2);
static unsigned long prevMicros = 0;
static boolean lastState = LOW;
if(lastState == LOW && currentState == HIGH){ // Rising edge
prevMicros = currentMicros;
lastState = currentState;
}
else if(lastState == HIGH && currentState == LOW){ // Falling edge
pulseWidth = currentMicros - prevMicros;
lastState = currentState;
}
}
// Uses a pin change interrupt and micros() to get the pulsewidth at pin 4
void getPulsewidth2(){
unsigned long currentMicros2 = micros();
boolean currentState2 = digitalRead(4);
static unsigned long prevMicros2 = 0;
static boolean lastState2 = LOW;
if(lastState2 == LOW && currentState2 == HIGH){ // Rising edge
prevMicros2 = currentMicros2;
lastState2 = currentState2;
}
else if(lastState2 == HIGH && currentState2 == LOW){ // Falling edge
pulseWidth2 = currentMicros2 - prevMicros2;
lastState2 = currentState2;
}
}
// This is the main playback interrupt, keep this nice and tight!!
ISR(TIMER1_COMPA_vect) {
OCR1A = currentSmpleRate;
if (curEngineSample >= idle_len) { // Loop the sample
curEngineSample = 0;
}
if (engineOn) {
OCR2B = pgm_read_byte(&idle_data[curEngineSample]); // Volume
curEngineSample++;
}
else OCR2B = 255; // Stop engine (volume = 0)
}