My program is a fan controller that reads the fan speed via interrupts (hall sensor) on pin 2 and sets the speed to the fan motor via pin 6.
I'm using serial read (parseInt) so I can type in the desired fan speed, but when I do, Serial stops displaying new lines. I suspect this is because of buffering associated with the serial read and that mucking with the priority of the interrupt.
Thoughts?
// Pin Defs
const int FANpin = 2;
const int CONTROLpin = 6;
// Variables
int CONTROLvalue = 0; // Fan Control Value (20-255)
int NbTopsFan; // Counts fan spins
int Calc; // Fan Calculation Output
typedef struct{ // Defines the structure for multiple fans and their dividers
char fantype;
unsigned int fandiv;
}fanspec;
fanspec fanspace[3]={{0,1},{1,2},{2,8}}; // Type of fan
char fan = 1; // Type 1
// Interrupt Function
void rpmcalc () {
NbTopsFan++; // Count fan rotations each interrupt
}
// Setup
void setup() {
Serial.begin(9600);
pinMode(FANpin, INPUT);
pinMode(CONTROLpin, OUTPUT);
attachInterrupt(0, rpmcalc, RISING);
}
// Main
void loop() {
NbTopsFan = 0; // Reset fan spin count
if (Serial.available() > 0) {
cli(); //Disable interrupts...not sure if working
CONTROLvalue = Serial.parseInt(); // Entering anything freezes serial output
analogWrite(CONTROLpin, CONTROLvalue); // Write the new speed value
}
sei(); // Enables interrupts to begin counting fan spins
delay (1000); // Wait 1 second
cli(); // Disable interrupts
Calc = ((NbTopsFan * 60)/fanspace[fan].fandiv);
// Times NbTopsFan (which is approximately the frequency the fan is spinning at) by 60 seconds before dividing by the fan's divider
Serial.print("Fan Value: ");
Serial.print(CONTROLvalue);
Serial.print(" / Fan Speed:");
Serial.print(Calc, DEC);
Serial.println(" RPM");
delay(500);
}
It would take an awfully fast fan before you need interrupts to get the speed.
Without learning BlinkWithoutDelay (which you should at some point soon anyway) you could try instead of:
sei(); // Enables interrupts to begin counting fan spins
delay (1000); // Wait 1 second
cli(); // Disable interrupts
No interrupt and get rid of the rest of the sei()'s and cli()'s.
declare these variables up top:
unsigned long startTime, waitTime = 1000UL; // for a 1 second timing loop
byte pinStateHistory;
this goes where the count test above was:
NbTopsFan = 0;
pinStateHistory = 0; // this might need to be 1, not sure just now, not a big deal
startTime = millis();
while ( millis() - startTime >= waitTime )
{
pinStateHistory = ( pinStateHistory * 2 ) & 3; // shift the last reads and mask off bits 2-7
pinStateHistory += digitalRead( FANpin ); // add the current read, now last and current reads are 1 value
if ( pinStateHistory == 1 ) // last read was LOW, current is HIGH, RISING edge detected
{
NbTopsFan++;
}
}
And that should give you your rpm count for the second tested.
Please note that this code blocks all other execution just as badly as delay().
You don't have to do this and the fix does not require interrupts.
I don't know whether your signal is fast enough to actually need interrupts, but if it does then turning interrupts on and off and delaying the sketch to count how many interrupts are received in a period is a horrible way to do it. Far better to have an interrupt handler which increments a global counter (which should be volatile, and unsigned, and big enough to hold the maximum number of interrupts you expect in your sample period) and then write some code which runs at regular intervals which determines how many interrupts have occurred in the preceding period. Use the technique demonstrated in blink without delay to run the code at regular intervals.
If your volatile counter needs to be bigger than a byte, when you need to read it in code running in the main context you should disable interrupts, copy the volatile variable to a local temporary variable, and re-enable interrupts.
One approach to counting interrupts is to zero the counter each time you read it. A better approach is to keep a copy of the previous counter value and subtract that from the new value to see how much the counter has changed by. That avoids needing to zero the counter, which means the volatile data is read-only in the main context, which reduces the scope for bugs caused by race conditions.
Rather than fiddling with the interrupts and CLI/SEI just use Serial.available() and Serial.read() to read one byte at a time into a buffer and convert the data to an INT separately.
What range of numbers do you want to send from the PC? If it is just 0-255 then you just need to send a single byte.