Port expander (MCP23017) and setWatch #949
Replies: 12 comments
-
Posted at 2017-10-22 by @allObjects Sure, MCP23017 has a really nice event / interrupt features... Configure an interrupt, connect it to a pin and watch that pin for the press off a button. On event, scan the keyboard, set a different interrupt and watch for the release of the button. This way you preserve the most cycles. Simpler - but spending cycles - is to scan the keyboard every 20..100 ms. A simple scan will not distinguish nor prevent multi button presses. The existing module makes MCP23017 pin usage quite transparent. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-22 by n00b Thanks for the reply @allObjects - So I did a quick test, hooked up via I2C and instantiated as "port". Tried:
Got an error:
I suspect that the virtual pin is not compatible with the Pin() object the setWatch function expects... Or, did you meant hooking up setWatch differently? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-22 by n00b I went ahead and tried the interval route, and seemed to have made it work. Possible downside is the interval eating resource for other stuff, but I think I can get away with it by being smart in clearing the interval when input is safely not needed. My solution:
Would still love to hear if it is possible to use setWatch btw! |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-22 by @allObjects @n00b, sorry to be not clear enough in my initial post. The watch is not on a virtual pin made visible through the available driver (module). The watch is on an Espruino pin(s) connected to one(both) of the interrupt pin(s) INTA (and INTB) of the MCP23017 Portexpander. You still need to do some scanning similar you did with the interval, but you do only when a press happens and you do less. You do less because the you get already row (or col) by reading the register that loads the pin states at interrupt time. The current module is more for predictable/'static' behavior... When I came across the MCP23017 Portexpander, I tried to write a module that supports all options. But there are too many to generalize, and the module would become too fat. Therefore, I gave up and decided to go custom for the particular application when using it. The general logic for time multiplexed reading of up to 64 buttons is: Setup for watching press event:
On watch (interrupt) press event:
Setup for release event:
On watch (interrupt) release event:
The above pseudo code does not talk about how to preset the register for detecting the interrupt. In the setup for watch press, it is all high. For the preset of release detection, you have to determine empirically (may what was read by the interrupt capture register). Also, no cover all corner cases are covered, such as things already changed again since the interrupt. This happens especially with bouncing (and this bouncing may have to be captured differently than in the bounce option of the watches). To capture/overcome switch bouncing is not a trivial thing... see conversation about Problems with setWatch. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-22 by n00b Thanks for the detailed explanations. Seems like since I have the row and column scanning routines already, the remaining thing to do is set up the interrupt. Will this work like a switch? So if I connect INTA to say B13 on espruino, do I simply setwatch B13 and anytime button is pressed on the matrix an event will fire? Or do I need to set up something between the expander GPIO and the INTA? Much appreciated! |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-23 by @allObjects
...not exactly, but closely... Depending how you setup the interrupt, it detects a change of a pin or it detects a difference with a preset value. I can go a bit more into detail when you describe how you plan your circuitry. PortExpannder has two ways to generate an interrupt: a pin changes state (from low to high or high low) or a pin's state is different from the specification in the in register in which you write the resting state: all high (1)... the same as your inputs have with no button pressed. Then you activate the interrupt. As soon as a port changes (is pulled low by pressing the switch), PortExpander notices a difference and fires the interrupt. Take a look at the data sheet , pages 12 and following. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-23 by n00b Looking at the datasheet, seems like I need to setup the expander's interrupt pin. How can I do that? Is it by writing via I2C? I'm afraid a lot of these hardware concepts is flying over my head :) Here's how I wire up the expander, espruino, and the matrix (omitting some details such as I2C pullup resistors, power, and gnd): |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-23 by @allObjects Nice pic of the schematics! I assume you are not connecting A0..A2 to anything and are thus setting HAEN - Hardware Address Enable - bit to 0 in control byte. (This feature allows you to address 8 PortExpanders (hard-wired, and even more, 'soft-wired').
That's correct. You write to the control/config registers of the PortExpander. They are listed on p12 of the datasheet: TABLE 3-1: REGISTER ADDRESSES. This is for creating in interrupt when ever the value changes on an input pin:
Have fun... (now you may understand why a generic module would be ginormous and practically just an unnecessary repetition of what a direct config already is). |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-26 by @allObjects Tried your code as published in post #4... What ever A0..3 I connect with A4..7, the |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-27 by @allObjects @n00b, got a version with interrupt and setWatch going. I was a pretty useful experience. I had MCP23017 laying around for quite a long time... but the complexity and many options had raised the threshold to pick it up. Now it is done (for the most part). To really not miss any events or get stuck in a particular state, |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-27 by n00b @allObjects That's awesome that you made your version! I'm dying to get back to the project, but still can't touch it until next week (if only I can do this all day everyday :)). I'll give it a shot, first reimplementing the interrupt setup and handling with my code to learn, and give your 'module' a try. I'd really be interested in comparing the perf of setInterval with your implementation. I moved to a NodeMCU esp8266 for a fuller build (with 3d printed chassis) - putting Date().ms comparison at the start and the end of my scan routine with setInterva of 50ms gives me ~50ms so no hit, but when I moved to try on ESP32 (for more pins) I get 150ms. Could this be also because of I2C performance difference? Anyway, thanks so much, will try and report back. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-27 by @allObjects Really? 150ms,... I get fully handled button down-up sequences (pressTimes) done in less than 20ms... without loading the system with scanning... it all depends how shortly you can press the button and how you set the optional My scan to decode the col at interrupt event has average of two (2) - half the number of cols - I2C writes/reads - 2 writes and 1 read - with a 1 bit shift and 3 comparison (includes the loop checks)... and this all in Espruino JS. Even if you keep the polling architecture for input in your app as I showed with the
application example, you gain a lot not wasting all this scanning when nothing happens AND gain responsiveness in your application. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-22 by n00b
I'm using an 8x8 keypad matrix, to save pins I am planning on using MCP23017 port expander (I2C). Will I be able to add button listener to make this work (via setWatch)?
Thanks!
Beta Was this translation helpful? Give feedback.
All reactions