Still getting to grips with C/Arduino, and just looking for someone to point me in the right direction.
I'm using an Arduino Micro, and I need to make short, accurately timed pulses, whilst the main program also handles SPI comms, Serial, User input via buttons/pots, and eventually bluetooth/wifi.
The pulses need to be 70 - 200us long, with a gap of 2 - 32ms, with the on time and off time adjustable but proportionate to each other. I thought it best to use Timer1 Interrupt to keep this accurate.
Here's what I've done - which works, but I feel like I'm using an ISR in a stupid way, as this will need a few for loops adding to make the on/off times modulate in a set pattern, and thats just 1 of 2 channels, and 1 of 20 "Patterns" I want to be able to choose between.
I tried making the ISR just "++count", where count is a volatile int, then make a function using the value of count to do the work, but it just can't act fast enough to keep up with the rate count is changing.
First time posting so not sure on etiquette for posting code, is screenshot OK or should I attach a text file or something to make it easier for people?
Please post your code using code tags instead of pictures of it.
Ahh I see, cheers here goes:
(Bear in mind the hz values in comments are not correct, its 16MHz)
cli(); // stop interrupts
//set timer1 interrupt at 1Hz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 1hz increments
OCR1A = 0;// = (16*10^6) / (1*1024) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS10, CS11 and CS12 bits for 1024 prescaler
TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
}
// Interrupt Service Routine
ISR(TIMER1_COMPA_vect)
{
++count;
if (count > (219 * frequency) && count < (235 * frequency))
{
digitalWrite(leftgate, HIGH);
}
else if (count == (235 * frequency))
{
digitalWrite(leftgate, LOW);
}
else if (count > (235 * frequency) && count < (251 * frequency))
{
digitalWrite(rightgate, HIGH);
}
else if (count == (251 * frequency))
{
digitalWrite(rightgate, LOW);
count = 0;
}
}
Your code is broken so no info on data types, but..
Here is something to consider, though; The MCU is 8 bit and performing mathematical instructions on 16 bit values requires at least two instruction-cycles. Your ISR has some multiplications which are performed multiple times (if they are not optimized by the compiler), and you could get about this like this:
++count;
int threshold1 = 235 * frequency;
int threshold2 = 251 * frequency;
if (count > (219 * frequency) && count < threshold1)
{
digitalWrite(leftgate, HIGH);
}
else if (count == threshold1)
{
digitalWrite(leftgate, LOW);
}
else if (count > threshold1 && count < threshold2)
{
digitalWrite(rightgate, HIGH);
}
else if (count == threshold2)
{
digitalWrite(rightgate, LOW);
count = 0;
}
The thresholds could ideally be calculated in loop() to get them out of the ISR. If "frequency" does not change, they could be constants.
On the other hand; Why fix it if it ain't broke!? 
Please post a complete program that actually compiles. Seeing variable types and other info is important.
Ah sorry, I thought posting the whole thing would be verbose - thanks for letting me know.
// Testing high frequency pulses using timer interrupt
#include <Wire.h>//Include the Wire library to talk I2C
#include <MCPDAC.h>//MCP DAC setup library
#include <LiquidCrystal.h>//Library for LCD Display
// pin definitions for LCD
#define LCD_PIN_RS 4
#define LCD_PIN_E 3
#define LCD_PIN_DB4 9
#define LCD_PIN_DB5 10
#define LCD_PIN_DB6 11
#define LCD_PIN_DB7 12
// object initialization for LCD
LiquidCrystal lcd(LCD_PIN_RS,LCD_PIN_E,LCD_PIN_DB4,LCD_PIN_DB5,LCD_PIN_DB6,LCD_PIN_DB7);
volatile int count = 0; // variable to store counter from ISR
const int leftgate = 5; //left MOSFET gate pin
const int rightgate = 6; //right MOSFET gate pin
const byte level_pot_pin = A0; // assign name to pin A0 from pot
const byte frequency_pot_pin = A1; // assign name to pin A1 from pot
unsigned long previous_time = 0; // variable to store time in microseconds
int level_value = 0; // variable to store the read value
int frequency_value = 0; // variable to store the read value
int millivolts = 0; // variable to store the mapped read value
int frequency = 0; // variable to store the mapped read value
int display_level = 0; // variable to store the mapped value for LCD
void setup()
{
// LCD Setup
lcd.begin(16, 2); // set the LCD's number of columns and rows
lcd.setCursor(0,0); // set cursor to top left
// Startup message
lcd.print("StimTech V1.0");
delay(1000);
lcd.setCursor(0,1);
lcd.print("Initialising");
delay(400);
lcd.print(".");
delay(400);
lcd.print(".");
delay(400);
lcd.print(".");
delay(400);
lcd.clear();
lcd.setCursor(0,0); // Set cursor to top left
lcd.print("Power Level: "); // print value label
// Set both N-Channel MOSFET gate pins to output
pinMode(leftgate, OUTPUT);
pinMode(rightgate, OUTPUT);
//Configuration for DAC
MCPDAC.begin(8);// CS on pin 8, no LDAC pin (tie it to ground).
MCPDAC.setGain(CHANNEL_A,GAIN_HIGH);// Set the gain to "HIGH" mode - 0 to 4096mV.
MCPDAC.shutdown(CHANNEL_A,false);// Do not shut down channel A
MCPDAC.shutdown(CHANNEL_B,true);// Shut down channel B
//Turn all outputs off for safety
MCPDAC.setVoltage(CHANNEL_A,4095); // turn P-channel MOSFET off.
Serial.begin(9600);
cli(); // stop interrupts
//set timer1 interrupt at 16MHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 16Mhz increments
OCR1A = 0;// (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS10, CS11 and CS12 bits
TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
}
// Interrupt Service Routine
ISR(TIMER1_COMPA_vect)
{
++count;
if (count > (219 * frequency) && count < (235 * frequency))
{
digitalWrite(leftgate, HIGH);
}
else if (count == (235 * frequency))
{
digitalWrite(leftgate, LOW);
}
else if (count > (235 * frequency) && count < (251 * frequency))
{
digitalWrite(rightgate, HIGH);
}
else if (count == (251 * frequency))
{
digitalWrite(rightgate, LOW);
count = 0;
}
}
// Function to print to LCD screen
void lcd_readout()
{
lcd.setCursor(13,0);
if (display_level < 10)
{
lcd.print("0");
lcd.print(display_level); // print power level after 0
}
else
{
lcd.print(display_level); // print power level after value label
}
}
// Function to get adjustment values from pots
void get_user_adj()
{
level_value = analogRead(level_pot_pin); // read voltage of analogue pin A0 from pot(0-5v)
millivolts = map(level_value, 0, 1023, 3000, 4095); // map value from pot to millivolts range for DAC output
display_level = map(millivolts, 3000, 4095, 99, 0); // map millivolts DAC output to a power level for LCD
frequency_value = analogRead(frequency_pot_pin); // read voltage of analogue pin A1 from pot
frequency = map(frequency_value, 0, 1023, 1 , 16); // map read value to a range for delays in program
}
void loop()
{
get_user_adj();
MCPDAC.setVoltage(CHANNEL_A,millivolts); // Set voltage of DAC
lcd_readout();
}
Libraries.zip (2.11 KB)
Ahh I see, well I guess running at that speed, cutting out repetition of tasks does make quite a difference though, cheers!
Danois90:
Your code is broken so no info on data types, but..
Here is something to consider, though; The MCU is 8 bit and performing mathematical instructions on 16 bit values requires at least two instruction-cycles. Your ISR has some multiplications which are performed multiple times (if they are not optimized by the compiler), and you could get about this like this:
++count;
int threshold1 = 235 * frequency;
int threshold2 = 251 * frequency;
if (count > (219 * frequency) && count < threshold1)
{
digitalWrite(leftgate, HIGH);
}
else if (count == threshold1)
{
digitalWrite(leftgate, LOW);
}
else if (count > threshold1 && count < threshold2)
{
digitalWrite(rightgate, HIGH);
}
else if (count == threshold2)
{
digitalWrite(rightgate, LOW);
count = 0;
}
The thresholds could ideally be calculated in loop() to get them out of the ISR. If "frequency" does not change, they could be constants.
On the other hand; Why fix it if it ain't broke!? ;-)
Writing ISR's is a question of reducing the used clock cycles to an absolute minimum, so repetitive computations should be avoided/eliminated.
Or you can do the scaling incrementally and eliminate all the multiplication:
ISR(TIMER1_COMPA_vect)
{
static int timerCount;
++timerCount;
if (timerCount > frequency)
{
timerCount = 0;
count++;
if (count == 219)
{
digitalWrite(leftgate, HIGH);
}
else if (count == 235)
{
digitalWrite(leftgate, LOW);
}
else if (count >= (251) //gt or eq for safety
{
digitalWrite(rightgate, LOW);
count = 0;
}
}
}
(and eliminate the repetitive write of the same value to the output port)
Ok got it, I can see what I need to do now, thanks guys!