当前位置: 首页 > news >正文

fmql之Linux中I2C总线框架

正点原子第44章        

I2C

 

zynq I2C

 

 

pcf8563芯片

我们用的是ds3231.

 

Linux I2C总线框架

 I2C总线驱动

这部分内容是半导体厂商编写的。

I2C总线设备

 

 

 zynq I2C适配器驱动

 

 

I2C设备驱动编写

 

使用设备树

 

代码编写

 设备树修改

 设备驱动编写

因为用的是ds3231,所以先找compatible的信息。

/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名    : ds3231.c作者      : Skylar版本      : V1.0描述      : IIC其他      : DS3231SN论坛      : www.openedv.com日志      : 初版V1.0 2024/10/10 创建***************************************************************//************************	dts
/{ // 根节点......
};&i2c0 {clock-frequency = <100000>;		// 100KHzrtc@68 {compatible = "maxim,ds3231";reg = <0x68>;};
};************************/#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
// #include <linux/input.h>
// #include <linux/timer.h>
// #include <linux/of_irq.h>
// #include <linux/interrupt.h>
// #include <linux/input-event-codes.h>
#include <linux/i2c.h>
#include <linux/bcd.h>#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/kern_levels.h>#define DEVICE_NAME		"ds3231"/** ds3231 内部寄存器定义*/
// #define DS3231_CTL_STATUS_1		0x00 /* 控制寄存器 1 */
// #define DS3231_CTL_STATUS_2		0x01 /* 控制寄存器 2 */
#define DS3231_VL_SECONDS		0x00 /* 时间: 秒 */
#define DS3231_MINUTES			0x01 /* 时间: 分 */
#define DS3231_HOURS			0x02 /* 时间: 小时 */
#define DS3231_DAYS				0x04 /* 日期: 天 */
#define DS3231_WEEKDAYS			0x03 /* 日期: 星期 */
#define DS3231_CENTURY_MONTHS	0x04 /* 日期: 月 */
#define DS3231_YEARS			0x06 /* 日期: 年 */#define YEAR_BASE				2000 /* 20xx 年 *//** 自定义结构体,用于表示时间和日期信息*/
struct ds3231_time {int sec; // 秒int min; // 分int hour; // 小时int day; // 日int wday; // 星期int mon; // 月份int year; // 年
};/** 自定义结构体 ds3231_dev* 用于描述 ds3231 设备*/
struct ds3231_dev {struct i2c_client *client; /* i2c 次设备 */dev_t devid; /* 设备号 */struct cdev cdev; /* cdev 结构体 */struct class *class; /* 类 */struct device *device; /* 设备 */
};static struct ds3231_dev ds3231;/** @description : 向 ds3231 设备多个连续的寄存器写入数据* @param – dev : ds3231 设备* @param – reg : 要写入的寄存器首地址* @param – buf : 待写入的数据缓存区地址* @param – len : 需要写入的字节长度* @return : 成功返回 0,失败返回一个负数*/
static int ds3231_write_reg(struct ds3231_dev *dev, u8 reg, u8 *buf, u8 len)
{struct i2c_client *client = dev->client;struct i2c_msg msg;u8 send_buf[17] = {0};int ret;if (16 < len) {dev_err(&client->dev, "%s: error: Invalid transfer byte length %d\n",__func__, len);return -EINVAL;}send_buf[0] = reg; // 寄存器首地址memcpy(&send_buf[1], buf, len); // 将要写入的数据存放到数组 send_buf 后面msg.addr = client->addr; // ds3231 从机地址msg.flags = client->flags; // 标记为写数据msg.buf = send_buf; // 要写入的数据缓冲区msg.len = len + 1; // 要写入的数据长度ret = i2c_transfer(client->adapter, &msg, 1);if (1 != ret) {dev_err(&client->dev, "%s: error: reg=0x%x, len=0x%x\n",__func__, reg, len);return -EIO;}return 0;
}/** @description : 从 ds3231 设备中读取多个连续的寄存器数据* @param – dev : ds3231 设备* @param – reg : 要读取的寄存器首地址* @param – buf : 数据存放缓存区地址* @param – len : 读取的字节长度* @return : 成功返回 0,失败返回一个负数*/
static int ds3231_read_reg(struct ds3231_dev *dev, u8 reg, u8 *buf, u8 len)
{struct i2c_client *client = dev->client;struct i2c_msg msg[2];int ret;/* msg[0]: 发送消息 */msg[0].addr = client->addr; // ds3231 从机地址msg[0].flags = client->flags; // 标记为写数据msg[0].buf = &reg; // 要写入的数据缓冲区msg[0].len = 1; // 要写入的数据长度/* msg[1]: 接收消息 */msg[1].addr = client->addr; // ds3231 从机地址msg[1].flags = client->flags | I2C_M_RD; // 标记为读数据msg[1].buf = buf; // 存放读数据的缓冲区msg[1].len = len; // 读取的字节长度ret = i2c_transfer(client->adapter, msg, 2);if (2 != ret) {dev_err(&client->dev, "%s: error: reg=0x%x, len=0x%x\n",__func__, reg, len);return -EIO;}return 0;
}/** @description : 打开设备* @param – inode : 传递给驱动的 inode* @param – filp : 设备文件,file 结构体有个叫做 private_data 的成员变量* 一般在 open 的时候将 private_data 指向设备结构体。* @return : 0 成功;其他 失败*/
static int ds3231_open(struct inode *inode, struct file *filp)
{filp->private_data = &ds3231;return 0;
}/** @description : 从设备读取数据* @param – filp : 要打开的设备文件(文件描述符)* @param – buf : 返回给用户空间的数据缓冲区* @param – cnt : 要读取的数据长度* @param – off : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t ds3231_read(struct file *filp, char __user *buf,size_t cnt, loff_t *off)
{struct ds3231_dev *dev = filp->private_data;struct i2c_client *client = dev->client;struct ds3231_time time = {0};u8 read_buf[9] = {0};int ret;/* 读寄存器数据 */// ret = ds3231_read_reg(dev, DS3231_CTL_STATUS_1,// 				read_buf, 9);// if (ret)// 	return ret;/* 校验时钟完整性 */if (read_buf[DS3231_VL_SECONDS] & 0x80) {dev_err(&client->dev,"low voltage detected, date/time is not reliable.\n");return -EINVAL;}/* 将 BCD 码转换为数据得到时间、日期 */time.sec = bcd2bin(read_buf[DS3231_VL_SECONDS] & 0x7F); // 秒time.min = bcd2bin(read_buf[DS3231_MINUTES] & 0x7F); // 分time.hour = bcd2bin(read_buf[DS3231_HOURS] & 0x3F); // 小时time.day = bcd2bin(read_buf[DS3231_DAYS] & 0x3F); // 日time.wday = read_buf[DS3231_WEEKDAYS] & 0x07; // 星期time.mon = bcd2bin(read_buf[DS3231_CENTURY_MONTHS] & 0x1F); // 月time.year = bcd2bin(read_buf[DS3231_YEARS]) + YEAR_BASE; // 年/* 将数据拷贝到用户空间 */return copy_to_user(buf, &time, sizeof(struct ds3231_time));
}/** @description : 向设备写数据* @param – filp : 设备文件,表示打开的文件描述符* @param – buf : 要写给设备写入的数据* @param – cnt : 要写入的数据长度* @param – offt : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t ds3231_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{struct ds3231_dev *dev = filp->private_data;struct ds3231_time time = {0};u8 write_buf[9] = {0};int ret;ret = copy_from_user(&time, buf, cnt); // 得到应用层传递过来的数据if(0 > ret)return -EFAULT;/* 将数据转换为 BCD 码 */write_buf[DS3231_VL_SECONDS] = bin2bcd(time.sec); // 秒write_buf[DS3231_MINUTES] = bin2bcd(time.min); // 分write_buf[DS3231_HOURS] = bin2bcd(time.hour); // 小时write_buf[DS3231_DAYS] = bin2bcd(time.day); // 日write_buf[DS3231_WEEKDAYS] = time.wday & 0x07; // 星期write_buf[DS3231_CENTURY_MONTHS] = bin2bcd(time.mon); // 月write_buf[DS3231_YEARS] = bin2bcd(time.year % 100); // 年/* 将数据写入寄存器 */ret = ds3231_write_reg(dev, DS3231_VL_SECONDS,&write_buf[DS3231_VL_SECONDS], 7);if (ret)return ret;return cnt;
}/** @description : 关闭/释放设备* @param – filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int ds3231_release(struct inode *inode, struct file *filp)
{return 0;
}/** file_operations 结构体变量*/
static const struct file_operations ds3231_ops = {.owner		= THIS_MODULE,.open		= ds3231_open,.read		= ds3231_read,.write		= ds3231_write,.release	= ds3231_release,
};/** @description : 初始化函数* @param – pdev : platform 设备指针* @return : 成功返回 0,失败返回负数*/
static int ds3231_init(struct platform_device *pdev)
{u8 val;int ret;ret = ds3231_read_reg(dev, DS3231_VL_SECONDS, &val, 1); // 读 VL_SECONDS 寄存器if (ret)return ret;val &= 0x7F; // 将寄存器最高一位清零,也就是将 VL 位清零return ds3231_write_reg(dev, DS3231_VL_SECONDS, &val, 1); // 写入VL_SECONDS寄存器
}/** @description : platform 驱动的 probe 函数,当驱动与设备* 匹配成功以后此函数会被执行* @param – pdev : platform 设备指针* @return : 0,成功;其他负值,失败*/
static int ds3231_probe(struct platform_device *pdev)
{int ret;dev_info(&pdev->dev, "I2C driver and device have been matched\n");/* 初始化 ds3231 */ds3231.client = client;ret = ds3231_init(&ds3231);if (ret)return ret;/* 申请设备号 */ret = alloc_chrdev_region(&ds3231.devid, 0, 1, DEVICE_NAME);if (ret)return ret;/* 初始化字符设备 cdev */ds3231.cdev.owner = THIS_MODULE;cdev_init(&ds3231.cdev, &ds3231_ops);/* 添加 cdev */ret = cdev_add(&ds3231.cdev, ds3231.devid, 1);if (ret)goto out1;/* 创建类 class */ds3231.class = class_create(THIS_MODULE, DEVICE_NAME);if (IS_ERR(ds3231.class)) {ret = PTR_ERR(ds3231.class);goto out2;}/* 创建设备 */ds3231.device = device_create(ds3231.class, &client->dev,ds3231.devid, NULL, DEVICE_NAME);if (IS_ERR(ds3231.device)) {ret = PTR_ERR(ds3231.device);goto out3;}i2c_set_clientdata(client, &ds3231);return 0;out3:class_destroy(ds3231.class);out2:cdev_del(&ds3231.cdev);out1:unregister_chrdev_region(ds3231.devid, 1);return ret;
}/** @description : platform 驱动的 remove 函数,当 platform 驱动模块* 卸载时此函数会被执行* @param – dev : platform 设备指针* @return : 0,成功;其他负值,失败*/
static int ds3231_remove(struct platform_device *pdev)
{struct ds3231_dev *ds3231 = i2c_get_clientdata(client);/* 注销设备 */device_destroy(ds3231->class, ds3231->devid);/* 注销类 */class_destroy(ds3231->class);/* 删除 cdev */cdev_del(&ds3231->cdev);/* 注销设备号 */unregister_chrdev_region(ds3231->devid, 1);return 0;
}/* 匹配列表 */
static const struct of_device_id ds3231_of_match[] = {{.compatible = "maxim,ds3231"},{/* Sentinel */}
};static struct platform_driver ds3231_driver = {.driver = {.name			= "ds3231",		/* platform_driver name*/.of_match_table	= ds3231_of_match,},.probe = ds3231_probe,.remove = ds3231_remove,
};module_platform_driver(ds3231_driver);MODULE_AUTHOR("Skylar <Skylar@33.com>");
MODULE_DESCRIPTION("I2C Driver, Input Subsystem");
MODULE_LICENSE("GPL");

