linux -- I2C设备驱动 -- MS32006(低压5V多通道电机驱动器)

news2024/11/27 5:39:09

产品简述

MS32006 是一款多通道电机驱动芯片, 其中包含两路步进电机驱动, 一路直流电机驱动; 每个通道的电流最高电流1.0A; 支持两相四线与四相五线步进电机。芯片采用 I2C 的通信接口控制模式, 兼容 3.3V/5V 的标准工业接口。
MS32006 总共集成了两路步进电机驱动器与一路直流电机驱动器, 通过 I2C 总线去控制电机的转动。 步进电机控制器可以选择全步进或者 1/2 的步进模式, 系统上一般用来做为小云台 X,Y 轴的运动控制。 直流电机也是通过 I2C 设置内部的寄存器, 来控制电机的正转, 反转, 刹车, 自由旋转这四个状态, 系统上可以用来做 IR-cut 的控制。

常用两相步进电机都是1.8°的步进角200个脉冲转一圈
如果给驱动器200个脉冲,电机转一圈,就叫整步
如果给400个脉冲,转一圈,叫半步。
800,1600,3200等脉冲转一圈,就叫细分步

主要特点

  • 双路步进电机驱动,整步进或1/2步进,最大工作电流1A
  • I2C串行总线通信控制电机
  • 指令缓存功能,电机按照当前指令转动时预存下一条指令
  • 集成一个直流电机驱动, 最大驱动电流±1.1A
  • QFN24 封装(背部散热片)

管脚图以及管脚说明

管脚图

主控侧主要关注SDATA,SCLK。分别为I2C总线的数据线和时钟线。
在这里插入图片描述

管脚说明

两路步进电机:OUT1A,OUT1B / OUT2A,OUT2B / OUT3A,OUT3B / OUT4A,OUT4B
一路直流电机:OUT5A,OUT5B
在这里插入图片描述

寄存器表

在这里插入图片描述

原理图分析

  1. 使用SoC上的I2C1,需配置I2C1引脚的复用,硬件上已经做了上拉
  2. 需要SoC提供FCLK,需配置时钟输出引脚的复用
  3. FLAG脚没有接
    在这里插入图片描述

驱动开发

1. 框架搭建

使用字符设备驱动,将i2c_driver注册到内核中

static int __init ms32006_init(void)
{
   
	pr_debug("[%s]: %d---enter!\n", __func__, __LINE__);
    
	return i2c_add_driver(&ms32006_driver);
}
module_init(ms32006_init);

static void __exit ms32006_exit(void)
{
   
	pr_debug("[%s]: %d---exit!\n", __func__, __LINE__);
    i2c_del_driver(&ms32006_driver);

    cdev_del(&ms32006cdev.cdev);
    unregister_chrdev_region(ms32006cdev.devid, MS32006_CNT);

    device_destroy(ms32006cdev.class, ms32006cdev.devid);
    class_destroy(ms32006cdev.class);

    pr_err(PREFIX "exit done!\n");
}
module_exit(ms32006_exit);

2. 在I2C设备和驱动匹配成功后的probe函数中,注册字符设备驱动

I2C 设备和驱动的匹配过程是由 I2C 核心来完成的, drivers/i2c/i2c-core.c 就是 I2C 的核心
部分, I2C 核心提供了一些与具体硬件无关的 API 函数
1、 i2c_adapter 注册/注销函数

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)

2、 i2c_driver 注册/注销函数

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)

设备和驱动的匹配过程也是由 I2C 总线完成的, I2C 总线的数据结构为 i2c_bus_type,定义
在 drivers/i2c/i2c-core.c 文件

736 struct bus_type i2c_bus_type = {
   
737 .name = "i2c",
738 .match = i2c_device_match,
739 .probe = i2c_device_probe,
740 .remove = i2c_device_remove,
741 .shutdown = i2c_device_shutdown,
742 };

match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 i2c_device_match 这个函数,此
函数内容如下:

457 static int i2c_device_match(struct device *dev, struct
device_driver *drv)
458 {
   
459 struct i2c_client *client = i2c_verify_client(dev);
460 struct i2c_driver *driver;
461
462 if (!client)
463 return 0;
464
465 /* Attempt an OF style match */
466 if (of_driver_match_device(dev, drv))
467 return 1;
468
469 /* Then ACPI style match */
470 if (acpi_driver_match_device(dev, drv))
471 return 1;
472
473 driver = to_i2c_driver(drv);
474 /* match on an id table if there is one */
475 if (driver->id_table)
476 return i2c_match_id(driver->id_table, client) != NULL;
477
478 return 0;
479 }

