不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动

一、前言

本文是系列文章的第3篇,介绍如何利用内核中现有的i2c bus驱动,在最少的代码量上,做到对slaver设备的控制。基于现有的i2c bus驱动,我们可直接在应用层编写slaver设备的控制代码!如此,大大降低了设备驱动的开发难度。当然,工资还是得照样领!

本文开发板基于Amlogic T972平台, Android 9.0

二、系列文章

第1篇 :不写一行代码(一):实现安卓基于GPIO的LED设备驱动

第2篇 :不写一行代码(二):实现安卓基于PWM的LED设备驱动

第3篇:不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动

三、准备工作

3.1 挑选I2C引脚

从芯片datasheet,结合开发板可用插座脚,如下所示,可挑选GPIOZ_1(SDA)、GPIOZ_2(SCL)作为实验对象

图一:Amlogic T972 Datasheet

image

图二:开发板J31引出的两排引脚

image

3.2 测试设备:QMI8658C

找了半天,没有更简单的i2c模块(例如基于I2C接口的热传感器,寄存器会很简单),只有一个六轴陀螺仪模块,所以就将就用吧!

image

  • VCC 3.3V – 红线
  • GND – 黑线
  • SCL2 – 黄线,连接GPIOZ_2(排针J31的PIN9)
  • SDA2 – 绿线 ,连接GPIOZ_1(排针J31的PIN8)
  • 白色和蓝色未使用

四、编写设备树节点

4.1 查找MUX

默认情况下,GPIOZ_1和GPIOZ_2是普通的GPIO引脚,无法作为I2C引脚来使用,所以我们需要修改这两个引脚的MUX配置,这些配置都是原厂已经写好的,我们只需找出来,然后填入到i2c1的控制器配置中即可。

//文件:common\arch\arm\boot\dts\amlogic\mesontl1.dtsi

	i2c1_z_pins:i2c1_z {
		mux {
			groups = "i2c1_sda_z",
				"i2c1_sck_z";
			function = "i2c1";
			bias-pull-up;
			drive-strength = <3>;
		};
	};

	/*
	 * i2c sleep mode1: set the gpio the input
	 * in this scene, pull up power is off
	 */
	i2c1_z_pins_slp_input:i2c1_z_slp_input {
		mux {
			groups = "GPIOZ_1", "GPIOZ_2";
			function = "gpio_periphs";
			input-enable;
			bias-disable;
		};
	};

4.2 修改i2c1引脚配置

4.2.1 修改前

&i2c1 {
	status = "okay";
	clock-frequency = <300000>;
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&i2c1_h_pins>; //未修改前,i2c1模块MUX连接的PIN脚不是GPIOZ_1和GPIOZ_2
	pinctrl-1 = <&i2c1_h_pins_slp_input>;//未修改前,i2c1模块MUX连接的PIN脚不是GPIOZ_1和GPIOZ_2

// …… 略 …… 
//添加测试所用设备的DTS节点描述
	i2c_tof_geo@6a {
		compatible = "test_i2c, i2c";
		reg = <0x6a>;
		status = "okay";
	};
};

4.2.2 修改后

&i2c1 {
	status = "okay";
	clock-frequency = <300000>;
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&i2c1_z_pins>; //选用GPIOZ_1和GPIOZ_2的PIN脚配置,GPIOZ_1和GPIOZ_2将连通i2c1控制器模块
	pinctrl-1 = <&i2c1_z_pins_slp_input>;

// …… 略 …… 
//添加测试所用设备的DTS节点描述,测试设备的slaver address=0x6A
	i2c_tof_geo@6a {
		compatible = "test_i2c, i2c";
		reg = <0x6a>;
		status = "okay";
	};
};

五、编译、烧录dt.img

参考系列文章第一篇(第四节):不写一行代码(一):实现安卓基于GPIO的LED设备驱动

5.1 烧录后效果

1)烧录后,通过sys-fs系统,切换到如下目录,可以看到设备1-006a,也就是i2c-1的第1个设备,地址为0x6A
x301:/sys # cd bus/i2c/devices/i2c-1/
x301:/sys/bus/i2c/devices/i2c-1 # ls
1-006a delete_device device i2c-dev name new_device of_node power subsystem uevent

(2) 继续进入1-006a设备的目录下,查看其设备树节点名称,可以看到:i2c_tof_geo
x301:/sys/bus/i2c/devices/i2c-1 # cd 1-006a/
x301:/sys/bus/i2c/devices/i2c-1/1-006a # ls
modalias name of_node power subsystem uevent
x301:/sys/bus/i2c/devices/i2c-1/1-006a # cat of_node/name
i2c_tof_geo
x301:/sys/bus/i2c/devices/i2c-1/1-006a #