/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名                 : ds3231APP.c作者                   : Skylar版本                   : V1.0描述                   : I2C   RTC其他                   : DS3231SN使用方法                : ./ds3231APP /dev/ds3231 read./ds3231APP /dev/ds3231 write论坛                   : www.openedv.com日志                   : 初版V1.0 2024/10/10 创建***************************************************************/#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
// #include <sys/ioctl.h>
// #include <signal.h>	struct ds3231_time {int sec; // 秒int min; // 分int hour; // 小时int day; // 日int wday; // 星期int mon; // 月份int year; // 年
};/** @description : main 主程序* @param – argc : argv 数组元素个数* @param – argv : 具体参数* @return : 0 成功;其他 失败*/
int main(int argc, char *argv[]
{int fd, ret;struct ds3231_time time = {0};if(argc != 3){printf("Usage:\n""\t./keyinputApp /dev/input/eventX  @ Open Key\n");return -1;}/* 打开设备 */fd = open(argv[1], O_RDWR);if(fd < 0){printf("Erroe: file %s open failed\r\n", argv[1]);return -1;}if(!strcmp(argv[2], "read"))    // 读取时间{/* 读取RTC */ret = read(fd, &time, sizeof(struct ds3231_time));if(ret < 0){printf("Error: file %s read failed\r\n", argv[1]);goto out;}printf("%d-%d-%d %d:%d:%d ", time.year, time.mon, time.day,time.hour, time.min, time.sec);switch(time.wday){case 0:printf("Sunday\n");break;case 1:printf("Monday\n");break;case 2:printf("Tuesday\n");break;case 3:printf("Wednesday\n");break;case 4:printf("Thursday\n");break;case 5:printf("Friday\n");break;case 6:printf("Saturday\n");break;}} else {int data;printf("Year: ");scanf("%d", &data);time.year = data;printf("Month: ");scanf("%d", &data);time.mon = data;printf("Day: ");scanf("%d", &data);time.day = data;printf("Date: ");scanf("%d", &data);time.wday = data;printf("Hour: ");scanf("%d", &data);time.hour = data;printf("Minute: ");scanf("%d", &data);time.min = data;printf("Second: ");scanf("%d", &data);time.sec = data;write(fd, &time, sizeof(struct ds3231_time));}out:close(fd);return 0;
})

 

