QNX---IMX6UL I2C 驱动分析

                                       QNX---IMX6UL I2C 驱动分析 

 

 I2C 是经常用到的一种总线协议,它只占用两个IO口资源,分别是SCL时钟信号线与SDA数据线,两根线就能将连接与总线上的设备实现数据通信,由于它的简便的构造设计,于是成为一种较为常用的通信方式。在QNX系统里,也提供了I2C驱动,它为我们提供了驱动模板,其驱动目录结构如下:

QNX---IMX6UL I2C 驱动分析_第1张图片

QNX I2C驱动提供了基本的硬件操作接口,其接口名称为i2c_master_funcs_t,这些接口是驱动里要求实现,包括读写、设置地址、总线速度、版本信息等等。每个接口对应于上面的一个文件.c,这样便于模块化。

typedef struct {
    /* size of this structure */
    size_t size;

    /*
     * Return version information
     * Returns:
     * 0    success
     * -1   failure
     */
    int (*version_info)(i2c_libversion_t *version);

    /*
     * Initialize master interface.
     * Returns a handle that is passed to all other functions.
     * Returns:
     * !NULL    success
     * NULL     failure
     */
    void *(*init)(int argc, char *argv[]);

    /*
     * Clean up driver.
     * Frees memory associated with "hdl".
     */
    void (*fini)(void *hdl);

    /*
     * Master send.
     * Parameters:
     * (in)     hdl         Handle returned from init()
     * (in)     buf         Buffer of data to send
     * (in)     len         Length in bytes of buf
     * (in)     stop        If !0, set stop condition when send completes
     * Returns:
     * bitmask of status bits
     */
    i2c_status_t (*send)(void *hdl, void *buf, unsigned int len,
                         unsigned int stop);
    /*
     * Master receive.
     * Parameters:
     * (in)     hdl         Handle returned from init()
     * (in)     buf         Buffer for received data
     * (in)     len         Length in bytes of buf
     * (in)     stop        If !0, set stop condition when recv completes
     * Returns:
     * bitmask of status bits
     */
    i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len,
                         unsigned int stop);

    /*
     * Force the master to free the bus.
     * Returns when the stop condition has been sent.
     * Returns:
     * 0    success
     * -1   failure
     */
    int (*abort)(void *hdl, int rcvid);

    /*
     * Specify the target slave address.
     * Returns:
     * 0    success
     * -1   failure
     */
    int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);

    /*
     * Specify the bus speed.
     * If an invalid bus speed is requested, this function should return
     * failure and leave the bus speed unchanged.
     * Parameters:
     * (in)     speed       Bus speed. Units are implementation-defined.
     * (out)    ospeed      Actual bus speed (if NULL, this is ignored)
     * Returns:
     * 0    success
     * -1   failure
     */
    int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);

    /*
     * Request info about the driver.
     * Returns:
     * 0    success
     * -1   failure
     */
    int (*driver_info)(void *hdl, i2c_driver_info_t *info);

    /*
     * Handle a driver-specific devctl().
     * Parameters:
     * (in)     cmd         Device command
     * (i/o)    msg         Message buffer
     * (in)     msglen      Length of message buffer in bytes
     * (out)    nbytes      Bytes to return (<= msglen)
     * (out)    info        Extra status information returned by devctl
     * Returns:
     * EOK      success
     * errno    failure
     */
    int (*ctl)(void *hdl, int cmd, void *msg, int msglen,
               int *nbytes, int *info);

    /*
	 * Reset i2c bus module.
	 * Returns:
	 * EOK    success
	 * EIO   failure
	 */
	 int (*bus_reset)(void *hdl);
} i2c_master_funcs_t;

下面针对IMX6UL 上的I2C驱动每一个模块进行分析。

1、version.c 提供版本信息。就是对i2c_libversion_t 结构体进行填充,这个没有啥好分析的,无非是提供一个版本信息而已。

int
mx35_version_info(i2c_libversion_t *version)
{
    version->major = I2CLIB_VERSION_MAJOR;
    version->minor = I2CLIB_VERSION_MINOR;
    version->revision = I2CLIB_REVISION;
    return 0;
}

 2、bus_speed.c ,设置总线速率。在mx35_set_bus_speed 传参中有一个很重要的参数为hdl,它是一个设备句柄,其与I2C驱动相关的参数通过这个参数传入进来。

