I have been recently working on developing a lab bench power supply unit (thread found here:
https://www.eevblog.com/forum/projects/anything-wrong-with-this-linear-psu-design/) 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.
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 encoder
// 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));
}
More specifically, here is the section I am having a problem with:
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.
Code: [Select]
CurrentSet += (vel * vel * vel * 0.002);
Code: [Select]
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 don't get it...
Any ideas?