运行测试

modprobe ds3231.ko

但是:

没有/dev/ds3231:

dts:(因为是模拟i2c)

i2c_gpio0: i2c-gpio-0 {#address-cells = <1>;#size-cells = <0>;compatible = "dallas,ds3232","i2c-gpio";// MIO56-SDA, MIO55-SCLgpios = <&portc 2 0&portc 1 0 >;status = "okay";i2c-gpio,delay-us = <5>; // 100k Hzrtc@68 {compatible = "maxim,ds3231";reg = <0x68>;	//IDstatus = "okay";};};

对比教程的dts:

 原来是因为,_of_match同时有了i2c-gpio0和rtc的匹配的compatible:(ds3231.c)

/* 匹配列表 */
static const struct of_device_id ds3231_of_match[] = {{.compatible = "maxim,ds3231"},{.compatible = "dallas,ds3232"},{.compatible = "i2c-gpio"},{/* Sentinel */}
};static struct i2c_driver ds3231_driver = {.driver = {.name			= "ds3231",		/* platform_driver name*/.of_match_table	= ds3231_of_match,},.probe = ds3231_probe,.remove = ds3231_remove,
};

修改成只匹配rtc@68的compatible属性。

RTC驱动框架

正点原子第45章

 

 

 

 AXI IIC

正点原子第46章

(目前不需要这部分,就先不看了)

需求

板子上的i2c是模拟i2c,所以需要i2c-gpio.c

而i2c的用途是与ds3231通信,也就是rtc的功能,所以也需要rec-ds3232.c

但是在这两个c文件中,前者实现了gpio电平翻转,后者实现了时间的收发。

Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线_i2c-gpio.c-CSDN博客

所以,要在i2c-gpio.c的基础上,添加i2c的通信协议(自己写吧?)。

所以是要在APP.c中实现?


http://www.mrgr.cn/news/57567.html

相关文章:

  • css动画烟花秀__烟花效果
  • 软考数据结构 -- (时间复杂度,线性结构,线性表,栈,队列,串,数组,矩阵,树,图)
  • C++在实际项目中的应用第一课:游戏开发中的C++
  • 从新手到高手:map和set的使用技巧全攻略(C++)
  • Redis持久化机制RDB持久化和AOF持久化
  • C++(week18): C++项目:搜索引擎
  • 开源模型应用落地-Qwen2-VL-7B-Instruct-vLLM-OpenAI API Client调用
  • 基于RabbitMQ,Redis,Redisson,RocketMQ四种技术实现订单延时关闭功能及其相关优缺点介绍(以12306为主题)
  • Stability.AI 发布 SD3.5 模型,能否逆袭击败 FLUX?如何在ComfyUI中的使用SD3.5?
  • 使用gpt2-medium基座说明模型微调
  • anolis os 8.8 修改kube-proxy的模式为ipvs-kubeadm部署
  • arcgis pro 3.3.1安装教程
  • 重学SpringBoot3-Spring WebFlux之HttpHandler和HttpServer
  • 代码随想录算法训练营第二十五天 | 491.递增子序列 46.全排列 47.全排列Ⅱ
  • LeetCode练习-删除链表的第n个结节
  • Hot100速刷计划day04(10-12)
  • 【网页布局技术】项目六 制作表格并使用CSS美化
  • 【Linux】进程信号(下)
  • CCRC-CDO首席数据官的主要工作内容
  • 全新原生鸿蒙HarmonyOS NEXT发布,书写国产操作系统新篇章!同时,触觉智能发布OpenHarmony5.0固件
  • (一)ArkTS语言——申明与类型
  • day7:软件包管理
  • 力扣247题详解:中心对称数 II 的多种解法与模拟面试
  • 自动粘贴神器,数据复制粘贴快速处理记事本
  • RK平台操作GPIO的两种方法
  • 爬虫中代理ip的选择和使用实战