int
mx35_set_bus_speed(void *hdl, unsigned int speed, unsigned int *ospeed)
{
    mx35_dev_t      *dev = hdl;
	unsigned int		i2c_div, i2c_freq_val;

    if (speed > 400000) {
        errno = EINVAL;
        return -1;
    }
	if(speed != dev->speed){ //判断当前速度与设置速度是否一致,一致就不需要设置 了
		i2c_div = dev->input_clk / speed;
		i2c_freq_val = find_best_ic( i2c_div );//获取时钟参数值
		out16( dev->regbase + MX35_I2C_FRQREG_OFF, i2c_freq_val );//设置I2C时钟速率
		dev->speed = speed; //保存当前设置的速率
		dev->i2c_freq_val = i2c_freq_val;
	}
	
    if (ospeed)
        *ospeed = dev->input_clk / I2C_Divs[ dev->i2c_freq_val ];

    return 0;
}

3、slave_addr.c 设置设备地址

int
mx35_set_slave_addr(void *hdl, unsigned int addr, i2c_addrfmt_t fmt)
{
    mx35_dev_t      *dev = hdl;

	if ((fmt != I2C_ADDRFMT_7BIT) && (fmt != I2C_ADDRFMT_10BIT)) {  //检测设备地址是否为这两种格式。
		errno = EINVAL;
		return -1;
	}

	dev->slave_addr = addr;
	dev->slave_addr_fmt = fmt;

    return 0;
}

4、send.c I2C发送数据

i2c_status_t
mx35_send(void *hdl, void *buf, unsigned int len, unsigned int stop)
{
    mx35_dev_t      *dev = hdl;
    i2c_status_t    status;

    if (len <= 0)
        return I2C_STATUS_DONE;

	if(mx35_wait_bus_not_busy(dev))//检测当前I2C是否处于忙状态
		return I2C_STATUS_ERROR;

	if (dev->slave_addr_fmt == I2C_ADDRFMT_7BIT)//发送器件地址
		status = mx35_sendaddr7(dev, dev->slave_addr, MX35_I2C_ADDR_WR, dev->restart);
	else
		status = mx35_sendaddr10(dev, dev->slave_addr, MX35_I2C_ADDR_WR, dev->restart);

	if (status)
		return status;

	while (len > 0) {
		status = mx35_sendbyte(dev, *(uint8_t *)buf);//发送I2C数据
		if (status)
			return status;
		++buf; --len;
	}
	if (stop)
		out16(dev->regbase + MX35_I2C_CTRREG_OFF, CTRREG_IEN);//是否停止发送

	dev->restart = !stop;
	return I2C_STATUS_DONE;
}

5、recv.c 接收处理 和发送流程一样。

i2c_status_t
mx35_recv(void *hdl, void *buf, unsigned int len, unsigned int stop){
    mx35_dev_t  *dev = hdl;
	i2c_status_t    status;

    if (len <= 0) 
        return I2C_STATUS_DONE;
	
	if(mx35_wait_bus_not_busy(dev))
		return I2C_STATUS_ERROR;

    /* send slave address */
	if (dev->slave_addr_fmt == I2C_ADDRFMT_7BIT)
		status = mx35_sendaddr7(dev, dev->slave_addr, MX35_I2C_ADDR_RD, dev->restart);
	else 
		status = mx35_sendaddr10(dev, dev->slave_addr, MX35_I2C_ADDR_RD, dev->restart);

	if (status)
		return status;

	if (len > 1)
		out16(dev->regbase + MX35_I2C_CTRREG_OFF, CTRREG_IEN | CTRREG_IIEN | CTRREG_MSTA);
	else
		out16(dev->regbase + MX35_I2C_CTRREG_OFF, CTRREG_IEN | CTRREG_IIEN | CTRREG_MSTA | CTRREG_TXAK);

	in16(dev->regbase + MX35_I2C_DATREG_OFF);

	while (len > 0){
		status = mx35_recvbyte(dev, buf, (len == 2), (len == 1) && stop);
		if (status)
			return status;
		++buf; --len;
	}

	if (!stop)
		mx35_wait_status(dev);

	dev->restart = !stop;
	return I2C_STATUS_DONE;
}

