步驟1:準(zhǔn)備工作
如果還沒(méi)有,請(qǐng)參閱我的其他Instructable旋轉(zhuǎn)編碼器閱讀,以了解如何設(shè)置硬件和Arduino IDE軟件。
硬件
圖片中顯示了需要使用中心按鈕的其他硬件連接。我使用Fritzing繪制了圖表,但它沒(méi)有代表最可能的引腳布局的旋轉(zhuǎn)編碼器組件,因此只需將該圖表與注釋結(jié)合使用,然后查看旋轉(zhuǎn)編碼器的照片,即可了解更多內(nèi)容。可能正在尋找旋轉(zhuǎn)編碼器引腳布局方面的信息。
旋轉(zhuǎn)編碼器一側(cè)(與具有三個(gè)引腳的一側(cè)相對(duì))的兩個(gè)引腳之一需要接地,而另一端則要連接到Arduino的數(shù)字引腳。我已將D4用于示例草圖。如果您選擇其他引腳,請(qǐng)不要忘記更改草圖中的 buttonPin 的值。
接下來(lái)是步驟2中的代碼。
步驟2:代碼
這是代碼。通過(guò)查看結(jié)構(gòu)和評(píng)論,我希望您會(huì)發(fā)現(xiàn)很容易適應(yīng)您的特定需求!
/*******Interrupt-based Rotary Encoder Menu Sketch*******
* by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt and Steve Spence, and code from Nick Gammon
* 3,638 bytes with debugging on UNO, 1,604 bytes without debugging
*/
// Rotary encoder declarations
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let‘s us know when we’re expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let‘s us know when we’re expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
// Button reading, including debounce without delay function declarations
const byte buttonPin = 4; // this is the Arduino pin we are connecting the push button to
byte oldButtonState = HIGH; // assume switch open because of pull-up resistor
const unsigned long debounceTime = 10; // milliseconds
unsigned long buttonPressTime; // when the switch last changed state
boolean buttonPressed = 0; // a flag variable
// Menu and submenu/setting declarations
byte Mode = 0; // This is which menu mode we are in at any given time (top level or one of the submenus)
const byte modeMax = 3; // This is the number of submenus/settings you want
byte setting1 = 0; // a variable which holds the value we set
byte setting2 = 0; // a variable which holds the value we set
byte setting3 = 0; // a variable which holds the value we set
/* Note: you may wish to change settingN etc to int, float or boolean to suit your application.
Remember to change “void setAdmin(byte name,*BYTE* setting)” to match and probably add some
“modeMax”-type overflow code in the “if(Mode == N && buttonPressed)” section*/
void setup() {
//Rotary encoder section of setup
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the “PinA” Interrupt Service Routine (below)
attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the “PinB” Interrupt Service Routine (below)
// button section of setup
pinMode (buttonPin, INPUT_PULLUP); // setup the button pin
// DEBUGGING section of setup
Serial.begin(9600); // DEBUGGING: opens serial port, sets data rate to 9600 bps
}
void loop() {
rotaryMenu();
// carry out other loop code here
}
void rotaryMenu() { //This handles the bulk of the menu functions without needing to install/include/compile a menu library
//DEBUGGING: Rotary encoder update display if turned
if(oldEncPos != encoderPos) { // DEBUGGING
Serial.println(encoderPos);// DEBUGGING. Sometimes the serial monitor may show a value just outside modeMax due to this function. The menu shouldn‘t be affected.
oldEncPos = encoderPos;// DEBUGGING
}// DEBUGGING
// Button reading with non-delay() debounce - thank you Nick Gammon!
byte buttonState = digitalRead (buttonPin);
if (buttonState != oldButtonState){
if (millis () - buttonPressTime 》= debounceTime){ // debounce
buttonPressTime = millis (); // when we closed the switch
oldButtonState = buttonState; // remember for next time
if (buttonState == LOW){
Serial.println (“Button closed”); // DEBUGGING: print that button has been closed
buttonPressed = 1;
}
else {
Serial.println (“Button opened”); // DEBUGGING: print that button has been opened
buttonPressed = 0;
}
} // end if debounce time up
} // end of state change
//Main menu section
if (Mode == 0) {
if (encoderPos 》 (modeMax+10)) encoderPos = modeMax; // check we haven’t gone out of bounds below 0 and correct if we have
else if (encoderPos 》 modeMax) encoderPos = 0; // check we haven‘t gone out of bounds above modeMax and correct if we have
if (buttonPressed){
Mode = encoderPos; // set the Mode to the current value of input if button has been pressed
Serial.print(“Mode selected: ”); //DEBUGGING: print which mode has been selected
Serial.println(Mode); //DEBUGGING: print which mode has been selected
buttonPressed = 0; // reset the button status so one press results in one action
if (Mode == 1) {
Serial.println(“Mode 1”); //DEBUGGING: print which mode has been selected
encoderPos = setting1; // start adjusting Vout from last set point
}
if (Mode == 2) {
Serial.println(“Mode 2”); //DEBUGGING: print which mode has been selected
encoderPos = setting2; // start adjusting Imax from last set point
}
if (Mode == 3) {
Serial.println(“Mode 3”); //DEBUGGING: print which mode has been selected
encoderPos = setting3; // start adjusting Vmin from last set point
}
}
}
if (Mode == 1 && buttonPressed) {
setting1 = encoderPos; // record whatever value your encoder has been turned to, to setting 3
setAdmin(1,setting1);
//code to do other things with setting1 here, perhaps update display
}
if (Mode == 2 && buttonPressed) {
setting2 = encoderPos; // record whatever value your encoder has been turned to, to setting 2
setAdmin(2,setting2);
//code to do other things with setting2 here, perhaps update display
}
if (Mode == 3 && buttonPressed){
setting3 = encoderPos; // record whatever value your encoder has been turned to, to setting 3
setAdmin(3,setting3);
//code to do other things with setting3 here, perhaps update display
}
}
// Carry out common activities each time a setting is changed
void setAdmin(byte name, byte setting){
Serial.print(“Setting ”); //DEBUGGING
Serial.print(name); //DEBUGGING
Serial.print(“ = ”); //DEBUGGING
Serial.println(setting);//DEBUGGING
encoderPos = 0; // reorientate the menu index - optional as we have overflow check code elsewhere
buttonPressed = 0; // reset the button status so one press results in one action
Mode = 0; // go back to top level of menu, now that we’ve set values
Serial.println(“Main Menu”); //DEBUGGING
}
//Rotary encoder interrupt service routine for one encoder pin
void PinA(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB‘s values
if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin’s rising edge
encoderPos --; //decrement the encoder‘s position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00000100) bFlag = 1; //signal that we’re expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
//Rotary encoder interrupt service routine for the other encoder pin
void PinB(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB‘s values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin’s rising edge
encoderPos ++; //increment the encoder‘s position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00001000) aFlag = 1; //signal that we’re expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
// end of sketch!
在任何對(duì)菜單執(zhí)行操作都不重要的行的每個(gè)注釋的開(kāi)頭,我都使用了“ DEBUGGING”。如果您對(duì)菜單功能滿意,則可能需要注釋掉或刪除這些行以縮小編譯的草圖尺寸。
請(qǐng)注意,菜單導(dǎo)航的關(guān)鍵部分是在用戶滾動(dòng)瀏覽選項(xiàng)和設(shè)置時(shí)反饋給用戶。因此,如果您選擇不包括DEBUGGING行,則可能應(yīng)該使用另一個(gè)可視指示器(例如LCD文本顯示器,LED),編碼器輸入正在導(dǎo)航菜單并更改設(shè)置。
如果我注釋掉DEBUGGING行(注意,菜單導(dǎo)航仍需要一些視覺(jué)反饋),Arduino Uno的編譯后代碼約為1,650字節(jié),希望在ATMEGA328P上留出足夠的空間以容納草圖中更令人興奮的部分!
轉(zhuǎn)到步驟3,了解菜單系統(tǒng)的工作原理。
步驟3:操作和結(jié)論
操作
如果上傳此草圖后在Arduino中打開(kāi)串行監(jiān)視器,并開(kāi)始轉(zhuǎn)動(dòng)編碼器軸,則應(yīng)該看到頂層菜單在子菜單/選項(xiàng)數(shù)中旋轉(zhuǎn)您擁有(使用 modeMax 變量進(jìn)行限制)。如果按中間的按鈕,您會(huì)看到已選擇要滾動(dòng)到的模式/子菜單,現(xiàn)在可以自由選擇要滾動(dòng)瀏覽該子菜單中的0-255值。現(xiàn)在,如果您按下中央按鈕,則將其設(shè)置為 setting1 或 setting2 或 setting3 等。Arduino自動(dòng)并立即返回
上電后,Arduino會(huì)記住您將每個(gè)設(shè)置設(shè)置為什么,并且如果您返回子菜單以獲取設(shè)置,則您已經(jīng)設(shè)置了一個(gè)值到此為止,它將從您選擇的最后一個(gè)值開(kāi)始進(jìn)行編碼器調(diào)整!
結(jié)論
我著手編寫一些基于草圖的代碼,旋轉(zhuǎn)編碼器導(dǎo)航Arduino的基本菜單。我還嘗試使其具有可讀性,以便與某些替代方案不同,有人可以看到菜單結(jié)構(gòu),并知道他們需要進(jìn)行哪些更改才能根據(jù)自己的需要定制菜單。
此代碼是基本的和通用的,專門用于演示功能,同時(shí)易于適應(yīng)您自己的應(yīng)用程序。它使用串行監(jiān)視器作為基本的調(diào)試工具,如果您想查看代碼的工作原理,也不需要單獨(dú)顯示。希望您發(fā)現(xiàn)它有用,并受到啟發(fā)進(jìn)行編輯,修改和改進(jìn)!
責(zé)任編輯:wv
-
編碼器
+關(guān)注
關(guān)注
45文章
3664瀏覽量
135051 -
Arduino
+關(guān)注
關(guān)注
188文章
6477瀏覽量
187594
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論