第 466 行, of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 I2C 设备节
点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C
设备和驱动匹配。
第 470 行, acpi_driver_match_device 函数用于 ACPI 形式的匹配。
第 476 行, i2c_match_id 函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C
设备名字和 i2c_device_id 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。

匹配成功后,执行probe,我们在probe中,注册字符设备驱动。

static const struct file_operations ms32006_ops = {
   
    .owner = THIS_MODULE,
    .open = ms32006_open,
    .read = ms32006_read,
    .release = ms32006_release,
    .unlocked_ioctl = ms32006_ioctl,
};

static int ms32006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
   
    uint32_t timer_idx;
    uint32_t irq,reg_addr;

    pr_debug("__func__:%s __line:%d \n", __func__, __LINE__);
    pr_err("__func__:%s __line:%d \n", __func__, __LINE__);
    if(ms32006cdev.major) {
   
        ms32006cdev.devid = MKDEV(ms32006cdev.major, 0);
        register_chrdev_region(ms32006cdev.devid, MS32006_CNT, MS32006_NAME);
    } else {
   
        alloc_chrdev_region(&ms32006cdev.devid, 0, MS32006_CNT, MS32006_NAME);
        ms32006cdev.major = MAJOR(ms32006cdev.devid);
    }

    cdev_init(&ms32006cdev.cdev, &ms32006_ops);
    cdev_add(&ms32006cdev.cdev, ms32006cdev.devid, MS32006_CNT);

    ms32006cdev.class = class_create(THIS_MODULE, MS32006_NAME);
    if (IS_ERR(ms32006cdev.class)) {
   
        return PTR_ERR(ms32006cdev.class);
    }

    ms32006cdev.device = device_create(ms32006cdev.class, NULL, ms32006cdev.devid, NULL, MS32006_NAME);
    if (IS_ERR(ms32006cdev.device)) {
   
        return PTR_ERR(ms32006cdev.device);
    }

    //ms32006cdev.nd = of_find_node_by_name(NULL, "motor_ms32006");
    ms32006cdev.nd = client->dev.of_node;
    if(ms32006cdev.nd == NULL)
    {
   
        pr_debug("ms32006 node can not found!\n");
        return -EINVAL;
    }
    pr_debug("ms32006 name:%s full name:%s\n",ms32006cdev.nd->name, ms32006cdev.nd->full_name);

    if(of_property_read_u32(ms32006cdev.nd, "timer_idx", &timer_idx))
    {
   
        pr_debug("property <timer_idx> not found\n");
        return -EINVAL;
    }
    pr_debug("ms32006 timer_idx:%u\n", timer_idx);
    
    pr_debug("i2c client irq:%d of_irq_get:%d\n",client->irq, of_irq_get(ms32006cdev.nd, 0));

    irq = client->irq;

    if(of_property_read_u32(ms32006cdev.nd, "reg", &reg_addr))
    {
   
        pr_debug("property <reg> not found\n");
        return -EINVAL;
    }
    pr_debug("ms32006 slave device address:%u\n", reg_addr);
    client->addr = reg_addr;

    ms32006cdev.private_data = client;
    ms32006cdev.is_initing = false;

    pr_debug("__func__:%s __line:%d \n", __func__, __LINE__);

	ms32006cdev.fclk = of_clk_get(ms32006cdev.nd, 0);
	if (IS_ERR(ms32006cdev.fclk)) {
   
		pr_debug("ms32006 dts fclk clock not found.\n");
		return PTR_ERR(ms32006cdev.fclk);
	}
	clk_prepare_enable(ms32006cdev.fclk);

	ms32006cdev.fclk_rate = clk_get_rate(ms32006cdev.fclk);
	pr_debug("ms32006 output fclk rate %u\n", ms32006cdev.fclk_rate);

    pr_notice(PREFIX "probe init done\n");
    spin_lock_init(&ms32006cdev.chip_lock);

    return 0;
}

