增量型旋轉(zhuǎn)編碼器作為輸入器件廣泛用于各種設(shè)備,例如汽車音響的音量調(diào)節(jié),收音機(jī)頻率調(diào)節(jié),示波器上的旋鈕。但遺憾的是在Zephyr中并沒有增量型旋轉(zhuǎn)編碼器的驅(qū)動(dòng),本文將基于現(xiàn)有的Sensor API, 說明如何添加增量型旋轉(zhuǎn)編碼器的驅(qū)動(dòng),本文不對(duì)驅(qū)動(dòng)操作硬件的實(shí)現(xiàn)細(xì)節(jié)進(jìn)行說明。
增量型旋轉(zhuǎn)編碼器硬件要點(diǎn)
本文使用的是KY-040旋轉(zhuǎn)編碼器,詳細(xì)信息見文末參考
button引腳是一個(gè)對(duì)地的開關(guān),按壓時(shí)接地
旋轉(zhuǎn)時(shí)A/B輸出有相差的正交脈沖。
旋轉(zhuǎn)一圈產(chǎn)生固定數(shù)量的脈沖
旋轉(zhuǎn)時(shí)一個(gè)脈沖內(nèi)旋轉(zhuǎn)軸可以有多個(gè)停留位置,例如1,2,4.
驅(qū)動(dòng)
驅(qū)動(dòng)API選擇
比較好的做法是為旋轉(zhuǎn)編碼器抽象新的驅(qū)動(dòng)API,但新的API要進(jìn)入Zephyr的主分支過程是非常漫長的,同時(shí)旋轉(zhuǎn)編碼器抽象API需要涵蓋眾多類型。因此我選用了現(xiàn)有的Senser API來對(duì)增量類型旋轉(zhuǎn)編碼器的API。
增量類型旋轉(zhuǎn)編碼器的按壓就是一個(gè)簡(jiǎn)單的button,用gpio就可以處理,因此旋轉(zhuǎn)編碼器的驅(qū)動(dòng)就只處理旋轉(zhuǎn)。編碼器的旋轉(zhuǎn)理解為是一個(gè)角度的傳感器,正反轉(zhuǎn)為轉(zhuǎn)動(dòng)方向,轉(zhuǎn)動(dòng)的距離就是角度,這里使用sensor API的SENSOR_CHAN_ROTATION來對(duì)其進(jìn)行操控。
設(shè)備樹綁定
設(shè)備樹綁定是對(duì)旋轉(zhuǎn)編碼器的硬件進(jìn)行抽象,一個(gè)增量式旋轉(zhuǎn)編碼器與旋轉(zhuǎn)相關(guān)的的硬件特性有如下信息:
輸入引腳A/B
旋轉(zhuǎn)一圈產(chǎn)生的脈沖
一個(gè)脈沖周期的穩(wěn)妥數(shù)量
創(chuàng)建dts/bindings/sensor/rotary-encoder.yaml內(nèi)容如下
description: |
Sensor driver for the relative-axis rotary encoder
compatible: “rotary-encoder”
properties:
label:
type: string
required: true
a-gpios:
type: phandle-array
required: true
description: A pin for the encoder
b-gpios:
type: phandle-array
required: true
description: B pin for the encoder
ppr:
type: int
description: Pulse Per Revolution
required: false
spp:
type: int
description: |
Number of steps (stable states) per period
1: Full-period mode (default)
2: Half-period mode
4: Quarter-period mode
required: false
驅(qū)動(dòng)代碼
從設(shè)備樹中獲取硬件信息
創(chuàng)建管理數(shù)據(jù)變量和讀取硬件信息
struct encoder_config {
const char *a_label;
const uint8_t a_pin;
const uint8_t a_flags;
const char *b_label;
const uint8_t b_pin;
const uint8_t b_flags;
const uint8_t ppr;
const uint8_t spp;
};
//創(chuàng)建管理數(shù)據(jù)和配置數(shù)據(jù)的宏
#define ENCODER_INST(n)
struct encoder_data encoder_data_##n;
const struct encoder_config encoder_cfg_##n = {
.a_label = DT_INST_GPIO_LABEL(n, a_gpios),
.a_pin = DT_INST_GPIO_PIN(n, a_gpios),
.a_flags = DT_INST_GPIO_FLAGS(n, a_gpios),
.b_label = DT_INST_GPIO_LABEL(n, b_gpios),
.b_pin = DT_INST_GPIO_PIN(n, b_gpios),
.b_flags = DT_INST_GPIO_FLAGS(n, b_gpios),
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, ppr), (1), (DT_INST_PROP(n, ppr))),
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, spp), (SPP_FULL), (DT_INST_PROP(n, spp))),
};
//根據(jù)設(shè)備樹對(duì)node進(jìn)行初始化,會(huì)從設(shè)備樹中讀取硬件信息放在struct encoder_config變量中
DT_INST_FOREACH_STATUS_OKAY(ENCODER_INST)
驅(qū)動(dòng)初始化
在啟動(dòng)的POST_KERNEL階段會(huì)調(diào)用encoder_init對(duì)驅(qū)動(dòng)進(jìn)行初始化
int encoder_init(const struct device *dev)
{
// GPIO的配置
// GPIO中斷安裝
// 旋轉(zhuǎn)編碼器GPIO初始化狀態(tài)讀取
// 驅(qū)動(dòng)初始化狀態(tài)設(shè)置
// 驅(qū)動(dòng)線程創(chuàng)建
// 使能中斷
}
//注冊(cè)驅(qū)動(dòng)
DEVICE_AND_API_INIT(encoder_##n, DT_INST_LABEL(n), encoder_init, &encoder_data_##n, &encoder_cfg_##n,
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &encoder_driver_api);
驅(qū)動(dòng)流程
旋轉(zhuǎn)編碼器依靠脈沖觸發(fā)GPIO中斷,中斷通知thread進(jìn)行處理
static void encoder_a_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct encoder_data *drv_data = CONTAINER_OF(cb, struct encoder_data, a_gpio_cb);
enable_int(drv_data-》dev, false);
drv_data-》intpin = 0b10;
//通知發(fā)生中斷
k_sem_give(&drv_data-》gpio_sem);
}
static void encoder_b_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct encoder_data *drv_data = CONTAINER_OF(cb, struct encoder_data, b_gpio_cb);
enable_int(drv_data-》dev, false);
drv_data-》intpin = 0b01;
//通知發(fā)生中斷
k_sem_give(&drv_data-》gpio_sem);
}
static void encoder_thread(void *dev_ptr, void *p2, void *p3)
{
while (1) {
//等待中斷通知
k_sem_take(&drv_data-》gpio_sem, K_FOREVER);
//根據(jù)A/B GPIO level情況判斷正反旋轉(zhuǎn)
//更新旋轉(zhuǎn)數(shù)據(jù)
//通過trigger handle通過應(yīng)用層
if (drv_data-》handler) {
drv_data-》handler(dev, drv_data-》trigger);
}
//使能中斷
enable_int(dev, true);
}
}
驅(qū)動(dòng)接口實(shí)現(xiàn)
sensor的接口有5個(gè), 詳細(xì)參考旋轉(zhuǎn)編碼器只用實(shí)現(xiàn)其中的2個(gè)既可以。
旋轉(zhuǎn)編碼器是主動(dòng)輸出型設(shè)備,無需軟件觸發(fā),因此可以不必實(shí)現(xiàn)channel_fetch,只用實(shí)現(xiàn)trigger_set用于注冊(cè)觸發(fā)時(shí)的callback,實(shí)現(xiàn)channel_get用于在callback時(shí)從driver獲取旋轉(zhuǎn)的角度既可以。
12static int encoder_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct encoder_data *drv_data = dev-》data;
enable_int(dev, false);
drv_data-》trigger = trig;
drv_data-》handler = handler;
enable_int(dev, true);
return 0;
}
static int encoder_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct encoder_data *drv_data = dev-》data;
const struct encoder_config *drv_cfg = dev-》config;
int32_t acc;
if (chan != SENSOR_CHAN_ROTATION) {
return -ENOTSUP;
}
acc = drv_data-》pulses;
val-》val1 = acc * FULL_ANGLE / (drv_cfg-》ppr * drv_cfg-》spp);
val-》val2 = acc * FULL_ANGLE - val-》val1 * (drv_cfg-》ppr * drv_cfg-》spp);
if (val-》val2) {
val-》val2 *= 1000000;
val-》val2 /= (drv_cfg-》ppr * drv_cfg-》spp);
}
return 0;
}
static const struct sensor_driver_api encoder_driver_api = {
.trigger_set = encoder_trigger_set,
.channel_get = encoder_channel_get,
};
驅(qū)動(dòng)使用
添加設(shè)備樹節(jié)點(diǎn)
在板子的dts中添加旋轉(zhuǎn)編碼器的設(shè)備樹節(jié)點(diǎn):
gpio1.22和gpio1.23是旋轉(zhuǎn)編碼器連接旋轉(zhuǎn)編碼器的A/B引腳。旋轉(zhuǎn)編碼器旋轉(zhuǎn)一圈有15個(gè)脈沖,每個(gè)脈沖下有2個(gè)穩(wěn)定狀態(tài)。
input_encoder: rotary_encoder {
compatible = “rotary-encoder”;
status = “okay”;
label = “INPUT_ENCODER”;
a-gpios = 《&gpio1 22 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)》;
b-gpios = 《&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)》;
ppr = 《15》;
spp = 《2》;
};
使用代碼
void encoder_callback(const struct device *dev,
struct sensor_trigger *trigger)
{
struct sensor_value val;
//旋轉(zhuǎn)編碼器旋轉(zhuǎn)發(fā)生,從驅(qū)動(dòng)讀出旋轉(zhuǎn)過的角度
sensor_channel_get(dev, SENSOR_CHAN_ROTATION, &val);
printk(“current %d.%d
”, val.val1, val.val2);
}
void main(void)
{
struct device *dev;
//獲取旋轉(zhuǎn)編碼器device
dev = device_get_binding(“INPUT_ENCODER”);
//注冊(cè)trigger callback,當(dāng)旋轉(zhuǎn)發(fā)生時(shí)將調(diào)用encoder_callback
sensor_trigger_set(dev, NULL, encoder_callback);
}
以上測(cè)試測(cè)序編譯完后跑起來的效果
current 12.0
current 24.0
current 36.0
current 48.0
current 36.0
current 24.0
current 12.0
current 0.0
current -12.0
current -24.0
current -36.0
current -48.0
參考
https://zh.wikipedia.org/wiki/%E6%97%8B%E8%BD%89%E7%B7%A8%E7%A2%BC%E5%99%A8
https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/input/rotary-encoder.txt
https://www.epitran.it/ebayDrive/datasheet/25.pdf
編輯:jq
-
Zephyr
+關(guān)注
關(guān)注
0文章
21瀏覽量
5963
原文標(biāo)題:Zephyr添加旋轉(zhuǎn)編碼器驅(qū)動(dòng)
文章出處:【微信號(hào):ZephyrProject,微信公眾號(hào):ZephyrProject】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論