Here is the second article of the series about reading rotary encoders.
In this section we will explore reading of 15 rotary encoders with Atmega1284 and Arduino IDE.
15 is the maximum of encoders that can be connected to the microcontroller, considering that we have to keep free some free pins to communicate results outside microcontroller.I chose to keep free the serial port pins PD0(RX) and PD1(TX) equivalent to the D8 si D9 in my Arduino setup. (you can read more about using Atmega1284 with Arduino IDE here).
My choice for the serial port was determined by the ease of connection and debugging.
This module can be used in a larger project or can be used standalone (almost) connecting RX and TX to an USB – MIDI interface like this
Schematic
The circuit diagram is made with gEDA. Outputs A and B are connected in pairs to the microcontroller ports. Serial port pins D8 and D9 (PD0 and PD1) are connected to J2 header along with VDD and GND.
In the following I will present my experimental setup for this schematic. It is made on a cheap 18x12cm prototype board. It may look ugly but represents the first stage to test the code and see if it really works as expected. This prototype will be the subject of an article on Instructables; there I will detail step by step making of this board.
Here are preview of PCB as has been designed in gEDA suite. It’s almost done but I will not put design files here, before we making first PCB (to be sure that everything is ok). Afterwards, all design files (geda and gerber) will be available to download.
Code
The code is based on finite state-machine (FSM) which was presented in my previous article. Actually I replicated one FSM to 15 (not transition table, only state variables).
The code was written to be achieved two goals:
- Interruptions to be executed as fast as possible
- Code to be easily read and understood
I chose to do not make an Arduino library. Even if the module is developed in Arduino is designed as an add-on to an Arduino project. Making a library would be worth to use this module as a shield, not in own firmware module (At least I see this way at this time).
I was in doubt whether to post the whole code in article body, but I tried to comment code as best I could in a way that code complete article text.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
/* File : megaEncoders.ino Version : 1.0 Date : 25.02.2015 Project : MegaEncoders 1.0 The MIT License (MIT) Copyright (c) 2015 Silviu - www.openhardware.ro Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* ====== definitions for encoder FSM (finite state machine) ===== */ /* *** states *** */ #define START 0x0 #define CW_Step1 0x1 #define CW_Step2 0x2 #define CW_Step3 0x3 #define CCW_Step1 0x4 #define CCW_Step2 0x5 #define CCW_Step3 0x6 /* *** flags *** */ #define CW_Flag 0b10000000 // ClockWise flag (bit 7) #define CCW_Flag 0b01000000 // CounterClokWise flag (bit 6) // transition table for encoder FSM const byte transition_table[7][4] = { /* 00 01 10 11 */ /* -----------------------------------------------------*/ /*START | */ {START, CCW_Step1, CW_Step1, START}, /*CW_Step1 | */ {CW_Step2, START, CW_Step1, START}, /*CW_Step2 | */ {CW_Step2, CW_Step3, CW_Step1, START}, /*CW_Step3 | */ {CW_Step2, CW_Step3, START, START | CW_Flag}, /*CCW_Step1 | */ {CCW_Step2, CCW_Step1, START, START}, /*CCW_Step2 | */ {CCW_Step2, CCW_Step1, CCW_Step3, START}, /*CCW_Step3 | */ {CCW_Step2, START, CCW_Step3, START | CCW_Flag} }; #define midi_min_value 0 // lowest value for MIDI control #define midi_max_value 127 // highest value for MIDI control #define nr_encoders 15 byte current_state[nr_encoders]; // global variable for encoders state // read-write only in ISR volatile byte counter[nr_encoders]; // global variable // read-write in ISR, read in main loop byte old_counter[nr_encoders]; byte pins_input[nr_encoders]; // channel 1 for first 10 encoders, channel 2 for last 5 encoders byte midi_channel[nr_encoders] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2}; // midi control (last five encoders emit on channel 2) byte midi_control[nr_encoders] = {15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 15, 16 ,17, 18, 19}; void setup() { // put your setup code here, to run once:; for (byte i=0; i < nr_encoders; i++) { // init all FSM with START state (defined as 0) current_state[i]=START; // init all old_counters with defined minimum values old_counter[i]=midi_min_value; // init all counters with defined minimum values counter[i]=midi_min_value; } // setting all pins for input with pull-up (exception RX and TX) setupPins(); // setting interrupt PCINT for all pins (exception RX and TX) setupPCINT(); Serial.begin(31250); // standard MIDI BAUD // can be changed if module is used in // other configuration //next two lines was used in debugging stage //Serial.begin(115200); //Serial.println("\r\nInit ok."); } void loop() { // put your main code here, to run repeatedly: for (byte i=0; i < nr_encoders; i++) { if (old_counter[i] != counter[i]) // counter value is changed { old_counter[i]=counter[i]; //Serial.print(i); //Serial.print(" : "); //Serial.println(old_counter[i]); // channel controller value SendMIDIControl(midi_channel[i], midi_control[i], old_counter[i]); } } } ISR(PCINT0_vect) { // not used digitalRead for speed reason byte pins_input_; pins_input_=PINA; pins_input[0] = pins_input_ & 0b00000011; pins_input[1] = pins_input_ & 0b00001100; pins_input[1] = pins_input[1] >> 2; pins_input[2] = pins_input_ & 0b00110000; pins_input[2] = pins_input[2] >> 4; pins_input[3] = pins_input_ & 0b11000000; pins_input[3] = pins_input[3] >> 6; current_state[0] = transition_table[current_state[0] & 0b00000111][pins_input[0]]; current_state[1] = transition_table[current_state[1] & 0b00000111][pins_input[1]]; current_state[2] = transition_table[current_state[2] & 0b00000111][pins_input[2]]; current_state[3] = transition_table[current_state[3] & 0b00000111][pins_input[3]]; if ((current_state[0] & CW_Flag) && (counter[0] <midi_max_value)) counter[0]++; if ((current_state[0] & CCW_Flag) && (counter[0] >midi_min_value)) counter[0]--; if ((current_state[1] & CW_Flag) && (counter[1] <midi_max_value)) counter[1]++; if ((current_state[1] & CCW_Flag) && (counter[1] >midi_min_value)) counter[1]--; if ((current_state[2] & CW_Flag) && (counter[2] <midi_max_value)) counter[2]++; if ((current_state[2] & CCW_Flag) && (counter[2] >midi_min_value)) counter[2]--; if ((current_state[3] & CW_Flag) && (counter[3] <midi_max_value)) counter[3]++; if ((current_state[3] & CCW_Flag) && (counter[3] >midi_min_value)) counter[3]--; } ISR(PCINT1_vect) { byte pins_input_; pins_input_=PINB; pins_input[4] = pins_input_ & 0b00000011; pins_input[5] = pins_input_ & 0b00001100; pins_input[5] = pins_input[5] >> 2; pins_input[6] = pins_input_ & 0b00110000; pins_input[6] = pins_input[6] >> 4; pins_input[7] = pins_input_ & 0b11000000; pins_input[7] = pins_input[7] >> 6; current_state[4] = transition_table[current_state[4] & 0b00000111][pins_input[4]]; current_state[5] = transition_table[current_state[5] & 0b00000111][pins_input[5]]; current_state[6] = transition_table[current_state[6] & 0b00000111][pins_input[6]]; current_state[7] = transition_table[current_state[7] & 0b00000111][pins_input[7]]; if ((current_state[4] & CW_Flag) && (counter[4] <midi_max_value)) counter[4]++; if ((current_state[4] & CCW_Flag) && (counter[4] >midi_min_value)) counter[4]--; if ((current_state[5] & CW_Flag) && (counter[5] <midi_max_value)) counter[5]++; if ((current_state[5] & CCW_Flag) && (counter[5] >midi_min_value)) counter[5]--; if ((current_state[6] & CW_Flag) && (counter[6] <midi_max_value)) counter[6]++; if ((current_state[6] & CCW_Flag) && (counter[6] >midi_min_value)) counter[6]--; if ((current_state[7] & CW_Flag) && (counter[7] <midi_max_value)) counter[7]++; if ((current_state[7] & CCW_Flag) && (counter[7] >midi_min_value)) counter[7]--; } ISR(PCINT2_vect) { byte pins_input_; pins_input_=PINC; pins_input[8] = pins_input_ & 0b00000011; pins_input[9] = pins_input_ & 0b00001100; pins_input[9] = pins_input[9] >> 2; pins_input[10] = pins_input_ & 0b00110000; pins_input[10] = pins_input[10] >> 4; pins_input[11] = pins_input_ & 0b11000000; pins_input[11] = pins_input[11] >> 6; current_state[8] = transition_table[current_state[8] & 0b00000111][pins_input[8]]; current_state[9] = transition_table[current_state[9] & 0b00000111][pins_input[9]]; current_state[10] = transition_table[current_state[10] & 0b00000111][pins_input[10]]; current_state[11] = transition_table[current_state[11] & 0b00000111][pins_input[11]]; if ((current_state[8] & CW_Flag) && (counter[8] <midi_max_value)) counter[8]++; if ((current_state[8] & CCW_Flag) && (counter[8] >midi_min_value)) counter[8]--; if ((current_state[9] & CW_Flag) && (counter[9] <midi_max_value)) counter[9]++; if ((current_state[9] & CCW_Flag) && (counter[9] >midi_min_value)) counter[9]--; if ((current_state[10] & CW_Flag) && (counter[10] <midi_max_value)) counter[10]++; if ((current_state[10] & CCW_Flag) && (counter[10] >midi_min_value)) counter[10]--; if ((current_state[11] & CW_Flag) && (counter[11] <midi_max_value)) counter[11]++; if ((current_state[11] & CCW_Flag) && (counter[11] >midi_min_value)) counter[11]--; } ISR(PCINT3_vect) { byte pins_input_; pins_input_=PIND; pins_input[12] = pins_input_ & 0b00001100; pins_input[12] = pins_input[12] >> 2; pins_input[13] = pins_input_ & 0b00110000; pins_input[13] = pins_input[13] >> 4; pins_input[14] = pins_input_ & 0b11000000; pins_input[14] = pins_input[14] >> 6; current_state[12] = transition_table[current_state[12] & 0b00000111][pins_input[12]]; current_state[13] = transition_table[current_state[13] & 0b00000111][pins_input[13]]; current_state[14] = transition_table[current_state[14] & 0b00000111][pins_input[14]]; if ((current_state[12] & CW_Flag) && (counter[12] <midi_max_value)) counter[12]++; if ((current_state[12] & CCW_Flag) && (counter[12] >midi_min_value)) counter[12]--; if ((current_state[13] & CW_Flag) && (counter[13] <midi_max_value)) counter[13]++; if ((current_state[13] & CCW_Flag) && (counter[13] >midi_min_value)) counter[13]--; if ((current_state[14] & CW_Flag) && (counter[14] <midi_max_value)) counter[14]++; if ((current_state[14] & CCW_Flag) && (counter[14] >midi_min_value)) counter[14]--; } void setupPCINT() { //PCICR – Pin Change Interrupt Control Register // PCIE0 PCIE1 PCIE2 PCIE3 PCICR=0; PCICR|=(1 << PCIE3) | (1 << PCIE2) | (1 << PCIE1) | (1 << PCIE0); PCMSK0=0; PCMSK1=0; PCMSK2=0; PCMSK3=0; PCMSK0 |= (1 << PCINT7) | (1 << PCINT6) | (1 << PCINT5) | (1 << PCINT4) | (1 << PCINT3) | (1 << PCINT2) | (1 << PCINT1) | (1 << PCINT0); PCMSK1 |= (1 << PCINT15) | (1 << PCINT14) | (1 << PCINT13) | (1 << PCINT12) | (1 << PCINT11) | (1 << PCINT10) | (1 << PCINT9) | (1 << PCINT8); PCMSK2 |= (1 << PCINT23) | (1 << PCINT22) | (1 << PCINT21) | (1 << PCINT20) | (1 << PCINT19) | (1 << PCINT18) | (1 << PCINT17) | (1 << PCINT16); PCMSK3 |= (1 << PCINT31) | (1 << PCINT30) | (1 << PCINT29) | (1 << PCINT28) | (1 << PCINT27) | (1 << PCINT26);// | (1 << PCINT25) | (1 << PCINT24) // => RX and TX pins } // setting all pins for input with pull-up (exception RX and TX) void setupPins() { DDRA=0b11111111; // Set entire PortA to input PORTA=0b11111111;// Activate pullup for entire PortA DDRB=0b11111111; // Set entire PortB to input PORTB=0b11111111;// Activate pullup for entire PortB DDRC=0b11111111; // Set entire PortC to input PORTC=0b11111111;// Activate pullup for entire PortC DDRD= 0b11111100; // Set PD2-PD7 to input PORTD=0b11111100; // Activate pullup for PD2-PD7 } void SendMIDIControl(byte channel, byte controller, byte value) { //0= channe l; 1=channel 2; ... byte tmpChannel = (channel & 0b00001111)-1; //midi data first bit allways 1, + 011 control change command + midi channel tmpChannel = 0b10110000 + tmpChannel; //midi data first bit allways 0 byte tmpController = controller & 0b01111111; //midi data first bit allways 0 byte tmpValue = value & 0b01111111; // write midi data in order: channel, controller, value Serial.write(tmpChannel); Serial.write(tmpController); Serial.write(tmpValue); } |
Download schematic and code: megaEncoders v1.
1 Comment
Add a Comment