I have been recently working on developing a lab bench power supply unit and plan to use a small arduino (ATmega328p based clone board) but have ran into a very strange problem while using the switch case statement.
Below is my code. Sorry if it's poorly written and sloppy, I am no expert coder (and typically rely on hardware solutions before turning over to software solutions one example is my use of a capacitor RC network to act as a digital buffer on an input pin. You'll see what I mean if you read the code.
switch(mode){
// Adjust voltage via the function below. If it is out of range, then clamp it by overwriting the value with the maximum value.
case 0:
// I multiply velocity to it's absolute value so that I get primitive velocity control. (if you turn the pot 2X fast, the voltage will 4X faster.)
//Serial.print("Set Voltage: ");
VoltageSet += (vel * vel * vel * 0.001);
if(VoltageSet > 15){
//Serial.print("upper V threshold reached! ");
VoltageSet = 15;
}
else if(VoltageSet < 0){
//Serial.print("lower V threshold reached! ");
VoltageSet = 0;
}
break;
// Adjust current via the function below. If it is out of range, then clamp it by overwriting the value with the maximum value.
case 1:
// I multiply velocity to it's absolute value so that I get primitive velocity control. (if you turn the pot 2X fast, the voltage will 4X faster.)
//Serial.print("Set Current:");
CurrentSet += (vel * vel * vel * 0.002);
if(CurrentSet > 5){
//Serial.print("upper I threshold reached! ");
CurrentSet = 5;
}
else if(CurrentSet < 0){
//Serial.print("lower V threshold reached! ");
CurrentSet = 0;
}
break;
// select saved values
case 2: ; break;
}
The particular code of interest is line 145 and line 160.
CurrentSet += (vel * vel * vel * 0.002);
VoltageSet += (vel * vel * vel * 0.001);
It appears as if these lines do not properly execute unless I uncomment the Serial.print() command directly above them. I feel this may be an issue with the compiler not interpreting these for some strange reason. This code is implementing velocity control over a rotary encoder. "val" is the frequency of rotation, which itself is not very accurate due to it's simple implementation, but I'll worry about improving it later. If I instead assign a literal expression in place of the "val" variable, then it works fine. I can have it steadily increment the voltage or current set up and up X number of times per loop. As soon as I replace that with the variable expression it stops working, unless I place a Serial.print statement directly above it.
I could not add the original code due to a stupid character limit in the original post. here is the full code.
const byte VoltageSetPin = 10;
const byte CurrentSetPim = 9;
// mode 0 = voltage set
// mode 1 = current set
// mode 2 =
byte mode = 0;
const int pinA = 2; // One of the pins from the encoder
const int pinB = 3; // The other pin from the encoder
const int pinC = 4; // The encoder is pushable, this pin is used to switch between modes 0 and 1
const int button1 = A1; // not yet implemented
const int button2 = A2; // not yet implemented
const int button3 = A3; // not yet implemented
const int button4 = A4; // not yet implemented
bool PastValueA = LOW;
bool PastValueB = LOW;
bool ValueA = LOW;
bool ValueB = LOW;
bool ValueC = LOW;
int vel = 0;
float VoltageSet = 0;
float CurrentSet = 0;
// this code waits for the specified pin to charge back up, the time it takes will depend on the RC time constant.
void WaitForHigh(bool pin){
while(!digitalRead(pin)){
pinMode(pin, INPUT_PULLUP);
}
pinMode(pin, INPUT);
}
/*
* Events 1 through 4 are interupt handlers for the the rotary encoder. They trigger on transisions and measure the state of the other pin to determine rotation direction.
* because they will get triggered several times during the delay at the beginning, that will give my an indication of the frequency of rotation. I want to change this to instead
* calculate the amount of microseconds elapsed between triggers as this is a more accurate method (no chance of triggers not occuring during other parts of the code.
*
* switch is supposed to be faster than if/else blocks, so I used them instead.
*/
void event1(){
switch(digitalRead(pinB)){
case HIGH: vel++; break;
case LOW: vel--; break;
}
}
void event2(){
switch(digitalRead(pinA)){
case LOW: vel++; break;
case HIGH: vel--; break;
}
}
void event3(){
switch(digitalRead(pinB)){
case LOW: vel++; break;
case HIGH: vel--; break;
}
}
void event4(){
ValueA = digitalRead(pinA);
switch(digitalRead(pinA)){
case HIGH: vel++; break;
case LOW: vel--; break;
}
}
void setup() {
/* These pins are used by the encoder. I want to be able to capture every event. */
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
/*
* because I ran out of interrupts for the rest of these pins, I have added an external 0.1uF capacitor to the pins
* to act as a rough-and-ready buffer. As soon as the switch is pressed, the charged capacitor is shorted to ground
* and the result is that it will stay discharged, at least for a reasonable length of time (judged by the RC time
* constant.) This capacitor also eliminates the need for debouncing.
*
* Once the code finally gets around to digitalRead();ing the pin, the capacitor will keep the pin at a LOW
* state until after that operation and then we can recharge and reset the capacitor. A 100K or larger resistor can
* help ensure nothing bleeds away the charge of the capacitor. If the value is too low, then the capacitor may
* charge back up before the long code gets around to digitalRead()ing it. If it's too high (basically non-existent)
* then there is a change internal leakage current will cause the voltage to sag to zero leading to a false trigger.
*
* the code directly below initially charges the capacitor so the code does not start thinking that the button was pressed.
*/
pinMode(pinC, INPUT_PULLUP);
//pinMode(button1, INPUT_PULLUP);
//pinMode(button2, INPUT_PULLUP);
//pinMode(button3, INPUT_PULLUP);
//pinMode(button4, INPUT_PULLUP);
WaitForHigh(pinC);
//pinMode(pinC, INPUT);
//pinMode(button1, INPUT);
//pinMode(button2, INPUT);
//pinMode(button3, INPUT);
//pinMode(button4, INPUT);
Serial.begin(9600);
interrupts();
attachInterrupt(digitalPinToInterrupt(pinA), event1, RISING);
attachInterrupt(digitalPinToInterrupt(pinB), event2, RISING);
attachInterrupt(digitalPinToInterrupt(pinA), event3, FALLING);
attachInterrupt(digitalPinToInterrupt(pinB), event4, FALLING);
}
void loop() {
vel = 0; // it is mandatory to set the velocity back to zero after the code loops and finishes. This ensures that there is an accurate reading.
delay(100); // simulates reallly long-ass code, allows time for the interrupts to gather data and count how revolutions on the
// if you pressing the switch is detected, then first pull the pin back high quickly but safely (using INPUT_PULLUP)
// then set pinC back to INPUT (for normal operation) and
if(!digitalRead(pinC)){
WaitForHigh(pinC);
pinMode(pinC, INPUT);
switch(mode){
case 0:
mode = 1;
break;
case 1:
mode = 0;
break;
}
}
switch(mode){
// Adjust voltage via the function below. If it is out of range, then clamp it by overwriting the value with the maximum value.
case 0:
// I multiply velocity to it's absolute value so that I get primitive velocity control. (if you turn the pot 2X fast, the voltage will 4X faster.)
//Serial.print("Set Voltage: ");
VoltageSet += vel * vel * vel * 0.001;
if(VoltageSet > 15){
//Serial.print("upper V threshold reached! ");
VoltageSet = 15;
}
else if(VoltageSet < 0){
//Serial.print("lower V threshold reached! ");
VoltageSet = 0;
}
break;
// Adjust current via the function below. If it is out of range, then clamp it by overwriting the value with the maximum value.
case 1:
// I multiply velocity to it's absolute value so that I get primitive velocity control. (if you turn the pot 2X fast, the voltage will 4X faster.)
//Serial.print("Set Current:");
CurrentSet += vel * vel * vel * 0.002;
if(CurrentSet > 5){
//Serial.print("upper I threshold reached! ");
CurrentSet = 5;
}
else if(CurrentSet < 0){
//Serial.print("lower V threshold reached! ");
CurrentSet = 0;
}
break;
// select saved values
case 2: ; break;
}
Serial.println(/*"pinC: " + String(ValueC) + */ "\t vel:" + String(vel) + '\t' + "Mode " + String(mode) + "\t Set Voltage: " + String(VoltageSet) + "\t Set Current: " + String(CurrentSet));
}
Apologies for not posting the code, this forum has too many limitations for newcomers. (5 minutes between posts!?!? 9000 Char limit!?!?!?! Ridiculous. >:( >:( EEVblog forum >> Arduino forum.)
"Post" does not necessarily mean just Copy & Paste.
"Post" also includes Reply, then scroll down and use the Attach button.
5 minutes goes away after 50 or 100 posts. It remains in the meantime to cut down on spammers pushing sneakers and muscle enhancers and sexual performance pills and Russian mail order dates and ...
Without it, we moderators cannot keep up on banning and deleting non-relevant posts. Grin and bear it for a bit.
// I multiply velocity to it's absolute value so that I get primitive velocity control.
...
VoltageSet += vel * vel * vel * 0.001;
The cube of a number does not calculate "it's absolute value". If vel is negative, then "vel * vel * vel * 0.001" is also negative.
Furthermore, if vel is greater than 31, "velvelvel" will exceed the capacity of a 16-bit int and give you rubbish for an answer.
Pete
P.S. If you really do want only the absolute value of the velocity, try this:
The cube of a number does not calculate "it's absolute value". If vel is negative, then "vel * vel * vel * 0.001" is also negative.
Furthermore, if vel is greater than 31, "velvelvel" will exceed the capacity of a 16-bit int and give you rubbish for an answer.
I know, that is the behavior I want. Otherwise the encoder counts up regardless of the direction of rotation. Like I said, it works if I have Serial.print("blah blah whatever"); or other logic directly above it. IDK why.
31^3 = 29791, yeah that gets close to the limits of int, I'll change that over to long and clamp the value of "val." But so far it does not seem to be an issue. Vel never reaches 31, I've only seen it go up to around 6 or so. Might change val to a byte datatype.
CrossRoads:
"Post" does not necessarily mean just Copy & Paste.
"Post" also includes Reply, then scroll down and use the Attach button.
5 minutes goes away after 50 or 100 posts. It remains in the meantime to cut down on spammers pushing sneakers and muscle enhancers and sexual performance pills and Russian mail order dates and ...
Without it, we moderators cannot keep up on banning and deleting non-relevant posts. Grin and bear it for a bit.
Yeah, I understand. I sure hope it goes away soon. I can't even edit posts right after posting them as a result of this limitation. I wonder why the EEVblog forum does not have problems with spammers...
How can I configure this forum to email/notify me of replies via email? esp by default?
And I hope my IP address <---[over there] is not broadcasted for everyone to see, what a security problem if it is! If it is, then Tor browser here I come! (except it's too late for that lol)
OK, so what does "I multiply velocity to it's absolute value" actually mean?
I doubt that this will work. I don't know what happens, never tried it, but I suspect that the last attachInterrupt is the one which will take effect (in this case pinA will be event3 FALLING and pinB will be event4 FALLING.
OK, so what does "I multiply velocity to it's absolute value" actually mean?
I doubt that this will work. I don't know what happens, never tried it, but I suspect that the last attachInterrupt is the one which will take effect (in this case pinA will be event3 FALLING and pinB will be event4 FALLING.
Code: [Select]
attachInterrupt(digitalPinToInterrupt(pinA), event1, RISING);
attachInterrupt(digitalPinToInterrupt(pinB), event2, RISING);
attachInterrupt(digitalPinToInterrupt(pinA), event3, FALLING);
attachInterrupt(digitalPinToInterrupt(pinB), event4, FALLING);
If you want an interrupt on either edge, you should use CHANGE and then sort out in the interrupt routine whether it was rising or falling.
Code: [Select]
int vel = 0;
This is accessed from within interrupt routines and must be declared volatile:
Code: [Select]
volatile int vel = 0;
I was not sure if it would work either. It appeared to work, but I think your right, I need to play with the code some more. It might be that only the top 2 have any effect and my encoder is running with half its maximum resolution. Not that it matters at the moment. I can get away with only 1 interrupt routine running.
I will give volatile data type modifier a try (and please do correct me if I use incorrect CS vocab, I did learn a lot of the vocab by taking a functional programming class (CS1113) at UVa.) BTW, what is the purpose of it? What does it do?
Now I need to get the PWM analogWrite() function to run faster (faster PWM) and achieve an accurate 12 bit or even 16 bit resolution.
Is there any way to make analogRead() run at higher than 10 bits on ATmega328? I don't feel like converting my entire arduino with an additional PWM, a comparator, and other analog/digital parts into into a successive approximation DAC
Also what sort of accuracy can I expect from changing the registers for the counting of the PWM to allow higher resolution? I'd imagine that also slows down the analog output too?
Powermax:
31^3 = 29791, yeah that gets close to the limits of int, I'll change that over to long and clamp the value of "val." But so far it does not seem to be an issue. Vel never reaches 31, I've only seen it go up to around 6 or so. Might change val to a byte datatype.
The "subject" has already been solved. But I do have other questions regarding arduino. This forum should probably be moved to a different section, but IDK how.