stm32与Linux.通信,linux下编写I2C驱动与stm32通信(一)

本文介绍了一个基于海思Hi3518e的嵌入式Linux系统的I2C驱动开发过程,重点解决了GPIO引脚控制问题,并详细描述了驱动的实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近做一个IPC的项目,其中用了海思的一套解决方案,用Hi3518e作为主芯片,上面搭载嵌入式linux系统。由于可行性验证阶段,没有做芯片级,而是先从系统级做起,用了一块已经移植好linux系统,带有网络文件系统服务的板子,该板子是专用于rtsp视频传输的,预留的引脚是在太少,只有两个用于IRCUT的引脚,而我们不仅仅需要rtsp服务,还需要在rtsp视频流中加入九轴陀螺仪的数据一起提供给上位机解析,只得再加一块stm32板子,用IRCUT的两个GPIO口模拟I2C与stm32通信,将与九轴陀螺仪通信以及控制LED亮度和电机控制等工作留给stm32,Hi3518从上位机接收命令传递给stm32,stm32获取九轴陀螺仪数据给Hi3518

由于I2C对时序的严格要求,而linux是一个多任务操作系统,在应用层无法满足严格的时序要求,只得在驱动层做。

1.将寄存器做相应的宏定义,包含相应的头文件,定义用户的数据结构体

#include

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

typedef struct I2C_DATA_S

{

unsigned chardev_addr;

unsigned intreg_addr;

unsigned intaddr_byte_num;

unsigned intdata;

unsigned intdata_byte_num;

}I2C_DATA_S;

#define GPIO_0_BASE 0x20180000

#define SCL                 (1 <<6)    /* GPIO 4_6 */

#define SDA                 (1 <<7)    /* GPIO 4_7 */

#define GPIO_I2C_SCL_REG    IO_ADDRESS(GPIO_0_BASE + 0x400)

#define GPIO_I2C_SDA_REG    IO_ADDRESS(GPIO_0_BASE + 0x200

#define GPIO_I2C_SCLSDA_REG   IO_ADDRESS(GPIO_0_BASE + 0x600)

2.按照linux miscdevice驱动开发的一般流程:定义相应的数据结构

static struct file_operationsgpioi2c_fops= {

.owner=THIS_MODULE,

.unlocked_ioctl=gpioi2c_ioctl,

.open=gpioi2c_open,

.release=gpioi2c_close

};

static struct miscdevice gpioi2c_dev= {

.minor=MISC_DYNAMIC_MINOR, //动态分配次设备号

.name="gpioi2c_ex",       //驱动名字

.fops= &gpioi2c_fops,                  //文件操作

};

3.编写模块的入口函数(init)和出口函数(exit)

staticint__init gpio_i2c_init(void)

{

intret;

//把内存映射到CPU空间,可直接操作GPIO寄存器组,返回的线性地址指向,由于Hi3518e的GPIO寄存器占用64K字节,故申请映射时使用0x10000作为参数

 reg_gpio0_base_va = ioremap_nocache((unsignedlong)GPIO_0_BASE, (unsignedlong)0x10000); ret = misc_register(&gpioi2c_dev);//注册设备

 if(0 != ret)

     return-1;

i2c_set(SCL | SDA);

return0;

}

staticvoid__exit gpio_i2c_exit(void)

{

iounmap((void*)reg_gpio0_base_va);//取消映射

misc_deregister(&gpioi2c_dev);  //将注册时申请的资源释放

}

module_init(gpio_i2c_init);//声明入口函数,模块被insmod时会自动调用gpio_i2c_init

module_exit(gpio_i2c_exit); //声明出口函数,模块被rmmod时会自动调用gpio_i2c_exit

4.编写unlock_ioctl,open,close函数

longgpioi2c_ioctl(structfile *file, unsignedintcmd, unsignedlongarg)

{

I2C_DATA_S __user *argp = (I2C_DATA_S __user*)arg;

unsigned chardevice_addr;

unsigned shortreg_addr;

unsigned shortreg_val;

unsigned intaddr_len;

unsigned intdata_len;

switch(cmd)

{

caseGPIO_I2C_READ:

device_addr =  argp->dev_addr;

reg_addr    =  argp->reg_addr;

addr_len    =  argp->addr_byte_num;

data_len    =  argp->data_byte_num;

reg_val = gpio_i2c_read_ex(device_addr, reg_addr, addr_len, data_len);

argp->data = reg_val ;

break;

caseGPIO_I2C_WRITE:

device_addr =  argp->dev_addr;

reg_addr    =  argp->reg_addr;

addr_len    =  argp->addr_byte_num;

data_len    =  argp->data_byte_num;

reg_val     =  argp->data;

gpio_i2c_write_ex(device_addr, reg_addr, addr_len, reg_val, data_len);

break;

default:

return-1;

}

return0;

}

intgpioi2c_open(structinode * inode,structfile * file)

{

return0;

}

intgpioi2c_close(structinode * inode,structfile * file)

{

return0;

}

至此,大概框架就搭好了,接下来就是按照I2C的时序要求编写相应的代码,在此就不全贴了,说说遇到的问题吧。

首先遇到的第一个问题就是驱动编写完成加载后,SCL和SDA的引脚均为低电平,而我在模块入口函数处将SDA和SCL均拉高了,通过在其中加printk语句,确认函数确实执行到了,但是就是没有起作用,对照datasheet检查了各种寄存器的地址和配置都没有问题,后来通过示波器发现两个引脚的电平都为0,但是不是很平稳,更像是一个未被控制的引脚,类似于三态的感觉,于是怀疑方向控制寄存器配置不对,于是在gpio_i2c_init函数中将这个寄存器的值打印出来发现是对的。排除了其他的寄存器配置后,只剩下一个管脚复用寄存器,由于复位后这个寄存器的值为0x00,即为GPIO模式,就没有对其进行配置。如下图所示:

0818b9ca8b590ca3270a3433284dd417.png

结果将这个寄存器的值打印出来发现是0x01,不是默认的GPIO模式,于是在入口函数中重新配置该寄存器,I2C时序正常。 hi3518e这边的工作算是完成了,接下来就是完成stm32的I2C从机配置和两者之间的通信协议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值