6、init.c 初始化相关参数,主要配置I2C 中断 基地址 时钟等参数

void *
mx35_init(int argc, char *argv[])
{
    mx35_dev_t      *dev;
  
    if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
        perror("ThreadCtl");
        return NULL;
    }

    dev = malloc(sizeof(mx35_dev_t));
    if (!dev)
        return NULL;


    if(strstr(argv[argc-1], "controller") !=NULL){
	sscanf(argv[argc-1],"controller=%u",&dev->unit);
	argc--;
    }
    else
    	dev->unit= -1;

    if (-1 == mx35_options(dev, argc, argv)) //根据命令 配置I2C相关参数包括基地址 中断号
        goto fail;
	
    if(dev->physbase == 0 || dev->intr ==0){//如果没有基地址和中断号 则驱动加载失败
	mx35_i2c_slogf(dev, _SLOG_ERROR, "i2c-mx35 error : Invalid  I2C controller physics based address or IRQ value.");
	mx35_i2c_slogf(dev, _SLOG_ERROR, "i2c-mx35 error : Please check the command line or Hwinfo default setting.");
	goto fail;
     }

    dev->regbase = mmap_device_io(dev->reglen, dev->physbase);//地址映射
    if (dev->regbase == (uintptr_t)MAP_FAILED) {
        perror("mmap_device_io");
        goto fail;
    }

    /* 启动I2C模块 */
	out16(dev->regbase + MX35_I2C_CTRREG_OFF, 0);
	out16(dev->regbase + MX35_I2C_STSREG_OFF, 0);
	delay(1);
	out16(dev->regbase + MX35_I2C_CTRREG_OFF, CTRREG_IEN);

    /* 初始化中断处理 */
    SIGEV_INTR_INIT(&dev->intrevent);
    dev->iid = InterruptAttachEvent(dev->intr, &dev->intrevent, 
            _NTO_INTR_FLAGS_TRK_MSK);
    if (dev->iid == -1) {
        perror("InterruptAttachEvent");
        goto fail;
    }

    /* 设置默认I2C速率*/
	mx35_set_bus_speed(dev, 100000, NULL);

#if 0
    /* Set Own Address */
	out16(dev->regbase + MX35_I2C_ADRREG_OFF , (dev->own_addr << 1));
#endif

    /* enable interrupts */
	out16(dev->regbase + MX35_I2C_CTRREG_OFF, in16(dev->regbase + MX35_I2C_CTRREG_OFF) | CTRREG_IIEN );
    return dev;

fail:
    free(dev);
    return NULL;
}

 总结:

I2C  驱动 QNX只提供与硬件相关的操作,具体实现过程并没有,那QNX是怎么运行的呢。实质和下面流程一样。在初始化里设置一个中断事件,这个事件将发送消息告诉主循环接收和发送数据。

 

#include     
 i2c_master_funcs_t  masterf;    
i2c_libversion_t    version;    
i2c_status_t        status;    void *hdl;     
i2c_master_getfuncs(&masterf, sizeof(masterf));//初始化硬件接口     masterf.version_info(&version);    
if ((version.major != I2CLIB_VERSION_MAJOR) ||        (version.minor > I2CLIB_VERSION_MINOR))    {        /* error */        ...    }   
  hdl = masterf.init(...); //调用初始化函数    
 masterf.set_bus_speed(hdl, ...); //设置总线速度 
while(1)
{ 
  InterruptWait (NULL, NULL);
masterf.set_slave_addr(hdl, ...); //设置从地址     
status = masterf.send(hdl, ...);//发送数据    
if (status != I2C_STATUS_DONE) {        /* error */        
if (!(status & I2C_STATUS_DONE))         
   masterf.abort(hdl);    }    
 status = masterf.recv(hdl, ...);//接收数据    
if (status != I2C_STATUS_DONE) 
{        /* error */        ...    }   
}
  masterf.fini(hdl);//完成I2C传输后,清理驱动程序并释放与给定句柄关联的所有内存
  InterruptUnmask(INTNUM, id);

}

 

 

 

你可能感兴趣的:(QNX系统与驱动)