3. 设备树中在I2C设备节点下,添加motor节点:

  1. pinctrl,在父节点i2c中已经配置了SCL和SDA两个pin的复用,这里需要复用FCLK参考时钟pin的复用
  2. 设置参考时钟的频率为24M
  3. 中断号的描述,其实没有用到,后期可能要使用中断。原理图中没有将flag脚接到SoC,无法通过中断提醒SoC
    motor0: motor0@30{
   
        compatible = "relmon,ms32006";
        reg = <0x18>;
        timer_idx = <3>;

        pinctrl-names = "default";
                /*pinctrl-0 = <&gpioa15_pinctrl>;*/
                pinctrl-0 = <&chip_out1_pinctrl>;

        clocks = <&ts_clk TS_CLK_TX5112_CHIP_OCLK_I2>;
                clock-names = "fclk";
                assigned-clocks = <&ts_clk TS_CLK_TX5112_CHIP_OCLK_I2>;
                assigned-clock-rates = <24000000>; 

        interrupt-parent = <&gic>;
        interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;       
    };

4. 实现file_operations中的接口

4.1 open,read以及release

open接口中主要做初始化:将ms32006重置后,设置一些初始的设置,并初始化runtime结构。
read接口提供了读取ms32006所有寄存器的功能
release用于释放资源

static int ms32006_open(struct inode *inode, struct file *filp)
{
   
    ms32006_op_stat_e op_stat;
    filp->private_data = &ms32006cdev;

    /* reset motor driver chip: will clear a part of register value */
    op_stat = ms32006_reset(&ms32006cdev);
    if(op_stat != SUCCESS) {
   
        return -EIO;
    }
    /* clear all register value */
    op_stat = __ms32006_init(&ms32006cdev);
    if(op_stat != SUCCESS) {
   
        return -EIO;
    }
    /* clear thread runtime information */
    op_stat = ms32006_thread_info_init(&ms32006cdev);
    if(op_stat != SUCCESS) {
   
        return -EIO;
    }

    return 0;
}

static int ms32006_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
   
    struct ms32006_dev* dev = filp->private_data;
    u8 reg_val[MS32006_REG_MAX_SZ];
    long err = 0;

    if(cnt < 0 || cnt > MS32006_REG_MAX_SZ)
    {
   
        return -EIO;
    }

    ms32006_readdata(dev, reg_val);
    err = copy_to_user(buf, reg_val, MS32006_REG_MAX_SZ);

    return 0;
}

