feat(encoder): 添加编码器模块支持

- 在CMakeLists.txt中添加encoder_module.c和encoder_event.c源文件
- 配置设备树pinctrl设置编码器引脚(QDEC_A和QDEC_B)
- 在设备树中启用qdec外设并配置相关参数
- 添加atguigu厂商前缀到vendor-prefixes.txt
- 创建encoder_event.h事件头文件定义编码器事件结构
- 在prj.conf中启用NRFX_QDEC和PINCTRL_DYNAMIC配置
- 实现encoder_module.c包含完整的编码器驱动逻辑
- 实现encoder_event.c处理编码器事件的发布和记录
This commit is contained in:
2026-04-10 10:40:28 +08:00
parent b9bb326e8b
commit e226338565
8 changed files with 308 additions and 0 deletions

View File

@@ -13,8 +13,10 @@ add_subdirectory(drivers)
target_sources(app PRIVATE
src/main.c
src/battery_module.c
src/encoder_module.c
src/keyboard_core_module.c
src/usb_hid_module.c
src/events/encoder_event.c
src/events/hid_led_event.c
src/mode_switch_module.c
src/events/keyboard_hid_report_event.c

View File

@@ -13,4 +13,21 @@
low-power-enable;
};
};
encoder_default: encoder_default {
group1 {
psels = <NRF_PSEL(QDEC_A, 0, 10)>,
<NRF_PSEL(QDEC_B, 1, 6)>;
bias-pull-up;
};
};
encoder_sleep: encoder_sleep {
group1 {
psels = <NRF_PSEL(QDEC_A, 0, 10)>,
<NRF_PSEL(QDEC_B, 1, 6)>;
low-power-enable;
bias-pull-up;
};
};
};

View File

@@ -15,6 +15,7 @@
aliases {
led0 = &myled0;
qdec0 = &qdec;
};
hid_kbd: hid_kbd {
@@ -147,6 +148,16 @@
status = "okay";
};
&qdec {
status = "okay";
pinctrl-0 = <&encoder_default>;
pinctrl-1 = <&encoder_sleep>;
pinctrl-names = "default", "sleep";
led-pre = <0>;
steps = <80>;
nordic,period = "SAMPLEPER_1024US";
};
&usbd {
status = "okay";
num-bidir-endpoints = <0>;

View File

@@ -1 +1,2 @@
atguigu Atguigu
injoinic Injoinic

View File

@@ -0,0 +1,22 @@
#ifndef BLINKY_ENCODER_EVENT_H_
#define BLINKY_ENCODER_EVENT_H_
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct encoder_event {
struct app_event_header header;
int8_t detents;
};
APP_EVENT_TYPE_DECLARE(encoder_event);
#ifdef __cplusplus
}
#endif
#endif /* BLINKY_ENCODER_EVENT_H_ */

View File

@@ -5,6 +5,8 @@ CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_NRFX_RTC2=y
CONFIG_NRFX_GPPI=y
CONFIG_NRFX_QDEC=y
CONFIG_PINCTRL_DYNAMIC=y
CONFIG_REBOOT=y
CONFIG_SENSOR=y
CONFIG_ADC=y

226
src/encoder_module.c Normal file
View File

