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

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));