How to switch between interrupt input and analog input on the same pin?

I want to connect a “one pin” 12key keypad to my Mbed board. The keypad sends a High pulse when a key is pressed, after some mS, the signal falls down to a level that is determent by the key pressed. It falls to zero when the key is released.

I have some code that get an interrupt each time a key is pressed, when this interrupt come, I want to keep the pull down resistor and read the analog value on the same pin that gave the interrupt.
And thereafter I want to set the pin back to detect a new interrupt (with the pull down resistor).

How can I do this with Mbed?

The HW and SW is tested, and works very well on another OS, but I want to use Mbed, and I want it to be usable on boards with different hardware (STM and nRF52832 MCU. The chosen pin, of course, have to support both interrupt and analog in, plus pull down resistor).

Is this possible with Mbed?

In other words, can I change the pin configuration / functionality “on the fly”?

I think the code could run on any board with a key input and analog input on the same pin (but the analog value wil not mean anything without my keypad).

One more question:
What do I have to do with the serial line (in my examle code) to enable Mbed to enter deep sleep mode?

Examle code can be found hear: GitHub - joett/mbed_test
(Did not know any other way to publish it…)

Hello Jan,

Can I change the pin configuration / functionality “on the fly”?

Try to create the InterruptIn and AnalogIn dynamically on the heap rather than as static variables. Keep global only the associated pointers. Then you’ll be able to delete and recreate them as needed. I tested your code with such modification on an mbed LPC1768 board. Since I didn’t have your special keyboard I connected one end of a push button to pin p15 and the other end over a 4k7 resistor to the +3.3V rail:

...
#define TRACE_GROUP "main"
#define KBD_IN      p15

InterruptIn  *kbd_int;    // Global pointer to an InterruptIn
AnalogIn     *ainn;       // Global pointer to an AnalogIn

DigitalOut led1(LED1);
DigitalOut led2(LED2);

EventQueue kbd_event_q(3 * EVENTS_EVENT_SIZE);     // Let kbd get its own EventQueue
Thread kbd_tread;                                  // Let kbd get its own Thread
...
int decode_kbd()
{
    // 	Her I want to change the pin mode to Analog In
    // 	But I find no way to do it correctly
    //  AnalogIn ainn(KBD_IN);      // When I do this, I get only one interrupt.
    ainn = new AnalogIn(KBD_IN);    // Create a new 'ainn' AnalogIn object on the heap connected to the 'KBD_IN' pin
    uint16_t adc_sample;

    thread_sleep_for(10);    // 10ms

    // 	Her I want to Read the Analog value from the same pin that gave the interrupt
    //     adc_sample = ainn.read_u16();
    adc_sample = ainn->read_u16();
    delete ainn;            // Delete the 'ainn' AnalogIn object from the heap

    thread_sleep_for(3);    // 3ms (this is probably not needed)

    // 	Her I want to change the pin mode to interrupt In and continue reciveing interrupts
    // 	But I find no way to do it correctly
    //     InterruptIn kbd_int(KBD_IN, PullDown);	// This is most likely Wrong, and have no effect
    //  The 'kbd_int' object could be re-created here.
    //  But I prerere to do it in the function where it was deleted (i.e. in 'kbd_int_handler')

    return(adc_sample);
}

void kbd_int_handler()
{
    kbd_event_q.break_dispatch();                       // Forces event queue's dispatch loop to terminate.
    delete kbd_int;                                     // Delete the kbd_int InterruptIn object from the heap
    led1 = !led1;
    printf("\n **** kbd_int() **** %d\n", decode_kbd()    /*, Thread::gettid()*/);
    thread_sleep_for(300);    // debounce the KBD_INT key (wait untill it stops bouncing) - may not be needed for your hadware/keyboard
    kbd_int = new InterruptIn(KBD_IN, PullDown);        // Create a new kbd_int InterruptIn object on the heap
    kbd_event_q.dispatch_forever();                     // Dispatch events without a timeout.
    kbd_int->rise(kbd_event_q.event(kbd_int_handler));  // Attach this function to handle the kbd_int's next 'rise' event
}

int main(void)
{
    ...
    kbd_int = new InterruptIn(KBD_IN, PullDown);        // Create a new 'kbd_int' InterruptIn object on the heap
    kbd_tread.start(callback(&kbd_event_q, &EventQueue::dispatch_forever));        // Let kbd få get its own Thread
    kbd_int->rise(kbd_event_q.event(kbd_int_handler));  // The 'rise' handler 'kbd_int_handler' will execute in the context of 'kbd_tread'
    ...
}