@@ -0,0 +1,226 @@
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#define MODULE encoder_module
#include <caf/events/module_state_event.h>
#include <caf/events/power_event.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include "encoder_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define ENCODER_QDEC_NODE DT_ALIAS(qdec0)
#define ENCODER_PULSES_PER_DETENT 4LL
#define ENCODER_DETENT_UDEG (360000000LL / (80LL / ENCODER_PULSES_PER_DETENT))
BUILD_ASSERT(DT_NODE_EXISTS(ENCODER_QDEC_NODE), "Missing qdec0 alias");
static const struct device *const qdec_dev = DEVICE_DT_GET(ENCODER_QDEC_NODE);
static struct k_work encoder_report_work;
static struct sensor_trigger encoder_trigger = {
.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_ROTATION,
};
static bool initialized;
static bool running;
static int64_t angle_remainder_udeg;
static int64_t sensor_value_to_udeg(const struct sensor_value *value)
{
return ((int64_t)value->val1 * 1000000LL) + value->val2;
}
static void submit_detents(int32_t detents)
{
while (detents != 0) {
struct encoder_event *event = new_encoder_event();
if (detents > INT8_MAX) {
event->detents = INT8_MAX;
detents -= INT8_MAX;
} else if (detents < INT8_MIN) {
event->detents = INT8_MIN;
detents -= INT8_MIN;
} else {
event->detents = (int8_t)detents;
detents = 0;
}
APP_EVENT_SUBMIT(event);
}
}
static void encoder_report_work_handler(struct k_work *work)
{
struct sensor_value rotation;
int64_t total_udeg;
int32_t detents;
int err;
ARG_UNUSED(work);
if (!running) {
return;
}
err = sensor_sample_fetch(qdec_dev);
if (err) {
LOG_WRN("QDEC sample fetch failed (%d)", err);
return;
}
err = sensor_channel_get(qdec_dev, SENSOR_CHAN_ROTATION, &rotation);
if (err) {
LOG_WRN("QDEC channel get failed (%d)", err);
return;
}
total_udeg = angle_remainder_udeg + sensor_value_to_udeg(&rotation);
detents = (int32_t)(total_udeg / ENCODER_DETENT_UDEG);
angle_remainder_udeg = total_udeg - ((int64_t)detents * ENCODER_DETENT_UDEG);
if (detents != 0) {
submit_detents(detents);
}
}
static void encoder_trigger_handler(const struct device *dev,
const struct sensor_trigger *trigger)
{
ARG_UNUSED(dev);
ARG_UNUSED(trigger);
if (!running) {
return;
}
k_work_submit(&encoder_report_work);
}
static int module_init(void)
{
int err;
if (!device_is_ready(qdec_dev)) {
LOG_ERR("QDEC device not ready");
return -ENODEV;
}
k_work_init(&encoder_report_work, encoder_report_work_handler);
angle_remainder_udeg = 0;
err = sensor_trigger_set(qdec_dev, &encoder_trigger, encoder_trigger_handler);
if (err) {
LOG_ERR("Cannot set QDEC trigger (%d)", err);
return err;
}
return 0;
}
static int module_start(void)
{
int err;
if (running) {
return 0;
}
err = pm_device_action_run(qdec_dev, PM_DEVICE_ACTION_RESUME);
if (err && (err != -EALREADY) && (err != -ENOTSUP)) {
LOG_ERR("Cannot resume QDEC device (%d)", err);
return err;
}
angle_remainder_udeg = 0;
running = true;
return 0;
}
static void module_pause(void)
{
int err;
if (!running) {
return;
}
err = pm_device_action_run(qdec_dev, PM_DEVICE_ACTION_SUSPEND);
if (err && (err != -EALREADY) && (err != -ENOTSUP)) {
LOG_WRN("Cannot suspend QDEC device (%d)", err);
}
angle_remainder_udeg = 0;
running = false;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
int err;
if (!initialized) {
err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (initialized) {
int err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
__ASSERT_NO_MSG(false);
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);
APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event);
APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);

View File

@@ -0,0 +1,27 @@
#include "encoder_event.h"
static void log_encoder_event(const struct app_event_header *aeh)
{
const struct encoder_event *event = cast_encoder_event(aeh);
APP_EVENT_MANAGER_LOG(aeh, "detents:%d", event->detents);
}
static void profile_encoder_event(struct log_event_buf *buf,
const struct app_event_header *aeh)
{
const struct encoder_event *event = cast_encoder_event(aeh);
nrf_profiler_log_encode_int8(buf, event->detents);
}
APP_EVENT_INFO_DEFINE(encoder_event,
ENCODE(NRF_PROFILER_ARG_S8),
ENCODE("detents"),
profile_encoder_event);
APP_EVENT_TYPE_DEFINE(encoder_event,
log_encoder_event,
&encoder_event_info,
APP_EVENT_FLAGS_CREATE(
APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE));