I'm trying to use a mirror galvo controlled by a mega 2560 to direct a laser. As a first test I want the laser to move along one axis by jumping equal intervals, but I'm having trouble. I'd like to know whether there are any obvious problems in my code (which I suspect may be the case as I'm new to timers and interrupts)?
void setup()
{
TCCR3B = _BV(CS30); //high freq ~50khz
pinMode(5, OUTPUT);
}
void loop() {
int output1= 5;
while(output1<255){
analogWrite(5,output1);
output1+=40;
delay(500);
}
output1=5;
}
When I run this code the mirror does achieve the full 5 degree range of movement it should. The problem is instead of moving equal intervals the mirror rotates the equivalent of .8 deg over what I suppose are the first two iterations of the while loop, then the mirror jump around 3.5deg of rotation. Then the mirror goes back to rotating by small increments again .75 deg for the last few iterations of the while loop.
I originally tried using my own settings for all timer and PWM settings, that did not work either:
void setup()
{
TCCR3A = TCCR3B = 0;
TCCR3A = _BV(COM3A1) | _BV(WGM31); //non-inverting
TCCR3B = _BV(CS30) | _BV(WGM32) | _BV(WGM33); //prescalar=1, fast pwm
ICR3= 500;
OCR3A= 6;
pinMode(5, OUTPUT);
}
void loop() {
while(OCR3A<500){
OCR3A+=80;
delay(500);
}
OCR3A=6;
}
Using this second code the mirror rotates by a constant 1 degree and stays put there.
edit: I do have a current gain circuit between the arduino and the mirrors, but I've tested both that circuit and the mirrors with a function generator. Off the function generator they work fine.
Related
I have a simple question about how to read rotary encoder input.
If I understand this image correctly, then every turn triggers a rise on pin A. Then, you have to check pin B, which is high if the encoder is turning clockwise and low if the encoder is turning counter clockwise.
I've tried to write my own code and not using any libraries, because I thought this would be really simple, but it turned out it was not.
This is the code I've written:
#define rotary_A 2
#define rotary_B 3
void setup()
{
pinMode(rotary_A, INPUT);
pinMode(rotary_B, INPUT);
attachInterrupt(digitalPinToInterrupt(rotary_A), rotary_spin, RISING);
Serial.begin(9600);
}
void rotary_spin()
{
if (digitalRead(rotary_B) == HIGH)
Serial.println("+");
else
Serial.println("-");
}
I was expecting to get + when I turn it clockwise and - when I turn it counter clockwise. However, I'm getting several outputs for each turn, like there were several interrupts triggered in rapid succession. For example, when I turn the encoder clockwise:
-
-
+
+
and counter clockwise:
+
+
-
-
-
-
The outputs are different every time, but the last character is always the right one.
What am I getting wrong? Is it not that simple or are there different types of encoders?
The question implies that there should only be a single interrupt per revolution. But encoders typically generate more than a single cycle per revolution--some in the thousands. That is probably why the interrupts seem to occur more rapidly than expected.
In a zero-latency interrupt environment, the code should work. But if the phase B pin is sampled too long after the phase A pin goes high, it will fail. This might occur if the next phase A rising edge occurs while Serial.println is still executing in the previous interrupt.
A simple test to see if this is the case is to turn the encoder very, very slowly. If this results in correct data, the problem is probably interrupt latency. Serial.println can then be replaced with something much quicker, like illuminating LEDs, to see if that resolves latency issues.
For actual use, you would need to make sure that the worse-case latency between phase A going high and phase B being sampled is adequate for the maximum rate of encoder rotation.
Final note: The code should be able to adequately detect direction, but cannot be used to increment/decrement a counter to track position. That requires more than one interrupt per cycle.
check this repository of mine on Github.
https://github.com/KingZuluKing/Rotary-Encoder-Peripheral-System
It has to be something like this, more details inside the repo.
void encode_Rotor_func()
{
lcd.clear();
Vol_Knob_stat=digitalRead(Vol_Knob);
if(Vol_Knob_stat==0)
{
Vol_Dir_stat=digitalRead(Vol_Dir);
if(Vol_Dir_stat==0 && deciBells<=9)
{
deciBells++;
lcd.setCursor(0, 0);
lcd.print("VOLUME= .");
lcd.setCursor(7, 0);
lcd.print(deciBells);
lcd.setCursor(10, 0);
lcd.print("dB's");
}
else if(Vol_Dir_stat==1 && deciBells >=-79)
{
deciBells--;
lcd.setCursor(0, 0);
lcd.print("VOLUME= .");
lcd.setCursor(7, 0);
lcd.print(deciBells);
lcd.setCursor(10, 0);
lcd.print("dB's");
}
else{
do something else etc.. etc....
}
}
}
I made a small sketch that makes clear how to interpret the encoder accordingly to the image sequence in your post:
Try this code with the equivalent pins CLK and DT connected to A2 and A1 receptively.
// Rotary Encoder Inputs
#define CLK A2
#define DT A1
int counter = 0;
String currentDir ="";
unsigned char currentPairBit = 0b0; // 8 bits
unsigned char lastPairBit = 0b0; // 8 bits
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
DDRC = 0b00000000; // Set Analog(C) encoder pins as inputs
}
void loop() {
while(true) { // while cycle is faster than loop!
// reads the analog states!
currentPairBit = PINC >> 1 & 0b11; // Gets A2(PC2) and A1(PC1) bits on C (analog input pins) (>> 1 = Jumps pin 0)
if ((lastPairBit & 0b11) != currentPairBit) {
lastPairBit = lastPairBit << 2 | currentPairBit;
// Bit Pairs Cyclic Sequence:
// 1. 2. 3. 4. 5.
// 11 | 01 | 00 | 10 | 11 for CCW
// 11 | 10 | 00 | 01 | 11 for CW
if (lastPairBit == 0b01001011 || lastPairBit == 0b10000111) {
if (lastPairBit == 0b01001011) {
currentDir = "CCW";
counter--;
} else {
currentDir = "CW";
counter++;
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
}
}
}
Then check the Serial Monitor to see how fast it is. Note that you should avoid the delay() function in your code or any other that interrupts the cycle by too much time. Consider using a second auxiliary Arduino for anything else than counting.
Resulting Serial Output:
I've been struggling with some code lately. Just like I mentioned in my question, I want to loop a single section of code under the loop() block only once. I have done my research and am aware of the two common ways to do this. One is to put the code I want to run once in the setup() block and the second being using the while(1) statement in the loop() block. Unfortunately, both ways are not suitable for my code. The first reason is it is compulsory for my code to be in the loop() section. I cannot use the second option as all the code under the loop() block ends up running once. Like I said before, I want only a section of code in the loop() block to run once.
For your information, the purpose of this code is to display in an LCD, the amount of milliliters left for the user to drink for a healthy water consumption. For example, a person should drink min 1800ml a day for proper hydration. If the user drinks 123ml of water, LCD should display (1800-123).
//Adds the liquid crystal library
#include <LiquidCrystal.h>
//defines lcd pin numbers
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// defines ultra sonic sensor pin numbers
const int trigPin = 8;
const int echoPin = 7;
// defines variables
long duration;
long volume;
long interval = 3600000; //1 hour
unsigned long stime = millis();
double pdist = 0;
double cdist = 0;
double mcons = 4.5; //128ml;
void setup() {
pinMode(echoPin, INPUT); // Sets the echoPin as an Input
pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
Serial.begin(9600); // Starts the serial communication
lcd.begin(16, 2); // Starts the lcd communication
}
void loop() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
//Converting the distance to cm
double cdist = duration / 29 / 2;
//Finding volume of the water
double volume_of_rem_height = 3.14*3*3*(cdist);
Serial.println(1800-volume_of_rem_height);
//I WANT ONLY THE BELOW STATEMENT TO BE RUN ONCE. REST ALL SHOULD CONTINUE
//TO LOOP.
lcd.print(1800-volume_of_rem_height);
The Arduino ruleas are easy:
Everything you want to be done exactly once when the Arduino is switched on, needs to go into init
Everything else neeeds to go into loop(at least, into loop's callchain.)
If you want something not done on every iteration on loop, stop the Arduino doing that by explicitly checking
Init two values of variables in init:
currentValue = lastValue = 0;
In loop, check whether something has changed from the last iteration, if yes, do the display update:
currentValue = 1800 - volume_of_rem_height
if (currentValue != lastValue) {
lastValue = currentValue;
lcd.print (currentValue)
}
This will only do the print once currentValue has really changed from the last iteration.
I have been working with an Arduino and have encountered a very strange problem. Here is the code I am using:
#include <TimerOne.h>
const int LED_PIN = 8;
const int PERIOD = 3000; // micros
void setup()
{
pinMode(LED_PIN, OUTPUT);
Timer1.initialize(PERIOD);
Timer1.attachInterrupt(sendPulse);
Serial.begin(115200);
}
void loop()
{
}
void sendPulse()
{
Serial.println(micros());
delayMicroseconds(x);
Serial.println();
}
So, I have tried changing the value of x in sendPulse(). If x is 300, for example, the Serial monitor outputs "3016 6016 9016...," as expected. However, something strange occurs when x is greater than or equal to 835 -- the Serial monitor outputs "3016 4992 7992...." My question is why is the time between the first and second interrupt not 3000? Furthermore, if I change the code within the interrupt to:
Serial.println(micros());
delayMicroseconds(x);
digitalWrite(LED_PIN, HIGH);
Serial.println();
The code acts strangely for x greater than or equal to 830, rather than 835. Why does this happen? Thank you!
According to this:
you shouldn't use Serial.print()/Serial.read() in an interrupt service routine, because latest version of Serial uses interrupts for read and write but they are disabled from within an ISR.
you shouldn't usedelay()/delayMicroseconds(), because again they make use of interrupts but they are disabled from within an ISR.. your risk making the timer miss some interrupts and loose track of the correct flow of time.
As a remark I repeat here what was said by #Unimportant in the comments: the code within an ISR should be kept as short and fast as possible.
Currently I'm working on a project where I have to read out pulses from a Arduino and check if the result is High or Low.
I had to write my own code to generate the high/low output from the Arduino:
//Pulse Generator Arduino Code
int potPin = 2; // select the input pin for the knob
int outputPin = 13; // select the pin for the output
float val = 0; // variable to store the value coming from the sensor
void setup() {
pinMode(outputPin, OUTPUT); // declare the outputPin as an OUTPUT
Serial.begin(9600);
}
void loop() {
val = analogRead(potPin); // read the value from the k
val = val/1024;
digitalWrite(outputPin, HIGH); // sets the output HIGH
delay(val*1000);
digitalWrite(outputPin, LOW); // sets the output LOW
delay(val*1000);
}
It uses a knob to change the delay between the pulses.
Im currently trying to read the high/low data with another Arduino (Lets call this one the "count Arduino") by simply connecting the 2 with the a cable from the "outputPin" to a port on the count Arduino.
I'm using digitalRead to read the port without any delay.
//Count Arduino Code
int sensorPin = 22;
int sensorState = 0;
void setup() {
pinMode(sensorPin, INPUT);
Serial.begin(9600);
}
void loop(){
sensorState = digitalRead(sensorPin);
Serial.println(sensorState);
}
First it tried with a pulse every 1 second but the result was a spam of a ton of lows and highs. Always 3 Lows and 3 highs and repeating. It wasn’t even close to one every 1 second but more like 1 every 1 millisecond.
I cant figure out what i'm doing wrong. Is it timing issue or is there a better way to detect these changes?
a spam of a ton of lows and highs
... happens if the GND of the two Arduinos are not connected.
Also, your reading arduino prints at every loop cycle, which were a few microseconds only, if the Serial buffer would not overflow.
Better printout changes only, or use a led to show what's happening.
void loop(){
static bool oldState;
bool sensorState = digitalRead(sensorPin);
if (sensorState != oldState) {
Serial.println(sensorState);
oldState = sensorState;
}
}
I have a project to control my projector via RS-232 commands, and the projector sends feedback to the arduino. Therefore I have a LCD screen with buttons. To make it nicer, I have an ultrasonic rangefinder that I want to use so that when you approach the device, the LCD backlight turns on for 30 seconds, then turns back off. I can't use a delay because I need to continue polling for buttons and serial information from the projector.
There are community libraries like Timer.h and SimpleTimer.h, but these only do oscillations, etc.
What I would like to do is this:
distance = measureUltrasonicDistance(ultrasonicPin); //returns in cm
if (distance <= 10) {
//digitalWrite(baclkightPin,HIGH);
//have this turn off 30 seconds later
}
This seems like a typical application for the event fuse library.
First thing to realize is that loop() is continuously invoked, and that you can use a "watchdog" pattern to keep the light on without using a timer.
unsigned long timeoff;
void setup() {
timeoff = millis();
}
void loop() {
distance = measureUltrasonicDistance(ultrasonicPin); //returns in cm
if (distance <= 10) {
digitalWrite(baclkightPin,HIGH);
// compute boundary of when light should be off
timeoff = millis() + 30L*1000L;
}
if (timeoff < millis()) {
digitalWrite(backlightPin, LOW);
}
}
Hope this helps, let me know if there are any problems.