在《龙芯2k0300 - 走马观碑组第21届智能汽车竞赛软硬件设计》中我们使用了LQ_1024线方向mini编码器。
一、LQ_1024线方向mini编码器
龙邱科技mini系列编码器具有以下特点:
● 分辨率有:A/B相正交输出256线、A/B相正交输出1024线、步进脉冲 + 方向输出512线、......;
●旋转速度高,最高转速可达10000rpm;
● 宽广的工作温度范围:-40℃ ~ +125℃。
● 抗扰性好。本产品采用霍尔检测技术,属于无接触检测,传感器运行不受灰尘或其它杂物影响,很好克服了基于光学检测原理的缺点;
● 体积小巧。直径D:14mm、高H:18mm、轴径:3mm。
如果你对编码器一点都不了解的话可以先参考这篇文章:《STM32F103霍尔编码器测速》。
龙邱科技提供的编码器,有带方向输出引脚和不带方向输出引脚两种,这里我使用的是正交A/B相1024线方向mini编码器,也就是说我使用的这个编码器除了有A/B输出引脚外,还有单独的方向输出引脚,这样我们就不用通过A/B相信号变化的先后顺序来判断运动方向。
1.1 工作原理
编码器的线数,是说编码器转一圈输出多少个脉冲,如果一个编码器是1024线,说明这个编码器转一圈对应的信号线会输出1024个脉冲,A/B两相转一圈发出的脉冲数一样的,不过存在90°相位差。
由于我使用的编码器带有方向输出引脚,因此:
- 通过对A相脉冲计数,通过读取单位时间t的脉冲信号的数量,就可以计算出转速;
- 通过判断方向引脚的高低电平,就可以知道是正转还是反转。
编码器码盘旋转一周A/B相输出的脉冲数目为N,在时间T内统计到的有效脉冲数目为S,那么转速为:
\[n = \frac{S}{NT} \times 60\]
1.2 硬件接线
1.2.1 左电机编码器
编码器一共引出6个引脚,左电机编码器与龙芯2K0300开发板的连接关系需严格对应,连接表如下:
引脚编号编码器引脚含义连接的龙芯GPIOPIN1GND必须可靠接地,否则可能出现显示异常GNDPIN23.3~5V直流供电3.3VPIN3AA 相输出,CMOS 电平GPIO67(SPI2_CS)PIN4Dir方向输出GPIO72(CAN2_RX)PIN5BB 相输出,CMOS 电平——PIN6nc悬空——1.2.2 右电机编码器
右电机编码器与龙芯2K0300开发板的连接关系需严格对应,连接表如下:
编码器引脚含义连接的龙芯GPIOPIN1GND必须可靠接地,否则可能出现显示异常GNDPIN23.3~5V直流供电3.3VPIN3AA 相输出,CMOS 电平GPIO64(SPI2_CLK)PIN4Dir方向输出GPIO73(CAN2_TX)PIN5BB 相输出,CMOS 电平——PIN6nc悬空——二、编码器设备驱动
2.1 编码器驱动
接下来我们在driver目录下创建子目录encoder_driver;- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/driver$ mkdir encoder_driver
- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/encoder_driver$ cd encoder_driver
复制代码 目录结构如下:- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/encoder_driver$ tree .
- .
- ├── Makefile
- └── encoder_driver.c
复制代码 2.1.1 encoder_driver
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/of.h>
- #include <linux/of_gpio.h>
- #include <linux/io.h>
- #include <linux/miscdevice.h>
- #include <linux/fs.h>
- #include <linux/uaccess.h>
- #define DEVICE_NAME "pulse_encoder"
- // 寄存器定义
- #define PWM_ENCODER_LOW_BUFFER 0x4
- #define PWM_ENCODER_FULL_BUFFER 0x8
- #define PWM_ENCODER_CTRL 0xC
- // 控制寄存器位定义
- #define PWM_ENCODER_EN (1 << 0) // 计数器使能
- #define PWM_ENCODER_CAPTE (1 << 8) // 测量脉冲使能 (编码器模式)
- // ioctl 命令
- #define ENCODER_IOC_MAGIC 'E'
- #define ENCODER_GET_COUNT _IOR(ENCODER_IOC_MAGIC, 1, struct encoder_counts)
- struct encoder_counts {
- long long count; // 脉冲计数
- int direction; // 旋转方向 (1: 正转, -1: 反转)
- };
- struct encoder_device {
- void __iomem *base; // 定时器寄存器基地址
- int dir_gpio; // 方向引脚
- int ppr; // 编码器每转脉冲数
- struct miscdevice miscdev;
- };
- static struct encoder_device *g_encoder;
- // 读取计数值和方向
- static long encoder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- struct encoder_device *enc = g_encoder;
- struct encoder_counts cnt;
- void __user *argp = (void __user *)arg;
- u32 full_buffer;
- unsigned long flags;
- if (cmd != ENCODER_GET_COUNT)
- return -ENOTTY;
- local_irq_save(flags);
-
- // 1. 读取硬件计数值 (32位)
- full_buffer = readl(enc->base + PWM_ENCODER_FULL_BUFFER);
-
- // 2. 读取方向GPIO电平
- cnt.direction = gpio_get_value(enc->dir_gpio) ? 1 : -1;
-
- // 3. 可选:将硬件计数值转换为更有意义的脉冲数
- // 公式基于代码中的参考:100000000 / FULL_BUFFER / 200
- // 这里的计算需要根据你的定时器时钟频率调整,此处直接返回原始值
- cnt.count = full_buffer;
-
- local_irq_restore(flags);
- if (copy_to_user(argp, &cnt, sizeof(cnt)))
- return -EFAULT;
-
- return 0;
- }
- static const struct file_operations encoder_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = encoder_ioctl,
- };
- // 驱动入口
- static int encoder_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct resource *res;
- u32 ctrl;
- int ret;
- // 分配设备结构体
- g_encoder = devm_kzalloc(dev, sizeof(*g_encoder), GFP_KERNEL);
- if (!g_encoder)
- return -ENOMEM;
- // 1. 获取并映射寄存器地址
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "Failed to get memory resource\n");
- return -ENOENT;
- }
-
- g_encoder->base = devm_ioremap(dev, res->start, resource_size(res));
- if (!g_encoder->base) {
- dev_err(dev, "Failed to ioremap\n");
- return -ENOMEM;
- }
- // 2. 获取方向GPIO
- g_encoder->dir_gpio = of_get_named_gpio(np, "dir-gpios", 0);
- if (!gpio_is_valid(g_encoder->dir_gpio)) {
- dev_err(dev, "Invalid dir-gpios in device tree\n");
- return -EINVAL;
- }
-
- ret = devm_gpio_request(dev, g_encoder->dir_gpio, "encoder_dir");
- if (ret) return ret;
- ret = gpio_direction_input(g_encoder->dir_gpio);
- if (ret) return ret;
- // 3. 获取PPR (每转脉冲数)
- if (of_property_read_u32(np, "rotary-encoder,steps-per-period", &g_encoder->ppr))
- g_encoder->ppr = 20; // 默认值
-
- // 4. 配置定时器为编码器模式
- ctrl = readl(g_encoder->base + PWM_ENCODER_CTRL);
- ctrl |= PWM_ENCODER_EN; // 使能计数器
- ctrl |= PWM_ENCODER_CAPTE; // 使能捕获/编码器模式
- writel(ctrl, g_encoder->base + PWM_ENCODER_CTRL);
-
- // 5. 注册MISC设备
- g_encoder->miscdev.minor = MISC_DYNAMIC_MINOR;
- g_encoder->miscdev.name = DEVICE_NAME;
- g_encoder->miscdev.fops = &encoder_fops;
-
- ret = misc_register(&g_encoder->miscdev);
- if (ret) {
- dev_err(dev, "Failed to register misc device\n");
- return ret;
- }
- dev_info(dev, "Encoder driver loaded, PPR=%d, base=0x%p\n",
- g_encoder->ppr, g_encoder->base);
- return 0;
- }
- static void encoder_remove(struct platform_device *pdev)
- {
- if (g_encoder) {
- misc_deregister(&g_encoder->miscdev);
- // 禁用编码器模式
- writel(0, g_encoder->base + PWM_ENCODER_CTRL);
- }
- }
- static const struct of_device_id encoder_of_match[] = {
- { .compatible = "pulse-dir-encoder" },
- { /* sentinel */ }
- };
- MODULE_DEVICE_TABLE(of, encoder_of_match);
- static struct platform_driver encoder_driver = {
- .probe = encoder_probe,
- .remove = encoder_remove,
- .driver = {
- .name = "pulse_dir_encoder_v2",
- .of_match_table = encoder_of_match,
- },
- };
- module_platform_driver(encoder_driver);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Pulse/Direction Encoder Driver using PWM Timer (No Interrupt)");
复制代码 2.1.2 Makefile
[code]# 内核源码路径KERNELDIR ?= /opt/2k0300/build-2k0300/workspace/linux-6.12# 当前驱动目录PWD := $(shell pwd)# 交叉编译工具链CROSS_COMPILE := loongarch64-linux-gnu-# 架构ARCH := loongarch# 所需文件夹BUILD_DIR := buildKO_DIR:=koSRC_DIR:=src# 编译目标obj-m := encoder.oencoder-y := encoder_driver.o \# 编译规则ll: prepare compile move_files# 提前创建目录prepare: @mkdir -p $(BUILD_DIR) $(KO_DIR) @echo "
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |