IIC通信驱动MCP4017可编程电阻编程(3)

本文介绍了MCP4017可编程电阻的工作原理、应用领域,详细阐述了如何通过IIC通信进行电阻值设置,包括编程思路和驱动源码实例,以及电阻值数据的读取和转换方法。
摘要由CSDN通过智能技术生成

        接着前两篇博客文章对IIC通信协议进行了由浅入深的讲解,本篇博客文章将以IIC为基础,对可编程电阻进行驱动。如对IIC有疑问,可参考前两篇博客的内容,本文重点为MCP4017讲解。

        IIC通信驱动硬件编程 (1)-CSDN博客

        IIC通信驱动EEPROM,AT24C02硬件存储器编程(2)-CSDN博客

1、什么是可编程电阻

        可编程电阻,也被称为数字电位器或电子电位器,是一种可以通过数字信号控制其电阻值的电子设备。这些设备通常包含一个电阻阵列和一个电子开关网络,使得电阻值可以通过编程方式在多个预设值之间进行切换。

        可编程电阻在许多应用中都有用,以下是一些主要的应用领域:

        ①、音频控制:可编程电阻可以用于音量控制,平衡器,均衡器和其他音频处理设备中。通过改变电阻值,可以控制音频信号的增益,从而实现音量的调整或音频特性的改变。

        ②、电源管理:在电源管理系统中,可编程电阻可用于动态调整电源分配,以满足系统的实时需求。例如,在电池供电的设备中,通过调整电阻值,可以优化电池的使用寿命。

        ③、传感器校准:某些传感器可能需要通过调整电阻值来进行校准。使用可编程电阻,可以方便地调整传感器的输出,以使其更准确地反映实际情况。

        ④、电机控制:在电机控制系统中,可编程电阻可以用于调整电机的运行速度或调整电机的电流。这对于需要精确控制电机运行的应用来说非常有用。

        ⑤、测试与测量:在电子测试设备中,可编程电阻可以作为模拟负载,用于测试电源,放大器和其他电子设备的性能。

        ⑥、自动控制系统:在自动控制系统中,可编程电阻可以用于调整系统的反馈回路,以优化系统的性能。

2、MCP4017官方手册阅读

        由手册可知,MCP4017是一款可编程的电阻,其内置了7位寄存器,共计127个档位的分辨率。

        MCP4017可编程电阻的数据存储类型为RAM,即电阻值数据掉电丢失,其工作电压为1.8V~5.5v。且MCP4017系列提供了5kΩ,10kΩ、50kΩ、100kΩ这4种产品型号供用户选择。

        本文案例中使用了MCP4017-104E,它的阻值最大是100kΩ。

        MCP4017引脚功能描述说明图

        MCP4017芯片内部结构的抽象框图

        简单来说就是,通过IIC总线传输了对应的阻值模式命令后,电阻滑子W内部将会向上或向下变动,此时实际输出的电阻值为RAW或RBW。

        编程改变的是电阻滑子W在A、B端子间的位置,以此达到可编程电阻的目的。

        可编程电阻的电阻内部原理示意图

        由开发板的原理图可知,使用的MCP4017,引脚B已经进行了接地操作,而引脚A在芯片内部上拉并且未引出,因此可编程电阻的阻值为BW间的阻值即RBW。

        又因W引脚上接了一个10k的恒定电阻R17,如果PB17引脚配置为ADC读取电压值的话,那么PB14引脚可读到的电压值为 V0 = Rbw / (R17+Rbw) * VCC

MCP4017的IIC通信从机设备地址为 010 1111 ---> 0x2F

根据IIC通信协议标准可知:

MCP4017的数据设备地址为:0101 1110 ---> 5E

MCP4017的数据设备地址为:0101 1111 ---> 5F

MCP4017内部有127个档位,当写入某个电阻值时,其对应的开关便会接通,其它的开关保持断开状态,因此,写入的值越大,MCP4017的电阻值也越大。

可编程电阻的参数设置值范围为:0x00 ~ 0x7F也就是 0 ~ 127

MCP4017-104E的最大阻值为100kΩ。

3、MCP4017编程思路

        由手册可知,除了设备的地址数据外,MCP4017的数据位最多只有7位,最高位无论发送什么,都会被舍弃掉

        因官方参考手册是MCP4017/18/19三种型号产品写在一起的,所以本文的MCP4017编程思路同样适用于MCP4018、MCP4019等可编程电阻。

         ①、MCP4017写电阻数据编程思路

①、发送IIC启动信号

②、发送MCP4017的地址

③、接收MCP4017的有效应答信号(0)

④、发送写入的1个字节数据(0~127)

⑤、接收MCP4017的有效应答信号(0)

⑥、发送IIC停止信号

        ②、MCP4017读电阻数据编程思路

①、发送IIC启动信号

②、发送MCP4017的地址

③、接收MCP4017的有效应答信号(0)

④、接收MCP4017的阻值数据

⑤、发送无效应答信号(1)

⑥、发送IIC停止信号

4、MCP4017驱动编程源码

        因官方参考手册是MCP4017/18/19三种型号产品写在一起的,所以本文的MCP4017驱动编程源码同样适用于MCP4018、MCP4019等可编程电阻驱动。

        IIC驱动程序的编写有疑问的话,可参考前几天写的这两篇博客文章:

         IIC通信驱动硬件编程 (1)-CSDN博客

        IIC通信驱动EEPROM,AT24C02硬件存储器编程(2)-CSDN博客

①、MCP4017.h

#ifndef __MCP4017_H
#define __MCP4017_H

#define MCP4017_READ_ADDR    0x
#define MCP4017_WRITE_ADDR   0x

void MCP4017_Init(void);
int MCP4017_Read_Res_Val(unsigned char *val);
int MCP4017_Write_Res_Val(unsigned char val);

#endif

②、MCP4017.c

#include "MCP4017.h"

/**
  * @brief MCP4017初始化
  * @param None
  * @retval None
  */
void MCP4017_Init(void)
{
    IIC_Init();
}

/**
  * @brief MCP4017读取电阻阻值
  * @param val:读取到的阻值数据
  * @retval 成功返回0,失败返回-1
  */
int MCP4017_Read_Res_Val(unsigned char *val)
{
    int ret = 0;
    IIC_Start();
    IIC_Write_Byte(MCP4017_READ_ADDR);
    ret = IIC_Wait_Ack();
    if(ret != 0)
    {
        IIC_Stop();
        return -1;
    }
    *val = IIC_Read_Byte();
    IIC_Write_Ack(1);
    IIC_Stop();
    

    return 0;
}

/**
  * @brief MCP4017写电阻阻值
  * @param val:设定的电阻值
  * @retval 成功返回0,失败返回-1
  */
int MCP4017_Write_Res_Val(unsigned char val)
{
    int ret = 0;
    IIC_Start();
    IIC_Write_Byte(MCP4017_WRITE_ADDR);
    ret = IIC_Wait_Ack();
    if(ret != 0)
    {
        IIC_Stop();
        return -1;
    }
    IIC_Write_Byte(val);
    ret = IIC_Wait_Ack();
    if(ret != 0)
    {
        IIC_Stop();
        return -1;
    }
    IIC_Stop();
    
    return 0;
}

③、MCP4017实际驱动效果图

        通过上电运行程序发现,MCP4017可编程电阻的阻值默认为50kΩ。如果只是重新下载程序,那么读出来的数据还会是上一次写入的程序,只有彻底断电后,才会读出初始默认的阻值。

