I am new to Zephyr and I try to get all my embedded software things over to Zephyr. I tried the Zephyr documentation and a lot of googling, but it is hard to figure out how to do this. I try to get to know how the "Device Tree" work, and how to use it, both inside the app and outside on the Zephyr system side. I try to go trough the samples and see how they are put together. Right now I'm testing the ... zephyrproject/zephyr/samples/basic/button/.
I have one input (button) and one optional output (led). The input can also be an analog input to the ADC mux. I modified it a bit, and it does what it should. But I want to change it to do more. I also want to let this interrupt (among others) take the cpu out of deep sleep. And go back to deep sleep when the job is done.
Question_1: How can I disable the interrupt, and change the input from an interrupt input to an analog input (in the interrupt/callback handler) and start the ADC? Then, in an other thread, I want to read the ADC and change the input back to an interrupt input, enable it, do whatever I want to do, and go back to deep sleep.
How can this be done in an "Zephyr way" (so it can work on different platforms), I do this on my nrf52dk_nrf52832 (without Zephyr), and it does what I want.
The input is from a numeric keypad, with one output, that goes high on a key pres and go back to an analog level that is different for each key, the level goes to zero when key is released. The goal is, of course, to decode what key is pressed, and do it with one io line.
Question_2: When/if this works with Zephyr; what do I have to do to make it a proper Zephyr device driver?
It wold be nice to have to examples to do this, one where all is done in the app. And one where it is done with an Zephyr "key_pad_device_driver".
(In my non Zephyr system, I, after the interrupt, wait a little bit (something like 0.3 - 3 ms), then read 10 analog samples, discard the two highest and the two lowest values, then if the span between these are accepted, I take the average of them and lock up the key that is pressed. This work surprisingly well).
Her is the code for my "button/src/main.c"
/*
* Copyright (c) 2016 Open-RnD Sp. z o.o.
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/printk.h>
#include <inttypes.h>
#define LED_ON_TIME 100
/*
* Get button configuration from the devicetree sw0 alias.
*
* At least a GPIO device and pin number must be provided. The 'flags'
* cell is optional.
*/
#define SW0_NODE DT_ALIAS(sw0)
#if DT_NODE_HAS_STATUS(SW0_NODE, okay)
#define SW0_GPIO_LABEL DT_GPIO_LABEL(SW0_NODE, gpios)
#define SW0_GPIO_PIN DT_GPIO_PIN(SW0_NODE, gpios)
#define SW0_GPIO_FLAGS (GPIO_INPUT | DT_GPIO_FLAGS(SW0_NODE, gpios))
#else
#error "Unsupported board: sw0 devicetree alias is not defined"
#define SW0_GPIO_LABEL ""
#define SW0_GPIO_PIN 0
#define SW0_GPIO_FLAGS 0
#endif
/* LED helpers, which use the led0 devicetree alias if it's available. */
static const struct device *initialize_led(void);
static struct gpio_callback button_cb_data;
K_SEM_DEFINE(kbd_pres_sem, 0, 1); /* starts off "not available" */ // K_SEM_DEFINE(kbd_pres_sem, 1, 1); /* starts off "available" */
void bp_cb(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
static int cp_cnt;
printk("\n# bp_cb(): %d Button pressed at: %" PRIu32 "... ", cp_cnt++, k_cycle_get_32());
k_sem_give(&kbd_pres_sem); // Giving the semaphore increments its count, unless the count is already equal to the limit.
}
/*
* The led0 devicetree alias is optional. If present, we'll use it
* to turn on the LED whenever the button is pressed.
*/
#define LED0_NODE DT_ALIAS(led0)
#if DT_NODE_HAS_STATUS(LED0_NODE, okay) && DT_NODE_HAS_PROP(LED0_NODE, gpios)
#define LED0_GPIO_LABEL DT_GPIO_LABEL(LED0_NODE, gpios)
#define LED0_GPIO_PIN DT_GPIO_PIN(LED0_NODE, gpios)
#define LED0_GPIO_FLAGS (GPIO_OUTPUT | DT_GPIO_FLAGS(LED0_NODE, gpios))
#endif
K_SEM_DEFINE(led_blink_sem, 0, 1); /* starts off "not available" */ // K_SEM_DEFINE(led_blink_sem, 1, 1); /* starts off "available" */
const struct device *button;
const struct device *led;
void main(void)
{
int ret;
int bp_cnt = 0;;
if((button = device_get_binding(SW0_GPIO_LABEL)) == NULL){
printk("Error: didn't find %s device\n", SW0_GPIO_LABEL);
return;
}
if((ret = gpio_pin_configure(button, SW0_GPIO_PIN, SW0_GPIO_FLAGS)) != 0){
printk("Error %d: failed to configure %s pin %d\n",
ret, SW0_GPIO_LABEL, SW0_GPIO_PIN);
return;
}
if((ret = gpio_pin_interrupt_configure(button, SW0_GPIO_PIN, GPIO_INT_EDGE_TO_ACTIVE)) != 0){
printk("Error %d: failed to configure interrupt on %s pin %d edg %x\n",
ret, SW0_GPIO_LABEL, SW0_GPIO_PIN, GPIO_INT_EDGE_TO_ACTIVE);
return;
}
gpio_init_callback(&button_cb_data, bp_cb, BIT(SW0_GPIO_PIN));
gpio_add_callback(button, &button_cb_data);
printk("\tSet up button at %s pin %d edg 0x%x\n", SW0_GPIO_LABEL, SW0_GPIO_PIN, GPIO_INT_EDGE_TO_ACTIVE);
led = initialize_led();
printk("\tPress the button, ");
while(1){
k_sem_take(&kbd_pres_sem, K_FOREVER); // Taking the semaphore decrements its count, unless the semaphore is unavailable (i.e. at zero).
printk("# main(): %d Button press detected at: %" PRIu32 "... ", bp_cnt++, k_cycle_get_32());
k_sem_give(&led_blink_sem); // Giving the semaphore increments its count, unless the count is already equal to the limit.
}
}
#ifdef LED0_GPIO_LABEL
void led_handler(void)
{
int lb_cnt = 0;
printk("\t######## led_handler: Start #########\n");
while(1){
gpio_pin_set(led, LED0_GPIO_PIN, 1); // ledd off
k_sem_take(&led_blink_sem, K_FOREVER); // Taking the semaphore decrements its count, unless the semaphore is unavailable (i.e. at zero).
printk(" # led_handler(): %d Led blink", lb_cnt++);
gpio_pin_set(led, LED0_GPIO_PIN, 0); // ledd on
k_msleep(LED_ON_TIME); // k_msleep(3000);
}
}
#define STACKSIZE 1024 /* size of stack area used by each thread */
#define PRIORITY 7 /* scheduling priority used by each thread */
K_THREAD_DEFINE(led_handler_id, STACKSIZE, led_handler, NULL, NULL, NULL, PRIORITY, 0, 0);
#endif /* LED0_GPIO_LABEL */
static const struct device *initialize_led(void)
{
#ifdef LED0_GPIO_LABEL
const struct device *led;
int ret;
led = device_get_binding(LED0_GPIO_LABEL);
if (led == NULL) {
printk("Didn't find LED device %s\n", LED0_GPIO_LABEL);
return NULL;
}
ret = gpio_pin_configure(led, LED0_GPIO_PIN, LED0_GPIO_FLAGS);
if (ret != 0) {
printk("Error %d: failed to configure LED device %s pin %d flg %x\n",
ret, LED0_GPIO_LABEL, LED0_GPIO_PIN, LED0_GPIO_FLAGS);
return NULL;
}
printk("\tSet up LED at %s pin %d flg 0x%x\n", LED0_GPIO_LABEL, LED0_GPIO_PIN, LED0_GPIO_FLAGS);
return led;
#else /* !defined(LED0_GPIO_LABEL) */
printk("No LED device was defined\n");
return NULL;
#endif /* LED0_GPIO_LABEL */
}
There are some more config stuff to an Zephyr app, but for the one that is able to answer this, I think it is not important now. But I think an answer do need some config things to work.