图示:

在这里插入图片描述

六、编写test程序

6.1 创建文件

如下在AOSP代码的development目录下:

(1)创建目录,姑且就叫 i2c-slaver

(2)创建 Android.mk 和 i2c-slaver.c 文件

szhou@bc04:~/T972/android_x301/source/t962x3-t972-android9.0/development$ tree i2c-slaver/
i2c-slaver/
├── Android.mk
└── i2c-slaver.c

0 directories, 2 files
szhou@bc04:~/T972/android_x301/source/t962x3-t972-android9.0/development$ 

6.2 源码:Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := i2c-slaver

LOCAL_SRC_FILES := i2c-slaver.c

include $(BUILD_EXECUTABLE)

6.3 源码:i2c-slaver.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/string.h>

static int i2c_trans(int fd, __u8 rw, __u8 cmd, __u32 size, union i2c_smbus_data *data)
{
	struct i2c_smbus_ioctl_data args;

	args.read_write = rw;//0:I2C_SMBUS_WRITE, 1:I2C_SMBUS_READ
	args.command = cmd;
	args.size = size;
	args.data = data;
	return ioctl(fd,I2C_SMBUS,&args);
}

 static int i2c_write(int fd, __u8 cmd, __u8 value)
 {
	 union i2c_smbus_data data;
	 data.byte = value;
	 return i2c_trans(fd,I2C_SMBUS_WRITE,cmd, I2C_SMBUS_BYTE_DATA, &data);
 }
									 
 static int  i2c_read(int fd, __u8 cmd, __u32 size, union i2c_smbus_data *data)
 {
	 __u32 i=0;
	 if (i2c_trans(fd,I2C_SMBUS_READ,cmd, size, data))
	 {
	 	 printf("i2c_trans failed\n");
		 return -1;
	 }
	 else
	 {
	 	 printf("\n");
		 for(i=0; i<size; i++){
			 printf("data[%d]=0x%02X \t", i, data->block[i]);
		 }
		 printf("\n");
		 return 0;
	 }
 }


int main()
{
	int fd;
	union i2c_smbus_data data;

	//打开i2c-1总线控制器
	fd = open("/dev/i2c-1", O_RDWR);
	if (fd == -1)
	{
		printf("zs, open i2c failed. \n");
		goto fail;

	}

	//访问Slaver地址为0x6A的设备
	if (ioctl(fd, I2C_SLAVE, 0x6A) < 0)
	{
		printf("zs, open i2c I2C_SLAVE failed. \n");
		goto fail;
	
	}

	//发送CMD初始化测试用的i2c-slaver设备,是一个陀螺仪,各位请换成自己的i2c设备
	i2c_write(fd, 0x06, 0xB0);
	usleep(300000);
	i2c_write(fd, 0x02, 0x60);
	i2c_write(fd, 0x03, 0x24);
	i2c_write(fd, 0x08, 0x83);

	//读取陀螺仪的X、Y、Z轴的原始数据,各2bytes,一共6bytes
	while(i2c_read(fd, 0x35, 6, &data) != -1){
		usleep(3*1000*1000); //每3秒读取一次
	} 

fail:
	close(fd);	
	return 0;
}

6.4 编译test程序

  • 注意:使用局部编译前,一般需要对整个源码做一次完整编译!
  • 生成位置:Install: out/target/product/x301/system/bin/i2c-slaver

编译步骤:

(1) source build/envsetup.sh
(2) lunch your-board
(3) make i2c-slaver

编译打印

root@d185403d1e6f:/home/builder/android_x301/source/t962x3-t972-android9.0# make i2c-slaver
// …… 略 ……
[100% 6/6] Install: out/target/product/x301/system/bin/i2c-slaver

#### build completed successfully (01:48 (mm:ss)) ####

root@d185403d1e6f:/home/builder/android_x301/source/t962x3-t972-android9.0#

七、测试

7.1 部署 i2c-slaver

1)ADB 推送i2c-slaver到板子上
Z:\T972\android_x301\source\t962x3-t972-android9.0\out\target\product\x301\system\bin>adb push i2c-slaver /data/
i2c-slaver: 1 file pushed, 0 skipped. 0.9 MB/s (16016 bytes in 0.016s)2)添加可执行权限
Z:\T972\android_x301\source\t962x3-t972-android9.0\out\target\product\x301\system\bin>adb shell chmod 777 /data/i2c-slaver