④、电阻值数据转换

        实际阻值 = 读出的数据 / 127 * 100kΩ

  • 31
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用 epoll 和 I2C 总线进行通信的字符设备驱动代码示例: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/i2c.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/semaphore.h> #include <linux/wait.h> #include <linux/epoll.h> #define DEVICE_NAME "i2c_char_device" #define CLASS_NAME "i2c_char_class" #define I2C_BUS_ID 1 #define I2C_SLAVE_ADDRESS 0x50 static struct i2c_client *client; static struct class *i2c_char_class = NULL; static struct device *i2c_char_device = NULL; static struct cdev i2c_char_cdev; static dev_t i2c_char_dev; static struct semaphore sem; static DECLARE_WAIT_QUEUE_HEAD(waitq); static struct epoll_event event; static int epoll_fd; static int i2c_char_open(struct inode *inode, struct file *filp) { if (down_interruptible(&sem)) return -ERESTARTSYS; return 0; } static int i2c_char_release(struct inode *inode, struct file *filp) { up(&sem); return 0; } static ssize_t i2c_char_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval = 0; char *data = kzalloc(count, GFP_KERNEL); if (!data) return -ENOMEM; if (down_interruptible(&sem)) return -ERESTARTSYS; if (i2c_master_recv(client, data, count) != count) { retval = -EIO; goto out; } if (copy_to_user(buf, data, count)) { retval = -EFAULT; goto out; } retval = count; out: kfree(data); up(&sem); return retval; } static ssize_t i2c_char_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval = 0; char *data = kzalloc(count, GFP_KERNEL); if (!data) return -ENOMEM; if (copy_from_user(data, buf, count)) { retval = -EFAULT; goto out; } if (down_interruptible(&sem)) return -ERESTARTSYS; if (i2c_master_send(client, data, count) != count) { retval = -EIO; goto out; } retval = count; out: kfree(data); up(&sem); return retval; } static struct file_operations i2c_char_fops = { .owner = THIS_MODULE, .open = i2c_char_open, .release = i2c_char_release, .read = i2c_char_read, .write = i2c_char_write, }; static int i2c_char_probe(struct i2c_client *cl, const struct i2c_device_id *id) { int ret = 0; client = cl; if (alloc_chrdev_region(&i2c_char_dev, 0, 1, DEVICE_NAME) < 0) { ret = -1; goto out; } cdev_init(&i2c_char_cdev, &i2c_char_fops); i2c_char_cdev.owner = THIS_MODULE; if (cdev_add(&i2c_char_cdev, i2c_char_dev, 1) < 0) { ret = -1; goto out_unregister; } i2c_char_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(i2c_char_class)) { ret = PTR_ERR(i2c_char_class); goto out_cdev_del; } i2c_char_device = device_create(i2c_char_class, NULL, i2c_char_dev, NULL, DEVICE_NAME); if (IS_ERR(i2c_char_device)) { ret = PTR_ERR(i2c_char_device); goto out_class_destroy; } epoll_fd = epoll_create(1); if (epoll_fd == -1) { ret = -1; goto out_device_destroy; } event.events = EPOLLIN | EPOLLRDHUP; event.data.fd = i2c_char_dev; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, i2c_char_dev, &event) == -1) { ret = -1; goto out_epoll_create; } sema_init(&sem, 1); return 0; out_epoll_create: close(epoll_fd); out_device_destroy: device_destroy(i2c_char_class, i2c_char_dev); out_class_destroy: class_destroy(i2c_char_class); out_cdev_del: cdev_del(&i2c_char_cdev); out_unregister: unregister_chrdev_region(i2c_char_dev, 1); out: return ret; } static int i2c_char_remove(struct i2c_client *cl) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, i2c_char_dev, &event); close(epoll_fd); device_destroy(i2c_char_class, i2c_char_dev); class_destroy(i2c_char_class); cdev_del(&i2c_char_cdev); unregister_chrdev_region(i2c_char_dev, 1); return 0; } static const struct i2c_device_id i2c_char_id[] = { { "i2c_char_device", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, i2c_char_id); static struct i2c_driver i2c_char_driver = { .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, }, .probe = i2c_char_probe, .remove = i2c_char_remove, .id_table = i2c_char_id, }; static int __init i2c_char_init(void) { return i2c_add_driver(&i2c_char_driver); } static void __exit i2c_char_exit(void) { i2c_del_driver(&i2c_char_driver); } module_init(i2c_char_init); module_exit(i2c_char_exit); MODULE_LICENSE("GPL"); ``` 在该示例中,使用了 I2C 总线驱动来进行数据传输,同时使用了 epoll 来实现异步读取。该驱动支持以下操作: - 打开设备文件时会获取一个信号量。 - 读取设备文件时会等待数据的到达,然后从 I2C 总线上读取数据并返回给用户空间。 - 写入设备文件时会将数据写入到 I2C 总线上。 - 关闭设备文件时会释放信号量。 在驱动的 probe 函数中,还创建了一个 epoll 实例,并将设备文件添加到 epoll 实例中进行监听。这样,在用户空间中就可以使用 epoll 等待设备文件中的数据到达,从而实现异步读取。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值