static int ms32006_release(struct inode *node, struct file *filp)
{
   
    struct ms32006_dev* dev = filp->private_data;

    if(!IS_ERR(dev->threads_handler->thread_a->motor_thread_p))
        kthread_stop(dev->threads_handler->thread_a->motor_thread_p);
    else
        pr_err(PREFIX "func:%s line:%d\n", __func__, __LINE__

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1530666.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析能力模型分析与展示

具体内容&#xff1a; 专业素质 专业素质-01 数据处理 能力定义•能通过各种数据处理工具及数据处理方法&#xff0c;对内外部海量数据进行清洗和运用&#xff0c;提供统一数据标准&#xff0c;为业务分析做好数据支持工作。 L1•掌握一…

【数据结构】数据结构和算法的重要性复杂度详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;数据结构_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.数据结构和算法 1.1什么是数据结构&#xff1f; 1.2 什么是算法&#xff1f; 1.3 数据结构和算法的重要性 1.4 如何学好数据…

线程和进程的区别和联系

一、什么是进程 进程(Process), 是一个具有独立功能的程序关于某个数据集合的一次运行活动&#xff0c;是系统进行 【资源分配和调度】 的一个独立单位。 进程是【程序】的【一次执行】(是计算机中程序的执行过程&#xff0c;而不是计算机中的程序)进程是系统进行【资源分配和…

【MySQL】知识点 + 1

# &#xff08;1&#xff09;查询当前日期、当前时间以及到2022年1月1日还有多少天&#xff0c;然后通过mysql命令执行命令。 select curdate() AS 当前日期,curtime() AS 当前时间,datediff(2022-01-01, curdate()) AS 距离2022年1月1日还有天数;# &#xff08;2&#xff09;利…

2024年 信息系统管理工程师(中级)

2024年信息系统管理工程师全套视频、历年真题及解析、历年真题视频解析、教材、模拟题、重点笔记等资料 1、2023、2022、2021、2020年全套教程精讲视频。 2、信息系统管理工程师历年真题及解析&#xff08;综合知识、案例分析&#xff09;、历年真题视频解析。 3、官方最新信…

【linux线程(三)】生产者消费者模型详解(多版本)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux线程 1. 前言2. 初识生产…

【数据结构】哈希表与哈希桶

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.概念 2.哈希冲突…

SylixOS工程如何生成map文件

生成.map文件通常是在编译链接阶段由编译器或链接器自动完成的。如果你需要手动生成.map文件&#xff0c;可以通过配置链接器选项来实现。 以bsp工程为例&#xff0c;在内核工程/libsylixos/SylixOS/mktemp/bsp.mk文件中添加-Wl,-Map,output.map选项来生成.map文件。

Java学习笔记:异常处理

Java学习笔记&#xff1a;异常处理 什么是异常异常体系结构&#xff1a;Error、Exception自定义异常 ​ **2024/3/19** 什么是异常 异常体系结构&#xff1a;Error、Exception 自定义异常

Orange3数据预处理(分类器组件)

创建类属性 从字符串属性创建类属性。 输入 数据&#xff1a;输入数据集 输出 数据&#xff1a;具有新类变量的数据集 功能 创建类属性功能从一个已存在的离散或字符串属性中创建一个新的类属性。该组件匹配所选属性的字符串值&#xff0c;并为匹配的实例构造一个新…

Spring Boot:筑基

Spring Boot 前言概述使用 Intellij idea 快速创建 Spring Boot 项目注意事项 前言 在学习 Spring 、SpringMVC 、MyBatis 和 JPA 框架的过程中&#xff0c;了解到 SSM 框架为 Java Web 开发提供了强大的后端支持&#xff0c;JPA 框架则简化了数据库的操作。然而&#xff0c;S…

【智能算法应用】智能算法优化BP神经网络思路

目录 1.思路2.设计 1.思路 在BP神经网络结构中&#xff0c;权值和阈值被视为模型的参数&#xff0c;它们在训练过程中需要通过反向传播算法进行学习&#xff0c;以使得网络的输出尽可能地接近真实标签。这意味着网络的目标是通过最小化均方误差&#xff08;MSE&#xff09;来调…

Docker专题-03 Log-Driver日志转存

Docker专题教程 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com 转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-03-19 csdn 博客名称&#xff1a;五维空间-影子&#xff0c;欢迎关注 说明 容器…

echarts饼图图例换行

legend: {left: "5%",bottom: "10%",orient: vertical,}, 完整代码 option {tooltip: {trigger: item},legend: {left: "5%",bottom: "10%",orient: vertical,},// legend: [// {// x: left,// left:"5%",// bottom: …

牛客题霸-SQL进阶篇(刷题记录二)

本文基于前段时间学习总结的 MySQL 相关的查询语法&#xff0c;在牛客网找了相应的 MySQL 题目进行练习&#xff0c;以便加强对于 MySQL 查询语法的理解和应用。 由于涉及到的数据库表较多&#xff0c;因此本文不再展示&#xff0c;只提供 MySQL 代码与示例输出。 部分题目因…

贪心算法(算法竞赛、蓝桥杯)--奶牛晒衣服

1、B站视频链接&#xff1a;A28 贪心算法 P1843 奶牛晒衣服_哔哩哔哩_bilibili 题目链接&#xff1a;奶牛晒衣服 - 洛谷 #include <bits/stdc.h> using namespace std; priority_queue<int> q;//用大根堆维护湿度的最大值 int n,a,b; int tim,maxn;int main(){s…

smodin(Al工具)

一、中文官网 ​​Smodin&#xff1a;多语言写作辅助​​ &#xff08;google账号登录&#xff09; ​​https://smodin.io/zh-cn/​​ 二、具体使用 2.1 写文章 (写 5 个或更多单词、一个问题或一个长标题。标题越好&#xff0c;文章就越好) 选择语言&#xff0c;输入标题…

数据之谜:解读Facebook的用户行为

在当今数字化时代&#xff0c;社交媒体平台已经成为人们生活中不可或缺的一部分&#xff0c;而Facebook作为全球最大的社交网络之一&#xff0c;其背后隐藏着许多数据之谜。本文将深入探讨Facebook的用户行为&#xff0c;并试图解读其中的奥秘。 用户行为数据的收集 Facebook作…

初探Springboot 参数校验

文章目录 前言Bean Validation注解 实践出真知异常处理 总结 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 前言 工作中我们经常会遇到验证字段是否必填&#xff0c;或者字段的值是否…

网络工程师练习题2

网络工程师 将专用IP地址转换为公用IP地址的技术是&#xff08;&#xff09;。 A.ARPB.DHCPC.UTMD.NAT 【答案】D 【解析】概念题&#xff0c;NAT技术将源地址从内部专用地址转换成可以在外部Internet上路由的全局IP地址。 R1、R2是一个自治系统中采用RIP路由协议的两个相…