图示如下:
image

7.2 运行 i2c-slaver

因为打印默认是输出到串口的,所以运行部分,我们在串口上执行,当然,你也可以通过adb执行,之后dmesg查看打印

130|:/data # ./i2c-slaver                                                      

//  ……  略 ……
data[0]=0x20    data[1]=0xD4    data[2]=0xF5    data[3]=0x4A    data[4]=0xF4    data[5]=0xBF 
[17541.246189@2]- [dhd-wlan0] wl_run_escan : LEGACY_SCAN sync ID: 176, bssidx: 0
data[0]=0x20    data[1]=0xCE    data[2]=0xF5    data[3]=0x4D    data[4]=0xF4    data[5]=0xB9 
//  ……  略 ……

data[0]=0x20    data[1]=0xF7    data[2]=0xF1    data[3]=0x99    data[4]=0xF9    data[5]=0x6E 
^C
130|:/data # 

运行效果的图示如下,通过晃动陀螺仪模块,可以看到它的值会大幅变动,如果静置的话,则值能基本保持不变或变化很小

图示如下:
image

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是一个使用8051单片机模拟I2C从设备(Slave)的示例代码: ```c #include <reg51.h> #define SDA P1_0 // SDA引脚连接到P1.0 #define SCL P1_1 // SCL引脚连接到P1.1 void I2C_Delay() { // 实现适当的延时函数,用于同步时钟信号 } void I2C_Start() { SDA = 1; // 确保SDA线为高电平 SCL = 1; // 确保SCL线为高电平 I2C_Delay(); SDA = 0; // 发送启动信号,将SDA线拉低 I2C_Delay(); SCL = 0; // 拉低SCL线,准备发送数据 } void I2C_Stop() { SDA = 0; // 确保SDA线为低电平 SCL = 1; // 确保SCL线为高电平 I2C_Delay(); SDA = 1; // 发送停止信号,将SDA线拉高 I2C_Delay(); } unsigned char I2C_Write(unsigned char data) { unsigned char i, ack; for (i = 0; i < 8; i++) { SDA = (data & 0x80) ? 1 : 0; // 发送数据的最高位到SDA线 data <<= 1; SCL = 1; // 拉高SCL线,发送数据位 I2C_Delay(); SCL = 0; // 拉低SCL线,准备发送下一位 I2C_Delay(); } SDA = 1; // 释放SDA线,等待应答 SCL = 1; // 拉高SCL线 I2C_Delay(); ack = SDA; // 读取应答信号 SCL = 0; // 拉低SCL线,准备接收下一个字节 I2C_Delay(); return ack; } unsigned char I2C_Read() { unsigned char i, data = 0; SDA = 1; // 确保SDA线为高电平 for (i = 0; i < 8; i++) { SCL = 1; // 拉高SCL线,准备接收数据位 I2C_Delay(); data <<= 1; data |= SDA; // 读取SDA线上的数据位 SCL = 0; // 拉低SCL线,准备接收下一位 I2C_Delay(); } return data; } void I2C_Ack() { SDA = 0; // 发送应答信号,将SDA线拉低 SCL = 1; // 拉高SCL线 I2C_Delay(); SCL = 0; // 拉低SCL线,准备接收下一个字节 I2C_Delay(); SDA = 1; // 释放SDA线 } void I2C_Nack() { SDA = 1; // 发送非应答信号,将SDA线拉高 SCL = 1; // 拉高SCL线 I2C_Delay(); SCL = 0; // 拉低SCL线,准备接收下一个字节 I2C_Delay(); SDA = 1; // 释放SDA线 } void I2C_Slave_Init() { // 初始化相关引脚和其他配置,如中断、地址等 } void I2C_Slave_Handler() { unsigned char data; // 处理主设备发送的命令和数据 data = I2C_Read(); // 根据接收到的数据进行相应操作 if (data == 0x00) { // 执行某个操作 } else if (data == 0x01) { // 执行另一个操作 } // 发送应答 I2C_Ack(); } void main() { I2C_Slave_Init(); while (1) { I2C_Start(); I2C_Slave_Handler(); I2C_Stop(); } } ``` 这是一个简单的8051单片机模拟I2C从设备代码示例。你需要根据具体的硬件连接和需求进行适当的修改和配置。同时,还需要根据你所使用的开发环境和编译器进行相应的设置和调整。请确保仔细阅读8051单片机的文档以及相关资料,并根据实际情况进行适当的修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿迷创客

感谢!您的支持是我写作的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值