Below is the printout on Termite 3.4 serial terminal:


[1B][2J[1B][7hARM Ltd

[1B][2K/> [1B][1D
[1B][2J[1B][7hARM Ltd

[1B][2K/> [1B][1D
 ***** mbed_kbd_test *****

 **** kbd_int() **** 65535

 **** kbd_int() **** 65535

 **** kbd_int() **** 65535

What do I have to do with the serial line (in my example code) to enable Mbed to enter deep sleep mode?

Unfortunately, I have no answer to that question. Maybe this thread can help you a bit.

Thank you very much for your guidance, it helped to demystify Mbed a bit.
I tried your code, but I did not manage to read more than one value from the adc. I tried to rearrange it a bit and put in a command to toggle the reading of the adc.
I see that when the ADC reading is off, I get interrupt for every key-press I do. When I turn on ADC reading (with the “ta” command), I get one more interrupt and the adc reading is as expected (different reading for different keys). I also made a while() loop so the tread is sleeping until the key is released, this also work.
When the key is released and the interrupt should be turned on again, nothing more happen, even though the command interpreter continue running.
If this work on the LPC1768 board, I can not understand other than that must be something wrong with the nRF52 stuff in Mbed.
As you may have noticed, I’m new to Mbed, and I’m not very comfortable with C++ either, but I have some 30 years experience with C on many different systems. But I’m struggling with Mbed (feel kind of outdated).
The single one reason for using Mbed is the LoRaWan stack, and the Mbed LoRaWan example run without problems on my board.
The board is the Telenor E002 / E004 board, it has a dual-band (868 and 433 MHz) LoRa radio and the BLE radio on the nRF52832.
https://shop.exploratory.engineering/products/ee-starter-pack-bundle
In some way I hope it is my lack of Mbed experience rather then the nRF52 Mbed implementation that is the reason for the problems.
I have updated the code I’m struggling with in the “github_repo”

By the way; here is an img of the kbd.

Her is the output from the terminal:

ARM Ltd
/>
***** mbed_kbd_test *****

**** Enter kbd_int_handler(); ****
**** Leaving kbd_int_handler(); ****

**** Enter kbd_int_handler(); ****
**** Leaving kbd_int_handler(); ****

**** Enter kbd_int_handler(); ****
**** Leaving kbd_int_handler(); ****

**** Enter kbd_int_handler(); ****
**** Leaving kbd_int_handler(); ****

**** Enter kbd_int_handler(); ****
**** Leaving kbd_int_handler(); ****
/>ta
adc_flagg = 1
/>
**** Enter kbd_int_handler(); ****
Start adc
ADC = 33527
Key release 20004628, 96 : 176
**** Leaving kbd_int_handler(); ****

After the “ta” command, I receive one interrupt, the analog value is read, and no more interrupt can be received.

To avoid forcing dispatch loop termination and restarting try the following:

...
EventQueue kbd_event_q(3 * EVENTS_EVENT_SIZE);  // Let kbd get its own EventQueue
Thread kbd_tread;                               // Let kbd get its own Thread
void on_kbd_int_rise();                         // function prototype
...
void kbd_int_handler()
{
    AnalogIn     *ainn;
    uint16_t adc_sample = 0;

    printf("\n **** Enter kbd_int_handler(); ****\n");

    delete kbd_int;                   // Delete the kbd_int InterruptIn object from the heap
    led1 = !led1;

    if(adc_flagg){
        printf("\tStart adc\n");
        ainn = new AnalogIn(KBD_IN);   // Create a new 'ainn' AnalogIn object on the heap connected to the 'KBD_IN' pin

        thread_sleep_for(10);          // 10ms for the kbd signal to stabilize

        printf("\tADC = %d\n", ainn->read_u16());        // Read one sample

        while((adc_sample = ainn->read_u16()) > 150 )
            thread_sleep_for(5);        // wait for signal to go low (key to be released)

        thread_sleep_for(500);
        printf("\tKey release %x, %d : %d\n", ainn, (int)adc_sample, (int)ainn->read_u16());

        delete ainn;            // Delete the 'ainn' AnalogIn object from the heap
    }

//  thread_sleep_for(300);    // debounce the KBD_INT key (wait untill it stops bouncing) - may not be needed for your hadware/keyboard
                              // The kbd signal is quit slow, so debouncing shoud not be nesseserry

    kbd_int = new InterruptIn(KBD_IN, PullDown);        // Create a new kbd_int InterruptIn object on the heap
    kbd_int->rise(on_kbd_int_rise);                     // Re-attach interrupt handler

    printf(" **** Leaving kbd_int_handler(); ****\n");
}

void on_kbd_int_rise()
{
    kbd_int->rise(NULL);                // Detach interrupt handler
    kbd_event_q.call(kbd_int_handler);  // Handle in event queue
}

int main(void)
{
    ...
    kbd_tread.start(callback(&kbd_event_q, &EventQueue::dispatch_forever)); // Let kbd få get its own Thread
    kbd_int = new InterruptIn(KBD_IN, PullDown);                            // Create a new 'kbd_int' InterruptIn object on the heap
    kbd_int->rise(on_kbd_int_rise);                                         // Attach interrupt handler
    ...
}

Sadly, same behavior. No interrupt after first adc read.
Do the “LPC1768 board” (or any other board) do this?
If so, it must be a nrf52832 HAL problem?

Below is the printout on serial monitor after five push & release:


[1B][2J[1B][7hARM Ltd

[1B][2K/> [1B][1D
[1B][2J[1B][7hARM Ltd

[1B][2K/> [1B][1D
 ***** mbed_kbd_test *****

 **** Enter kbd_int_handler(); ****
	Start adc
	ADC = 65503
	Key release 20004098, 0 : 57005
 **** Leaving kbd_int_handler(); ****

 **** Enter kbd_int_handler(); ****
	Start adc
	ADC = 65503
	Key release 20004098, 0 : 54477
 **** Leaving kbd_int_handler(); ****

 **** Enter kbd_int_handler(); ****
	Start adc
	ADC = 65503
	Key release 20004098, 0 : 56573
 **** Leaving kbd_int_handler(); ****

 **** Enter kbd_int_handler(); ****
	Start adc
	ADC = 65471
	Key release 20004098, 0 : 12002
 **** Leaving kbd_int_handler(); ****

I’ve got the same results on a NUCLEO-F446RE board when building with the online compiler.

Try to update to the latest Mbed OS 5:

  • In the online compiler right-click on the mbed-os and select Update...

I downloaded and installed the whole Mbed system just a few days ago. So it should be fairly new. I’m on a 2 mega bit internet line, so online tools is not an option.
I use the Mbed-cli and I found some examples to use as starting point. Among the examples was “mbed-os-example-lorawan”, this worked on my board, and I started look for a command processor, and I found en example for this as well (finally… I was almost about to port my own, this is what should be explaynd in ANY getting started guide).
Then I was able to start the LoRaWan stuff from a command, and I started to look for other useful things. I found what resulted in my “si” command (cmd_sys_info()), it tells me “Mbed OS Version: 999999” not to useful though.
I tried some “googling” and found the “mbed releases -u” command. This gives me more than 140 lines with version number, where “mbed-os-5.15.1” is the highest. I try to do the “mbed update” command on my mbed-os directory, but it refuse to do so, because I had to go in to “mbed-os/targets” to define my boards. No matter how much “googling” and doc reading I did, it was NOT possible to find a recipe for “HOW TO DEFINE AN OUT OF TREE BOARD”.
Hopefully when I compile with the Mbed-cli, the newest version is default.
So all in all, I’m close to give up on Mbed, the alternative is to port the Semtech LoRaMac to Zephyr. But I guess this is harder than learning Mbed.
So here I am, with only one analog reading from my key-pad…
But, honestly, this seems to be a nordic nRF52 problem, but I rely do NOT know.
I can of course, use my own keypad code, but this access the nrf52 registers directly and it is not portable, and the idea about an “os” falls somewhat apart.

Yes, life isn’t always easy… Maybe you can try to use two pins. One configured as InterruptIn to detect a key-pressed event (doesn’t have to be ADC capable) and another one configured as AnalogIn to read ADC. Connect both pins to same output on your special keypad. Then you don’t have to switch pin function and the code could be more simple.

Yes, maybe. But anyway, Thank you very much for your patience. And a happy weekend for you and your family…